Skip to content

[Sampler.BottomFloor] Add fixed-memory bottom-k log sampler - #5108

Open
Yun-Ting wants to merge 2 commits into
open-telemetry:mainfrom
Yun-Ting:Yun-Ting/bottom-floor-log-sampling
Open

[Sampler.BottomFloor] Add fixed-memory bottom-k log sampler#5108
Yun-Ting wants to merge 2 commits into
open-telemetry:mainfrom
Yun-Ting:Yun-Ting/bottom-floor-log-sampling

Conversation

@Yun-Ting

Copy link
Copy Markdown
Contributor

Fixes #
Design discussion issue #

Changes

Adds OpenTelemetry.Sampler.BottomFloor, a fixed-memory, self-calibrating
bottom-k sampler for log records.

Chatty services have no good options today: raising the log level loses the rare
error buried among a flood of routine records, fixed-rate head sampling loses it
too and reports nothing about what was dropped, and per-logger rate limiting
silently truncates whatever happens to be loudest. This component bounds export
volume while keeping rare callsites representable, and stamps every retained
record with the arithmetic needed to reconstruct the original counts.

How it works

Each arrival is assigned an exponential priority scaled by its callsite's
weight, and the reservoir retains the k + 1 smallest. When the window closes,
the k best form the sample and the remaining entry becomes the inclusion
threshold. Per-callsite estimates then set the next window's weights, so
frequent callsites are pushed down and rare or newly seen ones keep a high
chance of inclusion. The only required setting is the per-window budget k.

Retained records carry otel.logs.adjusted_count (the reciprocal of the
record's inclusion probability) and otel.logs.cv2 (an adequacy signal for the
estimate), so downstream aggregation switches from COUNT(*) to
SUM(otel.logs.adjusted_count) and stays unbiased.

What is included

  • BottomFloorSampler<TCallsite> — the algorithm, with no OpenTelemetry
    dependency, usable for non-log streams.
  • BottomFloorLogExporter — decorates a BaseExporter<LogRecord> and samples
    each export batch.
  • BottomFloorLogRecordProcessor — the ready-to-register form, combining
    batching and sampling. One export batch is one sampling window.
  • Opt-in per-span log coverage via MaxLogsPerSpanPerWindow (default 0), so a
    span's logs stay retrievable together while a chatty span cannot dominate.

Notes for reviewers

  • Compression ratio under load is Budget / maxExportBatchSize. A window no
    larger than the budget keeps everything it holds, so the constructor rejects
    that combination rather than silently no-op sampling. Quiet windows pass
    through whole, which is the "bottom floor" behaviour.

  • Two dependencies on SDK internals, both isolated and both degrading safely:

    • LogRecord.Copy() for retention. Records are pooled and reclaimed as the
      batch enumerator advances, so anything held past that visit must be copied.
      If it cannot be bound, the exporter forwards every batch unsampled
      rather than emitting recycled records.
    • BaseExporter<T>.ParentProvider forwarding, so a decorated exporter still
      resolves its Resource. Uses UnsafeAccessor on .NET 9+ and reflection
      earlier; degrades to a no-op.

    BaseExporter<T> exposes no supported seam for the second one: the setter is
    internal, there is no virtual hook, and no constructor overload. The one
    supported alternative — wrapping a BaseProcessor<T> and subclassing
    CompositeProcessor<T>, which does forward to children — was rejected because
    it would give up the batching and flush timer inherited from
    BatchExportProcessor and would not remove the LogRecord.Copy() dependency
    anyway. Rationale is recorded in DESIGN.md; an upstream API request is
    planned.

  • README snippets are compiled and executed as tests (ReadmeExampleTests),
    and a test asserts the README still matches the compiled source character for
    character. Editing a snippet therefore requires editing both files.

  • Sampling accuracy is exercised end to end by examples/bottom-floor, which
    recovers arrival counts to well under 1% at 10x compression.

Merge requirement checklist

  • CONTRIBUTING guidelines followed (license requirements, nullable enabled, static analysis, etc.)
  • Unit tests added/updated
  • Appropriate CHANGELOG.md files updated for non-trivial changes
  • Changes in public API reviewed (if applicable)

Yun-Ting and others added 2 commits August 21, 2026 16:05
… quality

Enroll the package in the verify-aot-compat job, correct the package description which still advertised span sampling after that scope was removed, and document how callsite identity affects sampling quality.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Yun-Ting
Yun-Ting requested a review from a team as a code owner August 21, 2026 23:24
@github-actions
github-actions Bot requested a review from jmacd August 21, 2026 23:24
@github-actions github-actions Bot added infra Infra work - CI/CD, code coverage, linters documentation Improvements or additions to documentation labels Aug 21, 2026
@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Aug 21, 2026

Copy link
Copy Markdown

Pull request dashboard status

Waiting on the author · refreshed 2026-08-22 15:28 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.

@codecov

codecov Bot commented Aug 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.64957% with 96 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.88%. Comparing base (9d4e55f) to head (f3fb975).
⚠️ Report is 159 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...etry.Sampler.BottomFloor/BottomFloorLogExporter.cs 65.21% 48 Missing ⚠️
...nTelemetry.Sampler.BottomFloor/CallsiteEstimate.cs 30.43% 16 Missing ⚠️
...y.Sampler.BottomFloor/ParentProviderPropagation.cs 66.66% 9 Missing ⚠️
.../OpenTelemetry.Sampler.BottomFloor/OfferOutcome.cs 46.66% 8 Missing ⚠️
...elemetry.Sampler.BottomFloor/BottomFloorSampler.cs 92.92% 7 Missing ⚠️
src/OpenTelemetry.Sampler.BottomFloor/KeptItem.cs 33.33% 6 Missing ⚠️
...elemetry.Sampler.BottomFloor/LogRecordRetention.cs 84.61% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #5108      +/-   ##
==========================================
+ Coverage   77.33%   77.88%   +0.55%     
==========================================
  Files         468      487      +19     
  Lines       19811    20746     +935     
