Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 33 additions & 32 deletions exporters/stdout/stdouttrace/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package stdouttrace // import "go.opentelemetry.io/otel/exporters/stdout/stdoutt
import (
"context"
"encoding/json"
"errors"
"fmt"
"sync"
"sync/atomic"
Expand Down Expand Up @@ -42,9 +43,39 @@ func New(options ...Option) (*Exporter, error) {
encoder: enc,
timestamps: cfg.Timestamps,
}
exporter.initSelfObservability()

return exporter, nil
if !x.SelfObservability.Enabled() {
return exporter, nil
}

exporter.selfObservabilityEnabled = true
exporter.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, e error
if exporter.spanInflightMetric, e = otelconv.NewSDKExporterSpanInflight(m); e != nil {
e = fmt.Errorf("failed to create span inflight metric: %w", e)
err = errors.Join(err, e)
}
if exporter.spanExportedMetric, e = otelconv.NewSDKExporterSpanExported(m); e != nil {
e = fmt.Errorf("failed to create span exported metric: %w", e)
err = errors.Join(err, e)
}
if exporter.operationDurationMetric, e = otelconv.NewSDKExporterOperationDuration(m); e != nil {
e = fmt.Errorf("failed to create operation duration metric: %w", e)
err = errors.Join(err, e)
}

return exporter, err
}

// Exporter is an implementation of trace.SpanSyncer that writes spans to stdout.
Expand All @@ -63,36 +94,6 @@ type Exporter struct {
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) (err error) {
if e.selfObservabilityEnabled {
Expand Down
44 changes: 44 additions & 0 deletions exporters/stdout/stdouttrace/trace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
mapi "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/sdk"
"go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/metric"
Expand Down Expand Up @@ -443,3 +444,46 @@ func TestSelfObservability(t *testing.T) {
})
}
}

type errMeterProvider struct {
mapi.MeterProvider

err error
}

func (m *errMeterProvider) Meter(string, ...mapi.MeterOption) mapi.Meter {
return &errMeter{err: m.err}
}

type errMeter struct {
mapi.Meter

err error
}

func (m *errMeter) Int64UpDownCounter(string, ...mapi.Int64UpDownCounterOption) (mapi.Int64UpDownCounter, error) {
return nil, m.err
}

func (m *errMeter) Int64Counter(string, ...mapi.Int64CounterOption) (mapi.Int64Counter, error) {
return nil, m.err
}

func (m *errMeter) Float64Histogram(string, ...mapi.Float64HistogramOption) (mapi.Float64Histogram, error) {
return nil, m.err
}

func TestSelfObservabilityInstrumentErrors(t *testing.T) {
orig := otel.GetMeterProvider()
t.Cleanup(func() { otel.SetMeterProvider(orig) })
mp := &errMeterProvider{err: assert.AnError}
otel.SetMeterProvider(mp)

t.Setenv("OTEL_GO_X_SELF_OBSERVABILITY", "true")
_, err := stdouttrace.New()
require.ErrorIs(t, err, assert.AnError, "new instrument errors")

assert.ErrorContains(t, err, "inflight metric")
assert.ErrorContains(t, err, "span exported metric")
assert.ErrorContains(t, err, "operation duration metric")
}
Loading