Skip to content

Commit 2ed85cb

Browse files
committed
Advertise ydb-sdk-tracing token in x-ydb-sdk-build-info when tracing is on
While a tracing provider is installed the SDK appends ydb-sdk-tracing/0.1.0 to the x-ydb-sdk-build-info header (native SDK, then SDK-owned feature tokens, then custom driver headers). The token list is aggregated by ydb.observability.sdk_build_info_tokens so future features (e.g. metrics) plug in with a single line.
1 parent 3adcd6b commit 2ed85cb

6 files changed

Lines changed: 91 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
* Introduce `ydb.observability` — vendor-neutral tracing entrypoint with a `TracingProvider` interface; `enable_tracing(provider)` accepts any implementation (custom or OpenTelemetry) and replaces the previously installed one. The SDK core no longer imports `opentelemetry`, so tracing can be enabled without the OpenTelemetry packages by supplying a custom provider
2+
* When tracing is enabled the SDK appends a `ydb-sdk-tracing/0.1.0` token to the `x-ydb-sdk-build-info` header, so the server can distinguish requests from tracing-enabled clients
23

34
## 3.30.0 ##
45
* Query session attach stream handles `NodeShutdown` and `SessionShutdown` session hints: on `NodeShutdown` the session's node connection is pessimized and the session is retired, on `SessionShutdown` the session is retired without touching the node

tests/tracing/test_observability_enable.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
span_finish_callback,
3737
)
3838

39+
from ydb.ydb_version import VERSION
40+
3941
from .conftest import FakeDriverConfig
4042

4143

@@ -301,6 +303,60 @@ def get_trace_metadata(self):
301303
assert provider.calls == 1
302304

303305

306+
class _BuildInfoCfg:
307+
database = None
308+
credentials = None
309+
310+
311+
class TestSdkBuildInfoHeader:
312+
"""Enabling tracing advertises a ``ydb-sdk-tracing`` token in x-ydb-sdk-build-info."""
313+
314+
def test_tokens_reflect_active_provider(self):
315+
from ydb.observability import sdk_build_info_tokens
316+
317+
assert sdk_build_info_tokens() == []
318+
enable_tracing(RecordingProvider())
319+
assert sdk_build_info_tokens() == ["ydb-sdk-tracing/0.1.0"]
320+
disable_tracing()
321+
assert sdk_build_info_tokens() == []
322+
323+
def test_header_gains_tracing_token_when_enabled(self):
324+
from ydb.connection import _construct_metadata
325+
326+
def build_info():
327+
return dict(_construct_metadata(_BuildInfoCfg(), None))["x-ydb-sdk-build-info"]
328+
329+
assert "ydb-sdk-tracing" not in build_info()
330+
331+
enable_tracing(RecordingProvider())
332+
header = build_info()
333+
disable_tracing()
334+
335+
assert header == f"ydb-python-sdk/{VERSION};ydb-sdk-tracing/0.1.0"
336+
337+
def test_tracing_token_precedes_driver_additional_headers(self):
338+
from ydb.connection import _construct_metadata
339+
340+
class _Cfg(_BuildInfoCfg):
341+
_additional_sdk_headers = ("lib1/1.0",)
342+
343+
enable_tracing(RecordingProvider())
344+
header = dict(_construct_metadata(_Cfg(), None))["x-ydb-sdk-build-info"]
345+
disable_tracing()
346+
347+
# native SDK, then SDK-owned tracing token, then custom driver headers
348+
assert header == f"ydb-python-sdk/{VERSION};ydb-sdk-tracing/0.1.0;lib1/1.0"
349+
350+
async def test_header_async_parity(self):
351+
from ydb.aio.connection import _construct_metadata
352+
353+
enable_tracing(RecordingProvider())
354+
header = dict(await _construct_metadata(_BuildInfoCfg(), None))["x-ydb-sdk-build-info"]
355+
disable_tracing()
356+
357+
assert header == f"ydb-python-sdk/{VERSION};ydb-sdk-tracing/0.1.0"
358+
359+
304360
class TestSplitEndpoint:
305361
"""Cover every branch of the endpoint parser."""
306362

