otelslog

package module
v0.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 12, 2023 License: Apache-2.0 Imports: 8 Imported by: 0

README

OpenTelemetry extensions - otelslog

Home Related
Home otelzerolog, otellogrus

Go CodeQL Dependency Review Go Reference

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
}

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

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AddTracingContext

func AddTracingContext(span trace.Span, err ...error) []slog.Attr

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

func ConvertToSlogFormat(attributes []attribute.KeyValue, result []slog.Attr) []slog.Attr

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

func WithAttributePrefix(prefix string) LogOption

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

func WithAttributes(attributes ...attribute.KeyValue) LogOption

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

func WithServiceName(serviceName string) LogOption

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

func WithSpanID(spanID string) LogOption

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

func WithTraceID(traceID string) LogOption

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

type Logger struct {
	*slog.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

func NewWithHandler(handler slog.Handler) *Logger

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:

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL