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

log

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Sep 26, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package log provides a leveled, field-oriented logger that travels through the request context.

The Logger interface is deliberately narrow so the backend can be replaced without touching callers. The implementation returned by New is backed by logrus.

There are two ways to log. The Logger methods take an explicit receiver:

logger := log.New(log.WithJSONFormat(), log.WithLevel(log.DebugLevel))
logger.Info("server started", log.NewField("port", 8080))

The package-level helpers instead pull the logger out of a context, which is how the rest of the devkit logs - httpserver and the Kafka consumer both put a logger in the context before invoking user code:

log.Info(ctx, "order accepted", log.NewField("order-id", id))

When the context carries no logger, FromContext falls back to a logger built with the package defaults rather than returning nil.

Fields accumulate. WithFields derives a context whose logger carries extra fields, so a value attached once at the edge shows up on every later entry:

ctx = log.WithFields(ctx, log.NewField("trace-id", traceID))

Error is the entry point for errors: it extracts the stack trace from errors wrapped with github.com/pkg/errors and appends it to the message. That is why this repository wraps with errors.Wrap and errors.WithStack rather than the standard library's %w verb.

The formatting helpers - Infof, Debugf, Warnf and Fatalf - end in "format string, args ...any" so that go vet checks the verbs against the arguments. They do not accept fields; combine fmt.Sprintf with Info when an entry needs both.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	WarningTypeCritical = NewField("warning-type", "critical")
)

WarningTypeCritical marks a warning that should page someone, as opposed to one that is only informative. The consumer attaches it when a message lands in the dead letter topic.

Functions

func Debug

func Debug(ctx context.Context, msg string, fields ...Field)

Debug logs msg at debug level using the logger in ctx.

func Debugf

func Debugf(ctx context.Context, format string, args ...any)

Debugf formats according to a format specifier and logs the result at debug level. See Infof for why fields are not accepted here.

func Error

func Error(ctx context.Context, err error, fields ...Field)

Error logs err at error level using the logger in ctx.

When err carries a stack trace - which errors wrapped with github.com/pkg/errors do - the trace is appended to the message. A nil err logs nothing.

Example

Error extracts the stack trace recorded by github.com/pkg/errors and appends it to the message. An error wrapped with the standard library's %w has no stack to extract, which is why this repository wraps with errors.Wrap.

package main

import (
	"context"
	"os"

	"github.com/diegodesousas/go-devkit/pkg/log"
	"github.com/pkg/errors"
)

func main() {
	ctx := log.WithLogger(context.Background(), log.New(log.WithJSONFormat()))

	err := errors.Wrap(os.ErrNotExist, "loading config")

	log.Error(ctx, err, log.NewField("path", "/etc/app.yaml"))
}

func Fatal

func Fatal(ctx context.Context, msg string, fields ...Field)

Fatal logs msg at fatal level and then terminates the process.

func FatalError

func FatalError(ctx context.Context, err error, fields ...Field)

FatalError logs err at fatal level and then terminates the process. Unlike Error it does not extract a stack trace, and it panics on a nil err.

func Fatalf

func Fatalf(ctx context.Context, format string, args ...any)

Fatalf formats according to a format specifier and logs the result at fatal level. See Infof for why fields are not accepted here.

func Info

func Info(ctx context.Context, msg string, fields ...Field)

Info logs msg at info level using the logger in ctx.

func Infof

func Infof(ctx context.Context, format string, args ...any)

Infof formats according to a format specifier and logs the result at info level. The signature ends in "format string, args ...any" so that go vet recognises it as a printf wrapper and checks the verbs against the arguments. Use Info with fmt.Sprintf when the entry also needs fields.

func Warn

func Warn(ctx context.Context, msg string, fields ...Field)

Warn logs msg at warn level using the logger in ctx.

func Warnf

func Warnf(ctx context.Context, format string, args ...any)

Warnf formats according to a format specifier and logs the result at warn level. See Infof for why fields are not accepted here.

func WithFields

func WithFields(ctx context.Context, fields ...Field) context.Context

WithFields returns a context whose logger carries fields in addition to whatever it already had, so a value attached once at the edge appears on every later entry.

Example

Fields attached to the context appear on every later entry, which is what makes a trace id usable for correlation.

package main

import (
	"context"

	"github.com/diegodesousas/go-devkit/pkg/log"
)

