Application 2023-10-08

Contextual Logging with slog

Learn contextual structured logging in Go using log/slog (Go 1.21+). Covers custom slog.Handler, structured JSON output, and injecting trace IDs from context into every log entry.

Read in: ja
Contextual Logging with slog

Overview

Summarizing contextual logging using log/slog in Go.

What is log/slog?

A package for structured logging added in Go1.21.

Structured logging is the output of logs as structured data.

Previously, to perform structured logging in Go, one had to either use third-party packages or implement it from scratch. Now, the standard package is also becoming a viable option.

By default, it can output in text or JSON format.

slog can hold context, allowing you to include request-based information.

Contextual logging with slog

Let's write code to create a Logger that includes request-based information in the logs.

Below is the code that assumes logging with a trace ID in the context.

By implementing the slog.Handler interface, you can create your own Handler.[]

package main

import (
	"context"
	"fmt"
	"log/slog"
	"net/http"
	"os"

	"github.com/google/uuid"
)

// TraceIDHandler represents the singular of trace id handler.
type TraceIDHandler struct {
	slog.Handler
}

type ctxTraceID struct{}

var ctxTraceIDKey = ctxTraceID{}

// Handle implements slog.Handler interface.
func (t TraceIDHandler) Handle(ctx context.Context, r slog.Record) error {
	tid, ok := ctx.Value(ctxTraceIDKey).(string)
	if ok {
		r.AddAttrs(slog.String("trace_id", tid))
	}
	return t.Handler.Handle(ctx, r)
}

// WithTraceID returns a context with a trace id.
func WithTraceID(ctx context.Context) context.Context {
	uuid, _ := uuid.NewRandom()
	return context.WithValue(ctx, ctxTraceIDKey, uuid.String())
}

func main() {
	mux := http.NewServeMux()

	handler := TraceIDHandler{slog.NewJSONHandler(os.Stdout, nil)}
	logger := slog.New(handler)

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		ctx := WithTraceID(r.Context())
		logger.InfoContext(ctx, "Log with TraceID")
		w.WriteHeader(http.StatusOK)
		fmt.Fprint(w, "Contextual Logging!")
	})

	http.ListenAndServe(":8085", mux)
}

When you run the above code, the following log is output:

{"time":"2023-10-08T17:06:44.423859+09:00","level":"INFO","msg":"Log with TraceID","trace_id":"4f9a0bb6-cf8d-4eef-82ea-2385b76d3a74"}

Impressions

I was using my own package for structured logging, but with the release of Go1.21, I switched to this.

References

Tags: slog contextual logging
Share: 𝕏 Post Facebook Hatena
✏️ View source / Discuss on GitHub
☕ Support

If you enjoy this blog, consider supporting it. Every bit helps keep it running!


Related Articles