Documentation ¶
Overview ¶
Package otelslog provides a function to extend structured logs using slog with the Open Telemetry trace related context.
Currently, slog is offered through golang.org/x/exp/slog slog.Logger is decorated with standard metadata extracted from the trace.SpanContext, a traceID, spanID and additional information is injected into a log.
The initialization uses file level variable configuration to set defaults for the functions to use. SetLogOptions can overwrite the defaults.
When the configuration is done AddTracingContext and AddTracingContextWithAttributes decorate slog logs with data from the trace context.
To add trace context data to logging, the context can be passed by using slog.LogAttrs(nil, slog.LevelInfo, "this is a log", otelslog.AddTracingContext(span)...) for example. The use of slog.LogAttrs is advised due to AddTracingContext and AddTracingContextWithAttributes returning []slog.Attr which slog.LogAttrs accepts as a type. Other functions accept ...any which in my tests resulted in !BADKEY entries.
Next to using native slog, this package also offers a Logger which extends the slog.Logger with its own functions to simplify working with slog.Logger's.
The Logger can be used as follows:
logger := otelslog.New() // pass span to AddTracingContext logger.WithTracingContext(nil, slog.LevelInfo, "in case of a success", span, nil) err := errors.New("example error")) // error case with attributes logger.WithTracingContextAndAttributes(ctx, slog.LevelError, "in case of a failure", span, err, attributes)
Functions
func SetLogOptions(options ...LogOption) func WithTraceID(traceID string) LogOption func WithSpanID(spanID string) LogOption func WithServiceName(serviceName string) LogOption func WithAttributePrefix(prefix string) LogOption func WithAttributes(attributes ...attribute.KeyValue) LogOption func AddTracingContext(span trace.Span, err ...error) func(event *zerolog.Event) func AddTracingContextWithAttributes(span trace.Span, attributes []attribute.KeyValue, err ...error) func(event *zerolog.Event) func New() *Logger func NewWithHandler(handler slog.Handler) *Logger func (l Logger) WithTracingContext(ctx context.Context, level slog.Level, msg string, span trace.Span, err error, attrs ...slog.Attr) func (l Logger) WithTracingContextAndAttributes(ctx context.Context, level slog.Level, msg string, span trace.Span, err error, attributes []attribute.KeyValue, attrs ...slog.Attr)
Types
type LogOption func(*logConfig)
Structs
type Logger struct { *slog.Logger } type logConfig struct { attributes []attribute.KeyValue serviceName string traceId string spanId string attributePrefix string }
import "github.com/vincentfree/opentelemetry/otelslog"
Index ¶
- func AddTracingContext(span trace.Span, err ...error) []slog.Attr
- func AddTracingContextWithAttributes(span trace.Span, attributes []attribute.KeyValue, err ...error) []slog.Attr
- func ConvertToSlogFormat(attributes []attribute.KeyValue, result []slog.Attr) []slog.Attr
- func SetLogOptions(options ...LogOption)
- type LogOption
- type Logger
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AddTracingContext ¶
AddTracingContext lets you add the trace context to a structured log
Example ¶
package main import ( "context" "errors" "github.com/vincentfree/opentelemetry/otelslog" "go.opentelemetry.io/otel" "log/slog" ) func main() { tracer := otel.Tracer("otelslog/example") _, span := tracer.Start(context.Background(), "example-span") // pass span to AddTracingContext slog.LogAttrs(nil, slog.LevelInfo, "in case of a success", otelslog.AddTracingContext(span)...) // or in the case of an error err := errors.New("example error") slog.LogAttrs(nil, slog.LevelError, "in case of a failure", otelslog.AddTracingContext(span, err)...) }
Output:
func AddTracingContextWithAttributes ¶
func AddTracingContextWithAttributes(span trace.Span, attributes []attribute.KeyValue, err ...error) []slog.Attr
AddTracingContextWithAttributes lets you add the trace context to a structured log, including attribute.KeyValue's to extend the log
Example ¶
package main import ( "context" "errors" "github.com/vincentfree/opentelemetry/otelslog" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "log/slog" ) func main() { tracer := otel.Tracer("otelslog/example") _, span := tracer.Start(context.Background(), "example-span") attributes := []attribute.KeyValue{ attribute.String("exampleKey", "exampleValue"), attribute.Bool("isValid", true), } // pass span to AddTracingContext slog.LogAttrs(nil, slog.LevelInfo, "in case of a success", otelslog.AddTracingContextWithAttributes(span, attributes)...) // or in the case of an error err := errors.New("example error") slog.LogAttrs(nil, slog.LevelError, "in case of a failure", otelslog.AddTracingContextWithAttributes(span, attributes, err)...) }
Output:
func ConvertToSlogFormat ¶ added in v0.0.3
ConvertToSlogFormat converts a list of attribute.KeyValue into the slog.Attr format and appends them to "result". The different types of attribute.KeyValue's are converted accordingly.
func SetLogOptions ¶
func SetLogOptions(options ...LogOption)
SetLogOptions takes LogOption's and overwrites library defaults
Example ¶
package main import ( "context" "errors" "github.com/vincentfree/opentelemetry/otelslog" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "log/slog" ) func main() { option := otelslog.WithAttributes(attribute.String("test", "value"), attribute.Bool("isValid", true)) // use of SetLogOptions otelslog.SetLogOptions(option) // set up tracer tracer := otel.Tracer("otelslog/example") _, span := tracer.Start(context.Background(), "example-span") // pass span to AddTracingContext slog.LogAttrs(nil, slog.LevelInfo, "in case of a success", otelslog.AddTracingContext(span)...) // or in the case of an error err := errors.New("example error") slog.LogAttrs(nil, slog.LevelError, "in case of a failure", otelslog.AddTracingContext(span, err)...) }
Output:
Types ¶
type LogOption ¶
type LogOption func(*logConfig)
LogOption takes a logConfig struct and applies changes. It can be passed to the SetLogOptions function to configure a logConfig struct.
func WithAttributePrefix ¶
WithAttributePrefix updates the default 'trace.attribute' attribute prefix
Example ¶
package main import ( "github.com/vincentfree/opentelemetry/otelslog" ) func main() { otelslog.SetLogOptions(otelslog.WithAttributePrefix("prefix")) // use AddTracingContext or AddTracingContextWithAttributes }
Output:
func WithAttributes ¶
WithAttributes adds global attributes that will be added to all structured logs. attributes have a prefix followed by the key of the attribute.
Example: if the attribute is of type string and the key is: 'http.method' then in the log it uses the default(but over-writable) 'trace.attribute' followed by 'http.method' so the end result is: 'trace.attribute.http.method'
Example ¶
package main import ( "context" "errors" "github.com/vincentfree/opentelemetry/otelslog" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "log/slog" ) func main() { option := otelslog.WithAttributes(attribute.String("test", "value"), attribute.Bool("isValid", true)) otelslog.SetLogOptions(option) tracer := otel.Tracer("otelslog/example") _, span := tracer.Start(context.Background(), "example-span") // pass span to AddTracingContext slog.LogAttrs(nil, slog.LevelInfo, "in case of a success", otelslog.AddTracingContext(span)...) // or in the case of an error err := errors.New("example error") slog.LogAttrs(nil, slog.LevelError, "in case of a failure", otelslog.AddTracingContext(span, err)...) }
Output:
func WithServiceName ¶
WithServiceName adds a service name to the field 'service.name' in your structured logs
Example ¶
package main import ( "github.com/vincentfree/opentelemetry/otelslog" ) func main() { otelslog.SetLogOptions(otelslog.WithServiceName("example-service")) // use AddTracingContext or AddTracingContextWithAttributes }
Output:
func WithSpanID ¶
WithSpanID overwrites the default 'spanID' field in the structured logs with your own key
Example ¶
package main import ( "github.com/vincentfree/opentelemetry/otelslog" ) func main() { otelslog.SetLogOptions(otelslog.WithSpanID("span-id")) // use AddTracingContext or AddTracingContextWithAttributes }
Output:
func WithTraceID ¶
WithTraceID overwrites the default 'traceID' field in the structured logs with your own key
Example ¶
package main import ( "github.com/vincentfree/opentelemetry/otelslog" ) func main() { otelslog.SetLogOptions(otelslog.WithTraceID("trace-id")) // use AddTracingContext or AddTracingContextWithAttributes }
Output:
type Logger ¶
func New ¶
func New() *Logger
New is a function that initializes a new Logger instance, with JSON logging enabled as the logging format.
Example ¶
package main import ( "context" "github.com/vincentfree/opentelemetry/otelslog" "go.opentelemetry.io/otel" "log/slog" ) func main() { tracer := otel.Tracer("otelslog/example") _, span := tracer.Start(context.Background(), "example-span") logger := otelslog.New() // pass span to AddTracingContext logger.WithTracingContext(nil, slog.LevelInfo, "in case of a success", span, nil) }
Output:
func NewWithHandler ¶
NewWithHandler initializes a new Logger instance with a specified slog.Handler. If no handler is provided (i.e., handler is nil), it calls the New() function to create a Logger with the default setup.
Example ¶
package main import ( "context" "github.com/vincentfree/opentelemetry/otelslog" "go.opentelemetry.io/otel" "log/slog" "os" ) func main() { tracer := otel.Tracer("otelslog/example") _, span := tracer.Start(context.Background(), "example-span") logger := otelslog.NewWithHandler(slog.NewTextHandler(os.Stdout, nil)) // pass span to AddTracingContext logger.WithTracingContext(nil, slog.LevelInfo, "in case of a success", span, nil) }
Output:
func (Logger) WithTracingContext ¶
func (l Logger) WithTracingContext(ctx context.Context, level slog.Level, msg string, span trace.Span, err error, attrs ...slog.Attr)
WithTracingContext is a method for the Logger struct which takes a context.Context and log parameters, including a span from distributed tracing and (optional) error information. When logging without an error, pass a nil
Example ¶
package main import ( "context" "errors" "github.com/vincentfree/opentelemetry/otelslog" "go.opentelemetry.io/otel" "log/slog" ) func main() { tracer := otel.Tracer("otelslog/example") _, span := tracer.Start(context.Background(), "example-span") logger := otelslog.New() // pass span to AddTracingContext logger.WithTracingContext(nil, slog.LevelInfo, "in case of a success", span, nil) slog.LogAttrs(nil, slog.LevelInfo, "in case of a success", otelslog.AddTracingContext(span)...) // or in the case of an error err := errors.New("example error") logger.WithTracingContext(nil, slog.LevelError, "in case of a failure", span, err) }
Output:
func (Logger) WithTracingContextAndAttributes ¶
func (l Logger) WithTracingContextAndAttributes(ctx context.Context, level slog.Level, msg string, span trace.Span, err error, attributes []attribute.KeyValue, attrs ...slog.Attr)
Example ¶
package main import ( "context" "errors" "github.com/vincentfree/opentelemetry/otelslog" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "log/slog" ) func main() { attributes := []attribute.KeyValue{ attribute.String("exampleKey", "exampleValue"), attribute.Bool("isValid", true), } tracer := otel.Tracer("otelslog/example") _, span := tracer.Start(context.Background(), "example-span") logger := otelslog.New() // pass span to AddTracingContext logger.WithTracingContextAndAttributes(nil, slog.LevelInfo, "in case of a success", span, nil, attributes) // or in the case of an error err := errors.New("example error") logger.WithTracingContextAndAttributes(nil, slog.LevelError, "in case of a failure", span, err, attributes) }
Output: