@@ -90,19 +90,28 @@ def response_hook(span: Span, params: typing.Union[
90
90
91
91
from opentelemetry import context as context_api
92
92
from opentelemetry import trace
93
+ from opentelemetry .instrumentation ._semconv import (
94
+ _get_schema_url ,
95
+ _HTTPStabilityMode ,
96
+ _OpenTelemetrySemanticConventionStability ,
97
+ _OpenTelemetryStabilitySignalType ,
98
+ _report_new ,
99
+ _set_http_method ,
100
+ _set_http_url ,
101
+ _set_status ,
102
+ )
93
103
from opentelemetry .instrumentation .aiohttp_client .package import _instruments
94
104
from opentelemetry .instrumentation .aiohttp_client .version import __version__
95
105
from opentelemetry .instrumentation .instrumentor import BaseInstrumentor
96
106
from opentelemetry .instrumentation .utils import (
97
- http_status_to_status_code ,
98
107
is_instrumentation_enabled ,
99
108
unwrap ,
100
109
)
101
110
from opentelemetry .propagate import inject
102
- from opentelemetry .semconv .trace import SpanAttributes
111
+ from opentelemetry .semconv .attributes . error_attributes import ERROR_TYPE
103
112
from opentelemetry .trace import Span , SpanKind , TracerProvider , get_tracer
104
113
from opentelemetry .trace .status import Status , StatusCode
105
- from opentelemetry .util .http import remove_url_credentials
114
+ from opentelemetry .util .http import remove_url_credentials , sanitize_method
106
115
107
116
_UrlFilterT = typing .Optional [typing .Callable [[yarl .URL ], str ]]
108
117
_RequestHookT = typing .Optional [
@@ -122,11 +131,46 @@ def response_hook(span: Span, params: typing.Union[
122
131
]
123
132
124
133
134
+ def _get_span_name (method : str ) -> str :
135
+ method = sanitize_method (method .upper ().strip ())
136
+ if method == "_OTHER" :
137
+ method = "HTTP"
138
+
139
+ return method
140
+
141
+
142
+ def _set_http_status_code_attribute (
143
+ span ,
144
+ status_code ,
145
+ metric_attributes = None ,
146
+ sem_conv_opt_in_mode = _HTTPStabilityMode .DEFAULT ,
147
+ ):
148
+ status_code_str = str (status_code )
149
+ try :
150
+ status_code = int (status_code )
151
+ except ValueError :
152
+ status_code = - 1
153
+ if metric_attributes is None :
154
+ metric_attributes = {}
155
+ # When we have durations we should set metrics only once
156
+ # Also the decision to include status code on a histogram should
157
+ # not be dependent on tracing decisions.
158
+ _set_status (
159
+ span ,
160
+ metric_attributes ,
161
+ status_code ,
162
+ status_code_str ,
163
+ server_span = False ,
164
+ sem_conv_opt_in_mode = sem_conv_opt_in_mode ,
165
+ )
166
+
167
+
125
168
def create_trace_config (
126
169
url_filter : _UrlFilterT = None ,
127
170
request_hook : _RequestHookT = None ,
128
171
response_hook : _ResponseHookT = None ,
129
172
tracer_provider : TracerProvider = None ,
173
+ sem_conv_opt_in_mode : _HTTPStabilityMode = _HTTPStabilityMode .DEFAULT ,
130
174
) -> aiohttp .TraceConfig :
131
175
"""Create an aiohttp-compatible trace configuration.
132
176
@@ -167,9 +211,12 @@ def create_trace_config(
167
211
__name__ ,
168
212
__version__ ,
169
213
tracer_provider ,
170
- schema_url = "https://opentelemetry.io/schemas/1.11.0" ,
214
+ schema_url = _get_schema_url ( sem_conv_opt_in_mode ) ,
171
215
)
172
216
217
+ # TODO: Use this when we have durations for aiohttp-client
218
+ metric_attributes = {}
219
+
173
220
def _end_trace (trace_config_ctx : types .SimpleNamespace ):
174
221
context_api .detach (trace_config_ctx .token )
175
222
trace_config_ctx .span .end ()
@@ -183,18 +230,22 @@ async def on_request_start(
183
230
trace_config_ctx .span = None
184
231
return
185
232
186
- http_method = params .method . upper ()
187
- request_span_name = f" { http_method } "
233
+ http_method = params .method
234
+ request_span_name = _get_span_name ( http_method )
188
235
request_url = (
189
236
remove_url_credentials (trace_config_ctx .url_filter (params .url ))
190
237
if callable (trace_config_ctx .url_filter )
191
238
else remove_url_credentials (str (params .url ))
192
239
)
193
240
194
- span_attributes = {
195
- SpanAttributes .HTTP_METHOD : http_method ,
196
- SpanAttributes .HTTP_URL : request_url ,
197
- }
241
+ span_attributes = {}
242
+ _set_http_method (
243
+ span_attributes ,
244
+ http_method ,
245
+ request_span_name ,
246
+ sem_conv_opt_in_mode ,
247
+ )
248
+ _set_http_url (span_attributes , request_url , sem_conv_opt_in_mode )
198
249
199
250
trace_config_ctx .span = trace_config_ctx .tracer .start_span (
200
251
request_span_name , kind = SpanKind .CLIENT , attributes = span_attributes
@@ -219,14 +270,13 @@ async def on_request_end(
219
270
220
271
if callable (response_hook ):
221
272
response_hook (trace_config_ctx .span , params )
273
+ _set_http_status_code_attribute (
274
+ trace_config_ctx .span ,
275
+ params .response .status ,
276
+ metric_attributes ,
277
+ sem_conv_opt_in_mode ,
278
+ )
222
279
223
- if trace_config_ctx .span .is_recording ():
224
- trace_config_ctx .span .set_status (
225
- Status (http_status_to_status_code (int (params .response .status )))
226
- )
227
- trace_config_ctx .span .set_attribute (
228
- SpanAttributes .HTTP_STATUS_CODE , params .response .status
229
- )
230
280
_end_trace (trace_config_ctx )
231
281
232
282
async def on_request_exception (
@@ -238,7 +288,13 @@ async def on_request_exception(
238
288
return
239
289
240
290
if trace_config_ctx .span .is_recording () and params .exception :
241
- trace_config_ctx .span .set_status (Status (StatusCode .ERROR ))
291
+ exc_type = type (params .exception ).__qualname__
292
+ if _report_new (sem_conv_opt_in_mode ):
293
+ trace_config_ctx .span .set_attribute (ERROR_TYPE , exc_type )
294
+
295
+ trace_config_ctx .span .set_status (
296
+ Status (StatusCode .ERROR , exc_type )
297
+ )
242
298
trace_config_ctx .span .record_exception (params .exception )
243
299
244
300
if callable (response_hook ):
@@ -271,6 +327,7 @@ def _instrument(
271
327
trace_configs : typing .Optional [
272
328
typing .Sequence [aiohttp .TraceConfig ]
273
329
] = None ,
330
+ sem_conv_opt_in_mode : _HTTPStabilityMode = _HTTPStabilityMode .DEFAULT ,
274
331
):
275
332
"""Enables tracing of all ClientSessions
276
333
@@ -293,6 +350,7 @@ def instrumented_init(wrapped, instance, args, kwargs):
293
350
request_hook = request_hook ,
294
351
response_hook = response_hook ,
295
352
tracer_provider = tracer_provider ,
353
+ sem_conv_opt_in_mode = sem_conv_opt_in_mode ,
296
354
)
297
355
trace_config ._is_instrumented_by_opentelemetry = True
298
356
client_trace_configs .append (trace_config )
@@ -344,12 +402,17 @@ def _instrument(self, **kwargs):
344
402
``trace_configs``: An optional list of aiohttp.TraceConfig items, allowing customize enrichment of spans
345
403
based on aiohttp events (see specification: https://docs.aiohttp.org/en/stable/tracing_reference.html)
346
404
"""
405
+ _OpenTelemetrySemanticConventionStability ._initialize ()
406
+ _sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability ._get_opentelemetry_stability_opt_in_mode (
407
+ _OpenTelemetryStabilitySignalType .HTTP ,
408
+ )
347
409
_instrument (
348
410
tracer_provider = kwargs .get ("tracer_provider" ),
349
411
url_filter = kwargs .get ("url_filter" ),
350
412
request_hook = kwargs .get ("request_hook" ),
351
413
response_hook = kwargs .get ("response_hook" ),
352
414
trace_configs = kwargs .get ("trace_configs" ),
415
+ sem_conv_opt_in_mode = _sem_conv_opt_in_mode ,
353
416
)
354
417
355
418
def _uninstrument (self , ** kwargs ):
0 commit comments