|
| 1 | +import { context, trace, TraceFlags, type Context } from '@opentelemetry/api'; |
1 | 2 | import type { BasicTracerProvider } from '@opentelemetry/sdk-trace-base'; |
2 | 3 | import type { Scope } from '@sentry/core'; |
3 | 4 | import { |
| 5 | + getAsyncContextStrategy, |
4 | 6 | getCurrentScope, |
5 | 7 | getIsolationScope, |
| 8 | + getMainCarrier, |
6 | 9 | Scope as ScopeClass, |
| 10 | + SentryNonRecordingSpan, |
7 | 11 | setAsyncContextStrategy, |
8 | 12 | withIsolationScope, |
9 | 13 | withScope, |
10 | 14 | } from '@sentry/core'; |
11 | 15 | import { afterAll, afterEach, beforeEach, describe, expect, it, test } from 'vitest'; |
12 | 16 | import { setOpenTelemetryContextAsyncContextStrategy } from '../src/asyncContextStrategy'; |
| 17 | +import { SENTRY_TRACE_STATE_CHILD_IGNORED } from '../src/constants'; |
| 18 | +import { setNodeOpenTelemetryContextAsyncContextStrategy } from '../src/nodeAsyncContextStrategy'; |
| 19 | +import { TraceState } from '../src/utils/TraceState'; |
13 | 20 | import { setupOtel } from './helpers/initOtel'; |
14 | 21 | import { cleanupOtel } from './helpers/mockSdkInit'; |
15 | 22 | import { getDefaultTestClientOptions, TestClient } from './helpers/TestClient'; |
@@ -81,6 +88,62 @@ describe('asyncContextStrategy', () => { |
81 | 88 | }); |
82 | 89 | }); |
83 | 90 |
|
| 91 | + test('tracing channel binding keeps the parent active for an ignored child span', () => { |
| 92 | + setNodeOpenTelemetryContextAsyncContextStrategy(); |
| 93 | + |
| 94 | + const parentSpan = trace.getTracer('test').startSpan('parent'); |
| 95 | + const ignoredSpan = trace.wrapSpanContext({ |
| 96 | + traceId: parentSpan.spanContext().traceId, |
| 97 | + spanId: '1234567890123456', |
| 98 | + traceFlags: TraceFlags.NONE, |
| 99 | + traceState: new TraceState().set(SENTRY_TRACE_STATE_CHILD_IGNORED, '1'), |
| 100 | + }); |
| 101 | + |
| 102 | + context.with(trace.setSpan(context.active(), parentSpan), () => { |
| 103 | + const binding = getAsyncContextStrategy(getMainCarrier()).getTracingChannelBinding?.(); |
| 104 | + const store = binding?.getStoreWithActiveSpan(ignoredSpan); |
| 105 | + |
| 106 | + expect(store).toBeDefined(); |
| 107 | + expect(trace.getSpan(store as Context)).toBe(parentSpan); |
| 108 | + }); |
| 109 | + |
| 110 | + parentSpan.end(); |
| 111 | + }); |
| 112 | + |
| 113 | + test('tracing channel binding keeps the parent active for a native ignored child span', () => { |
| 114 | + setNodeOpenTelemetryContextAsyncContextStrategy(); |
| 115 | + |
| 116 | + const parentSpan = trace.getTracer('test').startSpan('parent'); |
| 117 | + const ignoredSpan = new SentryNonRecordingSpan({ |
| 118 | + dropReason: 'ignored', |
| 119 | + traceId: parentSpan.spanContext().traceId, |
| 120 | + }); |
| 121 | + |
| 122 | + context.with(trace.setSpan(context.active(), parentSpan), () => { |
| 123 | + const binding = getAsyncContextStrategy(getMainCarrier()).getTracingChannelBinding?.(); |
| 124 | + const store = binding?.getStoreWithActiveSpan(ignoredSpan); |
| 125 | + |
| 126 | + expect(store).toBeDefined(); |
| 127 | + expect(trace.getSpan(store as Context)).toBe(parentSpan); |
| 128 | + }); |
| 129 | + |
| 130 | + parentSpan.end(); |
| 131 | + }); |
| 132 | + |
| 133 | + test('tracing channel binding activates a native ignored root span', () => { |
| 134 | + setNodeOpenTelemetryContextAsyncContextStrategy(); |
| 135 | + |
| 136 | + const ignoredSpan = new SentryNonRecordingSpan({ |
| 137 | + dropReason: 'ignored', |
| 138 | + traceId: '12345678901234567890123456789012', |
| 139 | + }); |
| 140 | + const binding = getAsyncContextStrategy(getMainCarrier()).getTracingChannelBinding?.(); |
| 141 | + const store = binding?.getStoreWithActiveSpan(ignoredSpan); |
| 142 | + |
| 143 | + expect(store).toBeDefined(); |
| 144 | + expect(trace.getSpan(store as Context)).toBe(ignoredSpan); |
| 145 | + }); |
| 146 | + |
84 | 147 | test('async scope inheritance', async () => { |
85 | 148 | const initialScope = getCurrentScope(); |
86 | 149 | const initialIsolationScope = getIsolationScope(); |
|
0 commit comments