Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions opentelemetry-sdk/src/metrics/internal/histogram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ where

buckets.total += value;
buckets.count += 1;
buckets.counts[index] += 1;
if !buckets.counts.is_empty() {
buckets.counts[index] += 1;
}

if value < buckets.min {
buckets.min = value;
}
Expand Down Expand Up @@ -96,7 +99,12 @@ impl<T: Number> Histogram<T> {
bounds.sort_by(|a, b| a.partial_cmp(b).expect("NaNs filtered out"));
}

let buckets_count = bounds.len() + 1;
let buckets_count = if bounds.is_empty() {
0
} else {
bounds.len() + 1
};

Histogram {
value_map: ValueMap::new(buckets_count, cardinality_limit),
init_time: AggregateTimeInitiator::default(),
Expand Down
60 changes: 60 additions & 0 deletions opentelemetry-sdk/src/metrics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,14 @@
histogram_aggregation_with_custom_bounds_helper(Temporality::Cumulative);
}

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn histogram_aggregation_with_empty_bounds() {
// Run this test with stdout enabled to see output.
// cargo test histogram_aggregation_with_empty_bounds --features=testing -- --nocapture
histogram_aggregation_with_empty_bounds_helper(Temporality::Delta);
histogram_aggregation_with_empty_bounds_helper(Temporality::Cumulative);
}

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn updown_counter_aggregation_cumulative() {
// Run this test with stdout enabled to see output.
Expand Down Expand Up @@ -2161,6 +2169,58 @@
assert_eq!(vec![1.0, 2.5, 5.5], data_point.bounds);
assert_eq!(vec![1, 1, 3, 0], data_point.bucket_counts);
}

fn histogram_aggregation_with_empty_bounds_helper(temporality: Temporality) {
let mut test_context = TestContext::new(temporality);
let histogram = test_context
.meter()
.u64_histogram("test_histogram")
.with_boundaries(vec![])
.build();
histogram.record(1, &[KeyValue::new("key1", "value1")]);
histogram.record(2, &[KeyValue::new("key1", "value1")]);
histogram.record(3, &[KeyValue::new("key1", "value1")]);
histogram.record(4, &[KeyValue::new("key1", "value1")]);
histogram.record(5, &[KeyValue::new("key1", "value1")]);

test_context.flush_metrics();

// Assert
let MetricData::Histogram(histogram_data) =
test_context.get_aggregation::<u64>("test_histogram", None)
else {
unreachable!()

Check warning on line 2192 in opentelemetry-sdk/src/metrics/mod.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-sdk/src/metrics/mod.rs#L2192

Added line #L2192 was not covered by tests
};
// Expecting 1 time-series.
assert_eq!(histogram_data.data_points.len(), 1);
if let Temporality::Cumulative = temporality {
assert_eq!(
histogram_data.temporality,
Temporality::Cumulative,
"Should produce cumulative"

Check warning on line 2200 in opentelemetry-sdk/src/metrics/mod.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-sdk/src/metrics/mod.rs#L2200

Added line #L2200 was not covered by tests
);
} else {
assert_eq!(
histogram_data.temporality,
Temporality::Delta,
"Should produce delta"

Check warning on line 2206 in opentelemetry-sdk/src/metrics/mod.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-sdk/src/metrics/mod.rs#L2206

Added line #L2206 was not covered by tests
);
}

// find and validate key1=value1 datapoint
let data_point =
find_histogram_datapoint_with_key_value(&histogram_data.data_points, "key1", "value1")
.expect("datapoint with key1=value1 expected");

assert_eq!(data_point.count, 5);
assert_eq!(data_point.sum, 15);

println!("bounds: {:?}", data_point.bounds);
println!("bucket_counts: {:?}", data_point.bucket_counts);
assert!(data_point.bounds.is_empty());
assert!(data_point.bucket_counts.is_empty());
}

fn gauge_aggregation_helper(temporality: Temporality) {
// Arrange
let mut test_context = TestContext::new(temporality);
Expand Down
26 changes: 14 additions & 12 deletions opentelemetry-stdout/src/metrics/exporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,19 +235,21 @@
println!("\t\t\t\t -> {}: {}", kv.key, kv.value.as_str());
}

println!("\t\t\tBuckets");
let mut lower_bound = f64::NEG_INFINITY;
for (i, &upper_bound) in data_point.bounds.iter().enumerate() {
let count = data_point.bucket_counts.get(i).unwrap_or(&0);
println!("\t\t\t\t {} to {} : {}", lower_bound, upper_bound, count);
lower_bound = upper_bound;
}
if !data_point.bucket_counts.is_empty() {
println!("\t\t\tBuckets");
let mut lower_bound = f64::NEG_INFINITY;
for (i, &upper_bound) in data_point.bounds.iter().enumerate() {
let count = data_point.bucket_counts.get(i).unwrap_or(&0);
println!("\t\t\t\t {} to {} : {}", lower_bound, upper_bound, count);
lower_bound = upper_bound;
}

Check warning on line 245 in opentelemetry-stdout/src/metrics/exporter.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-stdout/src/metrics/exporter.rs#L238-L245

Added lines #L238 - L245 were not covered by tests

let last_count = data_point
.bucket_counts
.get(data_point.bounds.len())
.unwrap_or(&0);
println!("\t\t\t\t{} to +Infinity : {}", lower_bound, last_count);
let last_count = data_point
.bucket_counts
.get(data_point.bounds.len())
.unwrap_or(&0);
println!("\t\t\t\t{} to +Infinity : {}", lower_bound, last_count);
}

Check warning on line 252 in opentelemetry-stdout/src/metrics/exporter.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-stdout/src/metrics/exporter.rs#L247-L252

Added lines #L247 - L252 were not covered by tests
}
}

Expand Down