|
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; |
@@ -555,4 +559,87 @@ void validateCacheIsBounded() { |
555 | 559 | // it never saw those resources before. |
556 | 560 | assertThat(predicateCalledCount.get()).isEqualTo(2); |
557 | 561 | } |
| 562 | + |
| 563 | + @Test |
| 564 | + void exemplarLabelsWithinLimit() { |
| 565 | + SpanContext spanContext = |
| 566 | + SpanContext.create( |
| 567 | + "00000000000000000000000000000001", |
| 568 | + "0000000000000001", |
| 569 | + TraceFlags.getSampled(), |
| 570 | + TraceState.getDefault()); |
| 571 | + ImmutableDoubleExemplarData exemplar = |
| 572 | + (ImmutableDoubleExemplarData) |
| 573 | + ImmutableDoubleExemplarData.create( |
| 574 | + Attributes.of(stringKey("short"), "val"), |
| 575 | + 1000L, |
| 576 | + spanContext, |
| 577 | + 1.0); |
| 578 | + |
| 579 | + MetricData metricData = |
| 580 | + ImmutableMetricData.createDoubleGauge( |
| 581 | + Resource.getDefault(), |
| 582 | + InstrumentationScopeInfo.create("test"), |
| 583 | + "my.gauge", |
| 584 | + "desc", |
| 585 | + "unit", |
| 586 | + ImmutableGaugeData.create( |
| 587 | + Collections.singletonList( |
| 588 | + ImmutableDoublePointData.create( |
| 589 | + 0, 1000, Attributes.empty(), 1.0, Collections.singletonList(exemplar))))); |
| 590 | + |
| 591 | + MetricSnapshots snapshots = converter.convert(Collections.singletonList(metricData)); |
| 592 | + assertThat(snapshots).isNotNull(); |
| 593 | + // Labels within limit — both trace/span and filtered attribute should be present |
| 594 | + io.prometheus.metrics.model.snapshots.GaugeSnapshot.GaugeDataPointSnapshot point = |
| 595 | + (io.prometheus.metrics.model.snapshots.GaugeSnapshot.GaugeDataPointSnapshot) |
| 596 | + snapshots.get(0).getDataPoints().get(0); |
| 597 | + Labels exemplarLabels = point.getExemplar().getLabels(); |
| 598 | + assertThat(exemplarLabels.get("trace_id")).isEqualTo(spanContext.getTraceId()); |
| 599 | + assertThat(exemplarLabels.get("span_id")).isEqualTo(spanContext.getSpanId()); |
| 600 | + assertThat(exemplarLabels.get("short")).isEqualTo("val"); |
| 601 | + } |
| 602 | + |
| 603 | + @Test |
| 604 | + void exemplarLabelsExceedingLimitDropsFilteredAttributes() { |
| 605 | + SpanContext spanContext = |
| 606 | + SpanContext.create( |
| 607 | + "00000000000000000000000000000001", |
| 608 | + "0000000000000001", |
| 609 | + TraceFlags.getSampled(), |
| 610 | + TraceState.getDefault()); |
| 611 | + // Build a filtered attribute whose name+value alone would push total over 128 |
| 612 | + String longValue = "x".repeat(100); |
| 613 | + ImmutableDoubleExemplarData exemplar = |
| 614 | + (ImmutableDoubleExemplarData) |
| 615 | + ImmutableDoubleExemplarData.create( |
| 616 | + Attributes.of(stringKey("long_attr"), longValue), |
| 617 | + 1000L, |
| 618 | + spanContext, |
| 619 | + 1.0); |
| 620 | + |
| 621 | + MetricData metricData = |
| 622 | + ImmutableMetricData.createDoubleGauge( |
| 623 | + Resource.getDefault(), |
| 624 | + InstrumentationScopeInfo.create("test"), |
| 625 | + "my.gauge", |
| 626 | + "desc", |
| 627 | + "unit", |
| 628 | + ImmutableGaugeData.create( |
| 629 | + Collections.singletonList( |
| 630 | + ImmutableDoublePointData.create( |
| 631 | + 0, 1000, Attributes.empty(), 1.0, Collections.singletonList(exemplar))))); |
| 632 | + |
| 633 | + MetricSnapshots snapshots = converter.convert(Collections.singletonList(metricData)); |
| 634 | + assertThat(snapshots).isNotNull(); |
| 635 | + io.prometheus.metrics.model.snapshots.GaugeSnapshot.GaugeDataPointSnapshot point = |
| 636 | + (io.prometheus.metrics.model.snapshots.GaugeSnapshot.GaugeDataPointSnapshot) |
| 637 | + snapshots.get(0).getDataPoints().get(0); |
| 638 | + Labels exemplarLabels = point.getExemplar().getLabels(); |
| 639 | + // trace_id and span_id are preserved |
| 640 | + assertThat(exemplarLabels.get("trace_id")).isEqualTo(spanContext.getTraceId()); |
| 641 | + assertThat(exemplarLabels.get("span_id")).isEqualTo(spanContext.getSpanId()); |
| 642 | + // filtered attribute is dropped to stay within limit |
| 643 | + assertThat(exemplarLabels.get("long_attr")).isNull(); |
| 644 | + } |
558 | 645 | } |
0 commit comments