logger

package
v0.33.0 Latest Latest
Warning

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

Go to latest
Published: Jul 24, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package logger provides GTB's logging boundary.

The Logger interface mirrors the standard library's *slog.Logger method set exactly, so a *slog.Logger satisfies logger.Logger directly and can be passed anywhere a Logger is expected (for example props.Props.Logger). Consumers remain free to supply their own implementation with the same method set.

Runtime level and format control are deliberately NOT part of the Logger interface — a plain *slog.Logger owns its level via its handler. They are exposed as optional capabilities through the Leveller and Reformatter interfaces and two package helpers that no-op when the logger does not implement them:

logger.SetLevel(log, slog.LevelDebug) // true if log implements Leveller
logger.SetFormatter(log, logger.JSONFormatter) // true if log implements Reformatter

To branch on whether a level is active, use the interface method directly:

if log.Enabled(ctx, slog.LevelDebug) { /* expensive diagnostics */ }

Constructors

  • NewCharm(w, opts...): GTB's default, backed by charmbracelet/log for coloured, styled terminal output. The returned Logger also implements Leveller and Reformatter, so --debug, log.level, and log.format take effect on it. Use this for props.Props.Logger.
  • NewCharmSlog(w, opts...) / NewCharmHandler(w, opts...): slog-native construction over the same Charm output — a *slog.Logger and an slog.Handler respectively.
  • NewSlog(handler): slog.New(handler) for ecosystem handlers (zap, zerolog, OpenTelemetry, custom). Wrap the handler with NewLevelGate for runtime level control via a shared *slog.LevelVar.
  • NewNoop(): a discarding *slog.Logger for tests where output is irrelevant.
  • NewBuffer() / NewCaptureHandler(): capture records in memory for test assertions.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrInvalidFormat = errors.New("invalid format")

ErrInvalidFormat is returned when parsing an unrecognised formatter string.

View Source
var ErrInvalidLevel = errors.New("invalid level")

ErrInvalidLevel is returned when parsing an unrecognised level string.

Functions

func NewBuffer

func NewBuffer() *bufferLogger

NewBuffer returns a bufferLogger that captures all log messages in memory. Use Entries(), Messages(), and Contains() to assert on captured output.

Example:

buf := logger.NewBuffer()
myFunc(buf)
if !buf.Contains("expected message") {
    t.Error("missing expected log message")
}
Example
package main

import (
	"gitlab.com/phpboyscout/go-tool-base/pkg/logger"
)

func main() {
	// Create a buffer logger for testing log output.
	buf := logger.NewBuffer()

	buf.Info("test message", "key", "value")

	if buf.Contains("test message") {
		// Message was logged
	}
}

func NewCharmHandler added in v0.30.0

func NewCharmHandler(w io.Writer, opts ...CharmOption) slog.Handler

NewCharmHandler builds a Charm-backed slog.Handler from the given options. It is the slog-first construction entry point: wrap it in slog.New (or use NewCharmSlog) to obtain a *slog.Logger with GTB's default human-friendly terminal output.

func NewCharmSlog added in v0.30.0

func NewCharmSlog(w io.Writer, opts ...CharmOption) *slog.Logger

NewCharmSlog builds a *slog.Logger backed by a Charm slog.Handler. This is the preferred way to construct GTB's default application logger under the slog-first design; the returned *slog.Logger satisfies the Logger interface.

func NewLevelGate added in v0.30.0

func NewLevelGate(inner slog.Handler, level *slog.LevelVar) slog.Handler

NewLevelGate wraps an slog.Handler so its effective level is controlled at runtime by level. Records below level.Level() are dropped before reaching inner. This is how the application logger honours --debug and log.level after construction: build the base handler at its most permissive level and gate it with a shared *slog.LevelVar that the command layer mutates.

func SetFormatter added in v0.30.0

func SetFormatter(log Logger, f Formatter) bool

SetFormatter changes log's output format when it implements Reformatter, reporting whether the change was applied.

func SetLevel added in v0.30.0

func SetLevel(log Logger, level slog.Level) bool

SetLevel changes log's minimum level when it implements Leveller, reporting whether the change was applied. A plain *slog.Logger does not implement Leveller, so SetLevel is a no-op for it (it owns its own level via its handler).

func ToSlog added in v0.30.0

func ToSlog(log Logger) *slog.Logger

ToSlog adapts a Logger to a *slog.Logger for packages that accept the standard library type directly. A nil logger yields a discarding logger; a value that is already a *slog.Logger is returned unchanged; otherwise a *slog.Logger is built over the logger's Handler.

This is a GTB adapter-boundary helper: framework code converts props.Logger here before handing it to a package that has adopted *slog.Logger. Extracted packages accept *slog.Logger directly and never need this package.

Types

type CaptureHandler added in v0.30.0

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

