Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix - add validation for custom buckets provided for Histograms #2351

Merged
merged 12 commits into from
Nov 27, 2024
6 changes: 6 additions & 0 deletions opentelemetry-sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
- These methods are intended for log appenders. Keep the clone of the provider handle, instead of depending on above methods.


- **Bug Fix:** Validates the `with_boundaries` bucket boundaries used in
Histograms. The boundaries provided by the user must be a non-empty vector,
sorted in strictly increasing order, and contain no duplicates. Instruments
will not record measurements if the boundaries are invalid.
[#2351](https://github.com/open-telemetry/opentelemetry-rust/pull/2351)

## 0.27.0

Released 2024-Nov-11
Expand Down
36 changes: 36 additions & 0 deletions opentelemetry-sdk/src/metrics/meter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,23 @@
return Histogram::new(Arc::new(NoopSyncInstrument::new()));
}

if let Some(ref boundaries) = builder.boundaries {
let validation_result = validate_buckets(boundaries);
if let Err(err) = validation_result {
// TODO: Include the buckets too in the error message.
// TODO: This validation is not done when Views are used to
// provide boundaries, and that should be fixed.
otel_error!(
name: "InstrumentCreationFailed",
meter_name = self.scope.name(),
instrument_name = builder.name.as_ref(),
message = "Measurements from this Histogram will be ignored.",
reason = format!("{}", err)

Check warning on line 408 in opentelemetry-sdk/src/metrics/meter.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-sdk/src/metrics/meter.rs#L405-L408

Added lines #L405 - L408 were not covered by tests
);
return Histogram::new(Arc::new(NoopSyncInstrument::new()));
}
}

match resolver
.lookup(
InstrumentKind::Histogram,
Expand Down Expand Up @@ -533,6 +550,25 @@
validate_instrument_name(name).and_then(|_| validate_instrument_unit(unit))
}

fn validate_buckets(buckets: &[f64]) -> MetricResult<()> {
if buckets.is_empty() {
return Err(MetricError::InvalidInstrumentConfiguration(
"Buckets must not be empty",
));
}

// validate that buckets are sorted and non-duplicate
for window in buckets.windows(2) {
if window[0] >= window[1] {
return Err(MetricError::InvalidInstrumentConfiguration(
"Buckets must be sorted and non-duplicate",
));
}
}

Ok(())
}

fn validate_instrument_name(name: &str) -> MetricResult<()> {
if name.is_empty() {
return Err(MetricError::InvalidInstrumentConfiguration(
Expand Down
21 changes: 21 additions & 0 deletions opentelemetry-sdk/src/metrics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,27 @@ mod tests {
// As instrument name is invalid, no metrics should be exported
test_context.check_no_metrics();
}

let invalid_buckets = vec![
vec![], // empty buckets
vec![1.0, 1.0], // duplicate buckets
vec![1.0, 2.0, 3.0, 2.0], // duplicate non consequent buckets
vec![1.0, 2.0, 3.0, 4.0, 2.5], // unsorted buckets
];
for buckets in invalid_buckets {
let test_context = TestContext::new(Temporality::Cumulative);
let histogram = test_context
.meter()
.f64_histogram("test")
.with_boundaries(buckets)
.build();
histogram.record(1.9, &[]);
test_context.flush_metrics();

// As buckets via Advisory params are invalid, no metrics should be
// exported
test_context.check_no_metrics();
}
}

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
Expand Down
Loading