slogotel

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Feb 21, 2026 License: MIT Imports: 9 Imported by: 2

README

slogotel

An slog.Handler wrapper library that bridges Go's log/slog with OpenTelemetry.

Features

  • Always injects trace_id / span_id — Adds trace context as log attributes whenever a valid SpanContext exists, regardless of the Span's Recording state
  • Span event recording — Records log messages as Span events when a Recording Span is present
  • Baggage injection — Adds Baggage members as log attributes when a Recording Span is present
  • Error Span status — Sets Span Status to Error when log level is slog.LevelError or above and the Span is Recording
  • Functional Options — Enable/disable each feature and customize key names
  • slogtest.TestHandler compliant — Passes the standard conformance tests

Installation

go get github.com/mocoarow/slogotel

Usage

Basic
package main

import (
	"context"
	"log/slog"
	"os"

	"github.com/mocoarow/slogotel"
)

func main() {
	ctx := context.Background()

	h := slogotel.New(slog.NewJSONHandler(os.Stdout, nil))
	logger := slog.New(h)

	// trace_id and span_id are automatically injected if a Span exists in the context
	logger.InfoContext(ctx, "request received", "path", "/api/users")
}

Output example:

{
  "time": "2024-01-01T00:00:00Z",
  "level": "INFO",
  "msg": "request received",
  "trace_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
  "span_id": "a1b2c3d4e5f6a1b2",
  "path": "/api/users"
}
With Options
h := slogotel.New(
	slog.NewJSONHandler(os.Stdout, nil),
	slogotel.WithSpanEvent(false),       // Disable Span event recording
	slogotel.WithBaggage(false),         // Disable Baggage injection
	slogotel.WithTraceIDKey("traceId"),  // Customize key names
	slogotel.WithSpanIDKey("spanId"),
	slogotel.WithTraceFlagsKey("traceFlags"),
)

Options

Option Default Description
WithSpanEvent(bool) true Record log messages as Span events on Recording Spans
WithBaggage(bool) true Add Baggage members as log attributes on Recording Spans
WithTraceIDKey(string) "trace_id" Key name for the trace_id attribute
WithSpanIDKey(string) "span_id" Key name for the span_id attribute
WithTraceFlagsKey(string) "" (disabled) Key name for the trace_flags attribute (empty string disables it)

Processing Flow

Handle(ctx, record)
  │
  ├── 1. Get SpanContext ← Independent of Recording state
  │     ├── trace_id is Valid → Add attribute
  │     └── span_id is Valid → Add attribute
  │
  ├── 2. Only when Span is Recording:
  │     ├── Add Baggage members as log attributes
  │     ├── Record log as a Span event
  │     └── Set Span Status to Error if level ≥ LevelError
  │
  └── 3. next.Handle(ctx, record)

Notes

  • If a Baggage key conflicts with an existing log attribute key, both values are appended to the record (the downstream handler, such as slog.JSONHandler, typically uses the last value)
  • The log.level attribute is automatically added to Span events
  • Group attributes are flattened with dot-separated keys (e.g., req.method)

License

MIT

Documentation

Overview

Package slogotel provides an slog.Handler wrapper that bridges Go's log/slog with OpenTelemetry.

Unlike other slog-OpenTelemetry integrations, slogotel always injects trace_id and span_id as log attributes whenever a valid trace.SpanContext exists in the context, regardless of the Span's Recording state. When the Span is Recording, it additionally records log messages as Span events, injects Baggage members as log attributes, and sets the Span status to Error for error-level logs.

Usage

h := slogotel.New(slog.NewJSONHandler(os.Stdout, nil))
logger := slog.New(h)
logger.InfoContext(ctx, "request received", "path", "/api/users")

Options

Use functional options to customize behavior:

h := slogotel.New(
	slog.NewJSONHandler(os.Stdout, nil),
	slogotel.WithSpanEvent(false),      // disable Span event recording
	slogotel.WithBaggage(false),        // disable Baggage injection
	slogotel.WithTraceIDKey("traceId"), // customize key names
	slogotel.WithSpanIDKey("spanId"),
)

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler is a slog.Handler that integrates with OpenTelemetry. It adds trace_id/span_id to log records regardless of Span recording state, and optionally records Span events, Baggage attributes, and error status only when the Span is recording.

func New

func New(next slog.Handler, opts ...Option) *Handler

New creates a new Handler wrapping the given slog.Handler. It panics if next is nil.

Example
package main

import (
	"log/slog"
	"os"

	"github.com/mocoarow/slogotel"
)

func main() {
	h := slogotel.New(slog.NewJSONHandler(os.Stdout, nil))
	logger := slog.New(h)
	logger.Info("hello", "key", "value")
}
Example (WithOptions)
package main

import (
	"log/slog"
	"os"

	"github.com/mocoarow/slogotel"
)

func main() {
	h := slogotel.New(
		slog.NewJSONHandler(os.Stdout, nil),
		slogotel.WithSpanEvent(false),
		slogotel.WithBaggage(false),
		slogotel.WithTraceIDKey("traceId"),
		slogotel.WithSpanIDKey("spanId"),
		slogotel.WithTraceFlagsKey("traceFlags"),
	)
	logger := slog.New(h)
	logger.Info("hello", "key", "value")
}

func (*Handler) Enabled

func (h *Handler) Enabled(ctx context.Context, level slog.Level) bool

Enabled reports whether the handler handles records at the given level.

func (*Handler) Handle

func (h *Handler) Handle(ctx context.Context, r slog.Record) error

Handle processes the log record, adding OpenTelemetry context.

func (*Handler) WithAttrs

func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler

WithAttrs returns a new Handler with the given attributes.

func (*Handler) WithGroup

func (h *Handler) WithGroup(name string) slog.Handler

WithGroup returns a new Handler with the given group name.

type Option

type Option func(*Handler)

Option configures a Handler.

func WithBaggage

func WithBaggage(enabled bool) Option

WithBaggage enables or disables adding Baggage members as log attributes. Default: true.

func WithSpanEvent

func WithSpanEvent(enabled bool) Option

WithSpanEvent enables or disables recording log messages as Span events. Default: true.

func WithSpanIDKey

func WithSpanIDKey(key string) Option

WithSpanIDKey sets the key name for span_id in log attributes. It panics if key is empty. Default: "span_id".

func WithTraceFlagsKey

func WithTraceFlagsKey(key string) Option

WithTraceFlagsKey sets the key name for trace_flags in log attributes. If empty, trace_flags will not be added. Default: "" (disabled).

func WithTraceIDKey

func WithTraceIDKey(key string) Option

WithTraceIDKey sets the key name for trace_id in log attributes. It panics if key is empty. Default: "trace_id".

Jump to

Keyboard shortcuts

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