@@ -115,7 +115,14 @@ def client_response_hook(span: Span, message: dict):
115
115
from opentelemetry .semconv .trace import SpanAttributes
116
116
from opentelemetry .trace import Span , set_span_in_context
117
117
from opentelemetry .trace .status import Status , StatusCode
118
- from opentelemetry .util .http import remove_url_credentials
118
+ from opentelemetry .util .http import (
119
+ OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST ,
120
+ OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE ,
121
+ get_custom_headers ,
122
+ normalise_request_header_name ,
123
+ normalise_response_header_name ,
124
+ remove_url_credentials ,
125
+ )
119
126
120
127
_ServerRequestHookT = typing .Optional [typing .Callable [[Span , dict ], None ]]
121
128
_ClientRequestHookT = typing .Optional [typing .Callable [[Span , dict ], None ]]
@@ -223,6 +230,41 @@ def collect_request_attributes(scope):
223
230
return result
224
231
225
232
233
+ def collect_custom_request_headers_attributes (scope ):
234
+ """returns custom HTTP request headers to be added into SERVER span as span attributes
235
+ Refer specification https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-request-and-response-headers"""
236
+
237
+ attributes = {}
238
+ custom_request_headers = get_custom_headers (
239
+ OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST
240
+ )
241
+
242
+ for header in custom_request_headers :
243
+ values = asgi_getter .get (scope , header )
244
+ if values :
245
+ key = normalise_request_header_name (header )
246
+ attributes .setdefault (key , []).extend (values )
247
+
248
+ return attributes
249
+
250
+
251
+ def collect_custom_response_headers_attributes (message ):
252
+ """returns custom HTTP response headers to be added into SERVER span as span attributes
253
+ Refer specification https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-request-and-response-headers"""
254
+ attributes = {}
255
+ custom_response_headers = get_custom_headers (
256
+ OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE
257
+ )
258
+
259
+ for header in custom_response_headers :
260
+ values = asgi_getter .get (message , header )
261
+ if values :
262
+ key = normalise_response_header_name (header )
263
+ attributes .setdefault (key , []).extend (values )
264
+
265
+ return attributes
266
+
267
+
226
268
def get_host_port_url_tuple (scope ):
227
269
"""Returns (host, port, full_url) tuple."""
228
270
server = scope .get ("server" ) or ["0.0.0.0" , 80 ]
@@ -342,6 +384,13 @@ async def __call__(self, scope, receive, send):
342
384
for key , value in attributes .items ():
343
385
current_span .set_attribute (key , value )
344
386
387
+ if current_span .kind == trace .SpanKind .SERVER :
388
+ custom_attributes = (
389
+ collect_custom_request_headers_attributes (scope )
390
+ )
391
+ if len (custom_attributes ) > 0 :
392
+ current_span .set_attributes (custom_attributes )
393
+
345
394
if callable (self .server_request_hook ):
346
395
self .server_request_hook (current_span , scope )
347
396
@@ -395,6 +444,18 @@ async def otel_send(message):
395
444
set_status_code (server_span , 200 )
396
445
set_status_code (send_span , 200 )
397
446
send_span .set_attribute ("type" , message ["type" ])
447
+ if (
448
+ server_span .is_recording ()
449
+ and server_span .kind == trace .SpanKind .SERVER
450
+ and "headers" in message
451
+ ):
452
+ custom_response_attributes = (
453
+ collect_custom_response_headers_attributes (message )
454
+ )
455
+ if len (custom_response_attributes ) > 0 :
456
+ server_span .set_attributes (
457
+ custom_response_attributes
458
+ )
398
459
399
460
propagator = get_global_response_propagator ()
400
461
if propagator :
0 commit comments