@@ -115,7 +115,14 @@ def client_response_hook(span: Span, message: dict):
115115from opentelemetry .semconv .trace import SpanAttributes
116116from opentelemetry .trace import Span , set_span_in_context
117117from 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+ )
119126
120127_ServerRequestHookT = typing .Optional [typing .Callable [[Span , dict ], None ]]
121128_ClientRequestHookT = typing .Optional [typing .Callable [[Span , dict ], None ]]
@@ -223,6 +230,41 @@ def collect_request_attributes(scope):
223230 return result
224231
225232
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+
226268def get_host_port_url_tuple (scope ):
227269 """Returns (host, port, full_url) tuple."""
228270 server = scope .get ("server" ) or ["0.0.0.0" , 80 ]
@@ -342,6 +384,13 @@ async def __call__(self, scope, receive, send):
342384 for key , value in attributes .items ():
343385 current_span .set_attribute (key , value )
344386
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+
345394 if callable (self .server_request_hook ):
346395 self .server_request_hook (current_span , scope )
347396
@@ -395,6 +444,18 @@ async def otel_send(message):
395444 set_status_code (server_span , 200 )
396445 set_status_code (send_span , 200 )
397446 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+ )
398459
399460 propagator = get_global_response_propagator ()
400461 if propagator :
0 commit comments