1
1
import type { Instrumentation } from '@opentelemetry/instrumentation' ;
2
2
import { DnsInstrumentation } from '@opentelemetry/instrumentation-dns' ;
3
- import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express' ;
3
+ import { ExpressInstrumentation , ExpressInstrumentationConfig } from '@opentelemetry/instrumentation-express' ;
4
4
import { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici' ;
5
5
import { GenericPoolInstrumentation } from '@opentelemetry/instrumentation-generic-pool' ;
6
- import { HttpInstrumentation } from '@opentelemetry/instrumentation-http' ;
6
+ import { HttpInstrumentation , HttpInstrumentationConfig } from '@opentelemetry/instrumentation-http' ;
7
7
import { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis' ;
8
8
import { NetInstrumentation } from '@opentelemetry/instrumentation-net' ;
9
9
import { PgInstrumentation } from '@opentelemetry/instrumentation-pg' ;
10
10
import { PinoInstrumentation } from '@opentelemetry/instrumentation-pino' ;
11
+ import { Span } from '@opentelemetry/api' ;
12
+ import { IncomingMessage , ClientRequest } from 'http' ;
13
+ import { Request } from 'express' ;
11
14
12
15
const InstrumentationMap = {
13
16
'@opentelemetry/instrumentation-http' : HttpInstrumentation ,
@@ -25,17 +28,56 @@ const InstrumentationMap = {
25
28
type ConfigArg < T > = T extends new ( ...args : infer U ) => unknown ? U [ 0 ] : never ;
26
29
export type InstrumentationConfigMap = {
27
30
[ Name in keyof typeof InstrumentationMap ] ?: ConfigArg < ( typeof InstrumentationMap ) [ Name ] > ;
31
+ } & {
32
+ // Add optional global ignore patterns
33
+ ignoreIncomingPaths ?: string [ ] ;
28
34
} ;
29
35
36
+ // Add default ignored endpoints
37
+ const DEFAULT_IGNORED_ENDPOINTS = [ '/health' , '/metrics' ] ;
38
+
39
+ // Add helper function to create span names
40
+ function createSpanName ( method : string , route : string ) : string {
41
+ return `${ method . toUpperCase ( ) } ${ route } ` ;
42
+ }
43
+
30
44
export function getAutoInstrumentations (
31
45
inputConfigs : InstrumentationConfigMap = { } ,
32
46
) : Instrumentation [ ] {
33
47
const keys = Object . keys ( InstrumentationMap ) as Array < keyof typeof InstrumentationMap > ;
48
+ const ignorePaths = [ ...DEFAULT_IGNORED_ENDPOINTS , ...( inputConfigs . ignoreIncomingPaths || [ ] ) ] ;
49
+
34
50
return keys
35
51
. map ( ( name ) => {
36
52
const Instance = InstrumentationMap [ name ] ;
37
- // Defaults are defined by the instrumentation itself
38
- const userConfig = inputConfigs [ name ] ?? { } ;
53
+ // Create a base config from user input or empty object
54
+ const userConfig = { ...( inputConfigs [ name ] || { } ) } ;
55
+
56
+ // Configure HTTP instrumentation
57
+ if ( name === '@opentelemetry/instrumentation-http' ) {
58
+ const httpConfig = userConfig as HttpInstrumentationConfig ;
59
+ httpConfig . ignoreIncomingRequestHook = ( request ) => {
60
+ const path = request . url ?. split ( '?' ) [ 0 ] || '/' ;
61
+ return ignorePaths . includes ( path ) ;
62
+ } ;
63
+ httpConfig . requestHook = ( span , request ) => {
64
+ const method = request . method || 'UNKNOWN' ;
65
+ const path = ( request instanceof IncomingMessage ? request . url : request . path ) ?. split ( '?' ) [ 0 ] || '/' ;
66
+ span . updateName ( createSpanName ( method , path ) ) ;
67
+ } ;
68
+ }
69
+
70
+ // Configure Express instrumentation
71
+ if ( name === '@opentelemetry/instrumentation-express' ) {
72
+ const expressConfig = userConfig as ExpressInstrumentationConfig ;
73
+ expressConfig . ignoreLayers = ignorePaths ;
74
+ expressConfig . requestHook = ( span , req ) => {
75
+ const method = req . request ?. method || 'UNKNOWN' ;
76
+ // Use the matched route path instead of raw URL if available
77
+ const route = ( req . request as any ) . route ?. path || req . request ?. url ?. split ( '?' ) [ 0 ] || '/' ;
78
+ span . updateName ( createSpanName ( method , route ) ) ;
79
+ } ;
80
+ }
39
81
40
82
try {
41
83
return new Instance ( userConfig ) ;
0 commit comments