Skip to content

Commit f233c6f

Browse files
authored
Merge pull request #1111 from newrelic/fix-asgiref-instrumentation
Fix Asgiref Instrumentation
2 parents 6f2efd9 + c3c224f commit f233c6f

File tree

4 files changed

+116
-4
lines changed

4 files changed

+116
-4
lines changed

newrelic/hooks/adapter_asgiref.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,23 @@
1414

1515
from newrelic.api.time_trace import current_trace
1616
from newrelic.common.object_wrapper import wrap_function_wrapper
17+
from newrelic.common.signature import bind_args
1718
from newrelic.core.context import ContextOf, context_wrapper_async
1819

1920

20-
def _bind_thread_handler(loop, source_task, *args, **kwargs):
21-
return source_task
21+
async def wrap_SyncToAsync__call__(wrapped, instance, args, kwargs):
22+
kwargs["_nr_current_trace"] = current_trace()
23+
return await wrapped(*args, **kwargs)
2224

2325

2426
def thread_handler_wrapper(wrapped, instance, args, kwargs):
25-
task = _bind_thread_handler(*args, **kwargs)
26-
with ContextOf(trace_cache_id=id(task), strict=False):
27+
try:
28+
bound_args = bind_args(wrapped, args, kwargs)
29+
trace = bound_args["args"][0].keywords.pop("_nr_current_trace", None)
30+
except Exception:
31+
trace = None
32+
33+
with ContextOf(trace=trace, strict=False):
2734
return wrapped(*args, **kwargs)
2835

2936

@@ -34,4 +41,5 @@ def main_wrap_wrapper(wrapped, instance, args, kwargs):
3441

3542
def instrument_asgiref_sync(module):
3643
wrap_function_wrapper(module, "SyncToAsync.thread_handler", thread_handler_wrapper)
44+
wrap_function_wrapper(module, "SyncToAsync.__call__", wrap_SyncToAsync__call__)
3745
wrap_function_wrapper(module, "AsyncToSync.main_wrap", main_wrap_wrapper)

tests/adapter_asgiref/conftest.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright 2010 New Relic, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
from testing_support.fixture.event_loop import ( # noqa: F401; pylint: disable=W0611
15+
event_loop as loop,
16+
)
17+
from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
18+
collector_agent_registration_fixture,
19+
collector_available_fixture,
20+
)
21+
22+
_default_settings = {
23+
"transaction_tracer.explain_threshold": 0.0,
24+
"transaction_tracer.transaction_threshold": 0.0,
25+
"transaction_tracer.stack_trace_threshold": 0.0,
26+
"debug.log_data_collector_payloads": True,
27+
"debug.record_transaction_failure": True,
28+
}
29+
30+
collector_agent_registration = collector_agent_registration_fixture(
31+
app_name="Python Agent Test (adapter_asgiref)", default_settings=_default_settings
32+
)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Copyright 2010 New Relic, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from asgiref.sync import async_to_sync, sync_to_async
16+
from testing_support.validators.validate_transaction_metrics import (
17+
validate_transaction_metrics,
18+
)
19+
20+
from newrelic.api.background_task import background_task
21+
from newrelic.api.function_trace import function_trace
22+
from newrelic.api.transaction import current_transaction
23+
24+
25+
@sync_to_async
26+
@function_trace()
27+
def sync_func():
28+
assert current_transaction()
29+
return 1
30+
31+
32+
@validate_transaction_metrics(
33+
"test_context_propagation:test_sync_to_async_context_propagation",
34+
scoped_metrics=[("Function/test_context_propagation:sync_func", 1)],
35+
rollup_metrics=[("Function/test_context_propagation:sync_func", 1)],
36+
background_task=True,
37+
)
38+
@background_task()
39+
def test_sync_to_async_context_propagation(loop):
40+
async def _test():
41+
return_val = await sync_func()
42+
assert return_val == 1, "Sync function failed to return"
43+
44+
loop.run_until_complete(_test())
45+
46+
47+
@async_to_sync
48+
@function_trace()
49+
async def async_func():
50+
assert current_transaction()
51+
return 1
52+
53+
54+
@validate_transaction_metrics(
55+
"test_context_propagation:test_async_to_sync_context_propagation",
56+
scoped_metrics=[("Function/test_context_propagation:async_func", 1)],
57+
rollup_metrics=[("Function/test_context_propagation:async_func", 1)],
58+
background_task=True,
59+
)
60+
@background_task()
61+
def test_async_to_sync_context_propagation():
62+
return_val = async_func()
63+
assert return_val == 1, "Async function failed to return"

tox.ini

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ envlist =
7171
postgres-datastore_psycopg2-{py27,py37,py38,py39,py310,py311,py312}-psycopg2latest,
7272
postgres-datastore_psycopg2cffi-{py27,pypy27,py37,py38,py39,py310,py311,py312}-psycopg2cffilatest,
7373
postgres-datastore_pyodbc-{py27,py37,py38,py39,py310,py311,py312}-pyodbclatest,
74+
python-adapter_asgiref-{py37,py38,py39,py310,py311,py312,pypy310}-asgireflatest,
75+
python-adapter_asgiref-py310-asgiref{0303,0304,0305,0306,0307},
7476
python-adapter_cheroot-{py27,py37,py38,py39,py310,py311,py312},
7577
python-adapter_daphne-{py37,py38,py39,py310,py311,py312}-daphnelatest,
7678
python-adapter_gevent-{py27,py37,py38,py310,py311,py312},
@@ -163,6 +165,12 @@ deps =
163165
WebTest==2.0.35
164166

165167
# Test Suite Dependencies
168+
adapter_asgiref-asgireflatest: asgiref
169+
adapter_asgiref-asgiref0303: asgiref<3.4
170+
adapter_asgiref-asgiref0304: asgiref<3.5
171+
adapter_asgiref-asgiref0305: asgiref<3.6
172+
adapter_asgiref-asgiref0306: asgiref<3.7
173+
adapter_asgiref-asgiref0307: asgiref<3.8
166174
adapter_cheroot: cheroot
167175
adapter_daphne-daphnelatest: daphne
168176
adapter_gevent: WSGIProxy2
@@ -386,6 +394,7 @@ extras =
386394
agent_streaming: infinite-tracing
387395

388396
changedir =
397+
adapter_asgiref: tests/adapter_asgiref
389398
adapter_cheroot: tests/adapter_cheroot
390399
adapter_daphne: tests/adapter_daphne
391400
adapter_gevent: tests/adapter_gevent

0 commit comments

Comments
 (0)