Skip to content

Commit 0ada248

Browse files
mauriciovasquezbernalAlex Boten
authored and
Alex Boten
committed
ext: Expect tracer provider instead of tracer in integrations (open-telemetry#602)
Standardize the interface that trace providers are specified in integrations, as specified in open-telemetry#585. Adding a helper to create and return a configured TracerProvider with a the span processor and the memory exporter api: Add tracer provider parameter to trace.get_tracer(). This eliminates the need for a helper function and boilerplate code to retrieve the appropriate tracer from a passed tracer_provider.
1 parent 7654c90 commit 0ada248

File tree

19 files changed

+163
-65
lines changed

19 files changed

+163
-65
lines changed

Diff for: docs/examples/opentelemetry-example-app/src/opentelemetry_example_app/grpc/hello_world_client.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@
6464
trace.get_tracer_provider().add_span_processor(
6565
SimpleExportSpanProcessor(ConsoleSpanExporter())
6666
)
67-
tracer = trace.get_tracer(__name__)
6867

6968

7069
def run():
@@ -73,7 +72,7 @@ def run():
7372
# of the code.
7473
with grpc.insecure_channel("localhost:50051") as channel:
7574

76-
channel = intercept_channel(channel, client_interceptor(tracer))
75+
channel = intercept_channel(channel, client_interceptor())
7776

7877
stub = helloworld_pb2_grpc.GreeterStub(channel)
7978

Diff for: docs/examples/opentelemetry-example-app/src/opentelemetry_example_app/grpc/hello_world_server.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@
6464
trace.get_tracer_provider().add_span_processor(
6565
SimpleExportSpanProcessor(ConsoleSpanExporter())
6666
)
67-
tracer = trace.get_tracer(__name__)
6867

6968

7069
class Greeter(helloworld_pb2_grpc.GreeterServicer):
@@ -75,7 +74,7 @@ def SayHello(self, request, context):
7574
def serve():
7675

7776
server = grpc.server(futures.ThreadPoolExecutor())
78-
server = intercept_server(server, server_interceptor(tracer))
77+
server = intercept_server(server, server_interceptor())
7978

8079
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
8180
server.add_insecure_port("[::]:50051")

Diff for: docs/examples/opentelemetry-example-app/src/opentelemetry_example_app/grpc/route_guide_client.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@
6767
trace.get_tracer_provider().add_span_processor(
6868
SimpleExportSpanProcessor(ConsoleSpanExporter())
6969
)
70-
tracer = trace.get_tracer(__name__)
7170

7271

7372
def make_route_note(message, latitude, longitude):
@@ -154,7 +153,7 @@ def run():
154153
# used in circumstances in which the with statement does not fit the needs
155154
# of the code.
156155
with grpc.insecure_channel("localhost:50051") as channel:
157-
channel = intercept_channel(channel, client_interceptor(tracer))
156+
channel = intercept_channel(channel, client_interceptor())
158157

159158
stub = route_guide_pb2_grpc.RouteGuideStub(channel)
160159

Diff for: docs/examples/opentelemetry-example-app/src/opentelemetry_example_app/grpc/route_guide_server.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@
6969
trace.get_tracer_provider().add_span_processor(
7070
SimpleExportSpanProcessor(ConsoleSpanExporter())
7171
)
72-
tracer = trace.get_tracer(__name__)
7372

7473

7574
def get_feature(feature_db, point):
@@ -164,7 +163,7 @@ def RouteChat(self, request_iterator, context):
164163

165164
def serve():
166165
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
167-
server = intercept_server(server, server_interceptor(tracer))
166+
server = intercept_server(server, server_interceptor())
168167

169168
route_guide_pb2_grpc.add_RouteGuideServicer_to_server(
170169
RouteGuideServicer(), server

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

+38-6
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,16 @@
2424
import mysql.connector
2525
import pyodbc
2626
27+
from opentelemetry import trace
2728
from opentelemetry.ext.dbapi import trace_integration
2829
from opentelemetry.trace import TracerProvider
2930
3031
trace.set_tracer_provider(TracerProvider())
31-
tracer = trace.get_tracer(__name__)
32+
3233
# Ex: mysql.connector
33-
trace_integration(tracer, mysql.connector, "connect", "mysql", "sql")
34+
trace_integration(mysql.connector, "connect", "mysql", "sql")
3435
# Ex: pyodbc
35-
trace_integration(tracer, pyodbc, "Connection", "odbc", "sql")
36+
trace_integration(pyodbc, "Connection", "odbc", "sql")
3637
3738
API
3839
---
@@ -44,13 +45,44 @@
4445

4546
import wrapt
4647

47-
from opentelemetry.trace import SpanKind, Tracer
48+
from opentelemetry.ext.dbapi.version import __version__
49+
from opentelemetry.trace import SpanKind, Tracer, TracerProvider, get_tracer
4850
from opentelemetry.trace.status import Status, StatusCanonicalCode
4951

5052
logger = logging.getLogger(__name__)
5153

5254

5355
def trace_integration(
56+
connect_module: typing.Callable[..., any],
57+
connect_method_name: str,
58+
database_component: str,
59+
database_type: str = "",
60+
connection_attributes: typing.Dict = None,
61+
tracer_provider: typing.Optional[TracerProvider] = None,
62+
):
63+
"""Integrate with DB API library.
64+
https://www.python.org/dev/peps/pep-0249/
65+
66+
Args:
67+
connect_module: Module name where connect method is available.
68+
connect_method_name: The connect method name.
69+
database_component: Database driver name or database name "JDBI", "jdbc", "odbc", "postgreSQL".
70+
database_type: The Database type. For any SQL database, "sql".
71+
connection_attributes: Attribute names for database, port, host and user in Connection object.
72+
tracer_provider: The :class:`TracerProvider` to use. If ommited the current configured one is used.
73+
"""
74+
tracer = get_tracer(__name__, __version__, tracer_provider)
75+
wrap_connect(
76+
tracer,
77+
connect_module,
78+
connect_method_name,
79+
database_component,
80+
database_type,
81+
connection_attributes,
82+
)
83+
84+
85+
def wrap_connect(
5486
tracer: Tracer,
5587
connect_module: typing.Callable[..., any],
5688
connect_method_name: str,
@@ -71,7 +103,7 @@ def trace_integration(
71103
"""
72104

73105
# pylint: disable=unused-argument
74-
def wrap_connect(
106+
def wrap_connect_(
75107
wrapped: typing.Callable[..., any],
76108
instance: typing.Any,
77109
args: typing.Tuple[any, any],
@@ -87,7 +119,7 @@ def wrap_connect(
87119

88120
try:
89121
wrapt.wrap_function_wrapper(
90-
connect_module, connect_method_name, wrap_connect
122+
connect_module, connect_method_name, wrap_connect_
91123
)
92124
except Exception as ex: # pylint: disable=broad-except
93125
logger.warning("Failed to integrate with DB API. %s", str(ex))

Diff for: ext/opentelemetry-ext-docker-tests/tests/mysql/test_mysql_functional.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def setUpClass(cls):
3535
cls._connection = None
3636
cls._cursor = None
3737
cls._tracer = cls.tracer_provider.get_tracer(__name__)
38-
trace_integration(cls._tracer)
38+
trace_integration(cls.tracer_provider)
3939
cls._connection = mysql.connector.connect(
4040
user=MYSQL_USER,
4141
password=MYSQL_PASSWORD,

Diff for: ext/opentelemetry-ext-docker-tests/tests/postgres/test_psycopg_functional.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def setUpClass(cls):
3535
cls._connection = None
3636
cls._cursor = None
3737
cls._tracer = cls.tracer_provider.get_tracer(__name__)
38-
trace_integration(cls._tracer)
38+
trace_integration(cls.tracer_provider)
3939
cls._connection = psycopg2.connect(
4040
dbname=POSTGRES_DB_NAME,
4141
user=POSTGRES_USER,

Diff for: ext/opentelemetry-ext-docker-tests/tests/pymongo/test_pymongo_functional.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class TestFunctionalPymongo(TestBase):
3131
def setUpClass(cls):
3232
super().setUpClass()
3333
cls._tracer = cls.tracer_provider.get_tracer(__name__)
34-
trace_integration(cls._tracer)
34+
trace_integration(cls.tracer_provider)
3535
client = MongoClient(
3636
MONGODB_HOST, MONGODB_PORT, serverSelectionTimeoutMS=2000
3737
)

Diff for: ext/opentelemetry-ext-grpc/src/opentelemetry/ext/grpc/__init__.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
# pylint:disable=no-name-in-module
1818
# pylint:disable=relative-beyond-top-level
1919

20+
from opentelemetry import trace
21+
from opentelemetry.ext.grpc.version import __version__
2022

21-
def client_interceptor(tracer):
23+
24+
def client_interceptor(tracer_provider=None):
2225
"""Create a gRPC client channel interceptor.
2326
2427
Args:
@@ -29,10 +32,12 @@ def client_interceptor(tracer):
2932
"""
3033
from . import _client
3134

35+
tracer = trace.get_tracer(__name__, __version__, tracer_provider)
36+
3237
return _client.OpenTelemetryClientInterceptor(tracer)
3338

3439

35-
def server_interceptor(tracer):
40+
def server_interceptor(tracer_provider=None):
3641
"""Create a gRPC server interceptor.
3742
3843
Args:
@@ -43,4 +48,6 @@ def server_interceptor(tracer):
4348
"""
4449
from . import _server
4550

51+
tracer = trace.get_tracer(__name__, __version__, tracer_provider)
52+
4653
return _server.OpenTelemetryServerInterceptor(tracer)

Diff for: ext/opentelemetry-ext-grpc/tests/test_server_interceptor.py

+12-14
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import grpc
2222

23+
import opentelemetry.ext.grpc
2324
from opentelemetry import trace
2425
from opentelemetry.ext.grpc import server_interceptor
2526
from opentelemetry.ext.grpc.grpcext import intercept_server
@@ -48,15 +49,11 @@ def service(self, handler_call_details):
4849

4950

5051
class TestOpenTelemetryServerInterceptor(TestBase):
51-
def setUp(self):
52-
super().setUp()
53-
self.tracer = self.tracer_provider.get_tracer(__name__)
54-
5552
def test_create_span(self):
5653
"""Check that the interceptor wraps calls with spans server-side."""
5754

5855
# Intercept gRPC calls...
59-
interceptor = server_interceptor(self.tracer)
56+
interceptor = server_interceptor()
6057

6158
# No-op RPC handler
6259
def handler(request, context):
@@ -87,18 +84,21 @@ def handler(request, context):
8784
self.assertEqual(span.name, "")
8885
self.assertIs(span.kind, trace.SpanKind.SERVER)
8986

87+
# Check version and name in span's instrumentation info
88+
self.check_span_instrumentation_info(span, opentelemetry.ext.grpc)
89+
9090
def test_span_lifetime(self):
9191
"""Check that the span is active for the duration of the call."""
9292

93-
tracer_provider = trace_sdk.TracerProvider()
94-
tracer = tracer_provider.get_tracer(__name__)
95-
interceptor = server_interceptor(tracer)
93+
interceptor = server_interceptor()
94+
tracer = self.tracer_provider.get_tracer(__name__)
9695

9796
# To capture the current span at the time the handler is called
9897
active_span_in_handler = None
9998

10099
def handler(request, context):
101100
nonlocal active_span_in_handler
101+
# The current span is shared among all the tracers.
102102
active_span_in_handler = tracer.get_current_span()
103103
return b""
104104

@@ -128,10 +128,9 @@ def handler(request, context):
128128
def test_sequential_server_spans(self):
129129
"""Check that sequential RPCs get separate server spans."""
130130

131-
tracer_provider = trace_sdk.TracerProvider()
132-
tracer = tracer_provider.get_tracer(__name__)
131+
tracer = self.tracer_provider.get_tracer(__name__)
133132

134-
interceptor = server_interceptor(tracer)
133+
interceptor = server_interceptor()
135134

136135
# Capture the currently active span in each thread
137136
active_spans_in_handler = []
@@ -176,10 +175,9 @@ def test_concurrent_server_spans(self):
176175
context.
177176
"""
178177

179-
tracer_provider = trace_sdk.TracerProvider()
180-
tracer = tracer_provider.get_tracer(__name__)
178+
tracer = self.tracer_provider.get_tracer(__name__)
181179

182-
interceptor = server_interceptor(tracer)
180+
interceptor = server_interceptor()
183181

184182
# Capture the currently active span in each thread
185183
active_spans_in_handler = []

Diff for: ext/opentelemetry-ext-mysql/src/opentelemetry/ext/mysql/__init__.py

+11-6
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@
2929
from opentelemetry.ext.mysql import trace_integration
3030
3131
trace.set_tracer_provider(TracerProvider())
32-
tracer = trace.get_tracer(__name__)
3332
34-
trace_integration(tracer)
33+
trace_integration()
3534
cnx = mysql.connector.connect(database='MySQL_Database')
3635
cursor = cnx.cursor()
3736
cursor.execute("INSERT INTO test (testField) VALUES (123)"
@@ -42,23 +41,29 @@
4241
---
4342
"""
4443

44+
import typing
45+
4546
import mysql.connector
4647

47-
from opentelemetry.ext.dbapi import trace_integration as db_integration
48-
from opentelemetry.trace import Tracer
48+
from opentelemetry.ext.dbapi import wrap_connect
49+
from opentelemetry.ext.mysql.version import __version__
50+
from opentelemetry.trace import TracerProvider, get_tracer
4951

5052

51-
def trace_integration(tracer: Tracer):
53+
def trace_integration(tracer_provider: typing.Optional[TracerProvider] = None):
5254
"""Integrate with MySQL Connector/Python library.
5355
https://dev.mysql.com/doc/connector-python/en/
5456
"""
57+
58+
tracer = get_tracer(__name__, __version__, tracer_provider)
59+
5560
connection_attributes = {
5661
"database": "database",
5762
"port": "server_port",
5863
"host": "server_host",
5964
"user": "user",
6065
}
61-
db_integration(
66+
wrap_connect(
6267
tracer,
6368
mysql.connector,
6469
"connect",

Diff for: ext/opentelemetry-ext-mysql/tests/test_mysql_integration.py

+28-5
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,43 @@
1616

1717
import mysql.connector
1818

19-
from opentelemetry.ext.mysql import trace_integration
19+
import opentelemetry.ext.mysql
20+
from opentelemetry.sdk import resources
2021
from opentelemetry.test.test_base import TestBase
2122

2223

2324
class TestMysqlIntegration(TestBase):
2425
def test_trace_integration(self):
25-
tracer = self.tracer_provider.get_tracer(__name__)
26+
with mock.patch("mysql.connector.connect") as mock_connect:
27+
mock_connect.get.side_effect = mysql.connector.MySQLConnection()
28+
opentelemetry.ext.mysql.trace_integration()
29+
cnx = mysql.connector.connect(database="test")
30+
cursor = cnx.cursor()
31+
query = "SELECT * FROM test"
32+
cursor.execute(query)
33+
34+
spans_list = self.memory_exporter.get_finished_spans()
35+
self.assertEqual(len(spans_list), 1)
36+
span = spans_list[0]
37+
38+
# Check version and name in span's instrumentation info
39+
self.check_span_instrumentation_info(span, opentelemetry.ext.mysql)
40+
41+
def test_custom_tracer_provider(self):
42+
resource = resources.Resource.create({})
43+
result = self.create_tracer_provider(resource=resource)
44+
tracer_provider, exporter = result
2645

2746
with mock.patch("mysql.connector.connect") as mock_connect:
2847
mock_connect.get.side_effect = mysql.connector.MySQLConnection()
29-
trace_integration(tracer)
48+
opentelemetry.ext.mysql.trace_integration(tracer_provider)
3049
cnx = mysql.connector.connect(database="test")
3150
cursor = cnx.cursor()
3251
query = "SELECT * FROM test"
3352
cursor.execute(query)
34-
spans_list = self.memory_exporter.get_finished_spans()
35-
self.assertEqual(len(spans_list), 1)
53+
54+
span_list = exporter.get_finished_spans()
55+
self.assertEqual(len(span_list), 1)
56+
span = span_list[0]
57+
58+
self.assertIs(span.resource, resource)

0 commit comments

Comments
 (0)