Skip to content

PYTHON-5945 Add OpenTelemetry Simple Command Support - #3071

Open
blink1073 wants to merge 2 commits into
mainfrom
PYTHON-5945
Open

blink1073 wants to merge 2 commits into
mainfrom
PYTHON-5945

Conversation

@blink1073

Copy link
Copy Markdown
Member

PYTHON-5945

Carries the content of #2946, which merged into the temporary otel feature branch, onto main. This is the bottom of the OpenTelemetry stack retargeted to track main directly.

PR Contents
1 this PR command spans
2 PYTHON-5947-operations operation spans
3 #2992 transaction spans
4 #2993 unified runner and vendored fixtures
5 #2994 getMore spans
6 #3000 error.type command span attribute
7 #3055 W3C traceparent propagation

Changes in this PR

Adds optional, opt-in OpenTelemetry tracing for server commands, conforming to the DRIVERS-719 client-side OpenTelemetry spec. Spec-driven unified test format compliance is deferred to PYTHON-5947 (every operation/transaction YAML test asserts an operation-level span, which is out of scope here); the spec's two command/config-scoped prose tests are included directly.

Test Plan

New tests in test/test_otel.py (and async mirror) using an in-memory span exporter, including the spec's two prose tests (env-var enable/disable, env-var-driven db.query.text), run under their own otel pytest marker/Evergreen test type (like encryption/kms) so opentelemetry stays an optional dependency. Verified no regression in existing command logging/monitoring test suites. Manually verified span output against a live server with the OTel console exporter.

Checklist

Checklist for Author

  • Did you update the changelog (if necessary)?
  • Is there test coverage?
  • Is any followup work tracked in a JIRA ticket? If so, add link(s). (PYTHON-5947)

Checklist for Reviewer

  • Does the title of the PR reference a JIRA Ticket?
  • Do you fully understand the implementation? (Would you be comfortable explaining how this code works to someone else?)
  • Is all relevant documentation (README or docstring) updated?

Stack created with GitHub Stacks CLI • Give Feedback 💬

Copilot AI lite review requested due to automatic review settings September 25, 2026 10:14
@blink1073
blink1073 requested a review from a team as a code owner September 25, 2026 10:14
@blink1073
blink1073 requested a review from NoahStapp September 25, 2026 10:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved configuration precedence and command-span correctness issues remain.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity · 3 Medium severity

Open (4)
What changed in this PR

Adds optional OpenTelemetry command-span tracing for synchronous and asynchronous PyMongo clients.

Changes:

  • Adds tracing configuration, span attributes, and lifecycle integration.
  • Adds OpenTelemetry dependency metadata and documentation.
  • Adds sync/async tests and Evergreen coverage.
File Description
uv.lock Locks OpenTelemetry dependencies
test/​test_otel.py Synchronous tracing tests
test/​asynchronous/​test_otel.py Asynchronous tracing tests
requirements/​opentelemetry.txt OpenTelemetry API dependency
pyproject.toml Optional dependency and pytest marker
pymongo/​synchronous/​mongo_client.py Sync tracing documentation
pymongo/​synchronous/​command_runner.py Sync tracing integration
pymongo/​pool_shared.py Connection address typing
pymongo/​common.py Tracing option validation
pymongo/​client_options.py Stores tracing configuration
pymongo/​asynchronous/​mongo_client.py Async tracing documentation
pymongo/​asynchronous/​command_runner.py Async tracing integration
pymongo/​_telemetry.py Span lifecycle integration
pymongo/​_otel.py OpenTelemetry span implementation
justfile Typing dependency setup
doc/​changelog.rst Feature changelog entry
.evergreen/​scripts/​utils.py OTel test mapping
.evergreen/​scripts/​setup_tests.py OTel test and coverage setup
.evergreen/​scripts/​generate_config.py OTel variant generation
.evergreen/​generated_configs/​variants.yml Generated OTel variant

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pymongo/_otel.py Outdated
Comment thread pymongo/_otel.py Outdated
Comment thread pymongo/_otel.py
Comment thread pymongo/_otel.py
@blink1073
blink1073 added this pull request to stack #3073 September 25, 2026 10:30
@codecov

