diff --git a/dev-packages/node-integration-tests/suites/tracing/mongoose-tracing-channel/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/mongoose-tracing-channel/instrument.mjs index 22bf57f14364..46a27dd03b74 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mongoose-tracing-channel/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/mongoose-tracing-channel/instrument.mjs @@ -2,7 +2,6 @@ import * as Sentry from '@sentry/node'; import { loggingTransport } from '@sentry-internal/node-integration-tests'; Sentry.init({ - traceLifecycle: process.env.STREAMED === 'true' ? 'stream' : 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', tracesSampleRate: 1.0, diff --git a/dev-packages/node-integration-tests/suites/tracing/mongoose-tracing-channel/test.ts b/dev-packages/node-integration-tests/suites/tracing/mongoose-tracing-channel/test.ts index 2a0cd427a3bb..5795d880503b 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mongoose-tracing-channel/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/mongoose-tracing-channel/test.ts @@ -1,4 +1,17 @@ -import type { SerializedStreamedSpanContainer } from '@sentry/core'; +import { + DB_COLLECTION_NAME, + DB_NAMESPACE, + DB_OPERATION_BATCH_SIZE, + DB_OPERATION_NAME, + DB_QUERY_TEXT, + DB_SYSTEM_NAME, + SENTRY_OP, + SENTRY_ORIGIN, + SENTRY_TRACE_LIFECYCLE, + SERVER_ADDRESS, + SERVER_PORT, +} from '@sentry/conventions/attributes'; +import { DB } from '@sentry/conventions/op'; import { MongoMemoryServer } from 'mongodb-memory-server-global'; import { afterAll, beforeAll, describe, expect } from 'vitest'; import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; @@ -24,83 +37,50 @@ describe('Mongoose tracing channel Test', () => { cleanupChildProcesses(); }); - const expectedSpan = (operation: string, extraData: Record = {}) => - expect.objectContaining({ - data: expect.objectContaining({ - 'db.system.name': 'mongodb', - 'db.namespace': 'test', - 'db.collection.name': 'blogposts', - 'db.operation.name': operation, - 'server.address': expect.any(String), - 'server.port': expect.any(Number), - ...extraData, - }), - description: `mongoose.blogposts.${operation}`, - op: 'db', - origin: 'auto.db.mongoose.diagnostic_channel', - }); - const expectedStreamedSpan = (operation: string, extraAttributes: Record = {}) => expect.objectContaining({ name: `${operation} blogposts`, is_segment: false, parent_span_id: expect.stringMatching(/^[\da-f]{16}$/), attributes: expect.objectContaining({ - 'db.collection.name': { type: 'string', value: 'blogposts' }, - 'db.namespace': { type: 'string', value: 'test' }, - 'db.operation.name': { type: 'string', value: operation }, - 'db.system.name': { type: 'string', value: 'mongodb' }, - 'sentry.op': { type: 'string', value: 'db' }, - 'sentry.origin': { type: 'string', value: 'auto.db.mongoose.diagnostic_channel' }, - 'sentry.trace_lifecycle': { type: 'string', value: 'stream' }, + [DB_COLLECTION_NAME]: { type: 'string', value: 'blogposts' }, + [DB_NAMESPACE]: { type: 'string', value: 'test' }, + [DB_OPERATION_NAME]: { type: 'string', value: operation }, + [DB_SYSTEM_NAME]: { type: 'string', value: 'mongodb' }, + [SENTRY_OP]: { type: 'string', value: DB }, + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.db.mongoose.diagnostic_channel' }, + [SENTRY_TRACE_LIFECYCLE]: { type: 'string', value: 'stream' }, + [SERVER_ADDRESS]: { type: 'string', value: expect.any(String) }, + [SERVER_PORT]: { type: 'integer', value: expect.any(Number) }, ...extraAttributes, }), }); - const EXPECTED_TRANSACTION = { - transaction: 'Test Transaction', - spans: expect.arrayContaining([ - expectedSpan('save'), - // filter values are redacted out of `db.query.text` - expectedSpan('findOne', { 'db.query.text': '{"title":"?"}' }), - expectedSpan('aggregate', { 'db.query.text': '[{"$match":{"title":"?"}}]' }), - expectedSpan('insertMany', { 'db.operation.batch.size': 2 }), - expectedSpan('bulkWrite', { 'db.operation.batch.size': 2 }), - // a cursor iteration emits a span per `.next()` via the `mongoose:cursor:next` channel - expectedSpan('find'), - ]), - }; - createEsmAndCjsTests( __dirname, 'scenario.mjs', 'instrument.mjs', (createTestRunner, test) => { test('subscribes to mongoose >= 9.7 diagnostics channels with stable semconv attributes', async () => { - await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); - }); - - test('names channel spans after the operation and collection with span streaming enabled', async () => { await createTestRunner() - .withEnv({ STREAMED: 'true' }) .expect({ - span: (container: SerializedStreamedSpanContainer) => { + span: container => { expect(container.items.find(item => item.is_segment)?.name).toBe('Test Transaction'); expect(container.items).toContainEqual(expectedStreamedSpan('save')); expect(container.items).toContainEqual( - expectedStreamedSpan('findOne', { 'db.query.text': { type: 'string', value: '{"title":"?"}' } }), + expectedStreamedSpan('findOne', { [DB_QUERY_TEXT]: { type: 'string', value: '{"title":"?"}' } }), ); expect(container.items).toContainEqual( expectedStreamedSpan('aggregate', { - 'db.query.text': { type: 'string', value: '[{"$match":{"title":"?"}}]' }, + [DB_QUERY_TEXT]: { type: 'string', value: '[{"$match":{"title":"?"}}]' }, }), ); expect(container.items).toContainEqual( - expectedStreamedSpan('insertMany', { 'db.operation.batch.size': { type: 'integer', value: 2 } }), + expectedStreamedSpan('insertMany', { [DB_OPERATION_BATCH_SIZE]: { type: 'integer', value: 2 } }), ); expect(container.items).toContainEqual( - expectedStreamedSpan('bulkWrite', { 'db.operation.batch.size': { type: 'integer', value: 2 } }), + expectedStreamedSpan('bulkWrite', { [DB_OPERATION_BATCH_SIZE]: { type: 'integer', value: 2 } }), ); expect(container.items).toContainEqual(expectedStreamedSpan('find')); }, @@ -112,12 +92,14 @@ describe('Mongoose tracing channel Test', () => { test('does not double-instrument: the legacy IITM mongoose patcher does not fire on 9.7', async () => { await createTestRunner() .expect({ - transaction: event => { - const spans = event.spans || []; + span: container => { + const spans = container.items; // The monkey-patch path (origin `auto.db.mongoose`) must be inactive on 9.7+. - expect(spans.find(span => span.origin === 'auto.db.mongoose')).toBeUndefined(); + expect(spans.find(span => span.attributes[SENTRY_ORIGIN]?.value === 'auto.db.mongoose')).toBeUndefined(); // ...while the diagnostics-channel path is active. - expect(spans.find(span => span.origin === 'auto.db.mongoose.diagnostic_channel')).toBeDefined(); + expect( + spans.find(span => span.attributes[SENTRY_ORIGIN]?.value === 'auto.db.mongoose.diagnostic_channel'), + ).toBeDefined(); }, }) .start() @@ -127,10 +109,10 @@ describe('Mongoose tracing channel Test', () => { test('never leaks raw filter values into db.query.text', async () => { await createTestRunner() .expect({ - transaction: event => { - const spans = event.spans || []; + span: container => { + const spans = container.items; for (const span of spans) { - const queryText = span.data?.['db.query.text']; + const queryText = span.attributes[DB_QUERY_TEXT]?.value; if (typeof queryText === 'string') { expect(queryText).not.toContain('Test'); } @@ -144,14 +126,20 @@ describe('Mongoose tracing channel Test', () => { test('nests the mongodb driver span under the mongoose channel span', async () => { await createTestRunner() .expect({ - transaction: event => { - const spans = event.spans || []; - const mongooseSave = spans.find(span => span.description === 'mongoose.blogposts.save'); + span: container => { + const spans = container.items; + const mongooseSave = spans.find( + span => + span.name === 'save blogposts' && + span.attributes[SENTRY_ORIGIN]?.value === 'auto.db.mongoose.diagnostic_channel', + ); expect(mongooseSave).toBeDefined(); // the underlying mongodb driver span must parent to the mongoose channel span, // proving the channel span is the active async context for the traced operation const driverChild = spans.find( - span => span.parent_span_id === mongooseSave?.span_id && span.origin === driverOrigin, + span => + span.parent_span_id === mongooseSave?.span_id && + span.attributes[SENTRY_ORIGIN]?.value === driverOrigin, ); expect(driverChild).toBeDefined(); }, @@ -163,14 +151,18 @@ describe('Mongoose tracing channel Test', () => { test('omits db.query.text for the empty-filter cursor and does not treat the cursor batchSize as a batch', async () => { await createTestRunner() .expect({ - transaction: event => { - const spans = event.spans || []; + span: container => { + const spans = container.items; // the `.find().cursor()` iteration runs with no filter, so there is no query text to emit - const cursorFind = spans.find(span => span.description === 'mongoose.blogposts.find'); + const cursorFind = spans.find( + span => + span.name === 'find blogposts' && + span.attributes[SENTRY_ORIGIN]?.value === 'auto.db.mongoose.diagnostic_channel', + ); expect(cursorFind).toBeDefined(); - expect(cursorFind?.data?.['db.query.text']).toBeUndefined(); + expect(cursorFind?.attributes[DB_QUERY_TEXT]).toBeUndefined(); // a cursor's `batchSize` is a fetch-tuning option, not a batch-operation size - expect(cursorFind?.data?.['db.operation.batch.size']).toBeUndefined(); + expect(cursorFind?.attributes[DB_OPERATION_BATCH_SIZE]).toBeUndefined(); }, }) .start() @@ -180,8 +172,6 @@ describe('Mongoose tracing channel Test', () => { { additionalDependencies: { mongoose: '^9.7' } }, ); - // A failed operation must flag the mongoose channel span as errored. mongodb error statuses map to - // `internal_error` through the OTel pipeline (same as the postgres/redis suites). createEsmAndCjsTests( __dirname, 'scenario-error.mjs', @@ -190,23 +180,12 @@ describe('Mongoose tracing channel Test', () => { test('flags the mongoose channel span as errored when the operation fails', async () => { await createTestRunner() .expect({ - transaction: event => { - const spans = event.spans || []; - const aggregateSpan = spans.find(span => span.description === 'mongoose.blogposts.aggregate'); - expect(aggregateSpan).toBeDefined(); - expect(aggregateSpan?.status).toBe('internal_error'); - }, - }) - .start() - .completed(); - }); - - test('flags the streamed mongoose channel span as errored when the operation fails', async () => { - await createTestRunner() - .withEnv({ STREAMED: 'true' }) - .expect({ - span: (container: SerializedStreamedSpanContainer) => { - const aggregateSpan = container.items.find(item => item.name === 'aggregate blogposts'); + span: container => { + const aggregateSpan = container.items.find( + item => + item.name === 'aggregate blogposts' && + item.attributes[SENTRY_ORIGIN]?.value === 'auto.db.mongoose.diagnostic_channel', + ); expect(aggregateSpan).toBeDefined(); expect(aggregateSpan?.status).toBe('error'); }, diff --git a/dev-packages/node-integration-tests/suites/tracing/mongoose-v5/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/mongoose-v5/instrument.mjs index 22bf57f14364..46a27dd03b74 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mongoose-v5/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/mongoose-v5/instrument.mjs @@ -2,7 +2,6 @@ import * as Sentry from '@sentry/node'; import { loggingTransport } from '@sentry-internal/node-integration-tests'; Sentry.init({ - traceLifecycle: process.env.STREAMED === 'true' ? 'stream' : 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', tracesSampleRate: 1.0, diff --git a/dev-packages/node-integration-tests/suites/tracing/mongoose-v5/test.ts b/dev-packages/node-integration-tests/suites/tracing/mongoose-v5/test.ts index 987f278f8d7e..4071fdbdf533 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mongoose-v5/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/mongoose-v5/test.ts @@ -1,4 +1,12 @@ -import type { SerializedStreamedSpanContainer } from '@sentry/core'; +import { + DB_COLLECTION_NAME, + DB_OPERATION_NAME, + DB_SYSTEM_NAME, + SENTRY_OP, + SENTRY_ORIGIN, + SENTRY_TRACE_LIFECYCLE, +} from '@sentry/conventions/attributes'; +import { DB } from '@sentry/conventions/op'; import { MongoMemoryServer } from 'mongodb-memory-server-global'; import { afterAll, beforeAll, describe, expect } from 'vitest'; import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; @@ -22,41 +30,18 @@ describe('Mongoose v5 Test', () => { cleanupChildProcesses(); }); - const expectedSpan = (operation: string) => - expect.objectContaining({ - data: expect.objectContaining({ - 'db.collection.name': 'blogposts', - 'db.operation.name': operation, - 'db.system.name': 'mongodb', - }), - description: `mongoose.BlogPost.${operation}`, - op: 'db', - origin, - }); - - const EXPECTED_TRANSACTION = { - transaction: 'Test Transaction', - spans: expect.arrayContaining([ - expectedSpan('save'), - expectedSpan('findOne'), - expectedSpan('aggregate'), - expectedSpan('insertMany'), - expectedSpan('bulkWrite'), - ]), - }; - const expectedStreamedSpan = (operation: string) => expect.objectContaining({ name: `${operation} blogposts`, is_segment: false, parent_span_id: expect.stringMatching(/^[\da-f]{16}$/), attributes: expect.objectContaining({ - 'db.collection.name': { type: 'string', value: 'blogposts' }, - 'db.operation.name': { type: 'string', value: operation }, - 'db.system.name': { type: 'string', value: 'mongodb' }, - 'sentry.op': { type: 'string', value: 'db' }, - 'sentry.origin': { type: 'string', value: origin }, - 'sentry.trace_lifecycle': { type: 'string', value: 'stream' }, + [DB_COLLECTION_NAME]: { type: 'string', value: 'blogposts' }, + [DB_OPERATION_NAME]: { type: 'string', value: operation }, + [DB_SYSTEM_NAME]: { type: 'string', value: 'mongodb' }, + [SENTRY_OP]: { type: 'string', value: DB }, + [SENTRY_ORIGIN]: { type: 'string', value: origin }, + [SENTRY_TRACE_LIFECYCLE]: { type: 'string', value: 'stream' }, }), }); @@ -68,14 +53,9 @@ describe('Mongoose v5 Test', () => { 'instrument.mjs', (createTestRunner, test) => { test('auto-instruments `mongoose` v5.', async () => { - await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); - }); - - test('auto-instruments `mongoose` v5 with span streaming enabled.', async () => { await createTestRunner() - .withEnv({ STREAMED: 'true' }) .expect({ - span: (container: SerializedStreamedSpanContainer) => { + span: container => { expect(container.items.find(item => item.is_segment)?.name).toBe('Test Transaction'); for (const operation of STREAMED_OPERATIONS) { diff --git a/dev-packages/node-integration-tests/suites/tracing/mongoose-v7/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/mongoose-v7/instrument.mjs index 22bf57f14364..46a27dd03b74 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mongoose-v7/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/mongoose-v7/instrument.mjs @@ -2,7 +2,6 @@ import * as Sentry from '@sentry/node'; import { loggingTransport } from '@sentry-internal/node-integration-tests'; Sentry.init({ - traceLifecycle: process.env.STREAMED === 'true' ? 'stream' : 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', tracesSampleRate: 1.0, diff --git a/dev-packages/node-integration-tests/suites/tracing/mongoose-v7/test.ts b/dev-packages/node-integration-tests/suites/tracing/mongoose-v7/test.ts index 97c253d412ac..42ff84c6dd25 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mongoose-v7/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/mongoose-v7/test.ts @@ -1,4 +1,12 @@ -import type { SerializedStreamedSpanContainer } from '@sentry/core'; +import { + DB_COLLECTION_NAME, + DB_OPERATION_NAME, + DB_SYSTEM_NAME, + SENTRY_OP, + SENTRY_ORIGIN, + SENTRY_TRACE_LIFECYCLE, +} from '@sentry/conventions/attributes'; +import { DB } from '@sentry/conventions/op'; import { MongoMemoryServer } from 'mongodb-memory-server-global'; import { afterAll, beforeAll, describe, expect } from 'vitest'; import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; @@ -20,41 +28,18 @@ describe('Mongoose v7 Test', () => { cleanupChildProcesses(); }); - const expectedSpan = (operation: string) => - expect.objectContaining({ - data: expect.objectContaining({ - 'db.collection.name': 'blogposts', - 'db.operation.name': operation, - 'db.system.name': 'mongodb', - }), - description: `mongoose.BlogPost.${operation}`, - op: 'db', - origin, - }); - - const EXPECTED_TRANSACTION = { - transaction: 'Test Transaction', - spans: expect.arrayContaining([ - expectedSpan('save'), - expectedSpan('findOne'), - expectedSpan('aggregate'), - expectedSpan('insertMany'), - expectedSpan('bulkWrite'), - ]), - }; - const expectedStreamedSpan = (operation: string) => expect.objectContaining({ name: `${operation} blogposts`, is_segment: false, parent_span_id: expect.stringMatching(/^[\da-f]{16}$/), attributes: expect.objectContaining({ - 'db.collection.name': { type: 'string', value: 'blogposts' }, - 'db.operation.name': { type: 'string', value: operation }, - 'db.system.name': { type: 'string', value: 'mongodb' }, - 'sentry.op': { type: 'string', value: 'db' }, - 'sentry.origin': { type: 'string', value: origin }, - 'sentry.trace_lifecycle': { type: 'string', value: 'stream' }, + [DB_COLLECTION_NAME]: { type: 'string', value: 'blogposts' }, + [DB_OPERATION_NAME]: { type: 'string', value: operation }, + [DB_SYSTEM_NAME]: { type: 'string', value: 'mongodb' }, + [SENTRY_OP]: { type: 'string', value: DB }, + [SENTRY_ORIGIN]: { type: 'string', value: origin }, + [SENTRY_TRACE_LIFECYCLE]: { type: 'string', value: 'stream' }, }), }); @@ -66,14 +51,9 @@ describe('Mongoose v7 Test', () => { 'instrument.mjs', (createTestRunner, test) => { test('auto-instruments `mongoose` v7.', async () => { - await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); - }); - - test('auto-instruments `mongoose` v7 with span streaming enabled.', async () => { await createTestRunner() - .withEnv({ STREAMED: 'true' }) .expect({ - span: (container: SerializedStreamedSpanContainer) => { + span: container => { expect(container.items.find(item => item.is_segment)?.name).toBe('Test Transaction'); for (const operation of STREAMED_OPERATIONS) { diff --git a/dev-packages/node-integration-tests/suites/tracing/mongoose-v8/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/mongoose-v8/instrument.mjs index 22bf57f14364..46a27dd03b74 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mongoose-v8/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/mongoose-v8/instrument.mjs @@ -2,7 +2,6 @@ import * as Sentry from '@sentry/node'; import { loggingTransport } from '@sentry-internal/node-integration-tests'; Sentry.init({ - traceLifecycle: process.env.STREAMED === 'true' ? 'stream' : 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', tracesSampleRate: 1.0, diff --git a/dev-packages/node-integration-tests/suites/tracing/mongoose-v8/test.ts b/dev-packages/node-integration-tests/suites/tracing/mongoose-v8/test.ts index 4109d26a70f4..cb3756e4e723 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mongoose-v8/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/mongoose-v8/test.ts @@ -1,4 +1,12 @@ -import type { SerializedStreamedSpanContainer } from '@sentry/core'; +import { + DB_COLLECTION_NAME, + DB_OPERATION_NAME, + DB_SYSTEM_NAME, + SENTRY_OP, + SENTRY_ORIGIN, + SENTRY_TRACE_LIFECYCLE, +} from '@sentry/conventions/attributes'; +import { DB } from '@sentry/conventions/op'; import { MongoMemoryServer } from 'mongodb-memory-server-global'; import { afterAll, beforeAll, describe, expect } from 'vitest'; import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; @@ -21,54 +29,18 @@ describe('Mongoose v8 Test', () => { cleanupChildProcesses(); }); - const EXPECTED_TRANSACTION = { - transaction: 'Test Transaction', - spans: expect.arrayContaining([ - expect.objectContaining({ - data: expect.objectContaining({ - 'db.collection.name': 'blogposts', - 'db.operation.name': 'save', - 'db.system.name': 'mongodb', - }), - description: 'mongoose.BlogPost.save', - op: 'db', - origin, - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'db.collection.name': 'blogposts', - 'db.operation.name': 'updateOne', - 'db.system.name': 'mongodb', - }), - description: 'mongoose.BlogPost.updateOne', - op: 'db', - origin, - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'db.collection.name': 'blogposts', - 'db.operation.name': 'deleteOne', - 'db.system.name': 'mongodb', - }), - description: 'mongoose.BlogPost.deleteOne', - op: 'db', - origin, - }), - ]), - }; - const expectedStreamedSpan = (operation: string) => expect.objectContaining({ name: `${operation} blogposts`, is_segment: false, parent_span_id: expect.stringMatching(/^[\da-f]{16}$/), attributes: expect.objectContaining({ - 'db.collection.name': { type: 'string', value: 'blogposts' }, - 'db.operation.name': { type: 'string', value: operation }, - 'db.system.name': { type: 'string', value: 'mongodb' }, - 'sentry.op': { type: 'string', value: 'db' }, - 'sentry.origin': { type: 'string', value: origin }, - 'sentry.trace_lifecycle': { type: 'string', value: 'stream' }, + [DB_COLLECTION_NAME]: { type: 'string', value: 'blogposts' }, + [DB_OPERATION_NAME]: { type: 'string', value: operation }, + [DB_SYSTEM_NAME]: { type: 'string', value: 'mongodb' }, + [SENTRY_OP]: { type: 'string', value: DB }, + [SENTRY_ORIGIN]: { type: 'string', value: origin }, + [SENTRY_TRACE_LIFECYCLE]: { type: 'string', value: 'stream' }, }), }); @@ -78,14 +50,9 @@ describe('Mongoose v8 Test', () => { 'instrument.mjs', (createTestRunner, test) => { test('auto-instruments `mongoose` v8 document methods.', async () => { - await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); - }); - - test('auto-instruments `mongoose` v8 document methods with span streaming enabled.', async () => { await createTestRunner() - .withEnv({ STREAMED: 'true' }) .expect({ - span: (container: SerializedStreamedSpanContainer) => { + span: container => { expect(container.items.find(item => item.is_segment)?.name).toBe('Test Transaction'); for (const operation of ['save', 'updateOne', 'deleteOne']) { diff --git a/dev-packages/node-integration-tests/suites/tracing/mongoose-v9/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/mongoose-v9/instrument.mjs index 22bf57f14364..46a27dd03b74 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mongoose-v9/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/mongoose-v9/instrument.mjs @@ -2,7 +2,6 @@ import * as Sentry from '@sentry/node'; import { loggingTransport } from '@sentry-internal/node-integration-tests'; Sentry.init({ - traceLifecycle: process.env.STREAMED === 'true' ? 'stream' : 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', tracesSampleRate: 1.0, diff --git a/dev-packages/node-integration-tests/suites/tracing/mongoose-v9/test.ts b/dev-packages/node-integration-tests/suites/tracing/mongoose-v9/test.ts index 55a80b82e9b9..abfba23f1f5c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mongoose-v9/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/mongoose-v9/test.ts @@ -1,4 +1,12 @@ -import type { SerializedStreamedSpanContainer } from '@sentry/core'; +import { + DB_COLLECTION_NAME, + DB_OPERATION_NAME, + DB_SYSTEM_NAME, + SENTRY_OP, + SENTRY_ORIGIN, + SENTRY_TRACE_LIFECYCLE, +} from '@sentry/conventions/attributes'; +import { DB } from '@sentry/conventions/op'; import { MongoMemoryServer } from 'mongodb-memory-server-global'; import { afterAll, beforeAll, describe, expect } from 'vitest'; import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; @@ -22,44 +30,18 @@ describe('Mongoose v9 Test', () => { cleanupChildProcesses(); }); - const expectedSpan = (operation: string) => - expect.objectContaining({ - data: expect.objectContaining({ - 'db.collection.name': 'blogposts', - 'db.operation.name': operation, - 'db.system.name': 'mongodb', - }), - description: `mongoose.BlogPost.${operation}`, - op: 'db', - origin, - }); - - const EXPECTED_TRANSACTION = { - transaction: 'Test Transaction', - spans: expect.arrayContaining([ - expectedSpan('save'), - expectedSpan('findOne'), - expectedSpan('aggregate'), - expectedSpan('insertMany'), - expectedSpan('bulkWrite'), - // Document instance methods are instrumented via Query.exec on v9 (no doc-method patch). - expectedSpan('updateOne'), - expectedSpan('deleteOne'), - ]), - }; - const expectedStreamedSpan = (operation: string) => expect.objectContaining({ name: `${operation} blogposts`, is_segment: false, parent_span_id: expect.stringMatching(/^[\da-f]{16}$/), attributes: expect.objectContaining({ - 'db.collection.name': { type: 'string', value: 'blogposts' }, - 'db.operation.name': { type: 'string', value: operation }, - 'db.system.name': { type: 'string', value: 'mongodb' }, - 'sentry.op': { type: 'string', value: 'db' }, - 'sentry.origin': { type: 'string', value: origin }, - 'sentry.trace_lifecycle': { type: 'string', value: 'stream' }, + [DB_COLLECTION_NAME]: { type: 'string', value: 'blogposts' }, + [DB_OPERATION_NAME]: { type: 'string', value: operation }, + [DB_SYSTEM_NAME]: { type: 'string', value: 'mongodb' }, + [SENTRY_OP]: { type: 'string', value: DB }, + [SENTRY_ORIGIN]: { type: 'string', value: origin }, + [SENTRY_TRACE_LIFECYCLE]: { type: 'string', value: 'stream' }, }), }); @@ -71,14 +53,9 @@ describe('Mongoose v9 Test', () => { 'instrument.mjs', (createTestRunner, test) => { test('auto-instruments `mongoose` v9.', async () => { - await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); - }); - - test('auto-instruments `mongoose` v9 with span streaming enabled.', async () => { await createTestRunner() - .withEnv({ STREAMED: 'true' }) .expect({ - span: (container: SerializedStreamedSpanContainer) => { + span: container => { expect(container.items.find(item => item.is_segment)?.name).toBe('Test Transaction'); for (const operation of STREAMED_OPERATIONS) { diff --git a/dev-packages/node-integration-tests/suites/tracing/mongoose/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/mongoose/instrument.mjs index 22bf57f14364..46a27dd03b74 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mongoose/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/mongoose/instrument.mjs @@ -2,7 +2,6 @@ import * as Sentry from '@sentry/node'; import { loggingTransport } from '@sentry-internal/node-integration-tests'; Sentry.init({ - traceLifecycle: process.env.STREAMED === 'true' ? 'stream' : 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', tracesSampleRate: 1.0, diff --git a/dev-packages/node-integration-tests/suites/tracing/mongoose/test.ts b/dev-packages/node-integration-tests/suites/tracing/mongoose/test.ts index cb3012ede40a..beeb52aa100d 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mongoose/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/mongoose/test.ts @@ -1,4 +1,13 @@ -import type { SerializedStreamedSpanContainer } from '@sentry/core'; +import { + DB_COLLECTION_NAME, + DB_NAMESPACE, + DB_OPERATION_NAME, + DB_SYSTEM_NAME, + SENTRY_OP, + SENTRY_ORIGIN, + SENTRY_TRACE_LIFECYCLE, +} from '@sentry/conventions/attributes'; +import { DB } from '@sentry/conventions/op'; import { MongoMemoryServer } from 'mongodb-memory-server-global'; import { afterAll, beforeAll, describe, expect } from 'vitest'; import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; @@ -20,90 +29,6 @@ describe('Mongoose experimental Test', () => { cleanupChildProcesses(); }); - const EXPECTED_TRANSACTION = { - transaction: 'Test Transaction', - spans: expect.arrayContaining([ - expect.objectContaining({ - data: expect.objectContaining({ - 'db.collection.name': 'blogposts', - 'db.namespace': 'test', - 'db.operation.name': 'save', - 'db.system.name': 'mongodb', - }), - description: 'mongoose.BlogPost.save', - op: 'db', - origin, - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'db.collection.name': 'blogposts', - 'db.namespace': 'test', - 'db.operation.name': 'findOne', - 'db.system.name': 'mongodb', - }), - description: 'mongoose.BlogPost.findOne', - op: 'db', - origin, - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'db.collection.name': 'blogposts', - 'db.namespace': 'test', - 'db.operation.name': 'aggregate', - 'db.system.name': 'mongodb', - }), - description: 'mongoose.BlogPost.aggregate', - op: 'db', - origin, - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'db.collection.name': 'blogposts', - 'db.namespace': 'test', - 'db.operation.name': 'insertMany', - 'db.system.name': 'mongodb', - }), - description: 'mongoose.BlogPost.insertMany', - op: 'db', - origin, - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'db.collection.name': 'blogposts', - 'db.namespace': 'test', - 'db.operation.name': 'bulkWrite', - 'db.system.name': 'mongodb', - }), - description: 'mongoose.BlogPost.bulkWrite', - op: 'db', - origin, - }), - // `remove` is patched only on mongoose 5/6. - expect.objectContaining({ - data: expect.objectContaining({ - 'db.collection.name': 'blogposts', - 'db.namespace': 'test', - 'db.operation.name': 'remove', - 'db.system.name': 'mongodb', - }), - description: 'mongoose.BlogPost.remove', - op: 'db', - origin, - }), - // A failing operation still produces a span, marked with an error status. - expect.objectContaining({ - data: expect.objectContaining({ - 'db.operation.name': 'save', - 'db.system.name': 'mongodb', - }), - description: 'mongoose.RequiredDoc.save', - op: 'db', - origin, - status: 'internal_error', - }), - ]), - }; - const expectedStreamedSpan = (operation: string, collection = 'blogposts', status = 'ok') => expect.objectContaining({ name: `${operation} ${collection}`, @@ -111,51 +36,49 @@ describe('Mongoose experimental Test', () => { parent_span_id: expect.stringMatching(/^[\da-f]{16}$/), status, attributes: expect.objectContaining({ - 'db.collection.name': { type: 'string', value: collection }, - 'db.namespace': { type: 'string', value: 'test' }, - 'db.operation.name': { type: 'string', value: operation }, - 'db.system.name': { type: 'string', value: 'mongodb' }, - 'sentry.op': { type: 'string', value: 'db' }, - 'sentry.origin': { type: 'string', value: origin }, - 'sentry.trace_lifecycle': { type: 'string', value: 'stream' }, + [DB_COLLECTION_NAME]: { type: 'string', value: collection }, + [DB_NAMESPACE]: { type: 'string', value: 'test' }, + [DB_OPERATION_NAME]: { type: 'string', value: operation }, + [DB_SYSTEM_NAME]: { type: 'string', value: 'mongodb' }, + [SENTRY_OP]: { type: 'string', value: DB }, + [SENTRY_ORIGIN]: { type: 'string', value: origin }, + [SENTRY_TRACE_LIFECYCLE]: { type: 'string', value: 'stream' }, }), }); createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createTestRunner, test) => { test('should auto-instrument `mongoose` package.', async () => { - await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); - }); - - test('nests the mongodb driver span under the mongoose span', async () => { await createTestRunner() .expect({ - transaction: event => { - const spans = event.spans || []; - const mongooseSave = spans.find(span => span.description === 'mongoose.BlogPost.save'); - expect(mongooseSave).toBeDefined(); - // the underlying mongodb driver span must be parented to the mongoose span - const driverChild = spans.find( - span => span.parent_span_id === mongooseSave?.span_id && span.origin === driverOrigin, - ); - expect(driverChild).toBeDefined(); + span: container => { + expect(container.items.find(item => item.is_segment)?.name).toBe('Test Transaction'); + + for (const operation of ['save', 'findOne', 'aggregate', 'insertMany', 'bulkWrite', 'remove']) { + expect(container.items).toContainEqual(expectedStreamedSpan(operation)); + } + + expect(container.items).toContainEqual(expectedStreamedSpan('save', 'requireddocs', 'error')); }, }) .start() .completed(); }); - test('should auto-instrument `mongoose` package with span streaming enabled.', async () => { + test('nests the mongodb driver span under the mongoose span', async () => { await createTestRunner() - .withEnv({ STREAMED: 'true' }) .expect({ - span: (container: SerializedStreamedSpanContainer) => { - expect(container.items.find(item => item.is_segment)?.name).toBe('Test Transaction'); - - for (const operation of ['save', 'findOne', 'aggregate', 'insertMany', 'bulkWrite', 'remove']) { - expect(container.items).toContainEqual(expectedStreamedSpan(operation)); - } - - expect(container.items).toContainEqual(expectedStreamedSpan('save', 'requireddocs', 'error')); + span: container => { + const spans = container.items; + const mongooseSave = spans.find( + span => span.name === 'save blogposts' && span.attributes[SENTRY_ORIGIN]?.value === origin, + ); + expect(mongooseSave).toBeDefined(); + // the underlying mongodb driver span must be parented to the mongoose span + const driverChild = spans.find( + span => + span.parent_span_id === mongooseSave?.span_id && span.attributes[SENTRY_ORIGIN]?.value === driverOrigin, + ); + expect(driverChild).toBeDefined(); }, }) .start() @@ -165,14 +88,17 @@ describe('Mongoose experimental Test', () => { test('parents a query to the span it was built in, not where it executes', async () => { await createTestRunner() .expect({ - transaction: event => { - const spans = event.spans || []; - const builder = spans.find(span => span.description === 'query-builder'); + span: container => { + const spans = container.items; + const builder = spans.find(span => span.name === 'query-builder'); expect(builder).toBeDefined(); // the query was built inside `query-builder` but awaited after it ended, so its exec // span must parent to `query-builder` rather than the active span at exec time const findExec = spans.find( - span => span.description === 'mongoose.BlogPost.findOne' && span.parent_span_id === builder?.span_id, + span => + span.name === 'findOne blogposts' && + span.attributes[SENTRY_ORIGIN]?.value === origin && + span.parent_span_id === builder?.span_id, ); expect(findExec).toBeDefined(); },