@@ -108,42 +108,43 @@ export class SentryHttpInstrumentation extends InstrumentationBase<SentryHttpIns
108
108
109
109
/** @inheritdoc */
110
110
public init ( ) : [ InstrumentationNodeModuleDefinition , InstrumentationNodeModuleDefinition ] {
111
- const handledRequests = new WeakSet < http . ClientRequest > ( ) ;
112
-
113
- const handleOutgoingRequestFinishOnce = ( request : http . ClientRequest , response ?: http . IncomingMessage ) : void => {
114
- if ( handledRequests . has ( request ) ) {
115
- return ;
116
- }
117
-
118
- handledRequests . add ( request ) ;
119
- this . _onOutgoingRequestFinish ( request , response ) ;
120
- } ;
111
+ // We register handlers when either http or https is instrumented
112
+ // but we only want to register them once, whichever is instrumented first
113
+ let hasRegisteredHandlers = false ;
121
114
122
115
const onHttpServerRequestStart = ( ( _data : unknown ) => {
123
116
const data = _data as { server : http . Server } ;
124
117
this . _patchServerEmitOnce ( data . server ) ;
125
118
} ) satisfies ChannelListener ;
126
119
127
- const onHttpsServerRequestStart = ( ( _data : unknown ) => {
128
- const data = _data as { server : http . Server } ;
129
- this . _patchServerEmitOnce ( data . server ) ;
130
- } ) satisfies ChannelListener ;
131
-
132
120
const onHttpClientResponseFinish = ( ( _data : unknown ) => {
133
121
const data = _data as { request : http . ClientRequest ; response : http . IncomingMessage } ;
134
- handleOutgoingRequestFinishOnce ( data . request , data . response ) ;
122
+ this . _onOutgoingRequestFinish ( data . request , data . response ) ;
135
123
} ) satisfies ChannelListener ;
136
124
137
125
const onHttpClientRequestError = ( ( _data : unknown ) => {
138
126
const data = _data as { request : http . ClientRequest } ;
139
- handleOutgoingRequestFinishOnce ( data . request , undefined ) ;
127
+ this . _onOutgoingRequestFinish ( data . request , undefined ) ;
140
128
} ) satisfies ChannelListener ;
141
129
130
+ /**
131
+ * You may be wondering why we register these diagnostics-channel listenrers in such InstrumentationNodeModuleDefinition,
132
+ * instead of simply subscribing to the events once in here.
133
+ * The reason for this is timing semantics: These functions are called once the http or https module is loaded.
134
+ * If we'd subscribe before that, there seem to be conflicts with the OTEL native instrumentation in some scenarios,
135
+ * especially the "import-on-top" pattern of setting up ESM applications.
136
+ */
142
137
return [
143
138
new InstrumentationNodeModuleDefinition (
144
139
'http' ,
145
140
[ '*' ] ,
146
141
( moduleExports : Http ) : Http => {
142
+ if ( hasRegisteredHandlers ) {
143
+ return moduleExports ;
144
+ }
145
+
146
+ hasRegisteredHandlers = true ;
147
+
147
148
subscribe ( 'http.server.request.start' , onHttpServerRequestStart ) ;
148
149
subscribe ( 'http.client.response.finish' , onHttpClientResponseFinish ) ;
149
150
@@ -163,7 +164,13 @@ export class SentryHttpInstrumentation extends InstrumentationBase<SentryHttpIns
163
164
'https' ,
164
165
[ '*' ] ,
165
166
( moduleExports : Https ) : Https => {
166
- subscribe ( 'http.server.request.start' , onHttpsServerRequestStart ) ;
167
+ if ( hasRegisteredHandlers ) {
168
+ return moduleExports ;
169
+ }
170
+
171
+ hasRegisteredHandlers = true ;
172
+
173
+ subscribe ( 'http.server.request.start' , onHttpServerRequestStart ) ;
167
174
subscribe ( 'http.client.response.finish' , onHttpClientResponseFinish ) ;
168
175
169
176
// When an error happens, we still want to have a breadcrumb
@@ -173,7 +180,7 @@ export class SentryHttpInstrumentation extends InstrumentationBase<SentryHttpIns
173
180
return moduleExports ;
174
181
} ,
175
182
( ) => {
176
- unsubscribe ( 'http.server.request.start' , onHttpsServerRequestStart ) ;
183
+ unsubscribe ( 'http.server.request.start' , onHttpServerRequestStart ) ;
177
184
unsubscribe ( 'http.client.response.finish' , onHttpClientResponseFinish ) ;
178
185
unsubscribe ( 'http.client.request.error' , onHttpClientRequestError ) ;
179
186
} ,
0 commit comments