Skip to content

feat(opentelemetry_rescaled): add rescaling meter provider#544

Open
sandersaares wants to merge 7 commits into
mainfrom
u/sasaares/opentelemetry-rescaled
Open

feat(opentelemetry_rescaled): add rescaling meter provider#544
sandersaares wants to merge 7 commits into
mainfrom
u/sasaares/opentelemetry-rescaled

Conversation

@sandersaares

@sandersaares sandersaares commented Jul 2, 2026

Copy link
Copy Markdown
Member

Adds a new opentelemetry_rescaled crate: a meter provider that wraps an inner OpenTelemetry meter provider and transparently emits rescaled side-by-side "sidecar" instruments for instruments configured per scope.

Problem

Some consumers need the same metric in a different unit or scale (for example, a duration measured in seconds also exported in milliseconds). Producing this by hand means duplicating every recording site, keeping two instruments in sync, and leaking the second unit into instrument-facing code.

What it delivers

A wrapping meter provider that, for instruments selected per scope at build time, creates a second instrument whose measurements are the original values multiplied by a fixed factor. The rescaling is invisible to instrument users, who interact only with their original instrument; the inner provider sees two independently registered instruments.

let outer = RescaledMetrics::builder(inner)
    .scope("my_scope_name", |scope| {
        scope.rescale("http.client.request.duration", "http.client.request.duration.millis", "ms", 1000.0);
    })
    .build();
  • Supports every synchronous and observable instrument kind.
  • Scales histogram bucket boundaries by the same factor so buckets stay meaningful.
  • Rounds and saturates when rescaling integer instruments.
  • The sidecar inherits the source description and requires a new unit; multiplication is the only transform.
  • Unconfigured scopes and instruments pass straight through to the inner provider with no wrapping cost.

See crates/opentelemetry_rescaled/docs/DESIGN.md for the architecture and design tenets.

sandersaares and others added 3 commits July 2, 2026 09:09
Adds the empty crate skeleton (Cargo.toml, lib.rs, README) and a design document (docs/DESIGN.md) describing the rescaling meter-provider wrapper and its open questions, for review before implementation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fold reviewer decisions into the design: round-and-saturate integer rescaling, scale explicit histogram boundaries (no-op for SDK defaults), mandatory sidecar unit, accepted observable double invocation, name-only scope matching with room for future exact matching, multiplication-only transform, build-time validation via panic, and a type-erased by-value inner provider.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wraps an inner OpenTelemetry meter provider and, for instruments configured per scope at build time, transparently emits rescaled side-by-side sidecar instruments (e.g. a seconds histogram gains a millis sidecar scaled by 1000). Supports all sync and observable instrument kinds, scales histogram bucket boundaries, rounds and saturates integer rescales, and passes unconfigured scopes/instruments straight through.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

⚠️ Breaking Changes Detected

error: failed to retrieve local crate data from git revision

Caused by:
    0: failed to retrieve manifest file from git revision source
    1: possibly due to errors: [
         failed when reading /home/runner/work/oxidizer/oxidizer/target/semver-checks/git-origin_main/64f4b2d034eca1b900afb1df4b7545bf6c69f81b/scripts/crate-template/Cargo.toml: TOML parse error at line 9, column 26
         |
       9 | keywords = ["oxidizer", {{CRATE_KEYWORDS}}]
         |                          ^
       missing key for inline table element, expected key
       : TOML parse error at line 9, column 26
         |
       9 | keywords = ["oxidizer", {{CRATE_KEYWORDS}}]
         |                          ^
       missing key for inline table element, expected key
       ,
         failed to parse /home/runner/work/oxidizer/oxidizer/target/semver-checks/git-origin_main/64f4b2d034eca1b900afb1df4b7545bf6c69f81b/Cargo.toml: no `package` table,
       ]
    2: package `opentelemetry_rescaled` not found in /home/runner/work/oxidizer/oxidizer/target/semver-checks/git-origin_main/64f4b2d034eca1b900afb1df4b7545bf6c69f81b

