2020 * - Refactored to use Sentry's span APIs instead of OpenTelemetry tracing APIs
2121 */
2222
23- import * as api from '@opentelemetry/api' ;
23+ import { SpanKind } from '@opentelemetry/api' ;
2424import type { InstrumentationConfig } from '@opentelemetry/instrumentation' ;
2525import { InstrumentationBase , InstrumentationNodeModuleDefinition , isWrapped } from '@opentelemetry/instrumentation' ;
26- import type { SpanAttributes } from '@sentry/core' ;
27- import { SDK_VERSION , SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN , SPAN_STATUS_ERROR , startSpan } from '@sentry/core' ;
26+ import type { Span , SpanAttributes } from '@sentry/core' ;
27+ import {
28+ getActiveSpan ,
29+ SDK_VERSION ,
30+ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ,
31+ SPAN_STATUS_ERROR ,
32+ startSpan ,
33+ } from '@sentry/core' ;
2834import { InstrumentationNodeModuleFile } from '../../InstrumentationNodeModuleFile' ;
2935import {
3036 ATTR_DB_NAME ,
@@ -56,7 +62,7 @@ const SUPPORTED_VERSIONS = [
5662// Max length of the query text captured in the `db.statement` attribute; ".." is appended when truncated.
5763const MAX_QUERY_LENGTH = 1022 ;
5864
59- const contextSymbol = Symbol ( 'opentelemetry .instrumentation-knex.context ' ) ;
65+ const parentSpanSymbol = Symbol ( 'sentry .instrumentation-knex.parent-span ' ) ;
6066
6167export class KnexInstrumentation extends InstrumentationBase < InstrumentationConfig > {
6268 public constructor ( config : InstrumentationConfig = { } ) {
@@ -138,34 +144,29 @@ export class KnexInstrumentation extends InstrumentationBase<InstrumentationConf
138144 [ ATTR_DB_STATEMENT ] : utils . limitLength ( query ?. sql , MAX_QUERY_LENGTH ) ,
139145 } ;
140146
141- // The query builder captures the context active when it was created (see `_storeContext`).
142- // We only instrument queries that run as part of an existing trace.
143- const parentContext = this . builder [ contextSymbol ] || api . context . active ( ) ;
144- const parentSpan = api . trace . getSpan ( parentContext ) ;
145- const hasActiveParent = parentSpan && api . trace . isSpanContextValid ( parentSpan . spanContext ( ) ) ;
146- if ( ! hasActiveParent ) {
147- return original . bind ( this ) ( ...arguments ) ;
148- }
147+ // The query builder captures the span active when it was created (see `_storeContext`).
148+ // `onlyIfParent` ensures we only instrument queries that run as part of an existing trace.
149+ const parentSpan : Span | undefined = this . builder [ parentSpanSymbol ] || getActiveSpan ( ) ;
149150
150151 const args = arguments ;
151- return api . context . with ( parentContext , ( ) =>
152- startSpan (
153- {
154- name : utils . getName ( name , operation , table ) ,
155- kind : api . SpanKind . CLIENT ,
156- attributes ,
157- } ,
158- span =>
159- // `Runner.query` returns a real, already-executing Promise, so it is safe to let
160- // `startSpan` await it and auto-end the span.
161- original . apply ( this , args ) . catch ( ( err : any ) => {
162- const formatter = utils . getFormatter ( this ) ;
163- const fullQuery = formatter ( query . sql , query . bindings || [ ] ) ;
164- const message = err . message . replace ( ` ${ fullQuery } - ` , '' ) ;
165- span . setStatus ( { code : SPAN_STATUS_ERROR , message } ) ;
166- throw err ;
167- } ) ,
168- ) ,
152+ return startSpan (
153+ {
154+ name : utils . getName ( name , operation , table ) ,
155+ kind : SpanKind . CLIENT ,
156+ attributes ,
157+ parentSpan ,
158+ onlyIfParent : true ,
159+ } ,
160+ span =>
161+ // `Runner.query` returns a real, already-executing Promise, so it is safe to let
162+ // `startSpan` await it and auto-end the span.
163+ original . apply ( this , args ) . catch ( ( err : any ) => {
164+ const formatter = utils . getFormatter ( this ) ;
165+ const fullQuery = formatter ( query . sql , query . bindings || [ ] ) ;
166+ const message = err . message . replace ( ` ${ fullQuery } - ` , '' ) ;
167+ span . setStatus ( { code : SPAN_STATUS_ERROR , message } ) ;
168+ throw err ;
169+ } ) ,
169170 ) ;
170171 } ;
171172 } ;
@@ -174,8 +175,10 @@ export class KnexInstrumentation extends InstrumentationBase<InstrumentationConf
174175 private _storeContext ( original : ( ...args : any [ ] ) => any ) {
175176 return function wrapped_logging_method ( this : any ) {
176177 const builder = original . apply ( this , arguments ) ;
177- Object . defineProperty ( builder , contextSymbol , {
178- value : api . context . active ( ) ,
178+ // Capture the span that is active when the query builder is created. The query often executes
179+ // in a different async context, so we reuse this span as the parent when the query runs.
180+ Object . defineProperty ( builder , parentSpanSymbol , {
181+ value : getActiveSpan ( ) ,
179182 } ) ;
180183 return builder ;
181184 } ;
0 commit comments