From 1a79f828648bdb2d1e65125eb689115ae343575c Mon Sep 17 00:00:00 2001 From: Marten Hennoch Date: Wed, 5 Nov 2025 18:29:17 +0200 Subject: [PATCH 1/2] Propagate action with traceparent --- .../src/OracleTelemetryTraceHandler.ts | 28 +++++++++++++++++++ .../instrumentation-oracledb/src/types.ts | 7 +++++ 2 files changed, 35 insertions(+) diff --git a/packages/instrumentation-oracledb/src/OracleTelemetryTraceHandler.ts b/packages/instrumentation-oracledb/src/OracleTelemetryTraceHandler.ts index e3a5151437..01d1a10802 100644 --- a/packages/instrumentation-oracledb/src/OracleTelemetryTraceHandler.ts +++ b/packages/instrumentation-oracledb/src/OracleTelemetryTraceHandler.ts @@ -24,6 +24,7 @@ import { SpanKind, trace, diag, + TraceFlags, } from '@opentelemetry/api'; import { ATTR_DB_NAMESPACE, @@ -63,6 +64,12 @@ function getTraceHandlerBaseClass( } } + +function _buildTraceparent(span: Span): string | undefined { + const sc = span.spanContext(); + return `00-${sc.traceId}-${sc.spanId}-0${Number(sc.traceFlags || TraceFlags.NONE).toString(16)}`; +} + export function getOracleTelemetryTraceHandlerClass( obj: typeof oracleDBTypes ): any { @@ -354,6 +361,27 @@ export function getOracleTelemetryTraceHandlerClass( }; if (traceContext.fn) { + if ( + this._instrumentConfig.traceContextPropagation && + (traceContext.operation === SpanNames.EXECUTE || + traceContext.operation === SpanNames.EXECUTE_MANY) + ) { + const connection = traceContext.additionalConfig?.self; + const traceparent = _buildTraceparent( + traceContext.userContext.span + ); + if (connection && 'action' in connection && traceparent) { + try { + connection.action = traceparent; + } catch (err) { + diag.debug( + 'Failed to set connection.action for trace propagation', + err + ); + } + } + } + // wrap the active span context to the exported function. traceContext.fn = context.bind( trace.setSpan(context.active(), traceContext.userContext.span), diff --git a/packages/instrumentation-oracledb/src/types.ts b/packages/instrumentation-oracledb/src/types.ts index 821554609e..c8b9451b44 100644 --- a/packages/instrumentation-oracledb/src/types.ts +++ b/packages/instrumentation-oracledb/src/types.ts @@ -91,4 +91,11 @@ export interface OracleInstrumentationConfig extends InstrumentationConfig { * @default false */ requireParentSpan?: boolean; + + /** + * Automatic propagation of trace context using V$SESSION.ACTION. + * + * @default false + */ + traceContextPropagation?: boolean; } From e58acdc68cd3dd3ae9e163d35dabf502a76a73bb Mon Sep 17 00:00:00 2001 From: Marten Hennoch Date: Thu, 13 Nov 2025 13:14:14 +0200 Subject: [PATCH 2/2] test --- .../src/OracleTelemetryTraceHandler.ts | 15 +++++++----- .../test/oracle.test.ts | 24 +++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/packages/instrumentation-oracledb/src/OracleTelemetryTraceHandler.ts b/packages/instrumentation-oracledb/src/OracleTelemetryTraceHandler.ts index 01d1a10802..24c1206488 100644 --- a/packages/instrumentation-oracledb/src/OracleTelemetryTraceHandler.ts +++ b/packages/instrumentation-oracledb/src/OracleTelemetryTraceHandler.ts @@ -25,6 +25,7 @@ import { trace, diag, TraceFlags, + SpanContext, } from '@opentelemetry/api'; import { ATTR_DB_NAMESPACE, @@ -64,10 +65,12 @@ function getTraceHandlerBaseClass( } } - -function _buildTraceparent(span: Span): string | undefined { - const sc = span.spanContext(); - return `00-${sc.traceId}-${sc.spanId}-0${Number(sc.traceFlags || TraceFlags.NONE).toString(16)}`; +export function buildTraceparent( + spanContext: SpanContext +): string | undefined { + return `00-${spanContext.traceId}-${spanContext.spanId}-0${Number( + spanContext.traceFlags || TraceFlags.NONE + ).toString(16)}`; } export function getOracleTelemetryTraceHandlerClass( @@ -367,8 +370,8 @@ export function getOracleTelemetryTraceHandlerClass( traceContext.operation === SpanNames.EXECUTE_MANY) ) { const connection = traceContext.additionalConfig?.self; - const traceparent = _buildTraceparent( - traceContext.userContext.span + const traceparent = buildTraceparent( + traceContext.userContext.span.spanContext() ); if (connection && 'action' in connection && traceparent) { try { diff --git a/packages/instrumentation-oracledb/test/oracle.test.ts b/packages/instrumentation-oracledb/test/oracle.test.ts index 4c7a2bf20e..a48f724186 100644 --- a/packages/instrumentation-oracledb/test/oracle.test.ts +++ b/packages/instrumentation-oracledb/test/oracle.test.ts @@ -36,6 +36,7 @@ import { import * as assert from 'assert'; import { OracleInstrumentation } from '../src'; import { SpanNames } from '../src/constants'; +import { buildTraceparent} from '../src/OracleTelemetryTraceHandler'; import { ATTR_DB_NAMESPACE, @@ -512,6 +513,7 @@ describe('oracledb', () => { instrumentation.setConfig({ enhancedDatabaseReporting: false, dbStatementDump: false, + traceContextPropagation: false, }); }); @@ -963,6 +965,28 @@ describe('oracledb', () => { }); }); + it('should propagate trace context via connection.action when enabled', async () => { + instrumentation.setConfig({ traceContextPropagation: true }); + const result = await connection.execute( + "select sys_context('USERENV', 'ACTION') as action from dual", + [], + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + const row = result.rows?.[0] as Record | undefined; + const actionValue = row?.ACTION; + const spans = memoryExporter.getFinishedSpans(); + const executeSpan = spans[spans.length - 1]; + assert.ok(executeSpan, 'expected span to verify trace propagation'); + assert.ok( + executeSpan.name.startsWith(SpanNames.EXECUTE), + `expected execute span, got ${executeSpan.name}` + ); + const expectedTraceparent = buildTraceparent( + executeSpan.spanContext() + ); + assert.strictEqual(actionValue, expectedTraceparent); + }); + it('should intercept connection.execute(sql, values) bind-by-name', async () => { const span = tracer.startSpan('test span'); await context.with(trace.setSpan(context.active(), span), async () => {