Do not use `asgi` name and version for tracer/meter for instrumentations using Asgi Middleware (#2580)

This commit is contained in:
Leighton Chen 2024-06-06 12:52:32 -07:00 committed by GitHub
parent bb9eebb73e
commit 5b841282ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 77 additions and 9 deletions

View File

@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased ## 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 ### Fixed
- `opentelemetry-instrumentation-httpx` Ensure httpx.get or httpx.request like methods are instrumented - `opentelemetry-instrumentation-httpx` Ensure httpx.get or httpx.request like methods are instrumented

View File

@ -461,6 +461,8 @@ class OpenTelemetryMiddleware:
scope and event which are sent as dictionaries for when the method send is called. 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 tracer_provider: The optional tracer provider to use. If omitted
the current globally configured one is used. 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 # pylint: disable=too-many-branches
@ -474,18 +476,23 @@ class OpenTelemetryMiddleware:
client_response_hook: ClientResponseHook = None, client_response_hook: ClientResponseHook = None,
tracer_provider=None, tracer_provider=None,
meter_provider=None, meter_provider=None,
tracer=None,
meter=None, meter=None,
http_capture_headers_server_request: list[str] | None = None, http_capture_headers_server_request: list[str] | None = None,
http_capture_headers_server_response: list[str] | None = None, http_capture_headers_server_response: list[str] | None = None,
http_capture_headers_sanitize_fields: list[str] | None = None, http_capture_headers_sanitize_fields: list[str] | None = None,
): ):
self.app = guarantee_single_callable(app) self.app = guarantee_single_callable(app)
self.tracer = trace.get_tracer( self.tracer = (
trace.get_tracer(
__name__, __name__,
__version__, __version__,
tracer_provider, tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0", schema_url="https://opentelemetry.io/schemas/1.11.0",
) )
if tracer is None
else tracer
)
self.meter = ( self.meter = (
get_meter( get_meter(
__name__, __name__,

View File

@ -309,6 +309,10 @@ class TestAsgiApplication(AsgiTestBase):
self.assertEqual(span.name, expected["name"]) self.assertEqual(span.name, expected["name"])
self.assertEqual(span.kind, expected["kind"]) self.assertEqual(span.kind, expected["kind"])
self.assertDictEqual(dict(span.attributes), expected["attributes"]) self.assertDictEqual(dict(span.attributes), expected["attributes"])
self.assertEqual(
span.instrumentation_scope.name,
"opentelemetry.instrumentation.asgi",
)
def test_basic_asgi_call(self): def test_basic_asgi_call(self):
"""Test that spans are emitted as expected.""" """Test that spans are emitted as expected."""
@ -728,6 +732,10 @@ class TestAsgiApplication(AsgiTestBase):
self.assertTrue(len(resource_metric.scope_metrics) != 0) self.assertTrue(len(resource_metric.scope_metrics) != 0)
for scope_metric in resource_metric.scope_metrics: for scope_metric in resource_metric.scope_metrics:
self.assertTrue(len(scope_metric.metrics) != 0) self.assertTrue(len(scope_metric.metrics) != 0)
self.assertEqual(
scope_metric.scope.name,
"opentelemetry.instrumentation.asgi",
)
for metric in scope_metric.metrics: for metric in scope_metric.metrics:
self.assertIn(metric.name, _expected_metric_names) self.assertIn(metric.name, _expected_metric_names)
data_points = list(metric.data.data_points) data_points = list(metric.data.data_points)

View File

@ -188,6 +188,7 @@ from opentelemetry.instrumentation.fastapi.version import __version__
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.metrics import get_meter from opentelemetry.metrics import get_meter
from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.trace import get_tracer
from opentelemetry.util.http import get_excluded_urls, parse_excluded_urls from opentelemetry.util.http import get_excluded_urls, parse_excluded_urls
_excluded_urls_from_env = get_excluded_urls("FASTAPI") _excluded_urls_from_env = get_excluded_urls("FASTAPI")
@ -221,6 +222,12 @@ class FastAPIInstrumentor(BaseInstrumentor):
excluded_urls = _excluded_urls_from_env excluded_urls = _excluded_urls_from_env
else: else:
excluded_urls = parse_excluded_urls(excluded_urls) 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( meter = get_meter(
__name__, __name__,
__version__, __version__,
@ -235,7 +242,8 @@ class FastAPIInstrumentor(BaseInstrumentor):
server_request_hook=server_request_hook, server_request_hook=server_request_hook,
client_request_hook=client_request_hook, client_request_hook=client_request_hook,
client_response_hook=client_response_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, meter=meter,
) )
app._is_instrumented_by_opentelemetry = True app._is_instrumented_by_opentelemetry = True
@ -298,6 +306,12 @@ class _InstrumentedFastAPI(fastapi.FastAPI):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*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( meter = get_meter(
__name__, __name__,
__version__, __version__,
@ -311,7 +325,8 @@ class _InstrumentedFastAPI(fastapi.FastAPI):
server_request_hook=_InstrumentedFastAPI._server_request_hook, server_request_hook=_InstrumentedFastAPI._server_request_hook,
client_request_hook=_InstrumentedFastAPI._client_request_hook, client_request_hook=_InstrumentedFastAPI._client_request_hook,
client_response_hook=_InstrumentedFastAPI._client_response_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, meter=meter,
) )
self._is_instrumented_by_opentelemetry = True self._is_instrumented_by_opentelemetry = True

View File

@ -117,6 +117,10 @@ class TestFastAPIManualInstrumentation(TestBase):
self.assertEqual(len(spans), 3) self.assertEqual(len(spans), 3)
for span in spans: for span in spans:
self.assertIn("GET /foobar", span.name) self.assertIn("GET /foobar", span.name)
self.assertEqual(
span.instrumentation_scope.name,
"opentelemetry.instrumentation.fastapi",
)
def test_uninstrument_app(self): def test_uninstrument_app(self):
self._client.get("/foobar") self._client.get("/foobar")
@ -197,6 +201,10 @@ class TestFastAPIManualInstrumentation(TestBase):
for resource_metric in metrics_list.resource_metrics: for resource_metric in metrics_list.resource_metrics:
self.assertTrue(len(resource_metric.scope_metrics) == 1) self.assertTrue(len(resource_metric.scope_metrics) == 1)
for scope_metric in resource_metric.scope_metrics: for scope_metric in resource_metric.scope_metrics:
self.assertEqual(
scope_metric.scope.name,
"opentelemetry.instrumentation.fastapi",
)
self.assertTrue(len(scope_metric.metrics) == 3) self.assertTrue(len(scope_metric.metrics) == 3)
for metric in scope_metric.metrics: for metric in scope_metric.metrics:
self.assertIn(metric.name, _expected_metric_names) self.assertIn(metric.name, _expected_metric_names)

View File

@ -185,6 +185,7 @@ from opentelemetry.instrumentation.starlette.package import _instruments
from opentelemetry.instrumentation.starlette.version import __version__ from opentelemetry.instrumentation.starlette.version import __version__
from opentelemetry.metrics import get_meter from opentelemetry.metrics import get_meter
from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.trace import get_tracer
from opentelemetry.util.http import get_excluded_urls from opentelemetry.util.http import get_excluded_urls
_excluded_urls = get_excluded_urls("STARLETTE") _excluded_urls = get_excluded_urls("STARLETTE")
@ -208,6 +209,12 @@ class StarletteInstrumentor(BaseInstrumentor):
tracer_provider=None, tracer_provider=None,
): ):
"""Instrument an uninstrumented Starlette application.""" """Instrument an uninstrumented Starlette application."""
tracer = get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
meter = get_meter( meter = get_meter(
__name__, __name__,
__version__, __version__,
@ -222,7 +229,8 @@ class StarletteInstrumentor(BaseInstrumentor):
server_request_hook=server_request_hook, server_request_hook=server_request_hook,
client_request_hook=client_request_hook, client_request_hook=client_request_hook,
client_response_hook=client_response_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, meter=meter,
) )
app.is_instrumented_by_opentelemetry = True app.is_instrumented_by_opentelemetry = True
@ -278,6 +286,12 @@ class _InstrumentedStarlette(applications.Starlette):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*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( meter = get_meter(
__name__, __name__,
__version__, __version__,
@ -291,7 +305,8 @@ class _InstrumentedStarlette(applications.Starlette):
server_request_hook=_InstrumentedStarlette._server_request_hook, server_request_hook=_InstrumentedStarlette._server_request_hook,
client_request_hook=_InstrumentedStarlette._client_request_hook, client_request_hook=_InstrumentedStarlette._client_request_hook,
client_response_hook=_InstrumentedStarlette._client_response_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, meter=meter,
) )
self._is_instrumented_by_opentelemetry = True self._is_instrumented_by_opentelemetry = True

View File

@ -98,6 +98,10 @@ class TestStarletteManualInstrumentation(TestBase):
self.assertEqual(len(spans), 3) self.assertEqual(len(spans), 3)
for span in spans: for span in spans:
self.assertIn("GET /foobar", span.name) self.assertIn("GET /foobar", span.name)
self.assertEqual(
span.instrumentation_scope.name,
"opentelemetry.instrumentation.starlette",
)
def test_starlette_route_attribute_added(self): def test_starlette_route_attribute_added(self):
"""Ensure that starlette routes are used as the span name.""" """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: for resource_metric in metrics_list.resource_metrics:
self.assertTrue(len(resource_metric.scope_metrics) == 1) self.assertTrue(len(resource_metric.scope_metrics) == 1)
for scope_metric in resource_metric.scope_metrics: for scope_metric in resource_metric.scope_metrics:
self.assertEqual(
scope_metric.scope.name,
"opentelemetry.instrumentation.starlette",
)
self.assertTrue(len(scope_metric.metrics) == 3) self.assertTrue(len(scope_metric.metrics) == 3)
for metric in scope_metric.metrics: for metric in scope_metric.metrics:
self.assertIn(metric.name, _expected_metric_names) self.assertIn(metric.name, _expected_metric_names)

View File

@ -533,6 +533,8 @@ class OpenTelemetryMiddleware:
incoming request. incoming request.
tracer_provider: Optional tracer provider to use. If omitted the current tracer_provider: Optional tracer provider to use. If omitted the current
globally configured one is used. globally configured one is used.
meter_provider: Optional meter provider to use. If omitted the current
globally configured one is used.
""" """
def __init__( def __init__(