1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-08-10 22:31:50 +02:00

Add support for native histogram exemplars (#6772)

Added support for exemplars in exponential histograms. 

Closes #5777

---------

Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
This commit is contained in:
Shivanth MP
2025-08-05 18:57:04 +02:00
committed by GitHub
parent d464abf1f3
commit 52d2f6652a
3 changed files with 35 additions and 5 deletions

View File

@@ -48,6 +48,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
See the [migration documentation](./semconv/v1.36.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.34.0.`(#7032) See the [migration documentation](./semconv/v1.36.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.34.0.`(#7032)
- Add experimental self-observability span metrics in `go.opentelemetry.io/otel/sdk/trace`. - Add experimental self-observability span metrics in `go.opentelemetry.io/otel/sdk/trace`.
Check the `go.opentelemetry.io/otel/sdk/trace/internal/x` package documentation for more information. (#7027) Check the `go.opentelemetry.io/otel/sdk/trace/internal/x` package documentation for more information. (#7027)
- Add native histogram exemplar support in `go.opentelemetry.io/otel/exporters/prometheus`. (#6772)
### Changed ### Changed

View File

@@ -382,8 +382,7 @@ func addExponentialHistogramMetric[N int64 | float64](
otel.Handle(err) otel.Handle(err)
continue continue
} }
m = addExemplars(m, dp.Exemplars, labelNamer)
// TODO(GiedriusS): add exemplars here after https://github.com/prometheus/client_golang/pull/1654#pullrequestreview-2434669425 is done.
ch <- m ch <- m
} }
} }

View File

@@ -1113,6 +1113,18 @@ func TestExemplars(t *testing.T) {
escapingScheme: model.NoEscaping, escapingScheme: model.NoEscaping,
validationScheme: model.UTF8Validation, validationScheme: model.UTF8Validation,
}, },
{
name: "exponential histogram",
recordMetrics: func(ctx context.Context, meter otelmetric.Meter) {
hist, err := meter.Int64Histogram("exponential_histogram")
require.NoError(t, err)
hist.Record(ctx, 9, attrsOpt)
},
expectedExemplarValue: 9,
expectedLabels: expectedNonEscapedLabels,
escapingScheme: model.NoEscaping,
validationScheme: model.UTF8Validation,
},
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
originalEscapingScheme := model.NameEscapingScheme originalEscapingScheme := model.NameEscapingScheme
@@ -1144,13 +1156,24 @@ func TestExemplars(t *testing.T) {
metric.WithReader(exporter), metric.WithReader(exporter),
metric.WithResource(res), metric.WithResource(res),
metric.WithView(metric.NewView( metric.WithView(metric.NewView(
metric.Instrument{Name: "*"}, metric.Instrument{Name: "foo"},
metric.Stream{ metric.Stream{
// filter out all attributes so they are added as filtered // filter out all attributes so they are added as filtered
// attributes to the exemplar // attributes to the exemplar
AttributeFilter: attribute.NewAllowKeysFilter(), AttributeFilter: attribute.NewAllowKeysFilter(),
}, },
)), ),
),
metric.WithView(metric.NewView(
metric.Instrument{Name: "exponential_histogram"},
metric.Stream{
Aggregation: metric.AggregationBase2ExponentialHistogram{
MaxSize: 20,
},
AttributeFilter: attribute.NewAllowKeysFilter(),
},
),
),
) )
meter := provider.Meter("meter", otelmetric.WithInstrumentationVersion("v0.1.0")) meter := provider.Meter("meter", otelmetric.WithInstrumentationVersion("v0.1.0"))
@@ -1179,16 +1202,23 @@ func TestExemplars(t *testing.T) {
case dto.MetricType_COUNTER: case dto.MetricType_COUNTER:
exemplar = metric.GetCounter().GetExemplar() exemplar = metric.GetCounter().GetExemplar()
case dto.MetricType_HISTOGRAM: case dto.MetricType_HISTOGRAM:
for _, b := range metric.GetHistogram().GetBucket() { h := metric.GetHistogram()
for _, b := range h.GetBucket() {
if b.GetExemplar() != nil { if b.GetExemplar() != nil {
exemplar = b.GetExemplar() exemplar = b.GetExemplar()
continue continue
} }
} }
if h.GetZeroThreshold() != 0 || h.GetZeroCount() != 0 ||
len(h.PositiveSpan) != 0 || len(h.NegativeSpan) != 0 {
require.NotNil(t, h.Exemplars)
exemplar = h.Exemplars[0]
}
} }
require.NotNil(t, exemplar) require.NotNil(t, exemplar)
require.Equal(t, tc.expectedExemplarValue, exemplar.GetValue()) require.Equal(t, tc.expectedExemplarValue, exemplar.GetValue())
require.Len(t, exemplar.GetLabel(), len(tc.expectedLabels)) require.Len(t, exemplar.GetLabel(), len(tc.expectedLabels))
for _, label := range exemplar.GetLabel() { for _, label := range exemplar.GetLabel() {
val, ok := tc.expectedLabels[label.GetName()] val, ok := tc.expectedLabels[label.GetName()]
require.True(t, ok) require.True(t, ok)