diff --git a/CHANGELOG.md b/CHANGELOG.md index b92f0c62c..710062398 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Breaking changes + +- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-fastapi`, `opentelemetry-instrumentation-starlette` Use `tracer` and `meter` of originating components instead of one from `asgi` middleware + ([#2580](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2580)) + ### Fixed - `opentelemetry-instrumentation-httpx` Ensure httpx.get or httpx.request like methods are instrumented diff --git a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py index 8edb3420b..e416e8dec 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py @@ -461,6 +461,8 @@ class OpenTelemetryMiddleware: scope and event which are sent as dictionaries for when the method send is called. tracer_provider: The optional tracer provider to use. If omitted the current globally configured one is used. + meter_provider: The optional meter provider to use. If omitted + the current globally configured one is used. """ # pylint: disable=too-many-branches @@ -474,17 +476,22 @@ class OpenTelemetryMiddleware: client_response_hook: ClientResponseHook = None, tracer_provider=None, meter_provider=None, + tracer=None, meter=None, http_capture_headers_server_request: list[str] | None = None, http_capture_headers_server_response: list[str] | None = None, http_capture_headers_sanitize_fields: list[str] | None = None, ): self.app = guarantee_single_callable(app) - self.tracer = trace.get_tracer( - __name__, - __version__, - tracer_provider, - schema_url="https://opentelemetry.io/schemas/1.11.0", + self.tracer = ( + trace.get_tracer( + __name__, + __version__, + tracer_provider, + schema_url="https://opentelemetry.io/schemas/1.11.0", + ) + if tracer is None + else tracer ) self.meter = ( get_meter( diff --git a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py index 902cd4ec7..d2fe6bc52 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py @@ -309,6 +309,10 @@ class TestAsgiApplication(AsgiTestBase): self.assertEqual(span.name, expected["name"]) self.assertEqual(span.kind, expected["kind"]) self.assertDictEqual(dict(span.attributes), expected["attributes"]) + self.assertEqual( + span.instrumentation_scope.name, + "opentelemetry.instrumentation.asgi", + ) def test_basic_asgi_call(self): """Test that spans are emitted as expected.""" @@ -728,6 +732,10 @@ class TestAsgiApplication(AsgiTestBase): self.assertTrue(len(resource_metric.scope_metrics) != 0) for scope_metric in resource_metric.scope_metrics: self.assertTrue(len(scope_metric.metrics) != 0) + self.assertEqual( + scope_metric.scope.name, + "opentelemetry.instrumentation.asgi", + ) for metric in scope_metric.metrics: self.assertIn(metric.name, _expected_metric_names) data_points = list(metric.data.data_points) diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py b/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py index 6f52c6ef3..263cc0fb7 100644 --- a/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py @@ -188,6 +188,7 @@ from opentelemetry.instrumentation.fastapi.version import __version__ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.metrics import get_meter from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.trace import get_tracer from opentelemetry.util.http import get_excluded_urls, parse_excluded_urls _excluded_urls_from_env = get_excluded_urls("FASTAPI") @@ -221,6 +222,12 @@ class FastAPIInstrumentor(BaseInstrumentor): excluded_urls = _excluded_urls_from_env else: excluded_urls = parse_excluded_urls(excluded_urls) + tracer = get_tracer( + __name__, + __version__, + tracer_provider, + schema_url="https://opentelemetry.io/schemas/1.11.0", + ) meter = get_meter( __name__, __version__, @@ -235,7 +242,8 @@ class FastAPIInstrumentor(BaseInstrumentor): server_request_hook=server_request_hook, client_request_hook=client_request_hook, client_response_hook=client_response_hook, - tracer_provider=tracer_provider, + # Pass in tracer/meter to get __name__and __version__ of fastapi instrumentation + tracer=tracer, meter=meter, ) app._is_instrumented_by_opentelemetry = True @@ -298,6 +306,12 @@ class _InstrumentedFastAPI(fastapi.FastAPI): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + tracer = get_tracer( + __name__, + __version__, + _InstrumentedFastAPI._tracer_provider, + schema_url="https://opentelemetry.io/schemas/1.11.0", + ) meter = get_meter( __name__, __version__, @@ -311,7 +325,8 @@ class _InstrumentedFastAPI(fastapi.FastAPI): server_request_hook=_InstrumentedFastAPI._server_request_hook, client_request_hook=_InstrumentedFastAPI._client_request_hook, client_response_hook=_InstrumentedFastAPI._client_response_hook, - tracer_provider=_InstrumentedFastAPI._tracer_provider, + # Pass in tracer/meter to get __name__and __version__ of fastapi instrumentation + tracer=tracer, meter=meter, ) self._is_instrumented_by_opentelemetry = True diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py b/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py index 5cf9a0d59..948bd343d 100644 --- a/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py @@ -117,6 +117,10 @@ class TestFastAPIManualInstrumentation(TestBase): self.assertEqual(len(spans), 3) for span in spans: self.assertIn("GET /foobar", span.name) + self.assertEqual( + span.instrumentation_scope.name, + "opentelemetry.instrumentation.fastapi", + ) def test_uninstrument_app(self): self._client.get("/foobar") @@ -197,6 +201,10 @@ class TestFastAPIManualInstrumentation(TestBase): for resource_metric in metrics_list.resource_metrics: self.assertTrue(len(resource_metric.scope_metrics) == 1) for scope_metric in resource_metric.scope_metrics: + self.assertEqual( + scope_metric.scope.name, + "opentelemetry.instrumentation.fastapi", + ) self.assertTrue(len(scope_metric.metrics) == 3) for metric in scope_metric.metrics: self.assertIn(metric.name, _expected_metric_names) diff --git a/instrumentation/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/__init__.py b/instrumentation/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/__init__.py index 83f5b5c52..4bb360893 100644 --- a/instrumentation/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/__init__.py @@ -185,6 +185,7 @@ from opentelemetry.instrumentation.starlette.package import _instruments from opentelemetry.instrumentation.starlette.version import __version__ from opentelemetry.metrics import get_meter from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.trace import get_tracer from opentelemetry.util.http import get_excluded_urls _excluded_urls = get_excluded_urls("STARLETTE") @@ -208,6 +209,12 @@ class StarletteInstrumentor(BaseInstrumentor): tracer_provider=None, ): """Instrument an uninstrumented Starlette application.""" + tracer = get_tracer( + __name__, + __version__, + tracer_provider, + schema_url="https://opentelemetry.io/schemas/1.11.0", + ) meter = get_meter( __name__, __version__, @@ -222,7 +229,8 @@ class StarletteInstrumentor(BaseInstrumentor): server_request_hook=server_request_hook, client_request_hook=client_request_hook, client_response_hook=client_response_hook, - tracer_provider=tracer_provider, + # Pass in tracer/meter to get __name__and __version__ of starlette instrumentation + tracer=tracer, meter=meter, ) app.is_instrumented_by_opentelemetry = True @@ -278,6 +286,12 @@ class _InstrumentedStarlette(applications.Starlette): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + tracer = get_tracer( + __name__, + __version__, + _InstrumentedStarlette._tracer_provider, + schema_url="https://opentelemetry.io/schemas/1.11.0", + ) meter = get_meter( __name__, __version__, @@ -291,7 +305,8 @@ class _InstrumentedStarlette(applications.Starlette): server_request_hook=_InstrumentedStarlette._server_request_hook, client_request_hook=_InstrumentedStarlette._client_request_hook, client_response_hook=_InstrumentedStarlette._client_response_hook, - tracer_provider=_InstrumentedStarlette._tracer_provider, + # Pass in tracer/meter to get __name__and __version__ of starlette instrumentation + tracer=tracer, meter=meter, ) self._is_instrumented_by_opentelemetry = True diff --git a/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py b/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py index 0accda18f..1e768982b 100644 --- a/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py @@ -98,6 +98,10 @@ class TestStarletteManualInstrumentation(TestBase): self.assertEqual(len(spans), 3) for span in spans: self.assertIn("GET /foobar", span.name) + self.assertEqual( + span.instrumentation_scope.name, + "opentelemetry.instrumentation.starlette", + ) def test_starlette_route_attribute_added(self): """Ensure that starlette routes are used as the span name.""" @@ -132,6 +136,10 @@ class TestStarletteManualInstrumentation(TestBase): for resource_metric in metrics_list.resource_metrics: self.assertTrue(len(resource_metric.scope_metrics) == 1) for scope_metric in resource_metric.scope_metrics: + self.assertEqual( + scope_metric.scope.name, + "opentelemetry.instrumentation.starlette", + ) self.assertTrue(len(scope_metric.metrics) == 3) for metric in scope_metric.metrics: self.assertIn(metric.name, _expected_metric_names) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 0a873d0fc..810a07e31 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -533,6 +533,8 @@ class OpenTelemetryMiddleware: incoming request. tracer_provider: Optional tracer provider to use. If omitted the current globally configured one is used. + meter_provider: Optional meter provider to use. If omitted the current + globally configured one is used. """ def __init__(