Skip to content

Commit 3ec7736

Browse files
authored
Update instrumentations to use tracer_provider for creating tracer if given, otherwise use global tracer provider (#402)
1 parent bdbc249 commit 3ec7736

File tree

33 files changed

+408
-95
lines changed

33 files changed

+408
-95
lines changed

Diff for: CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2525
([#387](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/387))
2626
- Update redis instrumentation to follow semantic conventions
2727
([#403](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/403))
28+
- Update instrumentations to use tracer_provider for creating tracer if given, otherwise use global tracer provider
29+
([#402](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/402))
2830
- `opentelemetry-instrumentation-wsgi` Replaced `name_callback` with `request_hook`
2931
and `response_hook` callbacks.
3032
([#424](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/424))

Diff for: instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/__init__.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,20 @@ def _instrument(self, **kwargs):
8383
tracer_provider=tracer_provider,
8484
)
8585

86+
# pylint:disable=no-self-use
8687
def _uninstrument(self, **kwargs):
8788
""""Disable aiopg instrumentation"""
8889
wrappers.unwrap_connect()
8990
wrappers.unwrap_create_pool()
9091

9192
# pylint:disable=no-self-use
92-
def instrument_connection(self, connection):
93+
def instrument_connection(self, connection, tracer_provider=None):
9394
"""Enable instrumentation in a aiopg connection.
9495
9596
Args:
9697
connection: The connection to instrument.
98+
tracer_provider: The optional tracer provider to use. If omitted
99+
the current globally configured one is used.
97100
98101
Returns:
99102
An instrumented connection.
@@ -103,6 +106,8 @@ def instrument_connection(self, connection):
103106
connection,
104107
self._DATABASE_SYSTEM,
105108
self._CONNECTION_ATTRIBUTES,
109+
version=__version__,
110+
tracer_provider=tracer_provider,
106111
)
107112

108113
def uninstrument_connection(self, connection):

Diff for: instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ async def traced_execution(
114114
else self._db_api_integration.name
115115
)
116116

117-
with self._db_api_integration.get_tracer().start_as_current_span(
117+
with self._db_api_integration._tracer.start_as_current_span(
118118
name, kind=SpanKind.CLIENT
119119
) as span:
120120
self._populate_span(span, cursor, *args)

Diff for: instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py

+26
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,32 @@ def test_instrument_connection(self):
204204
spans_list = self.memory_exporter.get_finished_spans()
205205
self.assertEqual(len(spans_list), 1)
206206

207+
def test_custom_tracer_provider_instrument_connection(self):
208+
resource = resources.Resource.create(
209+
{"service.name": "db-test-service"}
210+
)
211+
result = self.create_tracer_provider(resource=resource)
212+
tracer_provider, exporter = result
213+
214+
cnx = async_call(aiopg.connect(database="test"))
215+
216+
cnx = AiopgInstrumentor().instrument_connection(
217+
cnx, tracer_provider=tracer_provider
218+
)
219+
220+
cursor = async_call(cnx.cursor())
221+
query = "SELECT * FROM test"
222+
async_call(cursor.execute(query))
223+
224+
spans_list = exporter.get_finished_spans()
225+
self.assertEqual(len(spans_list), 1)
226+
span = spans_list[0]
227+
228+
self.assertEqual(
229+
span.resource.attributes["service.name"], "db-test-service"
230+
)
231+
self.assertIs(span.resource, resource)
232+
207233
def test_uninstrument_connection(self):
208234
AiopgInstrumentor().instrument()
209235
cnx = async_call(aiopg.connect(database="test"))

Diff for: instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,19 @@ class OpenTelemetryMiddleware:
167167
and a tuple, representing the desired span name and a
168168
dictionary with any additional span attributes to set.
169169
Optional: Defaults to get_default_span_details.
170+
tracer_provider: The optional tracer provider to use. If omitted
171+
the current globally configured one is used.
170172
"""
171173

172-
def __init__(self, app, excluded_urls=None, span_details_callback=None):
174+
def __init__(
175+
self,
176+
app,
177+
excluded_urls=None,
178+
span_details_callback=None,
179+
tracer_provider=None,
180+
):
173181
self.app = guarantee_single_callable(app)
174-
self.tracer = trace.get_tracer(__name__, __version__)
182+
self.tracer = trace.get_tracer(__name__, __version__, tracer_provider)
175183
self.span_details_callback = (
176184
span_details_callback or get_default_span_details
177185
)

Diff for: instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py

+18
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818

1919
import opentelemetry.instrumentation.asgi as otel_asgi
2020
from opentelemetry import trace as trace_api
21+
from opentelemetry.sdk import resources
2122
from opentelemetry.semconv.trace import SpanAttributes
2223
from opentelemetry.test.asgitestutil import (
2324
AsgiTestBase,
2425
setup_testing_defaults,
2526
)
27+
from opentelemetry.test.test_base import TestBase
2628

2729

2830
async def http_app(scope, receive, send):
@@ -211,6 +213,22 @@ def update_expected_span_name(expected):
211213
outputs = self.get_all_output()
212214
self.validate_outputs(outputs, modifiers=[update_expected_span_name])
213215

216+
def test_custom_tracer_provider_otel_asgi(self):
217+
resource = resources.Resource.create({"service-test-key": "value"})
218+
result = TestBase.create_tracer_provider(resource=resource)
219+
tracer_provider, exporter = result
220+
221+
app = otel_asgi.OpenTelemetryMiddleware(
222+
simple_asgi, tracer_provider=tracer_provider
223+
)
224+
self.seed_app(app)
225+
self.send_default_request()
226+
span_list = exporter.get_finished_spans()
227+
for span in span_list:
228+
self.assertEqual(
229+
span.resource.attributes["service-test-key"], "value"
230+
)
231+
214232
def test_behavior_with_scope_server_as_none(self):
215233
"""Test that middleware is ok when server is none in scope."""
216234

Diff for: instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/__init__.py

+6-13
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@
5050
from opentelemetry.trace import SpanKind
5151
from opentelemetry.trace.status import Status, StatusCode
5252

53-
_APPLIED = "_opentelemetry_tracer"
54-
5553

5654
def _hydrate_span_from_args(connection, query, parameters) -> dict:
5755
"""Get network and database attributes from connection."""
@@ -98,16 +96,11 @@ class AsyncPGInstrumentor(BaseInstrumentor):
9896
def __init__(self, capture_parameters=False):
9997
super().__init__()
10098
self.capture_parameters = capture_parameters
99+
self._tracer = None
101100

102101
def _instrument(self, **kwargs):
103-
tracer_provider = kwargs.get(
104-
"tracer_provider", trace.get_tracer_provider()
105-
)
106-
setattr(
107-
asyncpg,
108-
_APPLIED,
109-
tracer_provider.get_tracer("asyncpg", __version__),
110-
)
102+
tracer_provider = kwargs.get("tracer_provider")
103+
self._tracer = trace.get_tracer(__name__, __version__, tracer_provider)
111104

112105
for method in [
113106
"Connection.execute",
@@ -121,7 +114,6 @@ def _instrument(self, **kwargs):
121114
)
122115

123116
def _uninstrument(self, **__):
124-
delattr(asyncpg, _APPLIED)
125117
for method in [
126118
"execute",
127119
"executemany",
@@ -132,13 +124,14 @@ def _uninstrument(self, **__):
132124
unwrap(asyncpg.Connection, method)
133125

134126
async def _do_execute(self, func, instance, args, kwargs):
135-
tracer = getattr(asyncpg, _APPLIED)
136127

137128
exception = None
138129
params = getattr(instance, "_params", {})
139130
name = args[0] if args[0] else params.get("database", "postgresql")
140131

141-
with tracer.start_as_current_span(name, kind=SpanKind.CLIENT) as span:
132+
with self._tracer.start_as_current_span(
133+
name, kind=SpanKind.CLIENT
134+
) as span:
142135
if span.is_recording():
143136
span_attributes = _hydrate_span_from_args(
144137
instance,

Diff for: instrumentation/opentelemetry-instrumentation-asyncpg/tests/test_asyncpg_wrapper.py

-7
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
1-
import asyncpg
21
from asyncpg import Connection
32

43
from opentelemetry.instrumentation.asyncpg import AsyncPGInstrumentor
54
from opentelemetry.test.test_base import TestBase
65

76

87
class TestAsyncPGInstrumentation(TestBase):
9-
def test_instrumentation_flags(self):
10-
AsyncPGInstrumentor().instrument()
11-
self.assertTrue(hasattr(asyncpg, "_opentelemetry_tracer"))
12-
AsyncPGInstrumentor().uninstrument()
13-
self.assertFalse(hasattr(asyncpg, "_opentelemetry_tracer"))
14-
158
def test_duplicated_instrumentation(self):
169
AsyncPGInstrumentor().instrument()
1710
AsyncPGInstrumentor().instrument()

Diff for: instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py

+6-9
Original file line numberDiff line numberDiff line change
@@ -228,21 +228,18 @@ def __init__(
228228
}
229229
self._name = name
230230
self._version = version
231-
self._tracer_provider = tracer_provider
231+
self._tracer = get_tracer(
232+
self._name,
233+
instrumenting_library_version=self._version,
234+
tracer_provider=tracer_provider,
235+
)
232236
self.capture_parameters = capture_parameters
233237
self.database_system = database_system
234238
self.connection_props = {}
235239
self.span_attributes = {}
236240
self.name = ""
237241
self.database = ""
238242

239-
def get_tracer(self):
240-
return get_tracer(
241-
self._name,
242-
instrumenting_library_version=self._version,
243-
tracer_provider=self._tracer_provider,
244-
)
245-
246243
def wrapped_connection(
247244
self,
248245
connect_method: typing.Callable[..., typing.Any],
@@ -370,7 +367,7 @@ def traced_execution(
370367
else self._db_api_integration.name
371368
)
372369

373-
with self._db_api_integration.get_tracer().start_as_current_span(
370+
with self._db_api_integration._tracer.start_as_current_span(
374371
name, kind=SpanKind.CLIENT
375372
) as span:
376373
self._populate_span(span, cursor, *args)

Diff for: instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py

+28-8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from opentelemetry import trace as trace_api
2020
from opentelemetry.instrumentation import dbapi
21+
from opentelemetry.sdk import resources
2122
from opentelemetry.semconv.trace import SpanAttributes
2223
from opentelemetry.test.test_base import TestBase
2324

@@ -41,7 +42,7 @@ def test_span_succeeded(self):
4142
"user": "user",
4243
}
4344
db_integration = dbapi.DatabaseApiIntegration(
44-
self.tracer, "testcomponent", connection_attributes
45+
"testname", "testcomponent", connection_attributes
4546
)
4647
mock_connection = db_integration.wrapped_connection(
4748
mock_connect, {}, connection_props
@@ -73,7 +74,7 @@ def test_span_succeeded(self):
7374

7475
def test_span_name(self):
7576
db_integration = dbapi.DatabaseApiIntegration(
76-
self.tracer, "testcomponent", {}
77+
"testname", "testcomponent", {}
7778
)
7879
mock_connection = db_integration.wrapped_connection(
7980
mock_connect, {}, {}
@@ -106,7 +107,7 @@ def test_span_succeeded_with_capture_of_statement_parameters(self):
106107
"user": "user",
107108
}
108109
db_integration = dbapi.DatabaseApiIntegration(
109-
self.tracer,
110+
"testname",
110111
"testcomponent",
111112
connection_attributes,
112113
capture_parameters=True,
@@ -155,12 +156,10 @@ def test_span_not_recording(self):
155156
"host": "server_host",
156157
"user": "user",
157158
}
158-
mock_tracer = mock.Mock()
159159
mock_span = mock.Mock()
160160
mock_span.is_recording.return_value = False
161-
mock_tracer.start_span.return_value = mock_span
162161
db_integration = dbapi.DatabaseApiIntegration(
163-
mock_tracer, "testcomponent", connection_attributes
162+
"testname", "testcomponent", connection_attributes
164163
)
165164
mock_connection = db_integration.wrapped_connection(
166165
mock_connect, {}, connection_props
@@ -192,9 +191,30 @@ def test_span_failed(self):
192191
self.assertIs(span.status.status_code, trace_api.StatusCode.ERROR)
193192
self.assertEqual(span.status.description, "Exception: Test Exception")
194193

194+
def test_custom_tracer_provider_dbapi(self):
195+
resource = resources.Resource.create({"db-resource-key": "value"})
196+
result = self.create_tracer_provider(resource=resource)
197+
tracer_provider, exporter = result
198+
199+
db_integration = dbapi.DatabaseApiIntegration(
200+
self.tracer, "testcomponent", tracer_provider=tracer_provider
201+
)
202+
mock_connection = db_integration.wrapped_connection(
203+
mock_connect, {}, {}
204+
)
205+
cursor = mock_connection.cursor()
206+
with self.assertRaises(Exception):
207+
cursor.execute("Test query", throw_exception=True)
208+
209+
spans_list = exporter.get_finished_spans()
210+
self.assertEqual(len(spans_list), 1)
211+
span = spans_list[0]
212+
self.assertEqual(span.resource.attributes["db-resource-key"], "value")
213+
self.assertIs(span.status.status_code, trace_api.StatusCode.ERROR)
214+
195215
def test_executemany(self):
196216
db_integration = dbapi.DatabaseApiIntegration(
197-
self.tracer, "testcomponent"
217+
"testname", "testcomponent"
198218
)
199219
mock_connection = db_integration.wrapped_connection(
200220
mock_connect, {}, {}
@@ -210,7 +230,7 @@ def test_executemany(self):
210230

211231
def test_callproc(self):
212232
db_integration = dbapi.DatabaseApiIntegration(
213-
self.tracer, "testcomponent"
233+
"testname", "testcomponent"
214234
)
215235
mock_connection = db_integration.wrapped_connection(
216236
mock_connect, {}, {}

Diff for: instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/__init__.py

+8
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ def response_hook(span, request, response):
8484
from opentelemetry.instrumentation.django.middleware import _DjangoMiddleware
8585
from opentelemetry.instrumentation.django.version import __version__
8686
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
87+
from opentelemetry.trace import get_tracer
8788

8889
_logger = getLogger(__name__)
8990

@@ -105,6 +106,13 @@ def _instrument(self, **kwargs):
105106
if environ.get(OTEL_PYTHON_DJANGO_INSTRUMENT) == "False":
106107
return
107108

109+
tracer_provider = kwargs.get("tracer_provider")
110+
tracer = get_tracer(
111+
__name__, __version__, tracer_provider=tracer_provider,
112+
)
113+
114+
_DjangoMiddleware._tracer = tracer
115+
108116
_DjangoMiddleware._otel_request_hook = kwargs.pop("request_hook", None)
109117
_DjangoMiddleware._otel_response_hook = kwargs.pop(
110118
"response_hook", None

Diff for: instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
)
3232
from opentelemetry.propagate import extract
3333
from opentelemetry.semconv.trace import SpanAttributes
34-
from opentelemetry.trace import Span, SpanKind, get_tracer, use_span
34+
from opentelemetry.trace import Span, SpanKind, use_span
3535
from opentelemetry.util.http import get_excluded_urls, get_traced_request_attrs
3636

3737
try:
@@ -82,6 +82,7 @@ class _DjangoMiddleware(MiddlewareMixin):
8282

8383
_traced_request_attrs = get_traced_request_attrs("DJANGO")
8484
_excluded_urls = get_excluded_urls("DJANGO")
85+
_tracer = None
8586

8687
_otel_request_hook: Callable[[Span, HttpRequest], None] = None
8788
_otel_response_hook: Callable[
@@ -125,9 +126,7 @@ def process_request(self, request):
125126

126127
token = attach(extract(request_meta, getter=wsgi_getter))
127128

128-
tracer = get_tracer(__name__, __version__)
129-
130-
span = tracer.start_span(
129+
span = self._tracer.start_span(
131130
self._get_span_name(request),
132131
kind=SpanKind.SERVER,
133132
start_time=request_meta.get(

0 commit comments

Comments
 (0)