Skip to content

Commit ed203a4

Browse files
committed
Add support for mapping hints
1 parent cf6a514 commit ed203a4

File tree

7 files changed

+168
-33
lines changed

7 files changed

+168
-33
lines changed

x-pack/plugin/otel-data/src/main/java/org/elasticsearch/xpack/oteldata/otlp/datapoint/DataPoint.java

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,11 @@ public interface DataPoint {
7575
/**
7676
* Builds the metric value for the data point and writes it to the provided XContentBuilder.
7777
*
78+
* @param mappingHints hints for building the metric value
7879
* @param builder the XContentBuilder to write the metric value to
7980
* @throws IOException if an I/O error occurs while writing to the builder
8081
*/
81-
void buildMetricValue(XContentBuilder builder) throws IOException;
82+
void buildMetricValue(MappingHints mappingHints, XContentBuilder builder) throws IOException;
8283

8384
/**
8485
* Returns the dynamic template name for the data point based on its type and value.
@@ -133,7 +134,7 @@ public String getMetricName() {
133134
}
134135

135136
@Override
136-
public void buildMetricValue(XContentBuilder builder) throws IOException {
137+
public void buildMetricValue(MappingHints mappingHints, XContentBuilder builder) throws IOException {
137138
switch (dataPoint.getValueCase()) {
138139
case AS_DOUBLE -> builder.value(dataPoint.getAsDouble());
139140
case AS_INT -> builder.value(dataPoint.getAsInt());
@@ -200,20 +201,33 @@ public String getMetricName() {
200201
}
201202

202203
@Override
203-
public void buildMetricValue(XContentBuilder builder) throws IOException {
204-
builder.startObject();
205-
builder.startArray("counts");
206-
HistogramConverter.counts(dataPoint, builder::value);
207-
builder.endArray();
208-
builder.startArray("values");
209-
HistogramConverter.centroidValues(dataPoint, builder::value);
210-
builder.endArray();
211-
builder.endObject();
204+
public void buildMetricValue(MappingHints mappingHints, XContentBuilder builder) throws IOException {
205+
if (mappingHints.aggregateMetricDouble()) {
206+
buildAggregateMetricDouble(builder, dataPoint.getSum(), dataPoint.getCount());
207+
} else {
208+
builder.startObject();
209+
builder.startArray("counts");
210+
HistogramConverter.counts(dataPoint, builder::value);
211+
builder.endArray();
212+
builder.startArray("values");
213+
HistogramConverter.centroidValues(dataPoint, builder::value);
214+
builder.endArray();
215+
builder.endObject();
216+
}
217+
}
218+
219+
@Override
220+
public long getDocCount() {
221+
return dataPoint.getCount();
212222
}
213223

214224
@Override
215-
public String getDynamicTemplate() {
216-
return "histogram";
225+
public String getDynamicTemplate(MappingHints mappingHints) {
226+
if (mappingHints.aggregateMetricDouble()) {
227+
return "summary";
228+
} else {
229+
return "histogram";
230+
}
217231
}
218232

219233
@Override
@@ -253,20 +267,33 @@ public String getMetricName() {
253267
}
254268

255269
@Override
256-
public void buildMetricValue(XContentBuilder builder) throws IOException {
257-
builder.startObject();
258-
builder.startArray("counts");
259-
HistogramConverter.counts(dataPoint, builder::value);
260-
builder.endArray();
261-
builder.startArray("values");
262-
HistogramConverter.centroidValues(dataPoint, builder::value);
263-
builder.endArray();
264-
builder.endObject();
270+
public void buildMetricValue(MappingHints mappingHints, XContentBuilder builder) throws IOException {
271+
if (mappingHints.aggregateMetricDouble()) {
272+
buildAggregateMetricDouble(builder, dataPoint.getSum(), dataPoint.getCount());
273+
} else {
274+
builder.startObject();
275+
builder.startArray("counts");
276+
HistogramConverter.counts(dataPoint, builder::value);
277+
builder.endArray();
278+
builder.startArray("values");
279+
HistogramConverter.centroidValues(dataPoint, builder::value);
280+
builder.endArray();
281+
builder.endObject();
282+
}
283+
}
284+
285+
@Override
286+
public long getDocCount() {
287+
return dataPoint.getCount();
265288
}
266289

267290
@Override
268-
public String getDynamicTemplate() {
269-
return "histogram";
291+
public String getDynamicTemplate(MappingHints mappingHints) {
292+
if (mappingHints.aggregateMetricDouble()) {
293+
return "summary";
294+
} else {
295+
return "histogram";
296+
}
270297
}
271298

272299
@Override
@@ -282,4 +309,11 @@ public boolean isValid(Set<String> errors) {
282309
return true;
283310
}
284311
}
312+
313+
private static void buildAggregateMetricDouble(XContentBuilder builder, double sum, long valueCount) throws IOException {
314+
builder.startObject();
315+
builder.field("sum", sum);
316+
builder.field("value_count", valueCount);
317+
builder.endObject();
318+
}
285319
}

x-pack/plugin/otel-data/src/main/java/org/elasticsearch/xpack/oteldata/otlp/docbuilder/MappingHints.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ public record MappingHints(boolean aggregateMetricDouble, boolean docCount) {
3030
public static final String MAPPING_HINTS = "elasticsearch.mapping.hints";
3131

3232
private static final MappingHints EMPTY = new MappingHints(false, false);
33-
private static final String AGGREGATE_METRIC_DOUBLE = "aggregate_metric_double";
34-
private static final String DOC_COUNT = "_doc_count";
33+
public static final String AGGREGATE_METRIC_DOUBLE = "aggregate_metric_double";
34+
public static final String DOC_COUNT = "_doc_count";
3535

3636
public static MappingHints fromAttributes(List<KeyValue> attributes) {
3737
boolean aggregateMetricDouble = false;

x-pack/plugin/otel-data/src/main/java/org/elasticsearch/xpack/oteldata/otlp/docbuilder/MetricDocumentBuilder.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,25 @@ public HashMap<String, String> buildMetricDocument(XContentBuilder builder, Data
5151
buildDataPointAttributes(builder, dataPointGroup.dataPointAttributes(), dataPointGroup.unit());
5252
builder.field("_metric_names_hash", dataPointGroup.getMetricNamesHash());
5353

54+
long docCount = 0;
5455
builder.startObject("metrics");
5556
for (int i = 0, dataPointsSize = dataPoints.size(); i < dataPointsSize; i++) {
5657
DataPoint dataPoint = dataPoints.get(i);
5758
builder.field(dataPoint.getMetricName());
58-
dataPoint.buildMetricValue(builder);
59-
String dynamicTemplate = dataPoint.getDynamicTemplate(MappingHints.empty());
59+
MappingHints mappingHints = MappingHints.fromAttributes(dataPoint.getAttributes());
60+
dataPoint.buildMetricValue(mappingHints, builder);
61+
String dynamicTemplate = dataPoint.getDynamicTemplate(mappingHints);
6062
if (dynamicTemplate != null) {
6163
dynamicTemplates.put("metrics." + dataPoint.getMetricName(), dynamicTemplate);
6264
}
65+
if (mappingHints.docCount()) {
66+
docCount = dataPoint.getDocCount();
67+
}
6368
}
6469
builder.endObject();
70+
if (docCount > 0) {
71+
builder.field("_doc_count", docCount);
72+
}
6573
builder.endObject();
6674
return dynamicTemplates;
6775
}

x-pack/plugin/otel-data/src/test/java/org/elasticsearch/xpack/oteldata/otlp/OtlpUtils.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import io.opentelemetry.proto.metrics.v1.Sum;
2626
import io.opentelemetry.proto.resource.v1.Resource;
2727

28+
import org.elasticsearch.xpack.oteldata.otlp.docbuilder.MappingHints;
29+
2830
import java.util.ArrayList;
2931
import java.util.Arrays;
3032
import java.util.List;
@@ -42,6 +44,10 @@ public static KeyValue keyValue(String key, String value) {
4244
return KeyValue.newBuilder().setKey(key).setValue(AnyValue.newBuilder().setStringValue(value).build()).build();
4345
}
4446

47+
public static List<KeyValue> mappingHints(String... mappingHints) {
48+
return List.of(keyValue(MappingHints.MAPPING_HINTS, mappingHints));
49+
}
50+
4551
public static KeyValue keyValue(String key, String... values) {
4652
return KeyValue.newBuilder()
4753
.setKey(key)

x-pack/plugin/otel-data/src/test/java/org/elasticsearch/xpack/oteldata/otlp/datapoint/DataPointExponentialHistogramTests.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
import io.opentelemetry.proto.metrics.v1.Metric;
1313

1414
import org.elasticsearch.test.ESTestCase;
15+
import org.elasticsearch.xpack.oteldata.otlp.docbuilder.MappingHints;
1516

1617
import java.util.HashSet;
1718

1819
import static io.opentelemetry.proto.metrics.v1.AggregationTemporality.AGGREGATION_TEMPORALITY_CUMULATIVE;
1920
import static io.opentelemetry.proto.metrics.v1.AggregationTemporality.AGGREGATION_TEMPORALITY_DELTA;
21+
import static org.elasticsearch.xpack.oteldata.otlp.OtlpUtils.mappingHints;
2022
import static org.hamcrest.Matchers.contains;
2123
import static org.hamcrest.Matchers.containsString;
2224
import static org.hamcrest.Matchers.empty;
@@ -33,7 +35,22 @@ public void testExponentialHistogram() {
3335
.setExponentialHistogram(ExponentialHistogram.newBuilder().setAggregationTemporality(AGGREGATION_TEMPORALITY_DELTA).build())
3436
.build()
3537
);
36-
assertThat(doubleGauge.getDynamicTemplate(), equalTo("histogram"));
38+
assertThat(doubleGauge.getDynamicTemplate(MappingHints.empty()), equalTo("histogram"));
39+
assertThat(doubleGauge.isValid(validationErrors), equalTo(true));
40+
assertThat(validationErrors, empty());
41+
}
42+
43+
public void testExponentialHistogramMappingHint() {
44+
DataPoint.ExponentialHistogram doubleGauge = new DataPoint.ExponentialHistogram(
45+
ExponentialHistogramDataPoint.newBuilder().build(),
46+
Metric.newBuilder()
47+
.setExponentialHistogram(ExponentialHistogram.newBuilder().setAggregationTemporality(AGGREGATION_TEMPORALITY_DELTA).build())
48+
.build()
49+
);
50+
assertThat(
51+
doubleGauge.getDynamicTemplate(MappingHints.fromAttributes(mappingHints(MappingHints.AGGREGATE_METRIC_DOUBLE))),
52+
equalTo("summary")
53+
);
3754
assertThat(doubleGauge.isValid(validationErrors), equalTo(true));
3855
assertThat(validationErrors, empty());
3956
}
@@ -47,7 +64,7 @@ public void testExponentialHistogramUnsupportedTemporality() {
4764
)
4865
.build()
4966
);
50-
assertThat(doubleGauge.getDynamicTemplate(), equalTo("histogram"));
67+
assertThat(doubleGauge.getDynamicTemplate(MappingHints.empty()), equalTo("histogram"));
5168
assertThat(doubleGauge.isValid(validationErrors), equalTo(false));
5269
assertThat(validationErrors, contains(containsString("cumulative exponential histogram metrics are not supported")));
5370
}

x-pack/plugin/otel-data/src/test/java/org/elasticsearch/xpack/oteldata/otlp/datapoint/DataPointHistogramTests.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import io.opentelemetry.proto.metrics.v1.Metric;
1313

1414
import org.elasticsearch.test.ESTestCase;
15+
import org.elasticsearch.xpack.oteldata.otlp.docbuilder.MappingHints;
1516

1617
import java.util.HashSet;
1718

@@ -33,7 +34,7 @@ public void testExponentialHistogram() {
3334
.setHistogram(Histogram.newBuilder().setAggregationTemporality(AGGREGATION_TEMPORALITY_DELTA).build())
3435
.build()
3536
);
36-
assertThat(doubleGauge.getDynamicTemplate(), equalTo("histogram"));
37+
assertThat(doubleGauge.getDynamicTemplate(MappingHints.empty()), equalTo("histogram"));
3738
assertThat(doubleGauge.isValid(validationErrors), equalTo(true));
3839
assertThat(validationErrors, empty());
3940
}
@@ -45,7 +46,7 @@ public void testExponentialHistogramUnsupportedTemporality() {
4546
.setHistogram(Histogram.newBuilder().setAggregationTemporality(AGGREGATION_TEMPORALITY_CUMULATIVE).build())
4647
.build()
4748
);
48-
assertThat(doubleGauge.getDynamicTemplate(), equalTo("histogram"));
49+
assertThat(doubleGauge.getDynamicTemplate(MappingHints.empty()), equalTo("histogram"));
4950
assertThat(doubleGauge.isValid(validationErrors), equalTo(false));
5051
assertThat(validationErrors, contains(containsString("cumulative histogram metrics are not supported")));
5152
}
@@ -57,7 +58,7 @@ public void testExponentialHistogramInvalidBucketCountWithoutBounds() {
5758
.setHistogram(Histogram.newBuilder().setAggregationTemporality(AGGREGATION_TEMPORALITY_DELTA).build())
5859
.build()
5960
);
60-
assertThat(doubleGauge.getDynamicTemplate(), equalTo("histogram"));
61+
assertThat(doubleGauge.getDynamicTemplate(MappingHints.empty()), equalTo("histogram"));
6162
assertThat(doubleGauge.isValid(validationErrors), equalTo(false));
6263
assertThat(validationErrors, contains(containsString("histogram with a single bucket and no explicit bounds is not supported")));
6364
}

x-pack/plugin/otel-data/src/test/java/org/elasticsearch/xpack/oteldata/otlp/docbuilder/MetricDocumentBuilderTests.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import static org.elasticsearch.xpack.oteldata.otlp.OtlpUtils.createLongDataPoint;
4444
import static org.elasticsearch.xpack.oteldata.otlp.OtlpUtils.createSumMetric;
4545
import static org.elasticsearch.xpack.oteldata.otlp.OtlpUtils.keyValue;
46+
import static org.elasticsearch.xpack.oteldata.otlp.OtlpUtils.mappingHints;
4647
import static org.hamcrest.Matchers.equalTo;
4748
import static org.hamcrest.Matchers.hasEntry;
4849
import static org.hamcrest.Matchers.is;
@@ -231,6 +232,40 @@ public void testExponentialHistogram() throws Exception {
231232
assertThat(dynamicTemplates, hasEntry("metrics.exponential_histogram", "histogram"));
232233
}
233234

235+
public void testExponentialHistogramAsAggregateMetricDouble() throws Exception {
236+
Resource resource = Resource.newBuilder().build();
237+
InstrumentationScope scope = InstrumentationScope.newBuilder().build();
238+
239+
ExponentialHistogramDataPoint dataPoint = ExponentialHistogramDataPoint.newBuilder()
240+
.setTimeUnixNano(timestamp)
241+
.setStartTimeUnixNano(startTimestamp)
242+
.setSum(42)
243+
.setCount(1L)
244+
.addAllAttributes(mappingHints(MappingHints.AGGREGATE_METRIC_DOUBLE))
245+
.build();
246+
Metric metric = createExponentialHistogramMetric("histogram", "", List.of(), AGGREGATION_TEMPORALITY_DELTA);
247+
List<DataPoint> dataPoints = List.of(new DataPoint.ExponentialHistogram(dataPoint, metric));
248+
249+
DataPointGroupingContext.DataPointGroup dataPointGroup = new DataPointGroupingContext.DataPointGroup(
250+
resource,
251+
null,
252+
scope,
253+
null,
254+
List.of(),
255+
"",
256+
dataPoints,
257+
"metrics-generic.otel-default"
258+
);
259+
260+
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
261+
HashMap<String, String> dynamicTemplates = documentBuilder.buildMetricDocument(builder, dataPointGroup);
262+
263+
ObjectPath doc = ObjectPath.createFromXContent(JsonXContent.jsonXContent, BytesReference.bytes(builder));
264+
assertThat(doc.evaluate("metrics.histogram.sum"), equalTo(42.0));
265+
assertThat(doc.evaluate("metrics.histogram.value_count"), equalTo(1));
266+
assertThat(dynamicTemplates, hasEntry("metrics.histogram", "summary"));
267+
}
268+
234269
public void testHistogram() throws Exception {
235270
Resource resource = Resource.newBuilder().build();
236271
InstrumentationScope scope = InstrumentationScope.newBuilder().build();
@@ -264,4 +299,38 @@ public void testHistogram() throws Exception {
264299
assertThat(dynamicTemplates, hasEntry("metrics.histogram", "histogram"));
265300
}
266301

302+
public void testHistogramAsAggregateMetricDouble() throws Exception {
303+
Resource resource = Resource.newBuilder().build();
304+
InstrumentationScope scope = InstrumentationScope.newBuilder().build();
305+
306+
HistogramDataPoint dataPoint = HistogramDataPoint.newBuilder()
307+
.setTimeUnixNano(timestamp)
308+
.setStartTimeUnixNano(startTimestamp)
309+
.setSum(42)
310+
.setCount(1L)
311+
.addAllAttributes(mappingHints(MappingHints.AGGREGATE_METRIC_DOUBLE))
312+
.build();
313+
Metric metric = createHistogramMetric("histogram", "", List.of(), AGGREGATION_TEMPORALITY_DELTA);
314+
List<DataPoint> dataPoints = List.of(new DataPoint.Histogram(dataPoint, metric));
315+
316+
DataPointGroupingContext.DataPointGroup dataPointGroup = new DataPointGroupingContext.DataPointGroup(
317+
resource,
318+
null,
319+
scope,
320+
null,
321+
List.of(),
322+
"",
323+
dataPoints,
324+
"metrics-generic.otel-default"
325+
);
326+
327+
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
328+
HashMap<String, String> dynamicTemplates = documentBuilder.buildMetricDocument(builder, dataPointGroup);
329+
330+
ObjectPath doc = ObjectPath.createFromXContent(JsonXContent.jsonXContent, BytesReference.bytes(builder));
331+
assertThat(doc.evaluate("metrics.histogram.sum"), equalTo(42.0));
332+
assertThat(doc.evaluate("metrics.histogram.value_count"), equalTo(1));
333+
assertThat(dynamicTemplates, hasEntry("metrics.histogram", "summary"));
334+
}
335+
267336
}

0 commit comments

Comments
 (0)