ydb/aio/connection.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from ydb.driver import DriverConfig
2727
from ydb.settings import BaseRequestSettings
2828
from ydb import issues
29+
from ydb.observability import sdk_build_info_tokens
2930
from ydb.observability.tracing import get_trace_metadata
3031

3132
# Workaround for good IDE and universal for runtime
@@ -71,7 +72,8 @@ async def _construct_metadata(
7172
if settings.request_type is not None:
7273
metadata.append((YDB_REQUEST_TYPE_HEADER, settings.request_type))
7374

74-
metadata.append(_utilities.x_ydb_sdk_build_info_header(getattr(driver_config, "_additional_sdk_headers", ())))
75+
additional_sdk_headers = (*sdk_build_info_tokens(), *getattr(driver_config, "_additional_sdk_headers", ()))
76+
metadata.append(_utilities.x_ydb_sdk_build_info_header(additional_sdk_headers))
7577

7678
metadata.extend(get_trace_metadata())
7779

ydb/connection.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import grpc
2525
from . import issues, _apis, _utilities
2626
from . import default_pem
27+
from .observability import sdk_build_info_tokens
2728
from .observability.tracing import get_trace_metadata
2829

2930
_stubs_list = (
@@ -179,7 +180,8 @@ def _construct_metadata(driver_config, settings):
179180
metadata.append((YDB_REQUEST_TYPE_HEADER, settings.request_type))
180181
metadata.extend(getattr(settings, "headers", []))
181182

182-
metadata.append(_utilities.x_ydb_sdk_build_info_header(getattr(driver_config, "_additional_sdk_headers", ())))
183+
additional_sdk_headers = (*sdk_build_info_tokens(), *getattr(driver_config, "_additional_sdk_headers", ()))
184+
metadata.append(_utilities.x_ydb_sdk_build_info_header(additional_sdk_headers))
183185

184186
metadata.extend(get_trace_metadata())
185187

ydb/observability/__init__.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
every span is a :class:`~ydb.observability.tracing.NoopSpan`.
1414
"""
1515

16-
from typing import Optional
16+
from typing import List, Optional
1717

1818
from ydb.observability.tracing import (
1919
NoopSpan,
@@ -22,6 +22,7 @@
2222
SpanName,
2323
TracingProvider,
2424
_registry,
25+
_tracing_build_info_tokens,
2526
)
2627

2728

@@ -48,6 +49,19 @@ def get_active_provider() -> Optional[TracingProvider]:
4849
return _registry.get_provider() if _registry.is_active() else None
4950

5051

52+
def sdk_build_info_tokens() -> List[str]:
53+
"""All ``x-ydb-sdk-build-info`` feature tokens contributed by observability.
54+
55+
Aggregated across observability features so the SDK build-info header advertises
56+
every capability the client has turned on. Tracing contributes
57+
``ydb-sdk-tracing/<v>`` while active; future features (e.g. metrics via
58+
``enable_metrics``) append their own tokens here.
59+
"""
60+
tokens: List[str] = []
61+
tokens.extend(_tracing_build_info_tokens())
62+
return tokens
63+
64+
5165
__all__ = [
5266
"NoopSpan",
5367
"NoopTracingProvider",

ydb/observability/tracing.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,19 @@ def get_trace_metadata() -> List[Tuple[str, str]]:
146146
return list(_registry.get_trace_metadata())
147147

148148

149+
TRACING_SDK_BUILD_INFO = "ydb-sdk-tracing/0.1.0"
150+
151+
152+
def _tracing_build_info_tokens() -> List[str]:
153+
"""Tracing's contribution to the ``x-ydb-sdk-build-info`` header.
154+
155+
Returns ``["ydb-sdk-tracing/0.1.0"]`` once a provider is installed, otherwise an
156+
empty list. Aggregated with other features by
157+
:func:`ydb.observability.sdk_build_info_tokens`.
158+
"""
159+
return [TRACING_SDK_BUILD_INFO] if _registry.is_active() else []
160+
161+
149162
def _split_endpoint(endpoint: Optional[str]) -> Tuple[str, int]:
150163
ep = endpoint or ""
151164
if ep.startswith("grpcs://"):

0 commit comments

Comments
 (0)