codecov Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.01734% with 19 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pymongo/_otel.py 86.71% 10 Missing and 7 partials ⚠️
pymongo/common.py 87.50% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread pymongo/_otel.py Outdated
Comment thread pymongo/_otel.py Outdated
Comment thread pymongo/_telemetry.py
Comment thread pymongo/_telemetry.py Outdated
Comment thread pymongo/common.py Outdated
Comment thread pymongo/asynchronous/mongo_client.py Outdated
@blink1073
blink1073 marked this pull request as draft September 25, 2026 13:03
@blink1073

Copy link
Copy Markdown
Member Author

@NoahStapp the first commit was already merged in the last PR. I'm going to squash down all the copilot back and forth here into a second commit.

…butes

Fix tracing.enabled so an explicit client value overrides the
OTEL_PYTHON_INSTRUMENTATION_MONGODB_ENABLED environment variable,
fix db.query.text truncation so budgets smaller than the "..."
marker still honor the bound, and fix collection-name extraction so
user and role management commands do not expose usernames as
db.collection.name.
@blink1073
blink1073 force-pushed the PYTHON-5945 branch 2 times, most recently from e91db53 to 61da468 Compare September 25, 2026 15:56
@blink1073
blink1073 marked this pull request as ready for review September 25, 2026 17:26
pool_opts=pool_opts,
)
except Exception as exc:
except BaseException as exc:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If CancelledError is the only BaseException type we want to catch here, can we narrow this catch back down to (Exception, CancelledError)?

| **OpenTelemetry options:**
| (Requires the ``opentelemetry-api`` package; install with the ``pymongo[opentelemetry]`` extra.)

- `tracing`: (dict) Configuration for OpenTelemetry command spans, with keys:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we allow tracing to be passed as a connection string option? The spec doesn't say explicitly.


.. seealso:: The MongoDB documentation on `connections <https://dochub.mongodb.org/core/connections>`_.

.. versionchanged:: 4.18

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be 4.XX until we're ready for release.


.. seealso:: The MongoDB documentation on `connections <https://dochub.mongodb.org/core/connections>`_.

.. versionchanged:: 4.18

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be 4.XX until we're ready for release.

Comment thread pymongo/client_options.py
def tracing(self) -> _otel.TracingOptions:
"""The configured ``tracing`` option for OpenTelemetry command spans.

.. versionadded:: 4.18

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be 4.XX until we're ready for release.

if (
(topology_id is not None and _is_debug_enabled(_COMMAND_LOGGER))
or (listeners is not None and listeners.enabled_for_commands)
or _otel._is_tracing_enabled(tracing_options)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recall that we don't support enabling/disabling OTel after client construction, so we can determine if tracing is enabled or not one single time at creation instead of needing to make this call on every operation. There's a few other places we call _otel._is_tracing_enabled() every time instead of once at startup that could be removed as well.

Comment thread pymongo/_otel.py
Returns None when tracing is disabled/unavailable or the command is
sensitive (mirroring the redaction applied to logs).
"""
if not _is_tracing_enabled(tracing_options):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a check to ensure the span is recording before doing all the work below?

Comment thread pymongo/_otel.py
def _build_query_text(cmd: Mapping[str, Any], max_length: int) -> str:
"""Serialize ``cmd`` to extended JSON, redacted and truncated to ``max_length``.

Mirrors the truncation approach used for log messages: truncate field

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is the same approach we use for logging, can we use a shared helper instead of duplicating it here?

Comment thread pymongo/_otel.py


def _is_sensitive_command(command_name: str, speculative_hello: bool) -> bool:
"""Mirror the redaction rules in ``pymongo.logger.LogMessage._is_sensitive``."""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, if this is the same as the logger's version can we use a shared helper instead of duplicating?

Comment thread pymongo/_otel.py
# expose the username or role name as a collection.
_NOT_COLLECTION_COMMANDS = frozenset(
{
"createUser",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We redact createUser for command logging, so this won't ever get used. Same goes for updateUser.

This branch has not been deployed

No deployments
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.

3 participants