Skip to content

Commit

Permalink
feat(opentelemetry): Ignore propagation context for span creation
Browse files Browse the repository at this point in the history
  • Loading branch information
mydea committed Nov 29, 2024
1 parent bdbdcc8 commit fee464e
Show file tree
Hide file tree
Showing 36 changed files with 473 additions and 367 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ test('Sends exception to Sentry', async ({ baseURL }) => {
expect(errorEvent.transaction).toEqual('GET /test-exception/:id');

expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.any(String),
span_id: expect.any(String),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ test('Sends exception to Sentry', async ({ baseURL }) => {
expect(errorEvent.transaction).toEqual('GET /test-exception/:id');

expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.any(String),
span_id: expect.any(String),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});
});

Expand Down Expand Up @@ -112,7 +113,8 @@ test('Sends graphql exception to Sentry', async ({ baseURL }) => {
expect(errorEvent.transaction).toEqual('POST /graphql');

expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.any(String),
span_id: expect.any(String),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ test('Sends exception to Sentry', async ({ baseURL }) => {
expect(errorEvent.transaction).toEqual('GET /test-exception/:id');

expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.any(String),
span_id: expect.any(String),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ test('Sends exception to Sentry', async ({ baseURL }) => {
expect(errorEvent.transaction).toEqual('POST /graphql');

expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.any(String),
span_id: expect.any(String),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ test('Sends unexpected exception to Sentry if thrown in module with global filte
expect(errorEvent.transaction).toEqual('GET /example-module/unexpected-exception');

expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.any(String),
span_id: expect.any(String),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});
});

Expand Down Expand Up @@ -68,8 +69,9 @@ test('Sends unexpected exception to Sentry if thrown in module with local filter
expect(errorEvent.transaction).toEqual('GET /example-module-local-filter/unexpected-exception');

expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.any(String),
span_id: expect.any(String),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});
});

Expand Down Expand Up @@ -106,8 +108,9 @@ test('Sends unexpected exception to Sentry if thrown in module that was register
expect(errorEvent.transaction).toEqual('GET /example-module-registered-first/unexpected-exception');

expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.any(String),
span_id: expect.any(String),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ test('Sends unexpected exception to Sentry if thrown in module with global filte
expect(errorEvent.transaction).toEqual('GET /example-module/unexpected-exception');

expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.any(String),
span_id: expect.any(String),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});
});

Expand All @@ -52,8 +53,9 @@ test('Sends unexpected exception to Sentry if thrown in module with local filter
expect(errorEvent.transaction).toEqual('GET /example-module-local-filter/unexpected-exception');

expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.any(String),
span_id: expect.any(String),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});
});