func main() {
	ctx := log.WithLogger(context.Background(), log.New(log.WithJSONFormat()))
	ctx = log.WithFields(ctx, log.NewField("trace-id", "01H..."))

	log.Info(ctx, "charging card")
	log.Info(ctx, "card charged") // also carries trace-id
}

func WithLogger

func WithLogger(ctx context.Context, logger Logger) context.Context

WithLogger returns a context carrying logger, to be read back with FromContext.

Types

type Field

type Field struct {
	Key   string
	Value any
}

Field is one structured key/value pair attached to a log entry. Build one with NewField.

func NewField

func NewField(key string, value any) Field

NewField builds a Field.

type Level

type Level uint

Level is the minimum severity a logger emits, selected with WithLevel.

The values are identifiers, not an ordering: they do not increase with severity, so comparing two Levels says nothing about which is more verbose.

const (
	InfoLevel Level = iota
	ErrorLevel
	WarnLevel
	DebugLevel
)

The levels a logger can be configured with.

type Logger

type Logger interface {
	Info(msg string, fields ...Field)
	Error(msg string, fields ...Field)
	Debug(msg string, fields ...Field)
	Warn(msg string, fields ...Field)
	Fatal(msg string, fields ...Field)
	WithFields(fields ...Field) Logger
}

Logger writes leveled entries with structured fields.

The interface is intentionally small so the backend behind it can be replaced without changing callers. New returns the logrus-backed implementation, and LoggerMock implements it for tests.

Fatal writes the entry and then terminates the process.

func FromContext

func FromContext(ctx context.Context) Logger

FromContext returns the Logger carried by ctx.

When ctx carries none it returns a logger built with the package defaults, so the result is never nil - but also never the application configuration. A missing Logger middleware shows up as entries in text format on stderr.

Example

The package-level helpers read the logger out of the context, which is how a handler logs without being handed one.

package main

import (
	"context"

	"github.com/diegodesousas/go-devkit/pkg/log"
)

func main() {
	ctx := log.WithLogger(context.Background(), log.New(log.WithJSONFormat()))

	log.Info(ctx, "order accepted", log.NewField("order-id", "42"))

	// The Logger itself is reachable when a method on it is needed directly.
	log.FromContext(ctx).Warn("stock is low")
}

func New

func New(options ...Option) Logger

New returns a Logger writing to stderr at info level in text format.

Example

The output carries a timestamp, so this example is compiled but not run.

package main

import (
	"os"

	"github.com/diegodesousas/go-devkit/pkg/log"
)

func main() {
	logger := log.New(
		log.WithLevel(log.DebugLevel),
		log.WithJSONFormat(),
		log.WithOutput(os.Stdout),
	)

	logger.Info("server started", log.NewField("port", 8080))
}

type LoggerMock

type LoggerMock struct {
	mock.Mock
}

LoggerMock is a testify mock implementing Logger.

The leveled methods spread their fields as separate arguments, so an expectation lists them one by one:

m.On("Info", "started", log.NewField("port", 8080))

WithFields is the exception: it takes the fields as a single slice argument.

func (*LoggerMock) Debug

func (l *LoggerMock) Debug(msg string, fields ...Field)

func (*LoggerMock) Error

func (l *LoggerMock) Error(msg string, fields ...Field)

func (*LoggerMock) Fatal

func (l *LoggerMock) Fatal(msg string, fields ...Field)

func (*LoggerMock) Info

func (l *LoggerMock) Info(msg string, fields ...Field)

func (*LoggerMock) Warn

func (l *LoggerMock) Warn(msg string, fields ...Field)

func (*LoggerMock) WithFields

func (l *LoggerMock) WithFields(fields ...Field) Logger

type Option

type Option func(*settings)

Option configures a Logger built by New.

Unlike the options elsewhere in this repository, these mutate a settings pointer. That is historical - do not copy the shape into new packages.

func WithExitFunc

func WithExitFunc(exitFunc func(code int)) Option

WithExitFunc replaces the function Fatal calls after writing its entry. Defaults to os.Exit; substitute it to keep a test process alive.

func WithJSONFormat

func WithJSONFormat() Option

WithJSONFormat switches the output from text to JSON, which is what a log collector wants.

func WithLevel

func WithLevel(level Level) Option

WithLevel sets the minimum severity emitted. An unrecognised Level is ignored, leaving the default.

func WithOutput

func WithOutput(output io.Writer) Option

WithOutput redirects entries to output. Defaults to os.Stderr; a bytes.Buffer makes the output assertable in tests.

Jump to

Keyboard shortcuts

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