Stack backtrace:
   0: anyhow::error::<impl anyhow::Error>::msg
   1: cargo_semver_checks::rustdoc_gen::RustdocFromProjectRoot::get_crate_source
   2: cargo_semver_checks::rustdoc_gen::StatefulRustdocGenerator<cargo_semver_checks::rustdoc_gen::CoupledState>::prepare_generator
   3: cargo_semver_checks::Check::check_release::{{closure}}
   4: cargo_semver_checks::Check::check_release
   5: cargo_semver_checks::exit_on_error
   6: cargo_semver_checks::main
   7: std::sys::backtrace::__rust_begin_short_backtrace
   8: main

If the breaking changes are intentional then everything is fine - this message is merely informative.

Remember to apply a version number bump with the correct severity when publishing a version with breaking changes (1.x.x -> 2.x.x or 0.1.x -> 0.2.x).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.0%. Comparing base (b695afd) to head (732802b).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff            @@
##             main     #544    +/-   ##
========================================
  Coverage   100.0%   100.0%            
========================================
  Files         354      359     +5     
  Lines       26897    27189   +292     
========================================
+ Hits        26897    27189   +292     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…mple

Move test-only lint allows onto the affected test modules, condense API doc summary lines to a single line, shorten item paths (fmt::Debug, PhantomData), derive Debug struct names from type_name(), and use foldhash-backed maps for the scope/rule lookups. Adds a runnable print_metrics example that shows the rescaled sidecars next to their originals and a metric with no sidecar.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

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.

Pull request overview

Adds a new opentelemetry_rescaled crate that wraps an inner OpenTelemetry MeterProvider and transparently mirrors configured instruments into rescaled “sidecar” instruments (including histogram boundary scaling), with opt-in configuration per instrumentation scope.

Changes:

  • Introduces RescaledMetrics + builder that wraps an inner MeterProvider and conditionally applies per-scope rescale rules.
  • Implements instrument interception via a custom InstrumentProvider (sync fan-out + async dual-registration with scaling observers).
  • Adds end-to-end integration coverage, design docs, example, and spelling dictionary updates for the new terminology.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
crates/opentelemetry_rescaled/src/lib.rs Crate entrypoint, docs, and public exports.
crates/opentelemetry_rescaled/src/provider.rs RescaledMetrics provider wrapper + builder and scope rule wiring.
crates/opentelemetry_rescaled/src/config.rs Scope-level configuration model and validation for rescale rules.
crates/opentelemetry_rescaled/src/instruments.rs InstrumentProvider implementation: sync fan-out and async scaling observer registration.
crates/opentelemetry_rescaled/src/rescale.rs Core value-rescaling trait with rounding/saturation semantics + unit tests.
crates/opentelemetry_rescaled/tests/integration.rs End-to-end tests against SdkMeterProvider and in-memory exporter for all instrument kinds.
crates/opentelemetry_rescaled/examples/print_metrics.rs Runnable example showing sidecar emission and pass-through behavior.
crates/opentelemetry_rescaled/docs/DESIGN.md Architecture/design rationale and invariants for the crate.
crates/opentelemetry_rescaled/README.md Generated README content for the new crate.
crates/opentelemetry_rescaled/Cargo.toml New crate manifest, deps, and external-types allowlist.
Cargo.lock Adds opentelemetry_rescaled to the workspace lockfile.
AGENTS.md Codifies PR-description guidance in repo agent guidelines.
.spelling Adds new crate-related words to the spelling dictionary.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/opentelemetry_rescaled/README.md
Comment thread crates/opentelemetry_rescaled/docs/DESIGN.md Outdated
Add a placeholder logo.png (kept out of the package include allowlist) so the README image resolves, and correct the README heading and design-doc title to the standard OpenTelemetry capitalization.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sandersaares sandersaares marked this pull request as ready for review July 2, 2026 14:02
Copilot AI review requested due to automatic review settings July 2, 2026 14:02

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.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 2 comments.

Comment thread crates/opentelemetry_rescaled/README.md
Comment thread crates/opentelemetry_rescaled/docs/DESIGN.md Outdated
…esign doc

The public API exposes only MeterProvider and Meter (KeyValue is internal only), matching the allowed_external_types allowlist.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@ralfbiedert ralfbiedert left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not a fan of this for multiple reasons. If there is a need to record different metrics, I think different metrics should be recorded. I'd rather we have a system in observed or so where you can define 'metric buddies' or metric dependencies, than to hard code strings and ad-hoc conversion logic. Even if we needed to have this via hooks it should at least be somehow typesafe-ish w.r.t, observed.

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