Expand Down Expand Up @@ -82,8 +84,9 @@ test('Sends unexpected exception to Sentry if thrown in module that was register
expect(errorEvent.transaction).toEqual('GET /example-module-registered-first/unexpected-exception');

expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.any(String),
span_id: expect.any(String),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ test('Sends correct error event', async ({ baseURL }) => {
expect(errorEvent.transaction).toEqual('GET /test-exception/:id');

expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.any(String),
span_id: expect.any(String),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ test('Sends correct error event', async ({ baseURL }) => {
expect(errorEvent.transaction).toEqual('GET /test-exception/:id');

expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.any(String),
span_id: expect.any(String),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ test('Returns 400 from failed assert', async ({ baseURL }) => {
expect(errorEvent.transaction).toEqual('GET /test-assert/:condition');

expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.any(String),
span_id: expect.any(String),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ test('Sends correct error event', async ({ baseURL }) => {
expect(errorEvent.transaction).toEqual('GET /test-exception/:id');

expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.any(String),
span_id: expect.any(String),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,55 @@ test('Sends an API route transaction to OTLP', async ({ baseURL }) => {
const scopeSpans = json.resourceSpans?.[0]?.scopeSpans;
expect(scopeSpans).toBeDefined();

// Http server span & undici client spans are emitted
// Http server span & undici client spans are emitted,
// as well as the spans emitted via `Sentry.startSpan()`
// But our default node-fetch spans are not emitted
expect(scopeSpans.length).toEqual(2);
expect(scopeSpans.length).toEqual(3);

const httpScopes = scopeSpans?.filter(scopeSpan => scopeSpan.scope.name === '@opentelemetry/instrumentation-http');
const undiciScopes = scopeSpans?.filter(
scopeSpan => scopeSpan.scope.name === '@opentelemetry/instrumentation-undici',
);
const startSpanScopes = scopeSpans?.filter(scopeSpan => scopeSpan.scope.name === '@sentry/node');

expect(httpScopes.length).toBe(1);

expect(startSpanScopes.length).toBe(1);
expect(startSpanScopes[0].spans.length).toBe(2);
expect(startSpanScopes[0].spans).toEqual([
{
traceId: expect.any(String),
spanId: expect.any(String),
parentSpanId: expect.any(String),
name: 'test-span',
kind: 1,
startTimeUnixNano: expect.any(String),
endTimeUnixNano: expect.any(String),
attributes: [],
droppedAttributesCount: 0,
events: [],
droppedEventsCount: 0,
status: { code: 0 },
links: [],
droppedLinksCount: 0,
},
{
traceId: expect.any(String),
spanId: expect.any(String),
name: 'test-transaction',
kind: 1,
startTimeUnixNano: expect.any(String),
endTimeUnixNano: expect.any(String),
attributes: [{ key: 'sentry.op', value: { stringValue: 'e2e-test' } }],
droppedAttributesCount: 0,
events: [],
droppedEventsCount: 0,
status: { code: 0 },
links: [],
droppedLinksCount: 0,
},
]);

// Undici spans are emitted correctly
expect(undiciScopes.length).toBe(1);
expect(undiciScopes[0].spans.length).toBe(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ describe('awsIntegration', () => {
});

test('should auto-instrument aws-sdk v2 package.', done => {
createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSCATION }).start(done);
createRunner(__dirname, 'scenario.js').ignore('event').expect({ transaction: EXPECTED_TRANSCATION }).start(done);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ Sentry.withScope(scope => {
traceId: '12345678901234567890123456789012',
});

Sentry.startSpan({ name: 'test_span_1' }, () => undefined);
Sentry.startSpan({ name: 'test_span_2' }, () => undefined);
const spanIdTraceId = Sentry.startSpan(
{
name: 'test_span_1',
},
span1 => span1.spanContext().traceId,
);

Sentry.startSpan(
{
name: 'test_span_2',
attributes: { spanIdTraceId },
},
() => undefined,
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,19 @@ afterAll(() => {

test('should send manually started parallel root spans outside of root context with parentSpanId', done => {
createRunner(__dirname, 'scenario.ts')
.expect({ transaction: { transaction: 'test_span_1' } })
.expect({
transaction: {
transaction: 'test_span_1',
contexts: {
trace: {
span_id: expect.any(String),
parent_span_id: '1234567890123456',
trace_id: '12345678901234567890123456789012',
},
},
},
})
.expect({
transaction: {
transaction: 'test_span_2',
contexts: {
trace: {
span_id: expect.any(String),
parent_span_id: '1234567890123456',
trace_id: '12345678901234567890123456789012',
},
},
transaction: transaction => {
expect(transaction).toBeDefined();
const traceId = transaction.contexts?.trace?.trace_id;
expect(traceId).toBeDefined();
expect(transaction.contexts?.trace?.parent_span_id).toBeUndefined();

const trace1Id = transaction.contexts?.trace?.data?.spanIdTraceId;
expect(trace1Id).toBeDefined();

// Different trace ID as the first span
expect(trace1Id).not.toBe(traceId);
},
})
.start(done);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ test('should send manually started parallel root spans outside of root context',
const trace1Id = transaction.contexts?.trace?.data?.spanIdTraceId;
expect(trace1Id).toBeDefined();

// Same trace ID as the first span
expect(trace1Id).toBe(traceId);
// Different trace ID as the first span
expect(trace1Id).not.toBe(traceId);
},
})
.start(done);
Expand Down
Binary file added docs/assets/node-sdk-trace-propagation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions docs/trace-propagation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# How Trace Propagation Works in the JavaScript SDKs

Trace propagation describes how and when traceId & spanId are set and send for various types of events.
How this behaves varies a bit from Browser to Node SDKs.

## Node SDKs (OpenTelemetry based)

In the Node SDK and related OpenTelemetry-based SDKs, trace propagation works as follows:

![node-sdk-trace-propagation-scenarios](./assets/node-sdk-trace-propagation.png)

## Browser/Other SDKs

TODO
6 changes: 3 additions & 3 deletions packages/core/src/tracing/sentryNonRecordingSpan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {
SpanStatus,
SpanTimeInput,
} from '../types-hoist';
import { uuid4 } from '../utils-hoist/misc';
import { generateSpanId, generateTraceId } from '../utils-hoist';
import { TRACE_FLAG_NONE } from '../utils/spanUtils';

/**
Expand All @@ -18,8 +18,8 @@ export class SentryNonRecordingSpan implements Span {
private _spanId: string;

public constructor(spanContext: SentrySpanArguments = {}) {
this._traceId = spanContext.traceId || uuid4();
this._spanId = spanContext.spanId || uuid4().substring(16);
this._traceId = spanContext.traceId || generateTraceId();
this._spanId = spanContext.spanId || generateSpanId();
}

/** @inheritdoc */
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/tracing/sentrySpan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import {
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
} from '../semanticAttributes';
import { generateSpanId, generateTraceId } from '../utils-hoist';
import { logger } from '../utils-hoist/logger';
import { uuid4 } from '../utils-hoist/misc';
import { dropUndefinedKeys } from '../utils-hoist/object';
import { timestampInSeconds } from '../utils-hoist/time';
import {
Expand Down Expand Up @@ -76,8 +76,8 @@ export class SentrySpan implements Span {
* @hidden
*/
public constructor(spanContext: SentrySpanArguments = {}) {
this._traceId = spanContext.traceId || uuid4();
this._spanId = spanContext.spanId || uuid4().substring(16);
this._traceId = spanContext.traceId || generateTraceId();
this._spanId = spanContext.spanId || generateSpanId();
this._startTime = spanContext.startTimestamp || timestampInSeconds();

this._attributes = {};
Expand Down
5 changes: 2 additions & 3 deletions packages/core/src/utils-hoist/tracing.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { PropagationContext, TraceparentData } from '../types-hoist';

import { baggageHeaderToDynamicSamplingContext } from './baggage';
import { uuid4 } from './misc';
import { generateSpanId, generateTraceId } from './propagationContext';

// eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor -- RegExp is used for readability here
Expand Down Expand Up @@ -76,8 +75,8 @@ export function propagationContextFromHeaders(
* Create sentry-trace header from span context values.
*/
export function generateSentryTraceHeader(
traceId: string = uuid4(),
spanId: string = uuid4().substring(16),
traceId: string = generateTraceId(),
spanId: string = generateSpanId(),
sampled?: boolean,
): string {
let sampledString = '';
Expand Down
Loading

0 comments on commit fee464e

Please sign in to comment.