Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3774ae3
sdk/log: add self-observability metric for SimpleProcessor (#7016)
hostejulien Aug 3, 2025
f35f417
sdk/log: rename simpleProcessorInstanceCounter to simpleProcessorIDCo…
hostejulien Aug 10, 2025
abbd30b
sdk/log: remove unecessary line
hostejulien Aug 10, 2025
1c519f0
fix(sdk/log): use dynamic record count in SimpleProcessor metrics
hostejulien Aug 10, 2025
4ed9dca
refactor(sdl/log): consolidate duplicated metric recording logic
hostejulien Aug 10, 2025
f7270da
style(sdl/log): fix revive linter warnings in tests
hostejulien Aug 10, 2025
b7eb887
fix(sdl/log): use correct variable name and inline ID generation
hostejulien Aug 11, 2025
5bfc3cf
test(sdl/log): verify error propagation
hostejulien Aug 11, 2025
6b56739
refactor(sdl/log): consolidate duplicate metric recording
hostejulien Aug 11, 2025
8285463
test(sdk/log): remove unnecessary empty env variable
hostejulien Aug 11, 2025
db444f9
test(sdk/log): verify no metrics generated when self-observability is…
hostejulien Aug 11, 2025
c1ea6a0
test(sdk/log): add helper function for MeterProvider cleanup
hostejulien Aug 11, 2025
9bf5e76
test(sdk/log): use metricdatatest.AssertEqual for metric validation
hostejulien Aug 11, 2025
5f35c0a
test(sdk/log): remove redundant self-observabilty tests
hostejulien Aug 11, 2025
02d9a3c
chore(sdk/log): pre-allocate attributes slice
hostejulien Aug 11, 2025
669ef18
refactor(sdk/log): move self-observability metrics recording to defer…
hostejulien Aug 11, 2025
8c48ead
test(sk/log): remove export function && add helper to handle dynamic …
hostejulien Aug 11, 2025
589d36b
chore(sdk/log): add changelog
hostejulien Aug 11, 2025
f569a92
refactor(sdk/log): move defer to conditional block to reduce overhead
hostejulien Aug 12, 2025
4c64883
refactor(sdk/log): move defer to conditional block to reduce overhead
hostejulien Aug 12, 2025
c460385
fix(sdk/log): correct changelog reference issue ID to PR ID
hostejulien Aug 12, 2025
0e28c36
Merge branch 'main' into sdk-observability-logs-simple-processor
pellared Aug 12, 2025
a957add
Merge branch 'main' into sdk-observability-logs-simple-processor
hostejulien Aug 28, 2025
1ef9894
Merge branch 'main' into sdk-observability-logs-simple-processor
hostejulien Aug 29, 2025
e118397
Merge branch 'main' into sdk-observability-logs-simple-processor
hostejulien Aug 30, 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 @@ -66,6 +66,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 metrics for the simple log processor in `go.opentelemetry.io/otel/sdk/log`.
Adds `otel.sdk.processor.log.processed` metric when `OTEL_GO_X_SELF_OBSERVABILITY` environment variable is set to `true`. (#7127)

### Changed

Expand Down
1 change: 1 addition & 0 deletions sdk/log/internal/x/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ 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.log.created`
- `otel.sdk.processor.log.processed`

Please see the [Semantic conventions for OpenTelemetry SDK metrics] documentation for more details on these metrics.

Expand Down
67 changes: 61 additions & 6 deletions sdk/log/simple.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,36 @@ package log // import "go.opentelemetry.io/otel/sdk/log"

import (
"context"
"fmt"
"sync"
"sync/atomic"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/sdk"
"go.opentelemetry.io/otel/sdk/log/internal/x"
semconv "go.opentelemetry.io/otel/semconv/v1.36.0"
"go.opentelemetry.io/otel/semconv/v1.36.0/otelconv"
)

// Compile-time check SimpleProcessor implements Processor.
var _ Processor = (*SimpleProcessor)(nil)

// simpleProcessorIDCounter is used to generate unique component names.
var simpleProcessorIDCounter atomic.Uint64

// SimpleProcessor is an processor that synchronously exports log records.
//
// Use [NewSimpleProcessor] to create a SimpleProcessor.
type SimpleProcessor struct {
mu sync.Mutex
exporter Exporter

selfObservabilityEnabled bool
processedMetric otelconv.SDKProcessorLogProcessed
componentName string

noCmp [0]func() //nolint: unused // This is indeed used.
}

Expand All @@ -30,7 +47,33 @@ type SimpleProcessor struct {
// [NewBatchProcessor] instead. However, there may be exceptions where certain
// [Exporter] implementations perform better with this Processor.
func NewSimpleProcessor(exporter Exporter, _ ...SimpleProcessorOption) *SimpleProcessor {
return &SimpleProcessor{exporter: exporter}
s := &SimpleProcessor{
exporter: exporter,
componentName: fmt.Sprintf(
"%s/%d",
string(otelconv.ComponentTypeSimpleLogProcessor),
simpleProcessorIDCounter.Add(1)-1,
),
}
s.initSelfObservability()
return s
}

func (s *SimpleProcessor) initSelfObservability() {
if !x.SelfObservability.Enabled() {
return
}

s.selfObservabilityEnabled = true
mp := otel.GetMeterProvider()
m := mp.Meter("go.opentelemetry.io/otel/sdk/log",
metric.WithInstrumentationVersion(sdk.Version()),
metric.WithSchemaURL(semconv.SchemaURL))

var err error
if s.processedMetric, err = otelconv.NewSDKProcessorLogProcessed(m); err != nil {
otel.Handle(err)
}
}

var simpleProcRecordsPool = sync.Pool{
Expand All @@ -41,11 +84,7 @@ var simpleProcRecordsPool = sync.Pool{
}

// OnEmit batches provided log record.
func (s *SimpleProcessor) OnEmit(ctx context.Context, r *Record) error {
if s.exporter == nil {
return nil
}

func (s *SimpleProcessor) OnEmit(ctx context.Context, r *Record) (err error) {
s.mu.Lock()
defer s.mu.Unlock()

Expand All @@ -55,6 +94,22 @@ func (s *SimpleProcessor) OnEmit(ctx context.Context, r *Record) error {
simpleProcRecordsPool.Put(records)
}()

if s.selfObservabilityEnabled {
defer func() {
attrs := make([]attribute.KeyValue, 2, 3)
attrs[0] = s.processedMetric.AttrComponentType(otelconv.ComponentTypeSimpleLogProcessor)
attrs[1] = s.processedMetric.AttrComponentName(s.componentName)
if err != nil {
attrs = append(attrs, s.processedMetric.AttrErrorType(otelconv.ErrorTypeOther))
}
s.processedMetric.Add(context.Background(), int64(len(*records)), attrs...)
}()
}

if s.exporter == nil {
return nil
}

return s.exporter.Export(ctx, *records)
}

Expand Down
Loading
Loading