Skip to content

opentelemetry-sdk: add additional logs benchmarks & make additional perf improvements - #5784

Open
herin049 wants to merge 6 commits into
open-telemetry:mainfrom
herin049:feat/logs-benches
Open

herin049 wants to merge 6 commits into
open-telemetry:mainfrom
herin049:feat/logs-benches

Conversation

@herin049

@herin049 herin049 commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Description

As part of the logs stabilization effort, this PR adds SDK logs benchmarks to show whether the API/SDK shape needs to change for performance reasons. In particular, it focuses on the overhead of calling a disabled logger.

The benchmarks showed that Logger.emit only checked the logger config and never checked the processor's enabled() result. Thus, an unguarded emit still built the full ReadWriteLogRecord before dropping it. This PR fixes that as well.

Part of: #3361

Benchmark Results

Benchmark results

Ran with Apple M5 Pro, Python 3.13.11

Disabled logger

Benchmark Before After Change
logger_config-unguarded 167 ns 167 ns ±0%¹
logger_config-guarded 54 ns 46 ns −14%
processor_filter-unguarded 2.29 µs 458 ns 5.0× faster
processor_filter-guarded 317 ns 315 ns ±0%
no_processors-unguarded 2.37 µs 290 ns 8.2× faster
no_processors-guarded 181 ns 179 ns −1%
sdk_disabled-unguarded 128 ns 128 ns ±0%
sdk_disabled-guarded 39 ns 39 ns ±0%
api_proxy_noop-unguarded 196 ns 189 ns −4%
api_proxy_noop-guarded 69 ns 69 ns ±0%

There is still likely room for some improvement here, but I don't think that this will require a change in the shape of the API/SDK. One performance-adjacent feature that we may want to add in the future is the ability to have a log format string that is evaluated lazily (like the Python logger). However, I personally don't think this use-case is very common -- most users still rely on the built-in logging module (with the instrumentation lib) and we still provide logger.enabled(...) as a fallback.

cc: @aabmass

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Checklist:

See contributing.md for styleguide, changelog guidelines, and more.

  • I am a human and not a bot
  • I have run tox -e precommit
  • I have run tox -e typecheck
  • I have run the relevant tests and linting tox environments
  • This requires the following PR in contrib:

@herin049
herin049 requested a review from a team as a code owner October 15, 2026 01:52
@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Pull request dashboard status

Waiting on the author · refreshed 2026-10-16 02:30 UTC

Two things need attention:

  • Required checks are failing — investigate the failures.
  • 1 review item — respond to each (e.g. link a commit, explain why not, ask a follow-up):
    • Top-level threads: 1
Status above doesn't look right?
  • Just replied or pushed? Anything around or after the refresh time above may not be picked up yet — give it a few minutes.
  • Should this be with reviewers? Comment /dashboard route:reviewers to route it to them.
  • Anything wrong — including the routing? Report it with what you expected; it helps us improve the dashboard.

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.

🟡 Changes recommended

Early filtering can silently drop wrapped records when their instrumentation scope differs from the logger’s scope.

1 open finding
What changed in this PR

Supports logs stabilization by expanding benchmarks and reducing disabled-logger overhead in the SDK.

Changes:

  • Checks processor enablement before constructing log records.
  • Adds disabled-logger and emission benchmarks.
  • Updates filtering tests and preserves log metric accounting.
File Description
opentelemetry-sdk/​tests/​logs/​test_sdk_metrics.py Tests metric accounting with disabled processors.
opentelemetry-sdk/​tests/​logs/​test_logs.py Updates configuration assertions and tests early filtering.
opentelemetry-sdk/​src/​opentelemetry/​sdk/​_logs/​_internal/​__init__.py Adds early processor enablement checks.
opentelemetry-sdk/​benchmarks/​logs/​test_benchmark_logs.py Adds exception, active-span, and emission-style benchmarks.
opentelemetry-sdk/​benchmarks/​logs/​test_benchmark_logs_enabled.py Benchmarks guarded and unguarded logging.
.changelog/​5784.added Documents benchmarks and performance improvements.

🧠 Review effort: Balanced


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

Comment thread opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py Outdated
@github-project-automation github-project-automation Bot moved this from Ready for review to Approved PRs in Python PR digest Oct 15, 2026
@emdneto
emdneto requested a review from aabmass October 15, 2026 13:45
Comment thread opentelemetry-sdk/benchmarks/logs/test_benchmark_logs_enabled.py
Comment thread opentelemetry-sdk/benchmarks/logs/test_benchmark_logs_enabled.py
@herin049

herin049 commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

@aabmass Here are the updated results with info on allocations/GC collections (with kwargs vs log-record style emit):