CaptureHandler is an slog.Handler that records emitted records in memory for test assertions. It is the slog-first counterpart to the buffer logger: pass slog.New(logger.NewCaptureHandler()) into a package under test and assert on Records / Messages. Handlers derived via WithAttrs and WithGroup share the same underlying record store, so records land in one place regardless of which derived logger emitted them. It is safe for concurrent use.

func NewCaptureHandler added in v0.30.0

func NewCaptureHandler() *CaptureHandler

NewCaptureHandler returns a CaptureHandler that records everything at DebugLevel and above. Use SetLevel to raise the threshold (e.g. to exercise level-gated code paths in tests).

func (*CaptureHandler) Contains added in v0.30.0

func (h *CaptureHandler) Contains(substr string) bool

Contains reports whether any captured record's message contains substr.

func (*CaptureHandler) Enabled added in v0.30.0

func (h *CaptureHandler) Enabled(_ context.Context, level slog.Level) bool

Enabled reports whether level is at or above the handler's minimum level.

func (*CaptureHandler) Handle added in v0.30.0

func (h *CaptureHandler) Handle(_ context.Context, record slog.Record) error

Handle records a clone of r, decorated with any attributes accumulated through WithAttrs so assertions see the full attribute set.

func (*CaptureHandler) Messages added in v0.30.0

func (h *CaptureHandler) Messages() []string

Messages returns the message of every captured record, in order.

func (*CaptureHandler) Records added in v0.30.0

func (h *CaptureHandler) Records() []slog.Record

Records returns a snapshot of every captured record.

func (*CaptureHandler) Reset added in v0.30.0

func (h *CaptureHandler) Reset()

Reset clears every captured record from the shared store.

func (*CaptureHandler) SetLevel added in v0.30.0

func (h *CaptureHandler) SetLevel(level slog.Level)

SetLevel raises or lowers the minimum level at which records are captured. The level is shared with handlers derived via WithAttrs/WithGroup.

func (*CaptureHandler) WithAttrs added in v0.30.0

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

WithAttrs returns a derived handler that decorates future records with attrs and shares this handler's record store.

func (*CaptureHandler) WithGroup added in v0.30.0

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

WithGroup returns a derived handler that shares this handler's record store.

type CharmOption

type CharmOption func(*log.Options)

CharmOption configures the charmbracelet backend.

func WithCaller

func WithCaller(enabled bool) CharmOption

WithCaller enables or disables caller location in log output.

func WithFormatter added in v0.30.0

func WithFormatter(f Formatter) CharmOption

WithFormatter sets the initial output formatter.

func WithLevel

func WithLevel(level Level) CharmOption

WithLevel sets the initial log level.

func WithPrefix

func WithPrefix(prefix string) CharmOption

WithPrefix sets an initial prefix on the logger.

func WithTimestamp

func WithTimestamp(enabled bool) CharmOption

WithTimestamp enables or disables timestamps in log output.

type Config added in v0.30.0

type Config struct {
	Level     string `mapstructure:"level"     json:"level"     yaml:"level"`
	Format    string `mapstructure:"format"    json:"format"    yaml:"format"`
	Timestamp bool   `mapstructure:"timestamp" json:"timestamp" yaml:"timestamp"`
	Caller    bool   `mapstructure:"caller"    json:"caller"    yaml:"caller"`
}

Config is the typed, config-system-agnostic construction configuration for a logger. A host application (GTB or any other) unmarshals its config section into this struct and passes it to the package; the package never imports a config system to obtain it.

Level and Format are applied both at construction (via CharmOptions) and at runtime (via SetLevel / SetFormatter). Timestamp and Caller are construction-time only — the Logger interface exposes no runtime setter for them — so a host that wants them config-driven must build its logger from this Config (e.g. logger.NewCharm(w, cfg.CharmOptions()...)).

func DefaultConfig added in v0.30.0

func DefaultConfig() Config

DefaultConfig returns the package defaults: info level, human-readable text format, no timestamp, no caller — matching a freshly constructed NewCharm logger.

func Merge added in v0.30.0

func Merge(base, overlay Config) Config

Merge overlays a decoded Config onto a base (usually DefaultConfig). Empty string fields in the overlay preserve the base value; boolean fields take the overlay value directly (the package defaults are false, so an unset overlay boolean is indistinguishable from an explicit false and both keep the default).

func (Config) CharmOptions added in v0.30.0

func (c Config) CharmOptions() []CharmOption

CharmOptions renders the Config as options for NewCharm. An unparseable Level is skipped so the charm default (InfoLevel) survives rather than being reset to an arbitrary value.

func (Config) Formatter added in v0.30.0

func (c Config) Formatter() Formatter

Formatter returns the parsed Format, falling back to TextFormatter when the Format string is empty or unrecognised.

func (Config) Validate added in v0.30.0

func (c Config) Validate() error

Validate reports whether the Level and Format strings are recognised.

type Entry

type Entry struct {
	Level   Level
	Message string
	Keyvals []any
}

Entry represents a single captured log message.

type Formatter

type Formatter int

Formatter represents a log output format.

