slogx

package module
v0.0.0-...-0060eb9 Latest Latest
Warning

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

Go to latest
Published: Jan 4, 2024 License: MIT Imports: 6 Imported by: 0

README

Golang extensions for log/slog

Go Reference CI/CD Coverage Status Go Report Card Release

Recommendations

Using CtxHandler with linter

Disable non-Context slog functions (e.g. slog.Info) and methods using linter.

Example in golangci-lint config:

linters-settings:
  ...
  forbidigo:
    ...
    forbid:
      # slogx.CtxHandler support:
      - p: ^slog\.(Logger\.)?Error$
        msg: Use ErrorContext to support slogx.CtxHandler
      - p: ^slog\.(Logger\.)?Warn$
        msg: Use WarnContext to support slogx.CtxHandler
      - p: ^slog\.(Logger\.)?Info$
        msg: Use InfoContext to support slogx.CtxHandler
      - p: ^slog\.(Logger\.)?Debug$
        msg: Use DebugContext to support slogx.CtxHandler
    analyze-types: true

Documentation

Overview

Package slogx contains extensions for log/slog.

Index

Constants

View Source
const KeyStack = "stack"

Variables

This section is empty.

Functions

func ChainReplaceAttr

func ChainReplaceAttr(fs ...func([]string, slog.Attr) slog.Attr) func([]string, slog.Attr) slog.Attr

func ContextWithAttrs

func ContextWithAttrs(ctx context.Context, attrs ...any) context.Context

ContextWithAttrs applies attrs to a handler stored in ctx.

func ContextWithGroup

func ContextWithGroup(ctx context.Context, group string) context.Context

ContextWithGroup applies group to a handler stored in ctx.

func HandlerFromContext

func HandlerFromContext(ctx context.Context) slog.Handler

HandlerFromContext returns a Handler value stored in ctx if exists or nil.

func LaxCtxHandler

func LaxCtxHandler() ctxHandlerOption

LaxCtxHandler is an option for disable adding !BADCTX attr.

func LogAttrsSkip

func LogAttrsSkip(ctx context.Context, skip int, handler slog.Handler, level slog.Level, msg string, attrs ...slog.Attr)

LogAttrsSkip emits a log record using handler with the current time and the given level and message. Value skip=0 works exactly like (*slog.Logger).LogAttrs, value skip=1 skips caller of LogAttrsSkip() etc.

func LogSkip

func LogSkip(ctx context.Context, skip int, handler slog.Handler, level slog.Level, msg string, args ...any)

LogSkip emits a log record using handler with the current time and the given level and message. Value skip=0 works exactly like (*slog.Logger).Log, value skip=1 skips caller of LogSkip() etc.

func LoggerFromContext

func LoggerFromContext(ctx context.Context) *slog.Logger

LoggerFromContext returns a Logger value stored in ctx if exists or nil.

func NewContextWithHandler

func NewContextWithHandler(ctx context.Context, handler slog.Handler) context.Context

NewContextWithHandler returns a new Context that carries value handler.

func NewContextWithLogger

func NewContextWithLogger(ctx context.Context, log *slog.Logger) context.Context

NewContextWithLogger returns a new Context that carries value log.

func ParseLevel

func ParseLevel(levelName string) slog.Level

ParseLevel converts log level name into slog.Level. It is case insensitive, ignores surrounding spaces and accepts shortened level name. In case of unknown log level name it will return slog.LevelDebug.

func SetDefaultCtxHandler

func SetDefaultCtxHandler(ctx context.Context, fallback slog.Handler, opts ...ctxHandlerOption) context.Context

SetDefaultCtxHandler sets a CtxHandler as a default logger's handler and returns context with this handler inside.

func Stack

func Stack() slog.Attr

Stack returns a stack trace formatted as panic output. It excludes a call of Stack() itself.

Types

type CtxHandler

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

CtxHandler provides a way to use slog.Handler stored in a context instead of slog.Logger. This makes possible to store extra slog.Attr inside a context and make it magically work without needs to get slog.Logger out of context each time you need to log something.

CtxHandler should be used as a default logger's handler. So it's useful only for applications but not libraries - libraries shouldn't expect concrete configuration of default logger and can't expect availability of CtxHandler's features.

Usually when we need a context-specific logging we have to store pre-configured logger inside a context. But then everywhere we need to log something we have to get logger from context first, which is annoying. Also it means we have to use own logger instance and unable to use global logger and log using functions like slog.InfoContext. Example:

func main() {
	log := slog.New(slog.NewJSONHandler(os.Stdout, nil))
	slog.SetDefault(log)
	// ...
	srv := &http.Server{
		//...
	}
	srv.ListenAndServe()
	log.Info("done")
}

func (handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	log := slog.With("remote_addr", r.RemoteAddr)
	ctx = slogx.NewContextWithLogger(ctx, log)
	handleRequest(ctx)
}

func handleRequest(ctx context.Context) {
	log := slogx.LoggerFromContext(ctx) // <-- THIS LINE IS EVERYWHERE!
	log.Info("message")                 // Will also log "remote_addr" attribute.
}

With CtxHandler same functionality became:

func main() {
	handler := slog.NewJSONHandler(os.Stdout, nil)
	ctx := slogx.SetDefaultCtxHandler(context.Background(), handler)
	// ...
	srv := &http.Server{
		BaseContext: func(net.Listener) context.Context { return ctx },
		//...
	}
	srv.ListenAndServe()
	slog.InfoContext(ctx, "done")
}

func (handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	ctx = slogx.ContextWithAttrs(ctx, "remote_addr", r.RemoteAddr)
	handleRequest(ctx)
}

func handleRequest(ctx context.Context) {
	slog.InfoContext(ctx, "message")    // Will also log "remote_addr" attribute.
}

Code not aware about CtxHandler (e.g. libraries) will continue to work correctly, but there are some extra restrictions:

  • You should not modify default logger after initial configuration.
  • If you'll create new logger instance (e.g. using slog.With(...)) then you should not modify Attrs or Group inside ctx while using that logger instance.

Non-Context functions like slog.Info() will work, but they will ignore Attr/Group configured inside ctx.

By default CtxHandler will add attr with key "!BADCTX" and value ctx if ctx does not contain slog handler, but this can be disabled using LaxCtxHandler option.

func (*CtxHandler) Enabled

func (h *CtxHandler) Enabled(ctx context.Context, l slog.Level) bool

Enabled implements slog.Handler interface. It uses handler returned by HandlerFromContext or fallback handler.

func (*CtxHandler) Handle

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

Handle implements slog.Handler interface. It uses handler returned by HandlerFromContext or fallback handler. Adds !BADCTX attr if HandlerFromContext returns nil. Use LaxCtxHandler to disable this behaviour.

func (*CtxHandler) WithAttrs

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

WithAttrs implements slog.Handler interface.

func (*CtxHandler) WithGroup

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

WithGroup implements slog.Handler interface.

Jump to

Keyboard shortcuts

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