Skip to content

Commit 5741677

Browse files
authored
Serialize min/max values from OTel histograms (#11034)
Serialize min/max values from OTel histograms Co-authored-by: stuart.mcculloch <stuart.mcculloch@datadoghq.com>
1 parent bfa5ff5 commit 5741677

File tree

5 files changed

+72
-12
lines changed

5 files changed

+72
-12
lines changed

dd-java-agent/agent-otel/otel-bootstrap/src/main/java/datadog/trace/bootstrap/otel/metrics/data/OtelHistogramSketch.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,20 @@ OtlpDataPoint doCollect(boolean reset) {
2929
List<Double> binBoundaries;
3030
List<Double> binCounts;
3131
double sum;
32+
double min;
33+
double max;
3234
synchronized (histogram) {
3335
count = histogram.getCount();
3436
binBoundaries = histogram.getBinBoundaries();
3537
binCounts = histogram.getBinCounts();
3638
sum = histogram.getSum();
39+
min = histogram.getMinValue();
40+
max = histogram.getMaxValue();
3741
if (reset) {
3842
histogram.clear();
3943
}
4044
}
41-
return new OtlpHistogramPoint(count, binBoundaries, binCounts, sum);
45+
return new OtlpHistogramPoint(count, binBoundaries, binCounts, sum, min, max);
4246
}
4347

4448
/** Truncate IEEE-754 floating-point value to 10 bits precision. */

dd-java-agent/agent-otel/otel-bootstrap/src/main/java/datadog/trace/bootstrap/otel/metrics/data/OtlpHistogramPoint.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,21 @@ public final class OtlpHistogramPoint extends OtlpDataPoint {
77
public final List<Double> bucketBoundaries;
88
public final List<Double> bucketCounts;
99
public final double sum;
10+
public final double min;
11+
public final double max;
1012

1113
OtlpHistogramPoint(
12-
double count, List<Double> bucketBoundaries, List<Double> bucketCounts, double sum) {
14+
double count,
15+
List<Double> bucketBoundaries,
16+
List<Double> bucketCounts,
17+
double sum,
18+
double min,
19+
double max) {
1320
this.count = count;
1421
this.bucketBoundaries = bucketBoundaries;
1522
this.bucketCounts = bucketCounts;
1623
this.sum = sum;
24+
this.min = min;
25+
this.max = max;
1726
}
1827
}

dd-java-agent/agent-otel/otel-bootstrap/src/main/java/datadog/trace/bootstrap/otel/metrics/export/OtlpMetricsProto.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ public static byte[] recordDataPointMessage(GrowableBuffer buf, OtlpDataPoint po
117117
writeI64(buf, (long) histogram.count);
118118
writeTag(buf, 5, I64_WIRE_TYPE);
119119
writeI64(buf, histogram.sum);
120+
writeTag(buf, 11, I64_WIRE_TYPE);
121+
writeI64(buf, histogram.min);
122+
writeTag(buf, 12, I64_WIRE_TYPE);
123+
writeI64(buf, histogram.max);
120124
for (double bucketCount : histogram.bucketCounts) {
121125
writeTag(buf, 6, I64_WIRE_TYPE);
122126
writeI64(buf, (long) bucketCount);

dd-java-agent/agent-otel/otel-bootstrap/src/test/java/datadog/trace/bootstrap/otel/metrics/data/OtlpTestPoints.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ public static OtlpDoublePoint doublePoint(double value) {
1515
}
1616

1717
public static OtlpHistogramPoint histogramPoint(
18-
double count, List<Double> bucketBoundaries, List<Double> bucketCounts, double sum) {
19-
return new OtlpHistogramPoint(count, bucketBoundaries, bucketCounts, sum);
18+
double count,
19+
List<Double> bucketBoundaries,
20+
List<Double> bucketCounts,
21+
double sum,
22+
double min,
23+
double max) {
24+
return new OtlpHistogramPoint(count, bucketBoundaries, bucketCounts, sum, min, max);
2025
}
2126
}

dd-java-agent/agent-otel/otel-bootstrap/src/test/java/datadog/trace/bootstrap/otel/metrics/export/OtlpMetricsProtoTest.java

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@
5757
* double as_double = 4; sfixed64 as_int = 6; KeyValue attributes = 7; }
5858
* HistogramDataPoint { fixed64 start_time_unix_nano = 2; fixed64 time_unix_nano = 3;
5959
* fixed64 count = 4; double sum = 5; fixed64 bucket_counts = 6;
60-
* double explicit_bounds = 7; KeyValue attributes = 9; }
60+
* double explicit_bounds = 7; KeyValue attributes = 9;
61+
* double min = 11; double max = 12; }
6162
* </pre>
6263
*/
6364
class OtlpMetricsProtoTest {
@@ -242,14 +243,16 @@ private static MetricSpec histogram(
242243
List<Double> bounds,
243244
List<Double> counts,
244245
double sum,
246+
double min,
247+
double max,
245248
AttrSpec... attrs) {
246249
return new MetricSpec(
247250
name,
248251
null,
249252
null,
250253
OtelInstrumentType.HISTOGRAM,
251254
false,
252-
histogramPoint(count, bounds, counts, sum),
255+
histogramPoint(count, bounds, counts, sum, min, max),
253256
asList(attrs));
254257
}
255258

@@ -358,20 +361,32 @@ static Stream<Arguments> cases() {
358361
Arguments.of(
359362
"histogram no buckets",
360363
asList(
361-
scope("io.hist", histogram("response.time", 1.0, emptyList(), asList(1.0), 0.5)))),
364+
scope(
365+
"io.hist",
366+
histogram("response.time", 1.0, emptyList(), asList(1.0), 0.5, 0.5, 0.5)))),
362367

