@@ -81,13 +81,25 @@ def strip_query_params(url: yarl.URL) -> str:
81
81
)
82
82
from opentelemetry .propagate import inject
83
83
from opentelemetry .semconv .trace import SpanAttributes
84
- from opentelemetry .trace import SpanKind , TracerProvider , get_tracer
84
+ from opentelemetry .trace import Span , SpanKind , TracerProvider , get_tracer
85
85
from opentelemetry .trace .status import Status , StatusCode
86
86
from opentelemetry .util .http import remove_url_credentials
87
87
88
88
_UrlFilterT = typing .Optional [typing .Callable [[str ], str ]]
89
- _SpanNameT = typing .Optional [
90
- typing .Union [typing .Callable [[aiohttp .TraceRequestStartParams ], str ], str ]
89
+ _RequestHookT = typing .Optional [
90
+ typing .Callable [[Span , aiohttp .TraceRequestStartParams ], None ]
91
+ ]
92
+ _ResponseHookT = typing .Optional [
93
+ typing .Callable [
94
+ [
95
+ Span ,
96
+ typing .Union [
97
+ aiohttp .TraceRequestEndParams ,
98
+ aiohttp .TraceRequestExceptionParams ,
99
+ ],
100
+ ],
101
+ None ,
102
+ ]
91
103
]
92
104
93
105
@@ -108,7 +120,8 @@ def url_path_span_name(params: aiohttp.TraceRequestStartParams) -> str:
108
120
109
121
def create_trace_config (
110
122
url_filter : _UrlFilterT = None ,
111
- span_name : _SpanNameT = None ,
123
+ request_hook : _RequestHookT = None ,
124
+ response_hook : _ResponseHookT = None ,
112
125
tracer_provider : TracerProvider = None ,
113
126
) -> aiohttp .TraceConfig :
114
127
"""Create an aiohttp-compatible trace configuration.
@@ -134,15 +147,16 @@ def create_trace_config(
134
147
it as a span attribute. This can be useful to remove sensitive data
135
148
such as API keys or user personal information.
136
149
137
- :param str span_name: Override the default span name.
150
+ :param Callable request_hook: Optional callback that can modify span name and request params.
151
+ :param Callable response_hook: Optional callback that can modify span name and response params.
138
152
:param tracer_provider: optional TracerProvider from which to get a Tracer
139
153
140
154
:return: An object suitable for use with :py:class:`aiohttp.ClientSession`.
141
155
:rtype: :py:class:`aiohttp.TraceConfig`
142
156
"""
143
157
# `aiohttp.TraceRequestStartParams` resolves to `aiohttp.tracing.TraceRequestStartParams`
144
- # which doesn't exist in the aiottp intersphinx inventory.
145
- # Explicitly specify the type for the `span_name ` param and rtype to work
158
+ # which doesn't exist in the aiohttp intersphinx inventory.
159
+ # Explicitly specify the type for the `request_hook` and `response_hook ` param and rtype to work
146
160
# around this issue.
147
161
148
162
tracer = get_tracer (__name__ , __version__ , tracer_provider )
@@ -161,17 +175,15 @@ async def on_request_start(
161
175
return
162
176
163
177
http_method = params .method .upper ()
164
- if trace_config_ctx .span_name is None :
165
- request_span_name = "HTTP {}" .format (http_method )
166
- elif callable (trace_config_ctx .span_name ):
167
- request_span_name = str (trace_config_ctx .span_name (params ))
168
- else :
169
- request_span_name = str (trace_config_ctx .span_name )
178
+ request_span_name = "HTTP {}" .format (http_method )
170
179
171
180
trace_config_ctx .span = trace_config_ctx .tracer .start_span (
172
181
request_span_name , kind = SpanKind .CLIENT ,
173
182
)
174
183
184
+ if callable (request_hook ):
185
+ request_hook (trace_config_ctx .span , params )
186
+
175
187
if trace_config_ctx .span .is_recording ():
176
188
attributes = {
177
189
SpanAttributes .HTTP_METHOD : http_method ,
@@ -198,6 +210,9 @@ async def on_request_end(
198
210
if trace_config_ctx .span is None :
199
211
return
200
212
213
+ if callable (response_hook ):
214
+ response_hook (trace_config_ctx .span , params )
215
+
201
216
if trace_config_ctx .span .is_recording ():
202
217
trace_config_ctx .span .set_status (
203
218
Status (http_status_to_status_code (int (params .response .status )))
@@ -215,6 +230,9 @@ async def on_request_exception(
215
230
if trace_config_ctx .span is None :
216
231
return
217
232
233
+ if callable (response_hook ):
234
+ response_hook (trace_config_ctx .span , params )
235
+
218
236
if trace_config_ctx .span .is_recording () and params .exception :
219
237
trace_config_ctx .span .set_status (Status (StatusCode .ERROR ))
220
238
trace_config_ctx .span .record_exception (params .exception )
@@ -223,7 +241,7 @@ async def on_request_exception(
223
241
def _trace_config_ctx_factory (** kwargs ):
224
242
kwargs .setdefault ("trace_request_ctx" , {})
225
243
return types .SimpleNamespace (
226
- span_name = span_name , tracer = tracer , url_filter = url_filter , ** kwargs
244
+ tracer = tracer , url_filter = url_filter , ** kwargs
227
245
)
228
246
229
247
trace_config = aiohttp .TraceConfig (
@@ -240,7 +258,8 @@ def _trace_config_ctx_factory(**kwargs):
240
258
def _instrument (
241
259
tracer_provider : TracerProvider = None ,
242
260
url_filter : _UrlFilterT = None ,
243
- span_name : _SpanNameT = None ,
261
+ request_hook : _RequestHookT = None ,
262
+ response_hook : _ResponseHookT = None ,
244
263
):
245
264
"""Enables tracing of all ClientSessions
246
265
@@ -256,7 +275,8 @@ def instrumented_init(wrapped, instance, args, kwargs):
256
275
257
276
trace_config = create_trace_config (
258
277
url_filter = url_filter ,
259
- span_name = span_name ,
278
+ request_hook = request_hook ,
279
+ response_hook = response_hook ,
260
280
tracer_provider = tracer_provider ,
261
281
)
262
282
trace_config ._is_instrumented_by_opentelemetry = True
@@ -304,12 +324,14 @@ def _instrument(self, **kwargs):
304
324
``url_filter``: A callback to process the requested URL prior to adding
305
325
it as a span attribute. This can be useful to remove sensitive data
306
326
such as API keys or user personal information.
307
- ``span_name``: Override the default span name.
327
+ ``request_hook``: An optional callback that is invoked right after a span is created.
328
+ ``response_hook``: An optional callback which is invoked right before the span is finished processing a response.
308
329
"""
309
330
_instrument (
310
331
tracer_provider = kwargs .get ("tracer_provider" ),
311
332
url_filter = kwargs .get ("url_filter" ),
312
- span_name = kwargs .get ("span_name" ),
333
+ request_hook = kwargs .get ("request_hook" ),
334
+ response_hook = kwargs .get ("response_hook" ),
313
335
)
314
336
315
337
def _uninstrument (self , ** kwargs ):
0 commit comments