|
16 | 16 |
|
17 | 17 | import newrelic.packages.asgiref_compatibility as asgiref_compatibility |
18 | 18 | import newrelic.packages.six as six |
| 19 | +from newrelic.api.transaction import current_transaction |
19 | 20 | from newrelic.api.application import application_instance |
20 | 21 | from newrelic.api.web_transaction import WebTransaction |
21 | 22 | from newrelic.common.object_names import callable_name |
22 | | -from newrelic.common.object_wrapper import wrap_object, FunctionWrapper, function_wrapper |
| 23 | +from newrelic.common.object_wrapper import ( |
| 24 | + wrap_object, |
| 25 | + FunctionWrapper, |
| 26 | + function_wrapper, |
| 27 | +) |
23 | 28 | from newrelic.common.async_proxy import CoroutineProxy, LoopContext |
24 | 29 | from newrelic.api.html_insertion import insert_html_snippet, verify_body_exists |
25 | 30 |
|
@@ -105,7 +110,7 @@ def should_insert_html(self, headers): |
105 | 110 | # value 'identity' is used. Technically the value |
106 | 111 | # 'identity' should only be used in the header |
107 | 112 | # Accept-Encoding and not Content-Encoding. In |
108 | | - # other words, a WSGI application should not be |
| 113 | + # other words, a ASGI application should not be |
109 | 114 | # returning identity. We could check and allow it |
110 | 115 | # anyway and still do RUM insertion, but don't. |
111 | 116 |
|
@@ -251,21 +256,65 @@ async def send(self, event): |
251 | 256 | def ASGIApplicationWrapper( |
252 | 257 | wrapped, application=None, name=None, group=None, framework=None |
253 | 258 | ): |
254 | | - |
255 | 259 | def nr_asgi_wrapper(wrapped, instance, args, kwargs): |
256 | 260 | double_callable = asgiref_compatibility.is_double_callable(wrapped) |
257 | 261 | if double_callable: |
258 | 262 | is_v2_signature = (len(args) + len(kwargs)) == 1 |
259 | 263 | if not is_v2_signature: |
260 | 264 | return wrapped(*args, **kwargs) |
261 | | - wrapped = double_to_single_callable(wrapped) |
262 | 265 |
|
263 | 266 | scope = _bind_scope(*args, **kwargs) |
264 | 267 |
|
265 | 268 | if scope["type"] != "http": |
266 | 269 | return wrapped(*args, **kwargs) |
267 | 270 |
|
268 | 271 | async def nr_async_asgi(receive, send): |
| 272 | + # Check to see if any transaction is present, even an inactive |
| 273 | + # one which has been marked to be ignored or which has been |
| 274 | + # stopped already. |
| 275 | + |
| 276 | + transaction = current_transaction(active_only=False) |
| 277 | + |
| 278 | + if transaction: |
| 279 | + # If there is any active transaction we will return without |
| 280 | + # applying a new ASGI application wrapper context. In the |
| 281 | + # case of a transaction which is being ignored or which has |
| 282 | + # been stopped, we do that without doing anything further. |
| 283 | + |
| 284 | + if transaction.ignore_transaction or transaction.stopped: |
| 285 | + return await wrapped(scope, receive, send) |
| 286 | + |
| 287 | + # For any other transaction, we record the details of any |
| 288 | + # framework against the transaction for later reporting as |
| 289 | + # supportability metrics. |
| 290 | + |
| 291 | + if framework: |
| 292 | + transaction.add_framework_info( |
| 293 | + name=framework[0], version=framework[1] |
| 294 | + ) |
| 295 | + |
| 296 | + # Also override the web transaction name to be the name of |
| 297 | + # the wrapped callable if not explicitly named, and we want |
| 298 | + # the default name to be that of the ASGI component for the |
| 299 | + # framework. This will override the use of a raw URL which |
| 300 | + # can result in metric grouping issues where a framework is |
| 301 | + # not instrumented or is leaking URLs. |
| 302 | + |
| 303 | + settings = transaction._settings |
| 304 | + |
| 305 | + if name is None and settings: |
| 306 | + if framework is not None: |
| 307 | + naming_scheme = settings.transaction_name.naming_scheme |
| 308 | + if naming_scheme in (None, "framework"): |
| 309 | + transaction.set_transaction_name( |
| 310 | + callable_name(wrapped), priority=1 |
| 311 | + ) |
| 312 | + |
| 313 | + elif name: |
| 314 | + transaction.set_transaction_name(name, group, priority=1) |
| 315 | + |
| 316 | + return await wrapped(scope, receive, send) |
| 317 | + |
269 | 318 | with ASGIWebTransaction( |
270 | 319 | application=application_instance(application), |
271 | 320 | scope=scope, |
@@ -324,6 +373,7 @@ async def nr_async_asgi(receive, send): |
324 | 373 | return await coro |
325 | 374 |
|
326 | 375 | if double_callable: |
| 376 | + wrapped = double_to_single_callable(wrapped) |
327 | 377 | return nr_async_asgi |
328 | 378 | else: |
329 | 379 | return nr_async_asgi(*_bind_receive_send(*args, **kwargs)) |
|
0 commit comments