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 ¶
- Variables
- func Debug(ctx context.Context, msg string, fields ...Field)
- func Debugf(ctx context.Context, format string, args ...any)
- func Error(ctx context.Context, err error, fields ...Field)
- func Fatal(ctx context.Context, msg string, fields ...Field)
- func FatalError(ctx context.Context, err error, fields ...Field)
- func Fatalf(ctx context.Context, format string, args ...any)
- func Info(ctx context.Context, msg string, fields ...Field)
- func Infof(ctx context.Context, format string, args ...any)
- func Warn(ctx context.Context, msg string, fields ...Field)
- func Warnf(ctx context.Context, format string, args ...any)
- func WithFields(ctx context.Context, fields ...Field) context.Context
- func WithLogger(ctx context.Context, logger Logger) context.Context
- type Field
- type Level
- type Logger
- type LoggerMock
- func (l *LoggerMock) Debug(msg string, fields ...Field)
- func (l *LoggerMock) Error(msg string, fields ...Field)
- func (l *LoggerMock) Fatal(msg string, fields ...Field)
- func (l *LoggerMock) Info(msg string, fields ...Field)
- func (l *LoggerMock) Warn(msg string, fields ...Field)
- func (l *LoggerMock) WithFields(fields ...Field) Logger
- type Option
Examples ¶
Constants ¶
This section is empty.
Variables ¶
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 Debugf ¶
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 ¶
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"))
}
Output:
func FatalError ¶
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 ¶
Fatalf formats according to a format specifier and logs the result at fatal level. See Infof for why fields are not accepted here.
func Infof ¶
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 Warnf ¶
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 ¶
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
}
Output:
Types ¶
type Field ¶
Field is one structured key/value pair attached to a log entry. Build one with NewField.
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.
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 ¶
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")
}
Output:
func New ¶
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))
}
Output:
type LoggerMock ¶
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 ¶
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 ¶
WithLevel sets the minimum severity emitted. An unrecognised Level is ignored, leaving the default.
func WithOutput ¶
WithOutput redirects entries to output. Defaults to os.Stderr; a bytes.Buffer makes the output assertable in tests.