You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2026-06-03 18:35:08 +02:00
feat(stdouttrace): add experimental self-observability metrics (#7133)
Fixes https://github.com/open-telemetry/opentelemetry-go/issues/7008
This commit is contained in:
@@ -51,6 +51,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
- Add native histogram exemplar support in `go.opentelemetry.io/otel/exporters/prometheus`. (#6772)
|
||||
- Add experimental self-observability log metrics in `go.opentelemetry.io/otel/sdk/log`.
|
||||
Check the `go.opentelemetry.io/otel/sdk/log/internal/x` package documentation for more information. (#7121)
|
||||
- Add experimental self-observability trace exporter metrics in `go.opentelemetry.io/otel/exporters/stdout/stdouttrace`.
|
||||
Check the `go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/x` package documentation for more information. (#7133)
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@@ -3,4 +3,7 @@
|
||||
|
||||
// Package stdouttrace contains an OpenTelemetry exporter for tracing
|
||||
// telemetry to be written to an output destination as JSON.
|
||||
//
|
||||
// See [go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/x] for information about
|
||||
// the experimental features.
|
||||
package stdouttrace // import "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
|
||||
|
||||
@@ -10,7 +10,9 @@ replace (
|
||||
require (
|
||||
github.com/stretchr/testify v1.10.0
|
||||
go.opentelemetry.io/otel v1.37.0
|
||||
go.opentelemetry.io/otel/metric v1.37.0
|
||||
go.opentelemetry.io/otel/sdk v1.37.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0
|
||||
go.opentelemetry.io/otel/trace v1.37.0
|
||||
)
|
||||
|
||||
@@ -21,7 +23,6 @@ require (
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# Experimental Features
|
||||
|
||||
The `stdouttrace` exporter contains features that have not yet stabilized in the OpenTelemetry specification.
|
||||
These features are added to the `stdouttrace` exporter prior to stabilization in the specification so that users can start experimenting with them and provide feedback.
|
||||
|
||||
These feature may change in backwards incompatible ways as feedback is applied.
|
||||
See the [Compatibility and Stability](#compatibility-and-stability) section for more information.
|
||||
|
||||
## Features
|
||||
|
||||
- [Self-Observability](#self-observability)
|
||||
|
||||
### Self-Observability
|
||||
|
||||
The `stdouttrace` exporter provides a self-observability feature that allows you to monitor the SDK itself.
|
||||
|
||||
To opt-in, set the environment variable `OTEL_GO_X_SELF_OBSERVABILITY` to `true`.
|
||||
|
||||
When enabled, the SDK will create the following metrics using the global `MeterProvider`:
|
||||
|
||||
- `otel.sdk.exporter.span.inflight`
|
||||
- `otel.sdk.exporter.span.exported`
|
||||
- `otel.sdk.exporter.operation.duration`
|
||||
|
||||
Please see the [Semantic conventions for OpenTelemetry SDK metrics] documentation for more details on these metrics.
|
||||
|
||||
[Semantic conventions for OpenTelemetry SDK metrics]: https://github.com/open-telemetry/semantic-conventions/blob/v1.36.0/docs/otel/sdk-metrics.md
|
||||
|
||||
## Compatibility and Stability
|
||||
|
||||
Experimental features do not fall within the scope of the OpenTelemetry Go versioning and stability [policy](../../../../../VERSIONING.md).
|
||||
These features may be removed or modified in successive version releases, including patch versions.
|
||||
|
||||
When an experimental feature is promoted to a stable feature, a migration path will be included in the changelog entry of the release.
|
||||
There is no guarantee that any environment variable feature flags that enabled the experimental feature will be supported by the stable version.
|
||||
If they are supported, they may be accompanied with a deprecation notice stating a timeline for the removal of that support.
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package x documents experimental features for [go.opentelemetry.io/otel/exporters/stdout/stdouttrace].
|
||||
package x // import "go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/x"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SelfObservability is an experimental feature flag that determines if SDK
|
||||
// self-observability metrics are enabled.
|
||||
//
|
||||
// To enable this feature set the OTEL_GO_X_SELF_OBSERVABILITY environment variable
|
||||
// to the case-insensitive string value of "true" (i.e. "True" and "TRUE"
|
||||
// will also enable this).
|
||||
var SelfObservability = newFeature("SELF_OBSERVABILITY", func(v string) (string, bool) {
|
||||
if strings.EqualFold(v, "true") {
|
||||
return v, true
|
||||
}
|
||||
return "", false
|
||||
})
|
||||
|
||||
// Feature is an experimental feature control flag. It provides a uniform way
|
||||
// to interact with these feature flags and parse their values.
|
||||
type Feature[T any] struct {
|
||||
key string
|
||||
parse func(v string) (T, bool)
|
||||
}
|
||||
|
||||
func newFeature[T any](suffix string, parse func(string) (T, bool)) Feature[T] {
|
||||
const envKeyRoot = "OTEL_GO_X_"
|
||||
return Feature[T]{
|
||||
key: envKeyRoot + suffix,
|
||||
parse: parse,
|
||||
}
|
||||
}
|
||||
|
||||
// Key returns the environment variable key that needs to be set to enable the
|
||||
// feature.
|
||||
func (f Feature[T]) Key() string { return f.key }
|
||||
|
||||
// Lookup returns the user configured value for the feature and true if the
|
||||
// user has enabled the feature. Otherwise, if the feature is not enabled, a
|
||||
// zero-value and false are returned.
|
||||
func (f Feature[T]) Lookup() (v T, ok bool) {
|
||||
// https://github.com/open-telemetry/opentelemetry-specification/blob/62effed618589a0bec416a87e559c0a9d96289bb/specification/configuration/sdk-environment-variables.md#parsing-empty-value
|
||||
//
|
||||
// > The SDK MUST interpret an empty value of an environment variable the
|
||||
// > same way as when the variable is unset.
|
||||
vRaw := os.Getenv(f.key)
|
||||
if vRaw == "" {
|
||||
return v, ok
|
||||
}
|
||||
return f.parse(vRaw)
|
||||
}
|
||||
|
||||
// Enabled reports whether the feature is enabled.
|
||||
func (f Feature[T]) Enabled() bool {
|
||||
_, ok := f.Lookup()
|
||||
return ok
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package x
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSelfObservability(t *testing.T) {
|
||||
const key = "OTEL_GO_X_SELF_OBSERVABILITY"
|
||||
require.Equal(t, key, SelfObservability.Key())
|
||||
|
||||
t.Run("100", run(setenv(key, "100"), assertDisabled(SelfObservability)))
|
||||
t.Run("true", run(setenv(key, "true"), assertEnabled(SelfObservability, "true")))
|
||||
t.Run("True", run(setenv(key, "True"), assertEnabled(SelfObservability, "True")))
|
||||
t.Run("false", run(setenv(key, "false"), assertDisabled(SelfObservability)))
|
||||
t.Run("empty", run(assertDisabled(SelfObservability)))
|
||||
}
|
||||
|
||||
func run(steps ...func(*testing.T)) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
t.Helper()
|
||||
for _, step := range steps {
|
||||
step(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setenv(k, v string) func(t *testing.T) { //nolint:unparam // This is a reusable test utility function.
|
||||
return func(t *testing.T) { t.Setenv(k, v) }
|
||||
}
|
||||
|
||||
func assertEnabled[T any](f Feature[T], want T) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
t.Helper()
|
||||
assert.True(t, f.Enabled(), "not enabled")
|
||||
|
||||
v, ok := f.Lookup()
|
||||
assert.True(t, ok, "Lookup state")
|
||||
assert.Equal(t, want, v, "Lookup value")
|
||||
}
|
||||
}
|
||||
|
||||
func assertDisabled[T any](f Feature[T]) func(*testing.T) {
|
||||
var zero T
|
||||
return func(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
assert.False(t, f.Enabled(), "enabled")
|
||||
|
||||
v, ok := f.Lookup()
|
||||
assert.False(t, ok, "Lookup state")
|
||||
assert.Equal(t, zero, v, "Lookup value")
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,25 @@ package stdouttrace // import "go.opentelemetry.io/otel/exporters/stdout/stdoutt
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/x"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/trace"
|
||||
"go.opentelemetry.io/otel/sdk/trace/tracetest"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.36.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.36.0/otelconv"
|
||||
)
|
||||
|
||||
// otelComponentType is a name identifying the type of the OpenTelemetry component.
|
||||
const otelComponentType = "stdout_trace_exporter"
|
||||
|
||||
var zeroTime time.Time
|
||||
|
||||
var _ trace.SpanExporter = &Exporter{}
|
||||
@@ -26,10 +38,13 @@ func New(options ...Option) (*Exporter, error) {
|
||||
enc.SetIndent("", "\t")
|
||||
}
|
||||
|
||||
return &Exporter{
|
||||
exporter := &Exporter{
|
||||
encoder: enc,
|
||||
timestamps: cfg.Timestamps,
|
||||
}, nil
|
||||
}
|
||||
exporter.initSelfObservability()
|
||||
|
||||
return exporter, nil
|
||||
}
|
||||
|
||||
// Exporter is an implementation of trace.SpanSyncer that writes spans to stdout.
|
||||
@@ -40,10 +55,65 @@ type Exporter struct {
|
||||
|
||||
stoppedMu sync.RWMutex
|
||||
stopped bool
|
||||
|
||||
selfObservabilityEnabled bool
|
||||
selfObservabilityAttrs []attribute.KeyValue // selfObservability common attributes
|
||||
spanInflightMetric otelconv.SDKExporterSpanInflight
|
||||
spanExportedMetric otelconv.SDKExporterSpanExported
|
||||
operationDurationMetric otelconv.SDKExporterOperationDuration
|
||||
}
|
||||
|
||||
// initSelfObservability initializes self-observability for the exporter if enabled.
|
||||
func (e *Exporter) initSelfObservability() {
|
||||
if !x.SelfObservability.Enabled() {
|
||||
return
|
||||
}
|
||||
|
||||
e.selfObservabilityEnabled = true
|
||||
e.selfObservabilityAttrs = []attribute.KeyValue{
|
||||
semconv.OTelComponentName(fmt.Sprintf("%s/%d", otelComponentType, nextExporterID())),
|
||||
semconv.OTelComponentTypeKey.String(otelComponentType),
|
||||
}
|
||||
|
||||
mp := otel.GetMeterProvider()
|
||||
m := mp.Meter("go.opentelemetry.io/otel/exporters/stdout/stdouttrace",
|
||||
metric.WithInstrumentationVersion(sdk.Version()),
|
||||
metric.WithSchemaURL(semconv.SchemaURL),
|
||||
)
|
||||
|
||||
var err error
|
||||
if e.spanInflightMetric, err = otelconv.NewSDKExporterSpanInflight(m); err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
if e.spanExportedMetric, err = otelconv.NewSDKExporterSpanExported(m); err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
if e.operationDurationMetric, err = otelconv.NewSDKExporterOperationDuration(m); err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ExportSpans writes spans in json format to stdout.
|
||||
func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error {
|
||||
func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) (err error) {
|
||||
if e.selfObservabilityEnabled {
|
||||
count := int64(len(spans))
|
||||
|
||||
e.spanInflightMetric.Add(context.Background(), count, e.selfObservabilityAttrs...)
|
||||
defer func(starting time.Time) {
|
||||
// additional attributes for self-observability,
|
||||
// only spanExportedMetric and operationDurationMetric are supported
|
||||
addAttrs := make([]attribute.KeyValue, len(e.selfObservabilityAttrs), len(e.selfObservabilityAttrs)+1)
|
||||
copy(addAttrs, e.selfObservabilityAttrs)
|
||||
if err != nil {
|
||||
addAttrs = append(addAttrs, semconv.ErrorType(err))
|
||||
}
|
||||
|
||||
e.spanInflightMetric.Add(context.Background(), -count, e.selfObservabilityAttrs...)
|
||||
e.spanExportedMetric.Add(context.Background(), count, addAttrs...)
|
||||
e.operationDurationMetric.Record(context.Background(), time.Since(starting).Seconds(), addAttrs...)
|
||||
}(time.Now())
|
||||
}
|
||||
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -101,3 +171,11 @@ func (e *Exporter) MarshalLog() any {
|
||||
WithTimestamps: e.timestamps,
|
||||
}
|
||||
}
|
||||
|
||||
var exporterIDCounter atomic.Int64
|
||||
|
||||
// nextExporterID returns a new unique ID for an exporter.
|
||||
// the starting value is 0, and it increments by 1 for each call.
|
||||
func nextExporterID() int64 {
|
||||
return exporterIDCounter.Add(1) - 1
|
||||
}
|
||||
|
||||
@@ -7,18 +7,27 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
tracesdk "go.opentelemetry.io/otel/sdk/trace"
|
||||
"go.opentelemetry.io/otel/sdk/trace/tracetest"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.36.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.36.0/otelconv"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
@@ -227,3 +236,210 @@ func TestExporterShutdownNoError(t *testing.T) {
|
||||
t.Errorf("shutdown errored: expected nil, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelfObservability(t *testing.T) {
|
||||
defaultCallExportSpans := func(t *testing.T, exporter *stdouttrace.Exporter) {
|
||||
require.NoError(t, exporter.ExportSpans(context.Background(), tracetest.SpanStubs{
|
||||
{Name: "/foo"},
|
||||
{Name: "/bar"},
|
||||
}.Snapshots()))
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
enabled bool
|
||||
callExportSpans func(t *testing.T, exporter *stdouttrace.Exporter)
|
||||
assertMetrics func(t *testing.T, rm metricdata.ResourceMetrics)
|
||||
}{
|
||||
{
|
||||
name: "Disabled",
|
||||
enabled: false,
|
||||
callExportSpans: defaultCallExportSpans,
|
||||
assertMetrics: func(t *testing.T, rm metricdata.ResourceMetrics) {
|
||||
assert.Empty(t, rm.ScopeMetrics)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Enabled",
|
||||
enabled: true,
|
||||
callExportSpans: defaultCallExportSpans,
|
||||
assertMetrics: func(t *testing.T, rm metricdata.ResourceMetrics) {
|
||||
t.Helper()
|
||||
require.Len(t, rm.ScopeMetrics, 1)
|
||||
|
||||
sm := rm.ScopeMetrics[0]
|
||||
require.Len(t, sm.Metrics, 3)
|
||||
|
||||
assert.Equal(t, instrumentation.Scope{
|
||||
Name: "go.opentelemetry.io/otel/exporters/stdout/stdouttrace",
|
||||
Version: sdk.Version(),
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
}, sm.Scope)
|
||||
|
||||
metricdatatest.AssertEqual(t, metricdata.Metrics{
|
||||
Name: otelconv.SDKExporterSpanInflight{}.Name(),
|
||||
Description: otelconv.SDKExporterSpanInflight{}.Description(),
|
||||
Unit: otelconv.SDKExporterSpanInflight{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
semconv.OTelComponentName("stdout_trace_exporter/0"),
|
||||
semconv.OTelComponentTypeKey.String("stdout_trace_exporter"),
|
||||
),
|
||||
Value: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, sm.Metrics[0], metricdatatest.IgnoreTimestamp())
|
||||
|
||||
metricdatatest.AssertEqual(t, metricdata.Metrics{
|
||||
Name: otelconv.SDKExporterSpanExported{}.Name(),
|
||||
Description: otelconv.SDKExporterSpanExported{}.Description(),
|
||||
Unit: otelconv.SDKExporterSpanExported{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
semconv.OTelComponentName("stdout_trace_exporter/0"),
|
||||
semconv.OTelComponentTypeKey.String("stdout_trace_exporter"),
|
||||
),
|
||||
Value: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, sm.Metrics[1], metricdatatest.IgnoreTimestamp())
|
||||
|
||||
metricdatatest.AssertEqual(t, metricdata.Metrics{
|
||||
Name: otelconv.SDKExporterOperationDuration{}.Name(),
|
||||
Description: otelconv.SDKExporterOperationDuration{}.Description(),
|
||||
Unit: otelconv.SDKExporterOperationDuration{}.Unit(),
|
||||
Data: metricdata.Histogram[float64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.HistogramDataPoint[float64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
semconv.OTelComponentName("stdout_trace_exporter/0"),
|
||||
semconv.OTelComponentTypeKey.String("stdout_trace_exporter"),
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}, sm.Metrics[2], metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreValue())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Enabled, but ExportSpans returns error",
|
||||
enabled: true,
|
||||
callExportSpans: func(t *testing.T, exporter *stdouttrace.Exporter) {
|
||||
t.Helper()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
err := exporter.ExportSpans(ctx, tracetest.SpanStubs{
|
||||
{Name: "/foo"},
|
||||
{Name: "/bar"},
|
||||
}.Snapshots())
|
||||
require.Error(t, err)
|
||||
},
|
||||
assertMetrics: func(t *testing.T, rm metricdata.ResourceMetrics) {
|
||||
t.Helper()
|
||||
require.Len(t, rm.ScopeMetrics, 1)
|
||||
|
||||
sm := rm.ScopeMetrics[0]
|
||||
require.Len(t, sm.Metrics, 3)
|
||||
|
||||
assert.Equal(t, instrumentation.Scope{
|
||||
Name: "go.opentelemetry.io/otel/exporters/stdout/stdouttrace",
|
||||
Version: sdk.Version(),
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
}, sm.Scope)
|
||||
|
||||
metricdatatest.AssertEqual(t, metricdata.Metrics{
|
||||
Name: otelconv.SDKExporterSpanInflight{}.Name(),
|
||||
Description: otelconv.SDKExporterSpanInflight{}.Description(),
|
||||
Unit: otelconv.SDKExporterSpanInflight{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
semconv.OTelComponentName("stdout_trace_exporter/1"),
|
||||
semconv.OTelComponentTypeKey.String("stdout_trace_exporter"),
|
||||
),
|
||||
Value: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, sm.Metrics[0], metricdatatest.IgnoreTimestamp())
|
||||
|
||||
metricdatatest.AssertEqual(t, metricdata.Metrics{
|
||||
Name: otelconv.SDKExporterSpanExported{}.Name(),
|
||||
Description: otelconv.SDKExporterSpanExported{}.Description(),
|
||||
Unit: otelconv.SDKExporterSpanExported{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
semconv.OTelComponentName("stdout_trace_exporter/1"),
|
||||
semconv.OTelComponentTypeKey.String("stdout_trace_exporter"),
|
||||
semconv.ErrorType(context.Canceled),
|
||||
),
|
||||
Value: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, sm.Metrics[1], metricdatatest.IgnoreTimestamp())
|
||||
|
||||
metricdatatest.AssertEqual(t, metricdata.Metrics{
|
||||
Name: otelconv.SDKExporterOperationDuration{}.Name(),
|
||||
Description: otelconv.SDKExporterOperationDuration{}.Description(),
|
||||
Unit: otelconv.SDKExporterOperationDuration{}.Unit(),
|
||||
Data: metricdata.Histogram[float64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.HistogramDataPoint[float64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
semconv.OTelComponentName("stdout_trace_exporter/1"),
|
||||
semconv.OTelComponentTypeKey.String("stdout_trace_exporter"),
|
||||
semconv.ErrorType(context.Canceled),
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}, sm.Metrics[2], metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreValue())
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.enabled {
|
||||
t.Setenv("OTEL_GO_X_SELF_OBSERVABILITY", "true")
|
||||
}
|
||||
|
||||
original := otel.GetMeterProvider()
|
||||
defer otel.SetMeterProvider(original)
|
||||
|
||||
r := metric.NewManualReader()
|
||||
mp := metric.NewMeterProvider(metric.WithReader(r))
|
||||
otel.SetMeterProvider(mp)
|
||||
|
||||
exporter, err := stdouttrace.New(
|
||||
stdouttrace.WithWriter(io.Discard))
|
||||
require.NoError(t, err)
|
||||
|
||||
tt.callExportSpans(t, exporter)
|
||||
|
||||
var rm metricdata.ResourceMetrics
|
||||
require.NoError(t, r.Collect(context.Background(), &rm))
|
||||
|
||||
tt.assertMetrics(t, rm)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user