From 9c2800fbe91ef8a5e308729f4b1989b8f92b2fea Mon Sep 17 00:00:00 2001
From: Povilas Versockas
Date: Mon, 13 May 2024 11:35:33 +0300
Subject: [PATCH] fix(async-io): check for __name__ atribute when tracing
coroutine
---
CHANGELOG.md | 2 +
.../instrumentation/asyncio/__init__.py | 4 +-
.../tests/test_asyncio_anext.py | 59 +++++++++++++++++++
3 files changed, 64 insertions(+), 1 deletion(-)
create mode 100644 instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_anext.py
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 28e8a85c26..ab9b88a4ed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -51,6 +51,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#2461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2461))
- Remove SDK dependency from opentelemetry-instrumentation-grpc
([#2474](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2474))
+- `opentelemetry-instrumentation-asyncio` Check for __name__ attribute in the coroutine
+ ([#2521](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2521))
## Version 1.24.0/0.45b0 (2024-03-28)
diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py b/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py
index 72aa5fd2aa..aea7c585fc 100644
--- a/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py
+++ b/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py
@@ -254,7 +254,9 @@ def trace_item(self, coro_or_future):
# Task is already traced, return it
if isinstance(coro_or_future, asyncio.Task):
return coro_or_future
- if asyncio.iscoroutine(coro_or_future):
+ if asyncio.iscoroutine(coro_or_future) and hasattr(
+ coro_or_future, "__name__"
+ ):
return self.trace_coroutine(coro_or_future)
if futures.isfuture(coro_or_future):
return self.trace_future(coro_or_future)
diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_anext.py b/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_anext.py
new file mode 100644
index 0000000000..34a54823b4
--- /dev/null
+++ b/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_anext.py
@@ -0,0 +1,59 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import asyncio
+import sys
+from unittest.mock import patch
+
+# pylint: disable=no-name-in-module
+from opentelemetry.instrumentation.asyncio import AsyncioInstrumentor
+from opentelemetry.instrumentation.asyncio.environment_variables import (
+ OTEL_PYTHON_ASYNCIO_COROUTINE_NAMES_TO_TRACE,
+)
+from opentelemetry.test.test_base import TestBase
+from opentelemetry.trace import get_tracer
+
+from .common_test_func import async_func
+
+
+class TestAsyncioAnext(TestBase):
+ @patch.dict(
+ "os.environ",
+ {OTEL_PYTHON_ASYNCIO_COROUTINE_NAMES_TO_TRACE: "async_func"},
+ )
+ def setUp(self):
+ super().setUp()
+ AsyncioInstrumentor().instrument()
+ self._tracer = get_tracer(
+ __name__,
+ )
+
+ def tearDown(self):
+ super().tearDown()
+ AsyncioInstrumentor().uninstrument()
+
+ # Asyncio anext() does not have __name__ attribute, which is used to determine if the coroutine should be traced.
+ # This test is to ensure that the instrumentation does not break when the coroutine does not have __name__ attribute.
+ def test_asyncio_anext(self):
+ async def main():
+ async def async_gen():
+ for i in range(2):
+ yield i
+
+ async_gen_instance = async_gen()
+ a = anext(async_gen_instance)
+ await asyncio.create_task(a)
+
+ asyncio.run(main())
+ spans = self.memory_exporter.get_finished_spans()
+ self.assertEqual(len(spans), 0)