363368
// ── histogram — zero count and sum ────────────────────────────────────
364369
Arguments.of(
365370
"histogram zero count and sum",
366-
asList(scope("io.hist", histogram("idle.time", 0.0, emptyList(), asList(0.0), 0.0)))),
371+
asList(
372+
scope(
373+
"io.hist",
374+
histogram("idle.time", 0.0, emptyList(), asList(0.0), 0.0, 0.0, 0.0)))),
367375

368376
// ── histogram — single explicit bound ─────────────────────────────────
369377
Arguments.of(
370378
"histogram single bound",
371379
asList(
372380
scope(
373381
"io.hist",
374-
histogram("request.size", 5.0, asList(100.0), asList(4.0, 1.0), 280.0)))),
382+
histogram(
383+
"request.size",
384+
5.0,
385+
asList(100.0),
386+
asList(4.0, 1.0),
387+
280.0,
388+
20.0,
389+
200.0)))),
375390

376391
// ── histogram — with explicit bounds and attrs ────────────────────────
377392
Arguments.of(
@@ -385,6 +400,8 @@ static Stream<Arguments> cases() {
385400
asList(1.0, 5.0, 10.0),
386401
asList(2.0, 3.0, 4.0, 1.0),
387402
45.5,
403+
0.5,
404+
12.0,
388405
strAttr("region", "us-east"))))),
389406

390407
// ── histogram — many buckets with multiple attrs ───────────────────────
@@ -399,6 +416,8 @@ static Stream<Arguments> cases() {
399416
asList(1.0, 2.0, 5.0, 10.0, 25.0, 50.0, 100.0),
400417
asList(5.0, 10.0, 20.0, 30.0, 15.0, 12.0, 6.0, 2.0),
401418
4321.0,
419+
0.5,
420+
150.0,
402421
longAttr("shard", 3L),
403422
boolAttr("cached", false))))),
404423

@@ -886,12 +905,13 @@ private static void verifyNumberDataPoint(
886905
}
887906

888907
/**
889-
* Parses a {@code HistogramDataPoint} message body and asserts timestamps, count, sum, bucket
890-
* counts, explicit bounds, and attributes.
908+
* Parses a {@code HistogramDataPoint} message body and asserts timestamps, count, sum, min, max,
909+
* bucket counts, explicit bounds, and attributes.
891910
*
892911
* <pre>
893912
* HistogramDataPoint { start_time_unix_nano=2, time_unix_nano=3, count=4,
894-
* sum=5, bucket_counts=6, explicit_bounds=7, attributes=9 }
913+
* sum=5, bucket_counts=6, explicit_bounds=7, attributes=9,
914+
* min=11, max=12 }
895915
* </pre>
896916
*/
897917
private static void verifyHistogramDataPoint(CodedInputStream dp, MetricSpec expected)
@@ -901,6 +921,8 @@ private static void verifyHistogramDataPoint(CodedInputStream dp, MetricSpec exp
901921
boolean foundEndTime = false;
902922
boolean foundCount = false;
903923
boolean foundSum = false;
924+
boolean foundMin = false;
925+
boolean foundMax = false;
904926
List<Long> parsedBucketCounts = new ArrayList<>();
905927
List<Double> parsedBounds = new ArrayList<>();
906928
List<String> parsedAttrKeys = new ArrayList<>();
@@ -937,6 +959,20 @@ private static void verifyHistogramDataPoint(CodedInputStream dp, MetricSpec exp
937959
case 9: // attributes (repeated KeyValue)
938960
parsedAttrKeys.add(readKeyValueKey(dp.readBytes().newCodedInput()));
939961
break;
962+
case 11: // min (double via fixed64)
963+
assertEquals(
964+
Double.doubleToRawLongBits(hp.min),
965+
Double.doubleToRawLongBits(dp.readDouble()),
966+
"histogram min");
967+
foundMin = true;
968+
break;
969+
case 12: // max (double via fixed64)
970+
assertEquals(
971+
Double.doubleToRawLongBits(hp.max),
972+
Double.doubleToRawLongBits(dp.readDouble()),
973+
"histogram max");
974+
foundMax = true;
975+
break;
940976
default:
941977
dp.skipField(tag);
942978
}
@@ -946,6 +982,8 @@ private static void verifyHistogramDataPoint(CodedInputStream dp, MetricSpec exp
946982
assertTrue(foundEndTime, "time_unix_nano required for histogram " + expected.name);
947983
assertTrue(foundCount, "count required for histogram " + expected.name);
948984
assertTrue(foundSum, "sum required for histogram " + expected.name);
985+
assertTrue(foundMin, "min required for histogram " + expected.name);
986+
assertTrue(foundMax, "max required for histogram " + expected.name);
949987

950988
assertEquals(
951989
hp.bucketCounts.size(),

0 commit comments

Comments
 (0)