Benchmark Mean Alloc bytes/call Gen-0 GCs / 10k calls
logger_config-kwargs-unguarded 137 ns 109 0
logger_config-kwargs-guarded 46 ns 0 0
logger_config-log_record-unguarded 458 ns 325 0
logger_config-log_record-guarded 46 ns 0 0
processor_filter-kwargs-unguarded 432 ns 705 0
processor_filter-kwargs-guarded 311 ns 640 0
processor_filter-log_record-unguarded 802 ns 917 0
processor_filter-log_record-guarded 315 ns 640 0
no_processors-kwargs-unguarded 297 ns 705 0
no_processors-kwargs-guarded 176 ns 640 0
no_processors-log_record-unguarded 652 ns 917 0
no_processors-log_record-guarded 177 ns 640 0
sdk_disabled-kwargs-unguarded 131 ns 109 0
sdk_disabled-kwargs-guarded 39 ns 0 0
sdk_disabled-log_record-unguarded 446 ns 325 0
sdk_disabled-log_record-guarded 39 ns 0 0
api_proxy_noop-kwargs-unguarded 194 ns 109 0
api_proxy_noop-kwargs-guarded 70 ns 0 0
api_proxy_noop-log_record-unguarded 522 ns 325 0
api_proxy_noop-log_record-guarded 70 ns 0 0

Looks like none of the logger methods cause a gen-0 GC collection, which is good. I'd imagine we could improve the LogRecord results by lazily calculating some of the attributes in the log record itself, and only capture the current context greedily.

@herin049
herin049 requested a review from aabmass October 15, 2026 21:59

@aabmass aabmass left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you Lukas, the benchmarks look good and that's what I was going for. My AI agreed they make sense and duplicate the results, and made some suggestions based on it. Feel free to ignore them or do later (review comments here)

Comment thread opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py

def _emit_log_record_unguarded(logger, index):
logger.emit(
LogRecord(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

+1 on LogRecord.__init__ doing too much work eagerly (time_ns(), get_current(), and get_current_span()). Adding __slots__ to LogRecord (and ReadWriteLogRecord / ReadableLogRecord) would also save the extra 216 bytes/call (325 B vs 109 B) from the instance __dict__.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I've added __slots__ to all of these classes and the memory has improved a lot. I've deferred optimizing the LogRecord.__init__ method for now.

@aabmass

aabmass commented Oct 16, 2026

Copy link
Copy Markdown
Member

Filed one issue based on the doing the same benchmarking locally, lmk if it makes sense #5785

@herin049

herin049 commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor Author

@aabmass Updates results based on a few small optimizations (mostly adding slots):

Benchmark Mean before Mean after (Δ) Alloc before Alloc after (Δ)
logger_config-kwargs-unguarded 137 ns 132 ns (-3%) 109 B 109 B (±0%)
logger_config-kwargs-guarded 46 ns 46 ns (±0%) 0 B 0 B (±0%)
logger_config-log_record-unguarded 458 ns 445 ns (-3%) 325 B 277 B (-15%)
logger_config-log_record-guarded 46 ns 45 ns (-2%) 0 B 0 B (±0%)
processor_filter-kwargs-unguarded 432 ns 316 ns (-27%) 705 B 113 B (-84%)
processor_filter-kwargs-guarded 311 ns 195 ns (-37%) 640 B 48 B (-92%)
processor_filter-log_record-unguarded 802 ns 657 ns (-18%) 917 B 277 B (-70%)
processor_filter-log_record-guarded 315 ns 200 ns (-36%) 640 B 48 B (-92%)
no_processors-kwargs-unguarded 297 ns 188 ns (-37%) 705 B 113 B (-84%)
no_processors-kwargs-guarded 176 ns 81 ns (-54%) 640 B 48 B (-92%)
no_processors-log_record-unguarded 652 ns 526 ns (-19%) 917 B 277 B (-70%)
no_processors-log_record-guarded 177 ns 80 ns (-55%) 640 B 48 B (-92%)
sdk_disabled-kwargs-unguarded 131 ns 128 ns (-3%) 109 B 109 B (±0%)
sdk_disabled-kwargs-guarded 39 ns 39 ns (-1%) 0 B 0 B (±0%)
sdk_disabled-log_record-unguarded 446 ns 427 ns (-4%) 325 B 277 B (-15%)
sdk_disabled-log_record-guarded 39 ns 39 ns (-2%) 0 B 0 B (±0%)
api_proxy_noop-kwargs-unguarded 194 ns 191 ns (-2%) 109 B 109 B (±0%)
api_proxy_noop-kwargs-guarded 70 ns 71 ns (+2%) 0 B 0 B (±0%)
api_proxy_noop-log_record-unguarded 522 ns 516 ns (-1%) 325 B 277 B (-15%)
api_proxy_noop-log_record-guarded 70 ns 69 ns (-1%) 0 B 0 B (±0%)
enabled-unguarded 2694 ns 2507 ns (-7%) – –
enabled-guarded 3028 ns 2725 ns (-10%) – –

@herin049

Copy link
Copy Markdown
Contributor Author

I've deferred making some of the calls inside LogRecord lazy because it was becoming a bit messy, I'd prefer to break this up into another PR.

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

Status: Approved PRs

Development

Successfully merging this pull request may close these issues.

5 participants