|
20 | 20 | import io.opentelemetry.api.common.Attributes; |
21 | 21 | import io.opentelemetry.api.common.KeyValue; |
22 | 22 | import io.opentelemetry.api.common.Value; |
| 23 | +import io.opentelemetry.api.trace.SpanContext; |
| 24 | +import io.opentelemetry.api.trace.TraceFlags; |
| 25 | +import io.opentelemetry.api.trace.TraceState; |
23 | 26 | import io.opentelemetry.sdk.common.InstrumentationScopeInfo; |
24 | 27 | import io.opentelemetry.sdk.metrics.data.AggregationTemporality; |
25 | 28 | import io.opentelemetry.sdk.metrics.data.MetricData; |
26 | 29 | import io.opentelemetry.sdk.metrics.data.MetricDataType; |
| 30 | +import io.opentelemetry.sdk.metrics.internal.data.ImmutableDoubleExemplarData; |
27 | 31 | import io.opentelemetry.sdk.metrics.internal.data.ImmutableDoublePointData; |
28 | 32 | import io.opentelemetry.sdk.metrics.internal.data.ImmutableExponentialHistogramBuckets; |
29 | 33 | import io.opentelemetry.sdk.metrics.internal.data.ImmutableExponentialHistogramData; |
|
39 | 43 | import io.opentelemetry.sdk.resources.Resource; |
40 | 44 | import io.prometheus.metrics.expositionformats.ExpositionFormats; |
41 | 45 | import io.prometheus.metrics.model.snapshots.CounterSnapshot; |
| 46 | +import io.prometheus.metrics.model.snapshots.GaugeSnapshot.GaugeDataPointSnapshot; |
42 | 47 | import io.prometheus.metrics.model.snapshots.Labels; |
43 | 48 | import io.prometheus.metrics.model.snapshots.MetricSnapshot; |
44 | 49 | import io.prometheus.metrics.model.snapshots.MetricSnapshots; |
@@ -555,4 +560,79 @@ void validateCacheIsBounded() { |
555 | 560 | // it never saw those resources before. |
556 | 561 | assertThat(predicateCalledCount.get()).isEqualTo(2); |
557 | 562 | } |
| 563 | + |
| 564 | + @Test |
| 565 | + void exemplarLabelsWithinLimit() { |
| 566 | + SpanContext spanContext = |
| 567 | + SpanContext.create( |
| 568 | + "00000000000000000000000000000001", |
| 569 | + "0000000000000001", |
| 570 | + TraceFlags.getSampled(), |
| 571 | + TraceState.getDefault()); |
| 572 | + ImmutableDoubleExemplarData exemplar = |
| 573 | + (ImmutableDoubleExemplarData) |
| 574 | + ImmutableDoubleExemplarData.create( |
| 575 | + Attributes.of(stringKey("short"), "val"), 1000L, spanContext, 1.0); |
| 576 | + |
| 577 | + MetricData metricData = |
| 578 | + ImmutableMetricData.createDoubleGauge( |
| 579 | + Resource.getDefault(), |
| 580 | + InstrumentationScopeInfo.create("test"), |
| 581 | + "my.gauge", |
| 582 | + "desc", |
| 583 | + "unit", |
| 584 | + ImmutableGaugeData.create( |
| 585 | + Collections.singletonList( |
| 586 | + ImmutableDoublePointData.create( |
| 587 | + 0, 1000, Attributes.empty(), 1.0, Collections.singletonList(exemplar))))); |
| 588 | + |
| 589 | + MetricSnapshots snapshots = converter.convert(Collections.singletonList(metricData)); |
| 590 | + assertThat(snapshots).isNotNull(); |
| 591 | + // Labels within limit — both trace/span and filtered attribute should be present |
| 592 | + GaugeDataPointSnapshot point = (GaugeDataPointSnapshot) snapshots.get(0).getDataPoints().get(0); |
| 593 | + Labels exemplarLabels = point.getExemplar().getLabels(); |
| 594 | + assertThat(exemplarLabels.get("trace_id")).isEqualTo(spanContext.getTraceId()); |
| 595 | + assertThat(exemplarLabels.get("span_id")).isEqualTo(spanContext.getSpanId()); |
| 596 | + assertThat(exemplarLabels.get("short")).isEqualTo("val"); |
| 597 | + } |
| 598 | + |
| 599 | + @Test |
| 600 | + void exemplarLabelsExceedingLimitDropsFilteredAttributes() { |
| 601 | + SpanContext spanContext = |
| 602 | + SpanContext.create( |
| 603 | + "00000000000000000000000000000001", |
| 604 | + "0000000000000001", |
| 605 | + TraceFlags.getSampled(), |
| 606 | + TraceState.getDefault()); |
| 607 | + // Build a filtered attribute whose name+value alone would push total over 128 |
| 608 | + char[] chars = new char[100]; |
| 609 | + Arrays.fill(chars, 'x'); |
| 610 | + String longValue = new String(chars); |
| 611 | + ImmutableDoubleExemplarData exemplar = |
| 612 | + (ImmutableDoubleExemplarData) |
| 613 | + ImmutableDoubleExemplarData.create( |
| 614 | + Attributes.of(stringKey("long_attr"), longValue), 1000L, spanContext, 1.0); |
| 615 | + |
| 616 | + MetricData metricData = |
| 617 | + ImmutableMetricData.createDoubleGauge( |
| 618 | + Resource.getDefault(), |
| 619 | + InstrumentationScopeInfo.create("test"), |
| 620 | + "my.gauge", |
| 621 | + "desc", |
| 622 | + "unit", |
| 623 | + ImmutableGaugeData.create( |
| 624 | + Collections.singletonList( |
| 625 | + ImmutableDoublePointData.create( |
| 626 | + 0, 1000, Attributes.empty(), 1.0, Collections.singletonList(exemplar))))); |
| 627 | + |
| 628 | + MetricSnapshots snapshots = converter.convert(Collections.singletonList(metricData)); |
| 629 | + assertThat(snapshots).isNotNull(); |
| 630 | + GaugeDataPointSnapshot point = (GaugeDataPointSnapshot) snapshots.get(0).getDataPoints().get(0); |
| 631 | + Labels exemplarLabels = point.getExemplar().getLabels(); |
| 632 | + // trace_id and span_id are preserved |
| 633 | + assertThat(exemplarLabels.get("trace_id")).isEqualTo(spanContext.getTraceId()); |
| 634 | + assertThat(exemplarLabels.get("span_id")).isEqualTo(spanContext.getSpanId()); |
| 635 | + // filtered attribute is dropped to stay within limit |
| 636 | + assertThat(exemplarLabels.get("long_attr")).isNull(); |
| 637 | + } |
558 | 638 | } |
0 commit comments