Skip to content

Split tracing into vendor-neutral observability + OTel adapter#861

Open
KirillKurdyukov wants to merge 2 commits into
mainfrom
tracing-observability-refactor
Open

Split tracing into vendor-neutral observability + OTel adapter#861
KirillKurdyukov wants to merge 2 commits into
mainfrom
tracing-observability-refactor

Conversation

@KirillKurdyukov

Copy link
Copy Markdown
Contributor

Summary

Fixes an architectural issue: tracing was tightly coupled to OpenTelemetry — the SDK forced users to have opentelemetry installed just to enable tracing, and there was no way to plug in a custom backend.

This PR splits the module in two:

  • ydb.observability (new) — vendor-neutral interfaces (Span, TracingProvider), Noop implementation, SpanName, registry, and all SDK-side helpers (create_ydb_span, set_peer_attributes, span_finish_callback, get_trace_metadata). The SDK core imports only from here and never touches opentelemetry.
  • ydb.opentelemetry — becomes a concrete OtelTracingProvider that plugs into the same interface. ydb.opentelemetry.enable_tracing(tracer=None) is now a thin convenience that builds the provider and hands it to ydb.observability.enable_tracing(...).

New public API

from ydb.observability import Span, TracingProvider, enable_tracing, disable_tracing

class MyProvider:
    def create_span(self, name, attributes=None, kind=None) -> Span: ...
    def get_trace_metadata(self): ...

enable_tracing(MyProvider())   # any provider works, no OTel required
enable_tracing(MyProvider())   # second call *replaces* the previous provider
disable_tracing()              # back to Noop
  • Double-enable_tracing cleanly resets the previously installed provider (works for OTel and custom providers alike — previously ydb.opentelemetry.enable_tracing was silently idempotent).
  • ydb.opentelemetry.tracing remains as a re-export shim for backward compatibility.

Tests

  • All 55 existing tracing tests pass (55/55).
  • New tests/tracing/test_observability_enable.py (12 tests) uses a hand-rolled RecordingProviderno OTel involvement — to cover:
    • default Noop state / empty metadata,
    • custom provider receiving spans and metadata,
    • reset on double enable_tracing,
    • enable_tracing(None) == disable_tracing(),
    • disable_tracing reverts to Noop,
    • ydb.opentelemetry.enable_tracing replacing a custom provider,
    • duck-typed provider satisfying the TracingProvider protocol.

Test plan

  • tox -e black-format + tox -e black
  • tox -e style
  • tox -e mypy
  • tox -e py -- ydb -v (174 passed)
  • tox -e py -- tests/tracing -v (55 passed, including 12 new observability tests)
  • Full integration suite (requires Docker; local run)

🤖 Generated with Claude Code

Move all non-OTel tracing plumbing (Span/TracingProvider protocols, Noop
implementation, SpanName, registry, YDB attribute helpers) into a new
ydb.observability package. The OTel bridge in ydb.opentelemetry becomes a
concrete provider (OtelTracingProvider) that plugs into the same interface.

Users can now enable tracing without any OpenTelemetry dependency by
supplying a custom TracingProvider to ydb.observability.enable_tracing. A
second enable call always replaces the previously installed provider.

ydb.opentelemetry.tracing is kept as a thin re-export shim for backward
compatibility. All SDK internal imports (retries, connection, pool,
session, transaction — sync + aio) now go through ydb.observability.tracing.

Added tests/tracing/test_observability_enable.py covering: default Noop
state, custom provider wiring, reset semantics on double enable, disable
reverting to Noop, OTel enable replacing a custom provider, and duck-typed
providers satisfying the protocol.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@KirillKurdyukov KirillKurdyukov requested a review from vgvoleg July 10, 2026 10:18
@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.55%. Comparing base (a7bf659) to head (d6aed82).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #861      +/-   ##
==========================================
+ Coverage   81.38%   81.55%   +0.17%     
==========================================
  Files          94       96       +2     
  Lines       12197    12209      +12     
  Branches     1204     1201       -3     
==========================================
+ Hits         9926     9957      +31     
+ Misses       1807     1799       -8     
+ Partials      464      453      -11     
Flag Coverage Δ
integration 79.35% <100.00%> (+0.20%) ⬆️
unit 47.31% <47.26%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
ydb/aio/connection.py 88.81% <100.00%> (ø)
ydb/aio/pool.py 75.21% <100.00%> (ø)
ydb/aio/query/session.py 88.60% <100.00%> (ø)
ydb/aio/query/transaction.py 100.00% <100.00%> (ø)
ydb/connection.py 80.46% <100.00%> (ø)
ydb/observability/__init__.py 100.00% <100.00%> (ø)
ydb/observability/tracing.py 100.00% <100.00%> (ø)
ydb/opentelemetry/__init__.py 100.00% <ø> (+30.76%) ⬆️
ydb/opentelemetry/plugin.py 100.00% <100.00%> (+5.88%) ⬆️
ydb/opentelemetry/tracing.py 100.00% <100.00%> (+10.67%) ⬆️
... and 4 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

  - TestSplitEndpoint — parametric test covering every branch of _split_endpoint (grpcs://, grpc://, bare host:port, valid/malformed IPv6, missing colon, non-numeric port, None).
  - TestBuildYdbAttrsPeerOptionals — peer tuples with only address / only port / only location / empty location, hitting the three if x is not None branches in _build_ydb_attrs.
  - TestSetPeerAttributes — direct unit test for the set_peer_attributes helper (previously only reached through session flows that never had a non-None peer).
  - TestSpanFinishCallback — success and exception paths of the streaming _finish callback.
  - TestOpentelemetryTracingShimReexports — proves the ydb.opentelemetry.tracing back-compat shim re-exports the exact same objects as ydb.observability.tracing.
  - TestOtelEntrypointHandlesMissingPackage — uses sys.modules["ydb.opentelemetry.plugin"] = None to force the ImportError branches in ydb.opentelemetry.__init__.enable_tracing / disable_tracing.
  - TestOtelTracingSpanBridge — direct tests for TracingSpan.set_attribute and the defensive "exit before enter" branch in _AttachContext.
  - TestNoopContextManager — smoke tests for the Noop context manager and NoopTracingProvider returning the shared span + empty metadata.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant