Help us shape the future of Go! Take the 2026 Go Developer Survey

gologger

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Oct 2, 2026 License: MIT Imports: 16 Imported by: 0

README

gologger

English | Русский

A configurable logger built on the standard log/slog package: JSON and readable text output, context attributes, multiple destinations, and extensions through custom handlers and middleware.

Requires Go 1.27+. Recommended toolchain: Go 1.27.1. Uses the standard slog.NewMultiHandler and slog.DiscardHandler.

Installation

go get github.com/assurrussa/gologger@latest

Import path: github.com/assurrussa/gologger; package name: gologger.

Quick start

The following is the body of a function returning error. Required imports: context, log/slog, os, and github.com/assurrussa/gologger.

log, err := gologger.New(gologger.Config{Level: "info"},
    gologger.WithWriter(os.Stdout),
)
if err != nil {
    return err
}

ctx := gologger.WithValue(context.Background(), slog.String("request_id", "req-1"))
log.WithNamed("worker").InfoContext(ctx, "started")
return log.Close()

In an application, call Close after all users of the logger have finished. The executable example is also checked by go test ./....

New creates an independent instance: importing the package and constructing a logger do not change LogLevel or slog.Default(). To configure the application default, explicitly call gologger.SetDefault(log).

Configuration

Behavior of gologger.New without additional options:

Config field Default Behavior
Level WARN for an empty string Minimum level. An invalid value returns an error.
Env Empty string local and development enable readable pretty output; other values select JSON.
JSON false true forces JSON even in a local environment.
AddSource false Includes the caller's file and line.
AddVerbose false Adds program_info: PID, application version, and Go version.
Rate 0 Probability of keeping a record when 0 < Rate < 1; 0 and 1 keep all records.
Output Empty string Path to an additional buffered JSON file.

The primary output goes to stdout. Config.Output adds a file and keeps the primary output, including when a custom handler is supplied.

New does not read environment variables or apply the value-default and validate tags itself. The tags in Config are intended for the application's configuration layer. Options override configuration values.

Customization and extensions

  • WithWriter(io.Writer) replaces stdout for the built-in handler.
  • WithHandler(slog.Handler) replaces the built-in handler. The supplied handler controls its own level, AddSource, and ReplaceAttr settings.
  • WithAdditionalHandlers(...OptionHandler) adds destinations. Each factory receives its own copy of slog.HandlerOptions; a nil result is skipped.
  • WithMiddleware(...Middleware) wraps the combined handler. Middleware runs in the supplied order: the first wrapper is the outermost.
  • WithLevel(slog.Leveler) overrides Config.Level without mutating the supplied value. A *slog.LevelVar supports runtime level changes.
  • WithReplaceAttr configures attribute filtering, renaming, or redaction for built-in handlers and additional handler factories.
  • WithClosers(...io.Closer) transfers responsibility for closing additional resources after successful construction.

This example combines a dynamic level, an additional stderr destination, attribute redaction, and middleware. Required imports: log/slog, os, and github.com/assurrussa/gologger.

level := new(slog.LevelVar)
level.Set(slog.LevelDebug)

log, err := gologger.New(gologger.Config{},
    gologger.WithLevel(level),
    gologger.WithAdditionalHandlers(func(opts *slog.HandlerOptions) slog.Handler {
        return slog.NewTextHandler(os.Stderr, opts)
    }),
    gologger.WithReplaceAttr(func(_ []string, attr slog.Attr) slog.Attr {
        if attr.Key == "password" {
            return slog.String(attr.Key, "[redacted]")
        }
        return attr
    }),
    gologger.WithMiddleware(func(next slog.Handler) slog.Handler {
        return next.WithAttrs([]slog.Attr{slog.String("service", "api")})
    }),
)
if err != nil {
    return err
}
log.Info("ready", "password", "example")
level.Set(slog.LevelWarn)
return log.Close()

WithLevel affects built-in handlers and factories that use the supplied options. A handler passed through WithHandler controls its own threshold. Changing a LevelVar affects only loggers explicitly configured with that pointer.

Processing order: context attributes → middleware → sampling → destinations. Handlers and middleware must preserve the slog.Handler contract, including Enabled, WithAttrs, WithGroup, and concurrent access safety. If several independent handlers share a writer, the caller must synchronize that writer.

Context and derived loggers

WithValue(ctx, slog.Attr) creates a context containing an additional attribute. Pass it to InfoContext, DebugContext, WarnContext, ErrorContext, or LogAttrs to include the attribute in a record. Sibling contexts do not mutate each other's attributes.

WithNamed adds the name field; WithAttrs adds persistent attributes. Both return the Logger interface and share resources with the parent. The embedded *slog.Logger is available through the Logger field; inherited With and WithGroup methods return a regular *slog.Logger. The original *gologger.Log remains responsible for closing resources.

Error(err) creates a string attribute named error; pass a non-nil error. Discard creates a logger with no output; DiscardJSONWithWriter and DiscardTextWithWriter create loggers for the supplied writer. These functions use the shared LogLevel.

Sampling

For 0 < Rate < 1, the library makes one random decision per record, so all destinations receive the same selected records. Sampling applies to every level, including ERROR. Values 0 and 1 disable sampling.

Negative values, values greater than 1, and NaN cause New to return an error.

Public handlers

All paths below are prefixed with github.com/assurrussa/gologger/:

  • handlers/slogcontext adds attributes from the context.
  • handlers/slogpretty provides readable local output with groups, LogValuer, ReplaceAttr, and caller source information.
  • handlers/slogdiscard discards records.

Handlers work with a regular slog.New without a gologger.Log wrapper.

Resources and shutdown

Config.Output creates new files with mode 0600; permissions of existing files are unchanged. Records are buffered and persisted when the logger is closed.

Close() flushes and closes owned resources once. Flush() is a compatibility name for the same terminal operation, not a periodic buffer flush. All calls, including concurrent calls through derived loggers, return the stored close error. Stop the logger's users before closing it; logging after close is unsupported.

Supplied writers and handlers remain owned by the caller unless ownership is explicitly transferred, and are not closed by the library. WithClosers transfers responsibility only after construction succeeds. Resources close in reverse order; each io.Closer must flush its own buffers. The library starts no goroutines or background timers.

Global logger

SetDefault(log) updates both the package default and slog.Default(). The previous instance remains owned by the application and is not closed automatically. Default() returns the current package logger.

NewLogger creates and installs a global logger, updates the shared LogLevel, falls back to WARN for an invalid level, ignores JSON in local/development, and treats rates outside (0, 1) as disabled sampling. Use New for independent instances. Change the shared LogLevel through .Set; do not replace its pointer.

Validation

Requires Go 1.27+ and golangci-lint:

make check
./scripts/test-consumer.sh <published-version-or-commit>

make check runs vet, lint, and race tests five times. The consumer probe creates a separate Go module with GOWORK=off, downloads the specified published version without replace directives, and checks the public imports with the race detector.

License

MIT.

Documentation

Overview

Package gologger provides configurable slog loggers, context attributes and public handler extension points. New never changes the process default logger.

Index

Examples

Constants

This section is empty.

Variables

View Source
var LogLevel = legacyLevel()

LogLevel controls legacy NewLogger and Discard loggers. Use WithLevel for new code. Mutate it through Set; replacing this pointer is unsupported.

Functions

func Error

func Error(err error) slog.Attr

func SetDefault

func SetDefault(log *Log)

SetDefault explicitly installs log as both the package and slog default. The host remains responsible for closing any previous logger. log must be non-nil.

func WithValue

func WithValue(ctx context.Context, attr slog.Attr) context.Context

Types

type Config

type Config struct {
	Env string `` //nolint:lll // it's config
	/* 133-byte string literal not displayed */
	Rate       float64 `toml:"rate" long:"log-rate" env:"LOG_RATE" value-default:"0" validate:"min=0,max=1"`
	Level      string  `toml:"level" long:"log-level" env:"LOG_LEVEL" value-default:"info" validate:"required,oneof=debug info warn error"` //nolint:lll // it's config
	AddSource  bool    `toml:"add_source" long:"add-source" env:"LOG_ADD_SOURCE" validate:"boolean"`
	AddVerbose bool    `toml:"add_verbose" long:"add-verbose" env:"LOG_ADD_VERBOSE" validate:"boolean"`
	JSON       bool    `toml:"json" long:"log-json" env:"LOG_JSON" validate:"boolean"`
	Output     string  `toml:"output" long:"log-output" env:"LOG_OUTPUT"`
}

Config is the portable logging configuration. Tags describe configuration and environment mappings; New does not read the environment itself.

func (Config) IsDev

func (c Config) IsDev() bool

func (Config) IsLocal

func (c Config) IsLocal() bool

func (Config) IsProduction

func (c Config) IsProduction() bool

func (Config) IsStaging

func (c Config) IsStaging() bool

IsStaging recognizes the configured staging value and the legacy stage alias.

type Log

type Log struct {
	*slog.Logger
	// contains filtered or unexported fields
}

Log embeds slog.Logger and shares resource ownership with WithNamed/WithAttrs children. Close the root after all users have stopped logging.

func Default

func Default() *Log

Default returns this package's default without changing slog.Default.

func Discard

func Discard(handlers ...OptionHandler) *Log

Discard returns a no-op logger with optional additional destinations. Additional handlers receive the legacy shared LogLevel.

func DiscardJSONWithWriter

func DiscardJSONWithWriter(writer io.Writer) *Log

func DiscardTextWithWriter

func DiscardTextWithWriter(writer io.Writer) *Log

func New

func New(cfg Config, opts ...Option) (*Log, error)

New builds an independent logger. The default is JSON on stdout at WARN; local/development uses pretty output unless Config.JSON is true. It starts no goroutines and does not change LogLevel or slog.Default.

Example
package main

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

	"github.com/assurrussa/gologger"
)

func main() {
	log, err := gologger.New(gologger.Config{Level: "info"},
		gologger.WithWriter(os.Stdout),
		gologger.WithReplaceAttr(func(_ []string, attr slog.Attr) slog.Attr {
			if attr.Key == slog.TimeKey {
				return slog.Attr{}
			}
			return attr
		}))
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := log.Close(); err != nil {
			panic(err)
		}
	}()
	ctx := gologger.WithValue(context.Background(), slog.String("request_id", "r-1"))
	log.WithNamed("worker").InfoContext(ctx, "ready")
}
Output:
{"level":"INFO","msg":"ready","name":"worker","request_id":"r-1"}

func NewLogger

func NewLogger(cfg Config, handlers ...OptionHandler) (*Log, error)

NewLogger configures the global logger with a shared level, WARN fallback and local pretty output. Prefer New for independent instances. Use New followed by SetDefault only when process wiring requires it.

func (*Log) Close

func (l *Log) Close() error

Close flushes and closes owned resources once. Every caller receives the same result, including concurrent calls through derived loggers. Borrowed writers and handlers are never closed. Logging after Close is unsupported.

func (*Log) Flush

func (l *Log) Flush() error

Flush is a compatibility name for Close. It is terminal, not a periodic flush.

func (*Log) WithAttrs

func (l *Log) WithAttrs(attrs ...slog.Attr) Logger

func (*Log) WithNamed

func (l *Log) WithNamed(name string) Logger

type Logger

type Logger interface {
	Handler() slog.Handler
	DebugContext(ctx context.Context, msg string, args ...any)
	InfoContext(ctx context.Context, msg string, args ...any)
	WarnContext(ctx context.Context, msg string, args ...any)
	ErrorContext(ctx context.Context, msg string, args ...any)
	LogAttrs(ctx context.Context, level slog.Level, msg string, attrs ...slog.Attr)
	WithNamed(name string) Logger
	WithAttrs(attrs ...slog.Attr) Logger
}

Logger is the consumer-facing logging contract. Implementations may wrap any slog.Handler; lifecycle ownership remains with the concrete logger's creator.

type Middleware

type Middleware func(slog.Handler) slog.Handler

Middleware wraps a handler. It must preserve slog's concurrency and derivation contracts.

type Option

type Option func(*options)

Option configures a new logger without changing process defaults.

func WithAdditionalHandlers

func WithAdditionalHandlers(handlers ...OptionHandler) Option

WithAdditionalHandlers adds destinations alongside the primary handler.

func WithClosers

func WithClosers(closers ...io.Closer) Option

WithClosers transfers cleanup responsibility on successful construction only. Close/Flush calls each closer once in reverse order; a closer must flush its own buffers. Derived loggers share the same cleanup state.

func WithHandler

func WithHandler(handler slog.Handler) Option

WithHandler replaces the built-in console handler. The supplied handler owns its own level, source and attribute configuration. File output remains additive.

func WithLevel

func WithLevel(level slog.Leveler) Option

WithLevel overrides Config.Level without mutating the supplied level. A *slog.LevelVar permits runtime changes shared only by explicitly wired loggers.

func WithMiddleware

func WithMiddleware(middleware ...Middleware) Option

WithMiddleware wraps the combined destinations after context enrichment and before sampling. Middleware executes in the supplied order, first outermost.

func WithReplaceAttr

func WithReplaceAttr(replace func([]string, slog.Attr) slog.Attr) Option

WithReplaceAttr configures built-in handlers and additional handler factories.

func WithWriter

func WithWriter(writer io.Writer) Option

WithWriter replaces stdout for the built-in handler. The caller owns the writer and must flush/close it, or explicitly transfer ownership through WithClosers.

type OptionHandler

type OptionHandler func(*slog.HandlerOptions) slog.Handler

OptionHandler builds an additional handler using the configured slog options. A nil result skips this handler. Each factory receives its own options copy.

Directories

Path Synopsis
handlers
slogpretty
Package slogpretty formats slog's JSON output for local interactive use.
Package slogpretty formats slog's JSON output for local interactive use.

Jump to

Keyboard shortcuts

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