You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-11-23 22:34:47 +02:00
Per https://github.com/open-telemetry/opentelemetry-go/pull/6271#issuecomment-2657554647 > We agreed that we can move `FilterProcessor` directly to `sdk/log` as Logs SDK does not look to be stabilized soon. - Add the possibility to filter based on the resource and scope which is available for the SDK. The scope information is the most important as it gives the possibility to e.g. filter out logs emitted for a given logger. Thus e.g. https://github.com/open-telemetry/opentelemetry-specification/issues/4364 is not necessary. See https://github.com/open-telemetry/opentelemetry-specification/pull/4290#discussion_r1927546170 for more context. - It is going be an example for https://github.com/open-telemetry/opentelemetry-specification/issues/4363 There is a little overhead (IMO totally acceptable) because of data transformation. Most importantly, there is no new heap allocation. ``` goos: linux goarch: amd64 pkg: go.opentelemetry.io/otel/sdk/log cpu: 13th Gen Intel(R) Core(TM) i7-13800H │ old.txt │ new.txt │ │ sec/op │ sec/op vs base │ LoggerEnabled-20 4.589n ± 1% 319.750n ± 16% +6867.75% (p=0.000 n=10) │ old.txt │ new.txt │ │ B/op │ B/op vs base │ LoggerEnabled-20 0.000Ki ± 0% 1.093Ki ± 13% ? (p=0.000 n=10) │ old.txt │ new.txt │ │ allocs/op │ allocs/op vs base │ LoggerEnabled-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ ¹ all samples are equal ``` `Logger.Enabled` is still more efficient than `Logger.Emit` (benchmarks from https://github.com/open-telemetry/opentelemetry-go/pull/6315). ``` goos: linux goarch: amd64 pkg: go.opentelemetry.io/otel/sdk/log cpu: 13th Gen Intel(R) Core(TM) i7-13800H BenchmarkLoggerEmit/5_attributes-20 559934 2391 ns/op 39088 B/op 1 allocs/op BenchmarkLoggerEmit/10_attributes-20 1000000 5910 ns/op 49483 B/op 5 allocs/op BenchmarkLoggerEnabled-20 1605697 968.7 ns/op 1272 B/op 0 allocs/op PASS ok go.opentelemetry.io/otel/sdk/log 10.789s ``` Prior art: - https://github.com/open-telemetry/opentelemetry-go/pull/6271 - https://github.com/open-telemetry/opentelemetry-go/pull/6286 I also created for tracking purposes: - https://github.com/open-telemetry/opentelemetry-go/issues/6328
157 lines
4.7 KiB
Go
157 lines
4.7 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package log_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
|
|
logapi "go.opentelemetry.io/otel/log"
|
|
"go.opentelemetry.io/otel/log/global"
|
|
"go.opentelemetry.io/otel/sdk/log"
|
|
)
|
|
|
|
// Initialize OpenTelemetry Logs SDK and setup logging using a log bridge.
|
|
func Example() {
|
|
// Create an exporter that will emit log records.
|
|
// E.g. use go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp
|
|
// to send logs using OTLP over HTTP:
|
|
// exporter, err := otlploghttp.New(ctx)
|
|
var exporter log.Exporter
|
|
|
|
// Create a log record processor pipeline.
|
|
processor := log.NewBatchProcessor(exporter)
|
|
|
|
// Create a logger provider.
|
|
// You can pass this instance directly when creating a log bridge.
|
|
provider := log.NewLoggerProvider(
|
|
log.WithProcessor(processor),
|
|
)
|
|
|
|
// Handle shutdown properly so that nothing leaks.
|
|
defer func() {
|
|
err := provider.Shutdown(context.Background())
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
}()
|
|
|
|
// Register as global logger provider so that it can be used via global.Meter
|
|
// and accessed using global.GetMeterProvider.
|
|
// Most log bridges use the global logger provider as default.
|
|
// If the global logger provider is not set then a no-op implementation
|
|
// is used, which fails to generate data.
|
|
global.SetLoggerProvider(provider)
|
|
|
|
// Use a bridge so that you can emit logs using your Go logging library of preference.
|
|
// E.g. use go.opentelemetry.io/contrib/bridges/otelslog so that you can use log/slog:
|
|
// slog.SetDefault(otelslog.NewLogger("my/pkg/name", otelslog.WithLoggerProvider(provider)))
|
|
}
|
|
|
|
// Use a processor that filters out records based on the provided context.
|
|
func ExampleFilterProcessor() {
|
|
// Existing processor that emits telemetry.
|
|
var processor log.Processor = log.NewBatchProcessor(nil)
|
|
|
|
// Wrap the processor so that it ignores processing log records
|
|
// when a context deriving from WithIgnoreLogs is passed
|
|
// to the logging methods.
|
|
processor = &ContextFilterProcessor{Processor: processor}
|
|
|
|
// The created processor can then be registered with
|
|
// the OpenTelemetry Logs SDK using the WithProcessor option.
|
|
_ = log.NewLoggerProvider(
|
|
log.WithProcessor(processor),
|
|
)
|
|
}
|
|
|
|
type key struct{}
|
|
|
|
var ignoreLogsKey key
|
|
|
|
// WithIgnoreLogs returns a context which is used by [ContextFilterProcessor]
|
|
// to filter out log records.
|
|
func WithIgnoreLogs(ctx context.Context) context.Context {
|
|
return context.WithValue(ctx, ignoreLogsKey, true)
|
|
}
|
|
|
|
// ContextFilterProcessor filters out logs when a context deriving from
|
|
// [WithIgnoreLogs] is passed to its methods.
|
|
type ContextFilterProcessor struct {
|
|
log.Processor
|
|
|
|
lazyFilter sync.Once
|
|
// Support the FilterProcessor interface for the embedded processor.
|
|
filter log.FilterProcessor
|
|
}
|
|
|
|
// Compile time check.
|
|
var _ log.FilterProcessor = (*ContextFilterProcessor)(nil)
|
|
|
|
func (p *ContextFilterProcessor) OnEmit(ctx context.Context, record *log.Record) error {
|
|
if ignoreLogs(ctx) {
|
|
return nil
|
|
}
|
|
return p.Processor.OnEmit(ctx, record)
|
|
}
|
|
|
|
func (p *ContextFilterProcessor) Enabled(ctx context.Context, param log.EnabledParameters) bool {
|
|
p.lazyFilter.Do(func() {
|
|
if f, ok := p.Processor.(log.FilterProcessor); ok {
|
|
p.filter = f
|
|
}
|
|
})
|
|
return !ignoreLogs(ctx) && (p.filter == nil || p.filter.Enabled(ctx, param))
|
|
}
|
|
|
|
func ignoreLogs(ctx context.Context) bool {
|
|
_, ok := ctx.Value(ignoreLogsKey).(bool)
|
|
return ok
|
|
}
|
|
|
|
// Use a processor which redacts sensitive data from some attributes.
|
|
func ExampleProcessor() {
|
|
// Existing processor that emits telemetry.
|
|
var processor log.Processor = log.NewBatchProcessor(nil)
|
|
|
|
// Add a processor so that it redacts values from token attributes.
|
|
redactProcessor := &RedactTokensProcessor{}
|
|
|
|
// The created processor can then be registered with
|
|
// the OpenTelemetry Logs SDK using the WithProcessor option.
|
|
_ = log.NewLoggerProvider(
|
|
// Order is important here. Redact before handing to the processor.
|
|
log.WithProcessor(redactProcessor),
|
|
log.WithProcessor(processor),
|
|
)
|
|
}
|
|
|
|
// RedactTokensProcessor is a [log.Processor] decorator that redacts values
|
|
// from attributes containing "token" in the key.
|
|
type RedactTokensProcessor struct{}
|
|
|
|
// OnEmit redacts values from attributes containing "token" in the key
|
|
// by replacing them with a REDACTED value.
|
|
func (p *RedactTokensProcessor) OnEmit(ctx context.Context, record *log.Record) error {
|
|
record.WalkAttributes(func(kv logapi.KeyValue) bool {
|
|
if strings.Contains(strings.ToLower(kv.Key), "token") {
|
|
record.AddAttributes(logapi.String(kv.Key, "REDACTED"))
|
|
}
|
|
return true
|
|
})
|
|
return nil
|
|
}
|
|
|
|
// Shutdown returns nil.
|
|
func (p *RedactTokensProcessor) Shutdown(ctx context.Context) error {
|
|
return nil
|
|
}
|
|
|
|
// ForceFlush returns nil.
|
|
func (p *RedactTokensProcessor) ForceFlush(ctx context.Context) error {
|
|
return nil
|
|
}
|