You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-11-29 23:07:45 +02:00
Encapsulate observability in Logs SDK (#7315)
- [Follow
guidelines](a5dcd68ebb/CONTRIBUTING.md (encapsulation))
and encapsulate the Observability for the Logs SDK logger by moving it
into a single function.
- Add benchmarks to ensure no performance regressions
### Benchmarks
```console
$ benchstat main_2de26d1de.txt enc-sdk-log-obs_c06e888.txt
goos: linux
goarch: amd64
pkg: go.opentelemetry.io/otel/sdk/log
cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
│ main_2de26d1de.txt │ enc-sdk-log-obs_c06e888.txt │
│ sec/op │ sec/op vs base │
LoggerEmitObservability/Disabled-8 167.8n ± 4% 167.3n ± 5% ~ (p=0.796 n=10)
LoggerEmitObservability/Enabled-8 556.6n ± 4% 556.1n ± 4% ~ (p=0.955 n=10)
geomean 305.6n 305.0n -0.19%
│ main_2de26d1de.txt │ enc-sdk-log-obs_c06e888.txt │
│ B/op │ B/op vs base │
LoggerEmitObservability/Disabled-8 448.0 ± 0% 448.0 ± 0% ~ (p=1.000 n=10) ¹
LoggerEmitObservability/Enabled-8 448.0 ± 0% 448.0 ± 0% ~ (p=1.000 n=10) ¹
geomean 448.0 448.0 +0.00%
¹ all samples are equal
│ main_2de26d1de.txt │ enc-sdk-log-obs_c06e888.txt │
│ allocs/op │ allocs/op vs base │
LoggerEmitObservability/Disabled-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹
LoggerEmitObservability/Enabled-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹
geomean 1.000 1.000 +0.00%
¹ all samples are equal
```
This commit is contained in:
39
sdk/log/instrumentation.go
Normal file
39
sdk/log/instrumentation.go
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package log // import "go.opentelemetry.io/otel/sdk/log"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"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.37.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.37.0/otelconv"
|
||||
)
|
||||
|
||||
// newRecordCounterIncr returns a function that increments the log record
|
||||
// counter metric. If observability is disabled, it returns nil.
|
||||
func newRecordCounterIncr() (func(context.Context), error) {
|
||||
if !x.SelfObservability.Enabled() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
m := otel.GetMeterProvider().Meter(
|
||||
"go.opentelemetry.io/otel/sdk/log",
|
||||
metric.WithInstrumentationVersion(sdk.Version()),
|
||||
metric.WithSchemaURL(semconv.SchemaURL),
|
||||
)
|
||||
|
||||
created, err := otelconv.NewSDKLogCreated(m)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to create log created metric: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
inst := created.Inst()
|
||||
f := func(ctx context.Context) { inst.Add(ctx, 1) }
|
||||
return f, nil
|
||||
}
|
||||
@@ -5,18 +5,12 @@ package log // import "go.opentelemetry.io/otel/sdk/log"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/log"
|
||||
"go.opentelemetry.io/otel/log/embedded"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/log/internal/x"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.37.0/otelconv"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
@@ -31,8 +25,9 @@ type logger struct {
|
||||
provider *LoggerProvider
|
||||
instrumentationScope instrumentation.Scope
|
||||
|
||||
selfObservabilityEnabled bool
|
||||
logCreatedMetric otelconv.SDKLogCreated
|
||||
// recCntIncr increments the count of log records created. It will be nil
|
||||
// if observability is disabled.
|
||||
recCntIncr func(context.Context)
|
||||
}
|
||||
|
||||
func newLogger(p *LoggerProvider, scope instrumentation.Scope) *logger {
|
||||
@@ -40,18 +35,10 @@ func newLogger(p *LoggerProvider, scope instrumentation.Scope) *logger {
|
||||
provider: p,
|
||||
instrumentationScope: scope,
|
||||
}
|
||||
if !x.SelfObservability.Enabled() {
|
||||
return l
|
||||
}
|
||||
l.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 l.logCreatedMetric, err = otelconv.NewSDKLogCreated(m); err != nil {
|
||||
err = fmt.Errorf("failed to create log created metric: %w", err)
|
||||
l.recCntIncr, err = newRecordCounterIncr()
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
return l
|
||||
@@ -119,8 +106,8 @@ func (l *logger) newRecord(ctx context.Context, r log.Record) Record {
|
||||
attributeCountLimit: l.provider.attributeCountLimit,
|
||||
allowDupKeys: l.provider.allowDupKeys,
|
||||
}
|
||||
if l.selfObservabilityEnabled {
|
||||
l.logCreatedMetric.Add(ctx, 1)
|
||||
if l.recCntIncr != nil {
|
||||
l.recCntIncr(ctx)
|
||||
}
|
||||
|
||||
// This ensures we deduplicate key-value collections in the log body
|
||||
|
||||
@@ -10,7 +10,11 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/log"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
)
|
||||
|
||||
func BenchmarkLoggerEmit(b *testing.B) {
|
||||
@@ -62,6 +66,44 @@ func BenchmarkLoggerEmit(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkLoggerEmitObservability(b *testing.B) {
|
||||
r := log.Record{}
|
||||
|
||||
orig := otel.GetMeterProvider()
|
||||
b.Cleanup(func() { otel.SetMeterProvider(orig) })
|
||||
reader := metric.NewManualReader()
|
||||
mp := metric.NewMeterProvider(metric.WithReader(reader))
|
||||
otel.SetMeterProvider(mp)
|
||||
|
||||
run := func(logger *logger) func(b *testing.B) {
|
||||
return func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
logger.Emit(context.Background(), r)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
lp := NewLoggerProvider()
|
||||
scope := instrumentation.Scope{}
|
||||
|
||||
b.Run("Disabled", run(newLogger(lp, scope)))
|
||||
|
||||
b.Run("Enabled", func(b *testing.B) {
|
||||
b.Setenv("OTEL_GO_X_SELF_OBSERVABILITY", "true")
|
||||
|
||||
run(newLogger(lp, scope))(b)
|
||||
})
|
||||
|
||||
var rm metricdata.ResourceMetrics
|
||||
err := reader.Collect(context.Background(), &rm)
|
||||
require.NoError(b, err)
|
||||
require.Len(b, rm.ScopeMetrics, 1)
|
||||
}
|
||||
|
||||
func BenchmarkLoggerEnabled(b *testing.B) {
|
||||
logger := newTestLogger(b)
|
||||
ctx := context.Background()
|
||||
|
||||
Reference in New Issue
Block a user