Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
48b1568
copied code with minor modifications for docs and feature flag: OTEL_…
mahendrabishnoi2 Aug 4, 2025
c03b9f0
Merge branch 'main' into stdoutmetric-auto-instrumentation
mahendrabishnoi2 Aug 7, 2025
4ddd0b3
Merge branch 'main' into stdoutmetric-auto-instrumentation
mahendrabishnoi2 Aug 7, 2025
987b77e
added self-observability support to stdoutmetric exporter for below m…
mahendrabishnoi2 Aug 7, 2025
b4d2756
fixed broken link
mahendrabishnoi2 Aug 7, 2025
b7181a4
added changelog entry for self-observability support in stdoutmetric …
mahendrabishnoi2 Aug 7, 2025
c6f91ac
run `make precommit`
mahendrabishnoi2 Aug 7, 2025
a9ad0e2
fix a bug where attributes defined in ExporterMetrics are mutated
mahendrabishnoi2 Aug 9, 2025
7b1fb2d
test cases for ExporterMetrics
mahendrabishnoi2 Aug 9, 2025
ef4a629
test cases for stdoutmetric exporter
mahendrabishnoi2 Aug 9, 2025
51f2f2b
Merge branch 'main' into stdoutmetric-auto-instrumentation
mahendrabishnoi2 Aug 9, 2025
38eb316
remove unused receiver to make linter (unused-receiver) happy
mahendrabishnoi2 Aug 9, 2025
b67b593
fix version
mahendrabishnoi2 Aug 9, 2025
ecbb337
fix version
mahendrabishnoi2 Aug 9, 2025
d6d0dd8
make stdoutMetricExporterComponentType as constant
mahendrabishnoi2 Aug 11, 2025
ee64105
Use defer to call trackExportFunc, Thanks to @flc1125
mahendrabishnoi2 Aug 11, 2025
ddfb3c3
duration -> durationSeconds
mahendrabishnoi2 Aug 11, 2025
db64dc0
Merge branch 'main' into stdoutmetric-auto-instrumentation
mahendrabishnoi2 Aug 11, 2025
089252e
suppress linter as err is used in defer statement
mahendrabishnoi2 Aug 11, 2025
884ade7
instead of suppressing error, split if and err check on 2 lines
mahendrabishnoi2 Aug 11, 2025
32b6f61
Merge branch 'main' into stdoutmetric-auto-instrumentation
pellared Aug 12, 2025
460f303
addressed review comment: use named return to make code more readable
mahendrabishnoi2 Aug 16, 2025
9fea018
Merge branch 'main' into stdoutmetric-auto-instrumentation
mahendrabishnoi2 Aug 16, 2025
a6f0637
name component similar to https://github.com/open-telemetry/opentelem…
mahendrabishnoi2 Aug 16, 2025
938cbb0
flatten the self-observability initialization and return the error to…
mahendrabishnoi2 Aug 16, 2025
b247192
Merge branch 'main' into stdoutmetric-auto-instrumentation
mahendrabishnoi2 Aug 27, 2025
ee0328e
Merge branch 'main' into stdoutmetric-auto-instrumentation
mahendrabishnoi2 Aug 28, 2025
4c357a9
address review comments
mahendrabishnoi2 Aug 28, 2025
2edfed9
address review comments
mahendrabishnoi2 Aug 28, 2025
18e0530
generate internal counter package so that it can be tested by resetti…
mahendrabishnoi2 Aug 28, 2025
9976cfc
review comments: improve tests, merge tests
mahendrabishnoi2 Aug 28, 2025
2f84c09
Run `make precommit`
mahendrabishnoi2 Aug 28, 2025
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ The next release will require at least [Go 1.24].
- The `go.opentelemetry.io/otel/semconv/v1.37.0` package.
The package contains semantic conventions from the `v1.37.0` version of the OpenTelemetry Semantic Conventions.
See the [migration documentation](./semconv/v1.37.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.36.0.`(#7254)
- Add experimental self-observability stdoutmetric exporter metrics in `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric`.
Check the `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/internal/x` package documentation for more information. (#7150)

### Changed

Expand Down
80 changes: 73 additions & 7 deletions exporters/stdout/stdoutmetric/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,20 @@ import (
"sync"
"sync/atomic"

"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/internal/counter"
"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/internal/selfobservability"
"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/internal/x"
"go.opentelemetry.io/otel/internal/global"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/metricdata"
semconv "go.opentelemetry.io/otel/semconv/v1.36.0"
)

// otelComponentType is a name identifying the type of the OpenTelemetry
// component. It is not a standardized OTel component type, so it uses the
// Go package prefixed type name to ensure uniqueness and identity.
const otelComponentType = "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric.exporter"

// exporter is an OpenTelemetry metric exporter.
type exporter struct {
encVal atomic.Value // encoderHolder
Expand All @@ -25,6 +34,9 @@ type exporter struct {
aggregationSelector metric.AggregationSelector

redactTimestamps bool

selfObservabilityEnabled bool
exporterMetric *selfobservability.ExporterMetrics
}

// New returns a configured metric exporter.
Expand All @@ -34,12 +46,22 @@ type exporter struct {
func New(options ...Option) (metric.Exporter, error) {
cfg := newConfig(options...)
exp := &exporter{
temporalitySelector: cfg.temporalitySelector,
aggregationSelector: cfg.aggregationSelector,
redactTimestamps: cfg.redactTimestamps,
temporalitySelector: cfg.temporalitySelector,
aggregationSelector: cfg.aggregationSelector,
redactTimestamps: cfg.redactTimestamps,
selfObservabilityEnabled: x.SelfObservability.Enabled(),
}
exp.encVal.Store(*cfg.encoder)
return exp, nil
var err error
if exp.selfObservabilityEnabled {
componentName := fmt.Sprintf("%s/%d", otelComponentType, counter.NextExporterID())
exp.exporterMetric, err = selfobservability.NewExporterMetrics(
"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric",
semconv.OTelComponentName(componentName),
semconv.OTelComponentTypeKey.String(otelComponentType),
)
}
return exp, err
}

func (e *exporter) Temporality(k metric.InstrumentKind) metricdata.Temporality {
Expand All @@ -50,9 +72,12 @@ func (e *exporter) Aggregation(k metric.InstrumentKind) metric.Aggregation {
return e.aggregationSelector(k)
}

func (e *exporter) Export(ctx context.Context, data *metricdata.ResourceMetrics) error {
if err := ctx.Err(); err != nil {
return err
func (e *exporter) Export(ctx context.Context, data *metricdata.ResourceMetrics) (err error) {
trackExportFunc := e.trackExport(ctx, countDataPoints(data))
defer func() { trackExportFunc(err) }()
err = ctx.Err()
if err != nil {
return
}
if e.redactTimestamps {
redactTimestamps(data)
Expand All @@ -63,6 +88,13 @@ func (e *exporter) Export(ctx context.Context, data *metricdata.ResourceMetrics)
return e.encVal.Load().(encoderHolder).Encode(data)
}

func (e *exporter) trackExport(ctx context.Context, count int64) func(err error) {
if !e.selfObservabilityEnabled {
return func(error) {}
}
return e.exporterMetric.TrackExport(ctx, count)
}

func (*exporter) ForceFlush(context.Context) error {
// exporter holds no state, nothing to flush.
return nil
Expand Down Expand Up @@ -159,3 +191,37 @@ func redactDataPointTimestamps[T int64 | float64](sdp []metricdata.DataPoint[T])
}
return out
}

// countDataPoints counts the total number of data points in a ResourceMetrics.
func countDataPoints(rm *metricdata.ResourceMetrics) int64 {
if rm == nil {
return 0
}

var total int64
for _, sm := range rm.ScopeMetrics {
for _, m := range sm.Metrics {
switch data := m.Data.(type) {
case metricdata.Gauge[int64]:
total += int64(len(data.DataPoints))
case metricdata.Gauge[float64]:
total += int64(len(data.DataPoints))
case metricdata.Sum[int64]:
total += int64(len(data.DataPoints))
case metricdata.Sum[float64]:
total += int64(len(data.DataPoints))
case metricdata.Histogram[int64]:
total += int64(len(data.DataPoints))
case metricdata.Histogram[float64]:
total += int64(len(data.DataPoints))
case metricdata.ExponentialHistogram[int64]:
total += int64(len(data.DataPoints))
case metricdata.ExponentialHistogram[float64]:
total += int64(len(data.DataPoints))
case metricdata.Summary:
total += int64(len(data.DataPoints))
}
}
}
return total
}
Loading
Loading