1717 * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/15ef7506553f631ea4181391e0c5725a56f0d082/packages/instrumentation-knex
1818 * - Upstream version: @opentelemetry/instrumentation-knex@0.62.0
1919 * - Minor TypeScript strictness adjustments for this repository's compiler settings
20+ * - Refactored to use Sentry's span APIs instead of OpenTelemetry tracing APIs
2021 */
21- /* eslint-disable */
2222
2323import * as api from '@opentelemetry/api' ;
24- import { SDK_VERSION } from '@sentry/core' ;
25- import * as constants from './constants' ;
26- import {
27- InstrumentationBase ,
28- InstrumentationNodeModuleDefinition ,
29- isWrapped ,
30- SemconvStability ,
31- semconvStabilityFromStr ,
32- } from '@opentelemetry/instrumentation' ;
24+ import type { InstrumentationConfig } from '@opentelemetry/instrumentation' ;
25+ import { 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' ;
3328import { InstrumentationNodeModuleFile } from '../../InstrumentationNodeModuleFile' ;
34- import * as utils from './utils' ;
35- import { KnexInstrumentationConfig } from './types' ;
36- import {
37- ATTR_DB_COLLECTION_NAME ,
38- ATTR_DB_NAMESPACE ,
39- ATTR_DB_OPERATION_NAME ,
40- ATTR_DB_QUERY_TEXT ,
41- ATTR_DB_SYSTEM_NAME ,
42- ATTR_SERVER_ADDRESS ,
43- ATTR_SERVER_PORT ,
44- } from '@opentelemetry/semantic-conventions' ;
4529import {
4630 ATTR_DB_NAME ,
4731 ATTR_DB_OPERATION ,
@@ -53,65 +37,69 @@ import {
5337 ATTR_NET_PEER_PORT ,
5438 ATTR_NET_TRANSPORT ,
5539} from './semconv' ;
40+ import * as utils from './utils' ;
5641
5742const PACKAGE_NAME = '@sentry/instrumentation-knex' ;
43+ const ORIGIN = 'auto.db.otel.knex' ;
44+
45+ const MODULE_NAME = 'knex' ;
46+ const SUPPORTED_VERSIONS = [
47+ // use "lib/execution" for runner.js, "lib" for client.js as basepath, latest tested 0.95.6
48+ '>=0.22.0 <4' ,
49+ // use "lib" as basepath
50+ '>=0.10.0 <0.18.0' ,
51+ '>=0.19.0 <0.22.0' ,
52+ // use "src" as basepath
53+ '>=0.18.0 <0.19.0' ,
54+ ] ;
55+
56+ // Max length of the query text captured in the `db.statement` attribute; ".." is appended when truncated.
57+ const MAX_QUERY_LENGTH = 1022 ;
5858
5959const contextSymbol = Symbol ( 'opentelemetry.instrumentation-knex.context' ) ;
60- const DEFAULT_CONFIG : KnexInstrumentationConfig = {
61- maxQueryLength : 1022 ,
62- requireParentSpan : false ,
63- } ;
64-
65- export class KnexInstrumentation extends InstrumentationBase < KnexInstrumentationConfig > {
66- private _semconvStability : SemconvStability ;
67-
68- constructor ( config : KnexInstrumentationConfig = { } ) {
69- super ( PACKAGE_NAME , SDK_VERSION , { ...DEFAULT_CONFIG , ...config } ) ;
7060
71- this . _semconvStability = semconvStabilityFromStr ( 'database' , process . env . OTEL_SEMCONV_STABILITY_OPT_IN ) ;
61+ export class KnexInstrumentation extends InstrumentationBase < InstrumentationConfig > {
62+ public constructor ( config : InstrumentationConfig = { } ) {
63+ super ( PACKAGE_NAME , SDK_VERSION , config ) ;
7264 }
7365
74- override setConfig ( config : KnexInstrumentationConfig = { } ) {
75- super . setConfig ( { ...DEFAULT_CONFIG , ...config } ) ;
76- }
77-
78- init ( ) {
79- const module = new InstrumentationNodeModuleDefinition ( constants . MODULE_NAME , constants . SUPPORTED_VERSIONS ) ;
66+ public init ( ) : InstrumentationNodeModuleDefinition {
67+ const module = new InstrumentationNodeModuleDefinition ( MODULE_NAME , SUPPORTED_VERSIONS ) ;
8068
8169 module . files . push (
82- this . getClientNodeModuleFileInstrumentation ( 'src' ) ,
83- this . getClientNodeModuleFileInstrumentation ( 'lib' ) ,
84- this . getRunnerNodeModuleFileInstrumentation ( 'src' ) ,
85- this . getRunnerNodeModuleFileInstrumentation ( 'lib' ) ,
86- this . getRunnerNodeModuleFileInstrumentation ( 'lib/execution' ) ,
70+ this . _getClientNodeModuleFileInstrumentation ( 'src' ) ,
71+ this . _getClientNodeModuleFileInstrumentation ( 'lib' ) ,
72+ this . _getRunnerNodeModuleFileInstrumentation ( 'src' ) ,
73+ this . _getRunnerNodeModuleFileInstrumentation ( 'lib' ) ,
74+ this . _getRunnerNodeModuleFileInstrumentation ( 'lib/execution' ) ,
8775 ) ;
8876
8977 return module ;
9078 }
9179
92- private getRunnerNodeModuleFileInstrumentation ( basePath : string ) {
80+ private _getRunnerNodeModuleFileInstrumentation ( basePath : string ) : InstrumentationNodeModuleFile {
9381 return new InstrumentationNodeModuleFile (
9482 `knex/${ basePath } /runner.js` ,
95- constants . SUPPORTED_VERSIONS ,
83+ SUPPORTED_VERSIONS ,
9684 ( Runner : any , moduleVersion ?: string ) => {
97- this . ensureWrapped ( Runner . prototype , 'query' , this . createQueryWrapper ( moduleVersion ) ) ;
85+ this . _ensureWrapped ( Runner . prototype , 'query' , this . _createQueryWrapper ( moduleVersion ) ) ;
9886 return Runner ;
9987 } ,
100- ( Runner : any , _moduleVersion ?: string ) => {
88+ ( Runner : any ) => {
10189 this . _unwrap ( Runner . prototype , 'query' ) ;
10290 return Runner ;
10391 } ,
10492 ) ;
10593 }
10694
107- private getClientNodeModuleFileInstrumentation ( basePath : string ) {
95+ private _getClientNodeModuleFileInstrumentation ( basePath : string ) : InstrumentationNodeModuleFile {
10896 return new InstrumentationNodeModuleFile (
10997 `knex/${ basePath } /client.js` ,
110- constants . SUPPORTED_VERSIONS ,
98+ SUPPORTED_VERSIONS ,
11199 ( Client : any ) => {
112- this . ensureWrapped ( Client . prototype , 'queryBuilder' , this . storeContext . bind ( this ) ) ;
113- this . ensureWrapped ( Client . prototype , 'schemaBuilder' , this . storeContext . bind ( this ) ) ;
114- this . ensureWrapped ( Client . prototype , 'raw' , this . storeContext . bind ( this ) ) ;
100+ this . _ensureWrapped ( Client . prototype , 'queryBuilder' , this . _storeContext . bind ( this ) ) ;
101+ this . _ensureWrapped ( Client . prototype , 'schemaBuilder' , this . _storeContext . bind ( this ) ) ;
102+ this . _ensureWrapped ( Client . prototype , 'raw' , this . _storeContext . bind ( this ) ) ;
115103 return Client ;
116104 } ,
117105 ( Client : any ) => {
@@ -123,9 +111,7 @@ export class KnexInstrumentation extends InstrumentationBase<KnexInstrumentation
123111 ) ;
124112 }
125113
126- private createQueryWrapper ( moduleVersion ?: string ) {
127- const instrumentation = this ;
128-
114+ private _createQueryWrapper ( moduleVersion ?: string ) {
129115 return function wrapQuery ( original : ( ...args : any [ ] ) => any ) {
130116 return function wrapped_logging_method ( this : any , query : any ) {
131117 const config = this . client . config ;
@@ -137,83 +123,55 @@ export class KnexInstrumentation extends InstrumentationBase<KnexInstrumentation
137123 config ?. connection ?. filename ||
138124 config ?. connection ?. database ||
139125 utils . extractDatabaseFromConnectionString ( connectionString ) ;
140- const { maxQueryLength } = instrumentation . getConfig ( ) ;
141126
142- const attributes : api . Attributes = {
127+ const attributes : SpanAttributes = {
128+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : ORIGIN ,
143129 'knex.version' : moduleVersion ,
130+ [ ATTR_DB_SYSTEM ] : utils . mapSystem ( this . client . driverName ) ,
131+ [ ATTR_DB_SQL_TABLE ] : table ,
132+ [ ATTR_DB_OPERATION ] : operation ,
133+ [ ATTR_DB_USER ] : config ?. connection ?. user ,
134+ [ ATTR_DB_NAME ] : name ,
135+ [ ATTR_NET_PEER_NAME ] : config ?. connection ?. host ?? utils . extractHostFromConnectionString ( connectionString ) ,
136+ [ ATTR_NET_PEER_PORT ] : config ?. connection ?. port ?? utils . extractPortFromConnectionString ( connectionString ) ,
137+ [ ATTR_NET_TRANSPORT ] : config ?. connection ?. filename === ':memory:' ? 'inproc' : undefined ,
138+ [ ATTR_DB_STATEMENT ] : utils . limitLength ( query ?. sql , MAX_QUERY_LENGTH ) ,
144139 } ;
145- const transport = config ?. connection ?. filename === ':memory:' ? 'inproc' : undefined ;
146-
147- if ( instrumentation . _semconvStability & SemconvStability . OLD ) {
148- Object . assign ( attributes , {
149- [ ATTR_DB_SYSTEM ] : utils . mapSystem ( this . client . driverName ) ,
150- [ ATTR_DB_SQL_TABLE ] : table ,
151- [ ATTR_DB_OPERATION ] : operation ,
152- [ ATTR_DB_USER ] : config ?. connection ?. user ,
153- [ ATTR_DB_NAME ] : name ,
154- [ ATTR_NET_PEER_NAME ] : config ?. connection ?. host ?? utils . extractHostFromConnectionString ( connectionString ) ,
155- [ ATTR_NET_PEER_PORT ] : config ?. connection ?. port ?? utils . extractPortFromConnectionString ( connectionString ) ,
156- [ ATTR_NET_TRANSPORT ] : transport ,
157- } ) ;
158- }
159- if ( instrumentation . _semconvStability & SemconvStability . STABLE ) {
160- Object . assign ( attributes , {
161- [ ATTR_DB_SYSTEM_NAME ] : utils . mapSystem ( this . client . driverName ) ,
162- [ ATTR_DB_COLLECTION_NAME ] : table ,
163- [ ATTR_DB_OPERATION_NAME ] : operation ,
164- [ ATTR_DB_NAMESPACE ] : name ,
165- [ ATTR_SERVER_ADDRESS ] : config ?. connection ?. host ?? utils . extractHostFromConnectionString ( connectionString ) ,
166- [ ATTR_SERVER_PORT ] : config ?. connection ?. port ?? utils . extractPortFromConnectionString ( connectionString ) ,
167- } ) ;
168- }
169- if ( maxQueryLength ) {
170- const queryText = utils . limitLength ( query ?. sql , maxQueryLength ) ;
171- if ( instrumentation . _semconvStability & SemconvStability . STABLE ) {
172- attributes [ ATTR_DB_QUERY_TEXT ] = queryText ;
173- }
174- if ( instrumentation . _semconvStability & SemconvStability . OLD ) {
175- attributes [ ATTR_DB_STATEMENT ] = queryText ;
176- }
177- }
178140
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.
179143 const parentContext = this . builder [ contextSymbol ] || api . context . active ( ) ;
180144 const parentSpan = api . trace . getSpan ( parentContext ) ;
181145 const hasActiveParent = parentSpan && api . trace . isSpanContextValid ( parentSpan . spanContext ( ) ) ;
182- if ( instrumentation . _config . requireParentSpan && ! hasActiveParent ) {
146+ if ( ! hasActiveParent ) {
183147 return original . bind ( this ) ( ...arguments ) ;
184148 }
185149
186- const span = instrumentation . tracer . startSpan (
187- utils . getName ( name , operation , table ) ,
188- {
189- kind : api . SpanKind . CLIENT ,
190- attributes,
191- } ,
192- parentContext ,
150+ 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+ ) ,
193169 ) ;
194- const spanContext = api . trace . setSpan ( api . context . active ( ) , span ) ;
195-
196- return api . context
197- . with ( spanContext , original , this , ...arguments )
198- . then ( ( result : unknown ) => {
199- span . end ( ) ;
200- return result ;
201- } )
202- . catch ( ( err : any ) => {
203- const formatter = utils . getFormatter ( this ) ;
204- const fullQuery = formatter ( query . sql , query . bindings || [ ] ) ;
205- const message = err . message . replace ( fullQuery + ' - ' , '' ) ;
206- const exc = utils . otelExceptionFromKnexError ( err , message ) ;
207- span . recordException ( exc ) ;
208- span . setStatus ( { code : api . SpanStatusCode . ERROR , message } ) ;
209- span . end ( ) ;
210- throw err ;
211- } ) ;
212170 } ;
213171 } ;
214172 }
215173
216- private storeContext ( original : Function ) {
174+ private _storeContext ( original : ( ... args : any [ ] ) => any ) {
217175 return function wrapped_logging_method ( this : any ) {
218176 const builder = original . apply ( this , arguments ) ;
219177 Object . defineProperty ( builder , contextSymbol , {
@@ -223,7 +181,7 @@ export class KnexInstrumentation extends InstrumentationBase<KnexInstrumentation
223181 } ;
224182 }
225183
226- ensureWrapped ( obj : any , methodName : string , wrapper : ( original : any ) => any ) {
184+ private _ensureWrapped ( obj : any , methodName : string , wrapper : ( original : any ) => any ) : void {
227185 if ( isWrapped ( obj [ methodName ] ) ) {
228186 this . _unwrap ( obj , methodName ) ;
229187 }
0 commit comments