const (
	// TextFormatter formats log messages as human-readable text.
	TextFormatter Formatter = iota
	// JSONFormatter formats log messages as JSON.
	JSONFormatter
	// LogfmtFormatter formats log messages as logfmt.
	LogfmtFormatter
)

func ParseFormatter added in v0.30.0

func ParseFormatter(s string) (Formatter, error)

ParseFormatter converts a formatter string ("text", "json", "logfmt") to a Formatter value. The comparison is case-insensitive.

func (Formatter) String added in v0.30.0

func (f Formatter) String() string

String returns the formatter name ("text", "json", "logfmt").

type Level

type Level int

Level represents a logging severity level.

const (
	// DebugLevel is the most verbose level.
	DebugLevel Level = iota
	// InfoLevel is the default level.
	InfoLevel
	// WarnLevel is for potentially harmful situations.
	WarnLevel
	// ErrorLevel is for error conditions.
	ErrorLevel
	// FatalLevel is for fatal conditions that terminate the process.
	FatalLevel
)

func ParseLevel

func ParseLevel(s string) (Level, error)

ParseLevel converts a level string ("debug", "info", "warn", "error", "fatal") to a Level value.

func (Level) String

func (l Level) String() string

String returns the level name.

type Leveller added in v0.30.0

type Leveller interface {
	SetLevel(level slog.Level)
}

Leveller is optionally implemented by a Logger whose minimum level can be changed at runtime. GTB's default (Charm-backed) logger implements it; a plain *slog.Logger does not.

type Logger

type Logger interface {
	Debug(msg string, args ...any)
	Info(msg string, args ...any)
	Warn(msg string, args ...any)
	Error(msg string, args ...any)

	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)

	Log(ctx context.Context, level slog.Level, msg string, args ...any)
	LogAttrs(ctx context.Context, level slog.Level, msg string, attrs ...slog.Attr)

	With(args ...any) *slog.Logger
	WithGroup(name string) *slog.Logger

	Enabled(ctx context.Context, level slog.Level) bool
	Handler() slog.Handler
}

Logger is the unified logging interface for GTB. All packages accept this interface instead of a concrete logger type.

Logger is NOT safe for concurrent use unless the underlying backend documents otherwise. The charmbracelet and slog backends provided by this package are both safe for concurrent use. Logger is GTB's logging boundary. Its method set mirrors *slog.Logger exactly, so a *slog.Logger satisfies it directly and can be passed anywhere a Logger is expected — while consumers remain free to supply their own implementation.

Runtime level/format control (SetLevel/SetFormatter) is deliberately NOT part of this interface: those are application concerns, exposed via the optional Leveller and Reformatter interfaces and the SetLevel/SetFormatter helpers, so only the command layer adjusts them. Process termination (Fatal) belongs to command/errorhandling layers, and direct user output (Print) to pkg/output or Cobra writers — neither belongs on the logging boundary.

func NewCharm

func NewCharm(w io.Writer, opts ...CharmOption) Logger

NewCharm returns a Logger backed by charmbracelet/log. This is the default backend for CLI applications, providing coloured, styled terminal output.

Example
package main

import (
	"os"

	"gitlab.com/phpboyscout/go-tool-base/pkg/logger"
)

func main() {
	// Create a charmbracelet-based logger for CLI output.
	l := logger.NewCharm(os.Stderr,
		logger.WithTimestamp(true),
		logger.WithLevel(logger.InfoLevel),
	)

	l.Info("Application started", "version", "1.0.0")
	l.Debug("This won't appear at InfoLevel")
}

func NewNoop

func NewNoop() Logger

NewNoop returns a Logger that discards all output. Useful for tests where log output is irrelevant. It is a *slog.Logger over a discard handler, so it satisfies the Logger interface directly.

Example
package main

import (
	"gitlab.com/phpboyscout/go-tool-base/pkg/logger"
)

func main() {
	// Create a silent logger for tests.
	l := logger.NewNoop()

	// All calls are no-ops — no output produced.
	l.Info("This produces no output")
	l.Error("Neither does this")
}

func NewSlog

func NewSlog(handler slog.Handler) Logger

NewSlog returns a Logger backed by an slog.Handler. Under the slog-first design this is simply slog.New(handler); it exists as a named constructor for ecosystem integration (OpenTelemetry, Datadog, custom handlers).

Any library that implements or bridges to slog.Handler works here:

Zap:     logger.NewSlog(zapslog.NewHandler(zapCore))
Zerolog: logger.NewSlog(slogzerolog.Option{Logger: &zl}.NewHandler())
OTEL:    logger.NewSlog(otelslog.NewHandler(exporter))

For runtime level control, wrap the handler with NewLevelGate before passing it here (or use NewCharmSlog for GTB's default output).

type Reformatter added in v0.30.0

type Reformatter interface {
	SetFormatter(f Formatter)
}

Reformatter is optionally implemented by a Logger whose output format can be changed at runtime.

Jump to

Keyboard shortcuts

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