1+ from dataclasses import dataclass
12import logging
23from typing import Dict
3- from typing import Union
4+ from typing import Optional
45
56from ddtrace import config
67from ddtrace ._trace .span import Span
1314
1415log = logging .getLogger (__name__ )
1516
17+
18+ @dataclass
19+ class ProxyHeaderContext :
20+ system_name : str
21+ request_time : str
22+ method : str
23+ path : str
24+ resource_path : Optional [str ]
25+ domain_name : Optional [str ]
26+ stage : str
27+ account_id : Optional [str ]
28+ api_id : Optional [str ]
29+ region : Optional [str ]
30+ user : Optional [str ]
31+
32+
1633# Checking lower case and upper case versions per WSGI spec following ddtrace/propagation/http.py's
1734# logic to extract http headers
1835POSSIBLE_PROXY_HEADER_SYSTEM = _possible_header ("x-dd-proxy" )
1936POSSIBLE_PROXY_HEADER_START_TIME_MS = _possible_header ("x-dd-proxy-request-time-ms" )
2037POSSIBLE_PROXY_HEADER_PATH = _possible_header ("x-dd-proxy-path" )
38+ POSSIBLE_PROXY_HEADER_RESOURCE_PATH = _possible_header ("x-dd-proxy-resource-path" )
2139POSSIBLE_PROXY_HEADER_HTTPMETHOD = _possible_header ("x-dd-proxy-httpmethod" )
2240POSSIBLE_PROXY_HEADER_DOMAIN = _possible_header ("x-dd-proxy-domain-name" )
2341POSSIBLE_PROXY_HEADER_STAGE = _possible_header ("x-dd-proxy-stage" )
42+ POSSIBLE_PROXY_HEADER_ACCOUNT_ID = _possible_header ("x-dd-proxy-account-id" )
43+ POSSIBLE_PROXY_HEADER_API_ID = _possible_header ("x-dd-proxy-api-id" )
44+ POSSIBLE_PROXY_HEADER_REGION = _possible_header ("x-dd-proxy-region" )
45+ POSSIBLE_PROXY_HEADER_USER = _possible_header ("x-dd-proxy-user" )
2446
2547supported_proxies : Dict [str , Dict [str , str ]] = {
2648 "aws-apigateway" : {"span_name" : "aws.apigateway" , "component" : "aws-apigateway" }
@@ -38,17 +60,25 @@ def create_inferred_proxy_span_if_headers_exist(ctx, headers, child_of, tracer)
3860 if not proxy_context :
3961 return None
4062
41- proxy_span_info = supported_proxies [proxy_context ["proxy_system_name" ]]
63+ proxy_span_info = supported_proxies [proxy_context .system_name ]
64+
65+ method = proxy_context .method
66+ route = proxy_context .resource_path
67+ if route :
68+ resource = f"{ method } { route } "
69+ else :
70+ path = proxy_context .path
71+ resource = f"{ method } { path } "
4272
4373 span = tracer .start_span (
4474 proxy_span_info ["span_name" ],
45- service = proxy_context .get ( " domain_name" , config ._get_service () ),
46- resource = proxy_context [ "method" ] + " " + proxy_context [ "path" ] ,
75+ service = proxy_context .domain_name or config ._get_service (),
76+ resource = resource ,
4777 span_type = SpanTypes .WEB ,
4878 activate = True ,
4979 child_of = child_of ,
5080 )
51- span .start_ns = int (proxy_context [ " request_time" ] ) * 1000000
81+ span .start_ns = int (proxy_context . request_time ) * 1000000
5282
5383 set_inferred_proxy_span_tags (span , proxy_context )
5484
@@ -62,25 +92,43 @@ def finish_callback(_):
6292 ctx .set_item ("headers" , headers )
6393
6494
65- def set_inferred_proxy_span_tags (span , proxy_context ) -> Span :
66- span ._set_tag_str (COMPONENT , supported_proxies [proxy_context [ "proxy_system_name" ] ]["component" ])
95+ def set_inferred_proxy_span_tags (span : Span , proxy_context : ProxyHeaderContext ) -> Span :
96+ span ._set_tag_str (COMPONENT , supported_proxies [proxy_context . system_name ]["component" ])
6797
68- span ._set_tag_str (http .METHOD , proxy_context ["method" ])
69- span ._set_tag_str (http .URL , f"{ proxy_context ['domain_name' ]} { proxy_context ['path' ]} " )
70- span ._set_tag_str ("stage" , proxy_context ["stage" ])
98+ span ._set_tag_str (http .METHOD , proxy_context .method )
99+ span ._set_tag_str (http .URL , f"https://{ proxy_context .domain_name } { proxy_context .path } " )
100+ if proxy_context .resource_path :
101+ span ._set_tag_str (http .ROUTE , proxy_context .resource_path )
102+
103+ span ._set_tag_str ("stage" , proxy_context .stage )
104+ if proxy_context .account_id and proxy_context .api_id and proxy_context .region and proxy_context .user :
105+ span ._set_tag_str ("account_id" , proxy_context .account_id )
106+ span ._set_tag_str ("apiid" , proxy_context .api_id )
107+ span ._set_tag_str ("region" , proxy_context .region )
108+ span ._set_tag_str ("user" , proxy_context .user )
109+ span ._set_tag_str (
110+ "dd_resource_key" , f"arn:aws:apigateway:{ proxy_context .region } ::/restapis/{ proxy_context .api_id } "
111+ )
71112
72113 span .set_metric ("_dd.inferred_span" , 1 )
73114 return span
74115
75116
76- def extract_inferred_proxy_context (headers ) -> Union [ None , Dict [ str , str ] ]:
117+ def extract_inferred_proxy_context (headers ) -> Optional [ ProxyHeaderContext ]:
77118 proxy_header_system = str (_extract_header_value (POSSIBLE_PROXY_HEADER_SYSTEM , headers ))
78119 proxy_header_start_time_ms = str (_extract_header_value (POSSIBLE_PROXY_HEADER_START_TIME_MS , headers ))
79120 proxy_header_path = str (_extract_header_value (POSSIBLE_PROXY_HEADER_PATH , headers ))
121+ proxy_header_resource_path = str (_extract_header_value (POSSIBLE_PROXY_HEADER_RESOURCE_PATH , headers ))
122+
80123 proxy_header_httpmethod = str (_extract_header_value (POSSIBLE_PROXY_HEADER_HTTPMETHOD , headers ))
81124 proxy_header_domain = str (_extract_header_value (POSSIBLE_PROXY_HEADER_DOMAIN , headers ))
82125 proxy_header_stage = str (_extract_header_value (POSSIBLE_PROXY_HEADER_STAGE , headers ))
83126
127+ proxy_header_account_id = str (_extract_header_value (POSSIBLE_PROXY_HEADER_ACCOUNT_ID , headers ))
128+ proxy_header_api_id = str (_extract_header_value (POSSIBLE_PROXY_HEADER_API_ID , headers ))
129+ proxy_header_region = str (_extract_header_value (POSSIBLE_PROXY_HEADER_REGION , headers ))
130+ proxy_header_user = str (_extract_header_value (POSSIBLE_PROXY_HEADER_USER , headers ))
131+
84132 # Exit if start time header is not present
85133 if proxy_header_start_time_ms is None :
86134 return None
@@ -92,14 +140,19 @@ def extract_inferred_proxy_context(headers) -> Union[None, Dict[str, str]]:
92140 )
93141 return None
94142
95- return {
96- "request_time" : proxy_header_start_time_ms ,
97- "method" : proxy_header_httpmethod ,
98- "path" : proxy_header_path ,
99- "stage" : proxy_header_stage ,
100- "domain_name" : proxy_header_domain ,
101- "proxy_system_name" : proxy_header_system ,
102- }
143+ return ProxyHeaderContext (
144+ proxy_header_system ,
145+ proxy_header_start_time_ms ,
146+ proxy_header_httpmethod ,
147+ proxy_header_path ,
148+ proxy_header_resource_path ,
149+ proxy_header_domain ,
150+ proxy_header_stage ,
151+ proxy_header_account_id ,
152+ proxy_header_api_id ,
153+ proxy_header_region ,
154+ proxy_header_user ,
155+ )
103156
104157
105158def normalize_headers (headers ) -> Dict [str , str ]:
0 commit comments