6262
6363# FIXME: fix the importing of this private attribute when the location of the _SUPPRESS_HTTP_INSTRUMENTATION_KEY is defined.
6464from opentelemetry .context import _SUPPRESS_HTTP_INSTRUMENTATION_KEY
65+ from opentelemetry .instrumentation ._semconv import (
66+ _METRIC_ATTRIBUTES_CLIENT_DURATION_NAME ,
67+ _SPAN_ATTRIBUTES_ERROR_TYPE ,
68+ _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS ,
69+ _SPAN_ATTRIBUTES_NETWORK_PEER_PORT ,
70+ _filter_duration_attrs ,
71+ _get_schema_url ,
72+ _OpenTelemetrySemanticConventionStability ,
73+ _OpenTelemetryStabilityMode ,
74+ _OpenTelemetryStabilitySignalType ,
75+ _report_new ,
76+ _report_old ,
77+ _set_http_hostname ,
78+ _set_http_method ,
79+ _set_http_net_peer_name ,
80+ _set_http_network_protocol_version ,
81+ _set_http_port ,
82+ _set_http_scheme ,
83+ _set_http_status_code ,
84+ _set_http_url ,
85+ )
6586from opentelemetry .instrumentation .instrumentor import BaseInstrumentor
6687from opentelemetry .instrumentation .requests .package import _instruments
6788from opentelemetry .instrumentation .requests .version import __version__
7293from opentelemetry .metrics import Histogram , get_meter
7394from opentelemetry .propagate import inject
7495from opentelemetry .semconv .metrics import MetricInstruments
75- from opentelemetry .semconv .trace import SpanAttributes
7696from opentelemetry .trace import SpanKind , Tracer , get_tracer
7797from opentelemetry .trace .span import Span
78- from opentelemetry .trace .status import Status
98+ from opentelemetry .trace .status import StatusCode
7999from opentelemetry .util .http import (
80100 ExcludeList ,
81101 get_excluded_urls ,
82102 parse_excluded_urls ,
83103 remove_url_credentials ,
104+ sanitize_method ,
84105)
85106from opentelemetry .util .http .httplib import set_ip_on_next_http_connection
86107
94115# pylint: disable=R0915
95116def _instrument (
96117 tracer : Tracer ,
97- duration_histogram : Histogram ,
118+ duration_histogram_old : Histogram ,
119+ duration_histogram_new : Histogram ,
98120 request_hook : _RequestHookT = None ,
99121 response_hook : _ResponseHookT = None ,
100122 excluded_urls : ExcludeList = None ,
123+ sem_conv_opt_in_mode : _OpenTelemetryStabilityMode = _OpenTelemetryStabilityMode .DEFAULT ,
101124):
102125 """Enables tracing of all requests calls that go through
103126 :code:`requests.session.Session.request` (this includes
@@ -132,31 +155,58 @@ def get_or_create_headers():
132155 return wrapped_send (self , request , ** kwargs )
133156
134157 # See
135- # https://github.com/open-telemetry/opentelemetry-specification /blob/main/specification/trace/semantic_conventions/ http.md#http-client
136- method = request .method . upper ()
158+ # https://github.com/open-telemetry/semantic-conventions /blob/main/docs/http/ http-spans .md#http-client
159+ method = request .method
137160 span_name = get_default_span_name (method )
138161
139162 url = remove_url_credentials (request .url )
140163
141- span_attributes = {
142- SpanAttributes .HTTP_METHOD : method ,
143- SpanAttributes .HTTP_URL : url ,
144- }
164+ span_attributes = {}
165+ _set_http_method (
166+ span_attributes , method , span_name , sem_conv_opt_in_mode
167+ )
168+ _set_http_url (span_attributes , url , sem_conv_opt_in_mode )
145169
146- metric_labels = {
147- SpanAttributes .HTTP_METHOD : method ,
148- }
170+ metric_labels = {}
171+ _set_http_method (
172+ metric_labels , method , span_name , sem_conv_opt_in_mode
173+ )
149174
150175 try :
151176 parsed_url = urlparse (url )
152- metric_labels [SpanAttributes .HTTP_SCHEME ] = parsed_url .scheme
177+ if parsed_url .scheme :
178+ _set_http_scheme (
179+ metric_labels , parsed_url .scheme , sem_conv_opt_in_mode
180+ )
153181 if parsed_url .hostname :
154- metric_labels [SpanAttributes .HTTP_HOST ] = parsed_url .hostname
155- metric_labels [
156- SpanAttributes .NET_PEER_NAME
157- ] = parsed_url .hostname
182+ _set_http_hostname (
183+ metric_labels , parsed_url .hostname , sem_conv_opt_in_mode
184+ )
185+ _set_http_net_peer_name (
186+ metric_labels , parsed_url .hostname , sem_conv_opt_in_mode
187+ )
188+ if _report_new (sem_conv_opt_in_mode ):
189+ _set_http_hostname (
190+ span_attributes ,
191+ parsed_url .hostname ,
192+ sem_conv_opt_in_mode ,
193+ )
194+ # Use semconv library when available
195+ span_attributes [
196+ _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS
197+ ] = parsed_url .hostname
158198 if parsed_url .port :
159- metric_labels [SpanAttributes .NET_PEER_PORT ] = parsed_url .port
199+ _set_http_port (
200+ metric_labels , parsed_url .port , sem_conv_opt_in_mode
201+ )
202+ if _report_new (sem_conv_opt_in_mode ):
203+ _set_http_port (
204+ span_attributes , parsed_url .port , sem_conv_opt_in_mode
205+ )
206+ # Use semconv library when available
207+ span_attributes [
208+ _SPAN_ATTRIBUTES_NETWORK_PEER_PORT
209+ ] = parsed_url .port
160210 except ValueError :
161211 pass
162212
@@ -182,35 +232,78 @@ def get_or_create_headers():
182232 exception = exc
183233 result = getattr (exc , "response" , None )
184234 finally :
185- elapsed_time = max (
186- round ((default_timer () - start_time ) * 1000 ), 0
187- )
235+ elapsed_time = max (default_timer () - start_time , 0 )
188236 context .detach (token )
189237
190238 if isinstance (result , Response ):
239+ span_attributes = {}
191240 if span .is_recording ():
192- span .set_attribute (
193- SpanAttributes .HTTP_STATUS_CODE , result .status_code
241+ _set_http_status_code (
242+ span_attributes ,
243+ result .status_code ,
244+ sem_conv_opt_in_mode ,
194245 )
195- span . set_status (
196- Status ( http_status_to_status_code ( result .status_code ))
246+ _set_http_status_code (
247+ metric_labels , result .status_code , sem_conv_opt_in_mode
197248 )
198-
199- metric_labels [
200- SpanAttributes .HTTP_STATUS_CODE
201- ] = result .status_code
249+ status_code = http_status_to_status_code (
250+ result .status_code
251+ )
252+ span .set_status (status_code )
253+ if (
254+ _report_new (sem_conv_opt_in_mode )
255+ and status_code is StatusCode .ERROR
256+ ):
257+ span_attributes [_SPAN_ATTRIBUTES_ERROR_TYPE ] = str (
258+ result .status_code
259+ )
260+ metric_labels [_SPAN_ATTRIBUTES_ERROR_TYPE ] = str (
261+ result .status_code
262+ )
202263
203264 if result .raw is not None :
204265 version = getattr (result .raw , "version" , None )
205266 if version :
206- metric_labels [SpanAttributes .HTTP_FLAVOR ] = (
207- "1.1" if version == 11 else "1.0"
267+ # Only HTTP/1 is supported by requests
268+ version_text = "1.1" if version == 11 else "1.0"
269+ _set_http_network_protocol_version (
270+ metric_labels , version_text , sem_conv_opt_in_mode
208271 )
272+ if _report_new (sem_conv_opt_in_mode ):
273+ _set_http_network_protocol_version (
274+ span_attributes ,
275+ version_text ,
276+ sem_conv_opt_in_mode ,
277+ )
278+ for key , val in span_attributes .items ():
279+ span .set_attribute (key , val )
209280
210281 if callable (response_hook ):
211282 response_hook (span , request , result )
212283
213- duration_histogram .record (elapsed_time , attributes = metric_labels )
284+ if exception is not None and _report_new (sem_conv_opt_in_mode ):
285+ span .set_attribute (
286+ _SPAN_ATTRIBUTES_ERROR_TYPE , type (exception ).__qualname__
287+ )
288+ metric_labels [_SPAN_ATTRIBUTES_ERROR_TYPE ] = type (
289+ exception
290+ ).__qualname__
291+
292+ if duration_histogram_old is not None :
293+ duration_attrs_old = _filter_duration_attrs (
294+ metric_labels , _OpenTelemetryStabilityMode .DEFAULT
295+ )
296+ duration_histogram_old .record (
297+ max (round (elapsed_time * 1000 ), 0 ),
298+ attributes = duration_attrs_old ,
299+ )
300+ if duration_histogram_new is not None :
301+ duration_attrs_new = _filter_duration_attrs (
302+ metric_labels , _OpenTelemetryStabilityMode .HTTP
303+ )
304+ duration_histogram_new .record (
305+ elapsed_time , attributes = duration_attrs_new
306+ )
214307
215308 if exception is not None :
216309 raise exception .with_traceback (exception .__traceback__ )
@@ -254,7 +347,7 @@ def get_default_span_name(method):
254347 Returns:
255348 span name
256349 """
257- return method .strip ()
350+ return sanitize_method ( method .upper (). strip () )
258351
259352
260353class RequestsInstrumentor (BaseInstrumentor ):
@@ -276,34 +369,49 @@ def _instrument(self, **kwargs):
276369 ``excluded_urls``: A string containing a comma-delimited
277370 list of regexes used to exclude URLs from tracking
278371 """
372+ semconv_opt_in_mode = _OpenTelemetrySemanticConventionStability ._get_opentelemetry_stability_opt_in_mode (
373+ _OpenTelemetryStabilitySignalType .HTTP ,
374+ )
375+ schema_url = _get_schema_url (semconv_opt_in_mode )
279376 tracer_provider = kwargs .get ("tracer_provider" )
280377 tracer = get_tracer (
281378 __name__ ,
282379 __version__ ,
283380 tracer_provider ,
284- schema_url = "https://opentelemetry.io/schemas/1.11.0" ,
381+ schema_url = schema_url ,
285382 )
286383 excluded_urls = kwargs .get ("excluded_urls" )
287384 meter_provider = kwargs .get ("meter_provider" )
288385 meter = get_meter (
289386 __name__ ,
290387 __version__ ,
291388 meter_provider ,
292- schema_url = "https://opentelemetry.io/schemas/1.11.0" ,
293- )
294- duration_histogram = meter .create_histogram (
295- name = MetricInstruments .HTTP_CLIENT_DURATION ,
296- unit = "ms" ,
297- description = "measures the duration of the outbound HTTP request" ,
389+ schema_url = schema_url ,
298390 )
391+ duration_histogram_old = None
392+ if _report_old (semconv_opt_in_mode ):
393+ duration_histogram_old = meter .create_histogram (
394+ name = MetricInstruments .HTTP_CLIENT_DURATION ,
395+ unit = "ms" ,
396+ description = "measures the duration of the outbound HTTP request" ,
397+ )
398+ duration_histogram_new = None
399+ if _report_new (semconv_opt_in_mode ):
400+ duration_histogram_new = meter .create_histogram (
401+ name = _METRIC_ATTRIBUTES_CLIENT_DURATION_NAME ,
402+ unit = "s" ,
403+ description = "Duration of HTTP client requests." ,
404+ )
299405 _instrument (
300406 tracer ,
301- duration_histogram ,
407+ duration_histogram_old ,
408+ duration_histogram_new ,
302409 request_hook = kwargs .get ("request_hook" ),
303410 response_hook = kwargs .get ("response_hook" ),
304411 excluded_urls = _excluded_urls_from_env
305412 if excluded_urls is None
306413 else parse_excluded_urls (excluded_urls ),
414+ sem_conv_opt_in_mode = semconv_opt_in_mode ,
307415 )
308416
309417 def _uninstrument (self , ** kwargs ):
0 commit comments