Skip to content
This repository was archived by the owner on Mar 31, 2026. It is now read-only.

Commit 549cfaa

Browse files
committed
all: implement custom tracer_provider injection
An important feature for observability is to allow the injection of a custom tracer_provider instead of always using the global tracer_provider.
1 parent 41604fe commit 549cfaa

File tree

7 files changed

+50
-11
lines changed

7 files changed

+50
-11
lines changed

google/cloud/spanner_v1/_opentelemetry_tracing.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,16 @@ def get_tracer(tracer_provider=None):
5151

5252

5353
@contextmanager
54-
def trace_call(name, session, extra_attributes=None):
54+
def trace_call(name, session, extra_attributes=None, tracer_provider=None):
5555
if not HAS_OPENTELEMETRY_INSTALLED or not session:
5656
# Empty context manager. Users will have to check if the generated value is None or a span
5757
yield None
5858
return
5959

60-
tracer = get_tracer()
60+
if tracer_provider is None and getattr(session, "_tracer_provider", None):
61+
tracer_provider = session._tracer_provider
62+
63+
tracer = get_tracer(tracer_provider)
6164

6265
# Set base attributes that we know for every trace created
6366
attributes = {

google/cloud/spanner_v1/client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ def __init__(
146146
query_options=None,
147147
route_to_leader_enabled=True,
148148
directed_read_options=None,
149+
tracer_provider=None,
149150
):
150151
self._emulator_host = _get_spanner_emulator_host()
151152

@@ -187,6 +188,7 @@ def __init__(
187188

188189
self._route_to_leader_enabled = route_to_leader_enabled
189190
self._directed_read_options = directed_read_options
191+
self._tracer_provider = tracer_provider
190192

191193
@property
192194
def credentials(self):
@@ -371,6 +373,7 @@ def instance(
371373
self._emulator_host,
372374
labels,
373375
processing_units,
376+
tracer_provider=self._tracer_provider,
374377
)
375378

376379
def list_instances(self, filter_="", page_size=None):

google/cloud/spanner_v1/database.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ def __init__(
156156
database_role=None,
157157
enable_drop_protection=False,
158158
proto_descriptors=None,
159+
tracer_provider=None,
159160
):
160161
self.database_id = database_id
161162
self._instance = instance
@@ -178,11 +179,15 @@ def __init__(
178179
self._reconciling = False
179180
self._directed_read_options = self._instance._client.directed_read_options
180181
self._proto_descriptors = proto_descriptors
182+
self._tracer_provider = tracer_provider
181183

182184
if pool is None:
183-
pool = BurstyPool(database_role=database_role)
185+
pool = BurstyPool(
186+
database_role=database_role, tracer_provider=self._tracer_provider
187+
)
184188

185189
self._pool = pool
190+
self._pool._tracer_provider = self._tracer_provider
186191
pool.bind(self)
187192

188193
@classmethod
@@ -742,7 +747,12 @@ def session(self, labels=None, database_role=None):
742747
# If role is specified in param, then that role is used
743748
# instead.
744749
role = database_role or self._database_role
745-
return Session(self, labels=labels, database_role=role)
750+
return Session(
751+
self,
752+
labels=labels,
753+
database_role=role,
754+
tracer_provider=self._tracer_provider,
755+
)
746756

747757
def snapshot(self, **kw):
748758
"""Return an object which wraps a snapshot.

google/cloud/spanner_v1/instance.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ def __init__(
122122
emulator_host=None,
123123
labels=None,
124124
processing_units=None,
125+
tracer_provider=None,
125126
):
126127
self.instance_id = instance_id
127128
self._client = client
@@ -145,6 +146,7 @@ def __init__(
145146
if labels is None:
146147
labels = {}
147148
self.labels = labels
149+
self._tracer_provider = tracer_provider
148150

149151
def _update_from_pb(self, instance_pb):
150152
"""Refresh self from the server-provided protobuf.
@@ -499,6 +501,7 @@ def database(
499501
database_role=database_role,
500502
enable_drop_protection=enable_drop_protection,
501503
proto_descriptors=proto_descriptors,
504+
tracer_provider=self._tracer_provider,
502505
)
503506
else:
504507
return TestDatabase(

google/cloud/spanner_v1/pool.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,12 @@ class AbstractSessionPool(object):
4242

4343
_database = None
4444

45-
def __init__(self, labels=None, database_role=None):
45+
def __init__(self, labels=None, database_role=None, tracer_provider=None):
4646
if labels is None:
4747
labels = {}
4848
self._labels = labels
4949
self._database_role = database_role
50+
self._tracer_provider = tracer_provider
5051

5152
@property
5253
def labels(self):
@@ -178,8 +179,11 @@ def __init__(
178179
default_timeout=DEFAULT_TIMEOUT,
179180
labels=None,
180181
database_role=None,
182+
tracer_provider=None,
181183
):
182-
super(FixedSizePool, self).__init__(labels=labels, database_role=database_role)
184+
super(FixedSizePool, self).__init__(
185+
labels=labels, database_role=database_role, tracer_provider=tracer_provider
186+
)
183187
self.size = size
184188
self.default_timeout = default_timeout
185189
self._sessions = queue.LifoQueue(size)
@@ -284,8 +288,12 @@ class BurstyPool(AbstractSessionPool):
284288
:param database_role: (Optional) user-assigned database_role for the session.
285289
"""
286290

287-
def __init__(self, target_size=10, labels=None, database_role=None):
288-
super(BurstyPool, self).__init__(labels=labels, database_role=database_role)
291+
def __init__(
292+
self, target_size=10, labels=None, database_role=None, tracer_provider=None
293+
):
294+
super(BurstyPool, self).__init__(
295+
labels=labels, database_role=database_role, tracer_provider=tracer_provider
296+
)
289297
self.target_size = target_size
290298
self._database = None
291299
self._sessions = queue.LifoQueue(target_size)
@@ -392,8 +400,11 @@ def __init__(
392400
ping_interval=3000,
393401
labels=None,
394402
database_role=None,
403+
tracer_provider=None,
395404
):
396-
super(PingingPool, self).__init__(labels=labels, database_role=database_role)
405+
super(PingingPool, self).__init__(
406+
labels=labels, database_role=database_role, tracer_provider=tracer_provider
407+
)
397408
self.size = size
398409
self.default_timeout = default_timeout
399410
self._delta = datetime.timedelta(seconds=ping_interval)
@@ -546,6 +557,7 @@ def __init__(
546557
ping_interval=3000,
547558
labels=None,
548559
database_role=None,
560+
tracer_provider=None,
549561
):
550562
"""This throws a deprecation warning on initialization."""
551563
warn(
@@ -561,6 +573,7 @@ def __init__(
561573
ping_interval,
562574
labels=labels,
563575
database_role=database_role,
576+
tracer_provider=tracer_provider,
564577
)
565578

566579
self.begin_pending_transactions()

google/cloud/spanner_v1/session.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,13 @@ class Session(object):
6363
_session_id = None
6464
_transaction = None
6565

66-
def __init__(self, database, labels=None, database_role=None):
66+
def __init__(self, database, labels=None, database_role=None, tracer_provider=None):
6767
self._database = database
6868
if labels is None:
6969
labels = {}
7070
self._labels = labels
7171
self._database_role = database_role
72+
self._tracer_provider = tracer_provider
7273

7374
def __lt__(self, other):
7475
return self._session_id < other._session_id

tests/unit/test__opentelemetry_tracing.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,13 @@ def _make_rpc_error(error_cls, trailing_metadata=None):
3030
def _make_session():
3131
from google.cloud.spanner_v1.session import Session
3232

33-
return mock.Mock(autospec=Session, instance=True)
33+
session = mock.Mock(autospec=Session, instance=True)
34+
# Setting _tracer_provider to None is to avoid the nasty spill-over
35+
# of mock._tracer_provider spuriously failing tests, because per
36+
# unittest.mock.Mock's definition invoking any attribute or method
37+
# returns another mock.
38+
setattr(session, "_tracer_provider", None)
39+
return session
3440

3541

3642
# Skip all of these tests if we don't have OpenTelemetry

0 commit comments

Comments
 (0)