==========================================
+ Hits        15320    16158     +838     
- Misses       4491     4588      +97     
Flag Coverage Δ
unittests-Contrib.Shared.Tests 88.49% <ø> (-1.37%) ⬇️
unittests-DynamicControl 92.77% <ø> (?)
unittests-Exporter.Geneva 56.72% <ø> (+0.26%) ⬆️
unittests-Exporter.InfluxDB 97.96% <ø> (ø)
unittests-Exporter.OneCollector 95.60% <ø> (ø)
unittests-Extensions 96.19% <ø> (+5.58%) ⬆️
unittests-Extensions.Enrichment 100.00% <ø> (ø)
unittests-Extensions.Enrichment.AspNetCore 86.27% <ø> (ø)
unittests-Extensions.Enrichment.Http 94.33% <ø> (ø)
unittests-Instrumentation.AWS 85.35% <ø> (+0.12%) ⬆️
unittests-Instrumentation.AspNet 79.15% <ø> (+1.65%) ⬆️
unittests-Instrumentation.AspNetCore 87.20% <ø> (ø)
unittests-Instrumentation.Cassandra 92.30% <ø> (ø)
unittests-Instrumentation.ConfluentKafka 83.42% <ø> (+0.61%) ⬆️
unittests-Instrumentation.ElasticsearchClient 80.36% <ø> (ø)
unittests-Instrumentation.EntityFrameworkCore 81.02% <ø> (ø)
unittests-Instrumentation.EventCounters 76.31% <ø> (+0.86%) ⬆️
unittests-Instrumentation.GrpcCore 91.12% <ø> (ø)
unittests-Instrumentation.GrpcNetClient 78.30% <ø> (+0.20%) ⬆️
unittests-Instrumentation.Hangfire 88.88% <ø> (ø)
unittests-Instrumentation.Http 76.00% <ø> (+0.79%) ⬆️
unittests-Instrumentation.Kusto 92.44% <ø> (+0.01%) ⬆️
unittests-Instrumentation.Owin 89.15% <ø> (+0.26%) ⬆️
unittests-Instrumentation.Process 100.00% <ø> (ø)
unittests-Instrumentation.Quartz 77.77% <ø> (ø)
unittests-Instrumentation.Remoting 65.59% <ø> (ø)
unittests-Instrumentation.Runtime 100.00% <ø> (ø)
unittests-Instrumentation.ServiceFabricRemoting 39.91% <ø> (ø)
unittests-Instrumentation.SqlClient 84.88% <ø> (+2.52%) ⬆️
unittests-Instrumentation.StackExchangeRedis 94.98% <ø> (+0.62%) ⬆️
unittests-Instrumentation.Wcf 82.64% <ø> (ø)
unittests-OpAmp.Client 86.51% <ø> (-0.09%) ⬇️
unittests-PersistentStorage 69.50% <ø> (+0.29%) ⬆️
unittests-PowerShellScripts 86.27% <ø> (ø)
unittests-Resources.AWS 72.78% <ø> (ø)
unittests-Resources.Azure 84.18% <ø> (ø)
unittests-Resources.Container 80.76% <ø> (ø)
unittests-Resources.Gcp 80.70% <ø> (ø)
unittests-Resources.Host 72.34% <ø> (-0.13%) ⬇️
unittests-Resources.OperatingSystem 77.16% <ø> (ø)
unittests-Resources.Process 90.90% <ø> (ø)
unittests-Resources.ProcessRuntime 80.76% <ø> (ø)
unittests-Sampler.AWS 96.05% <ø> (+0.02%) ⬆️
unittests-Sampler.BottomFloor 72.64% <72.64%> (?)

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

Files with missing lines Coverage Δ
...mpler.BottomFloor/BottomFloorLogRecordProcessor.cs 100.00% <100.00%> (ø)
...ampler.BottomFloor/BottomFloorLogSamplerOptions.cs 100.00% <100.00%> (ø)
...OpenTelemetry.Sampler.BottomFloor/WindowSummary.cs 100.00% <100.00%> (ø)
...elemetry.Sampler.BottomFloor/LogRecordRetention.cs 84.61% <84.61%> (ø)
src/OpenTelemetry.Sampler.BottomFloor/KeptItem.cs 33.33% <33.33%> (ø)
...elemetry.Sampler.BottomFloor/BottomFloorSampler.cs 92.92% <92.92%> (ø)
.../OpenTelemetry.Sampler.BottomFloor/OfferOutcome.cs 46.66% <46.66%> (ø)
...y.Sampler.BottomFloor/ParentProviderPropagation.cs 66.66% <66.66%> (ø)
...nTelemetry.Sampler.BottomFloor/CallsiteEstimate.cs 30.43% <30.43%> (ø)
...etry.Sampler.BottomFloor/BottomFloorLogExporter.cs 65.21% <65.21%> (ø)

... and 51 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@martincostello

Copy link
Copy Markdown
Member

If we were to add this, I think it needs more test coverage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation infra Infra work - CI/CD, code coverage, linters

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants