Documentation
¶
Overview ¶
Package logger provides a structured logging abstraction with support for multiple backends, customizable field mappers, and hierarchical logger contexts.
The logger package is designed around three core concepts: loggers, adapters, and mappers. Loggers provide the user-facing API, adapters bridge to concrete logging implementations, and mappers control how special values (names, errors, stack traces) are converted to log fields.
Basic Usage ¶
Create a logger with an adapter and start logging:
import (
"dev.gaijin.team/go/golib/logger"
"dev.gaijin.team/go/golib/logger/bufferadapter"
"dev.gaijin.team/go/golib/fields"
)
func main() {
adapter, _ := bufferadapter.New()
lgr := logger.New(adapter)
lgr.Info("application started", fields.F("version", "1.0.0"))
lgr.Error("operation failed", err, fields.F("operation", "save"))
lgr.Flush()
}
Log Levels ¶
The logger supports five standard log levels, ordered from most to least important:
LevelError - Unrecoverable errors that prevent operation completion LevelWarning - Recoverable errors or concerning situations LevelInfo - Informational messages about application progress (default) LevelDebug - Detailed information useful during development LevelTrace - Very detailed information for diagnosing problems
By default, loggers are created with a maximum level of LevelInfo, meaning Debug and Trace messages are filtered out. Use WithLevel to change this:
lgr := logger.New(adapter, logger.WithLevel(logger.LevelDebug))
Automatic Caller Capture ¶
The logger can automatically capture and include caller information (file and line number) for log entries at or below a specified level threshold. This is useful for tracking the source location of important log messages like errors and warnings without manually adding caller information.
Enable automatic caller capture using WithCallerAtLevel:
// Capture caller for Error and Warning logs only
lgr := logger.New(adapter, logger.WithCallerAtLevel(logger.LevelWarning))
lgr.Error("operation failed", err) // includes caller: "/path/to/file.go:123"
lgr.Info("processing") // no caller information
By default, automatic caller capture is disabled. When enabled, the caller information is formatted and added as a field to log entries using the caller mapper (see Customization section below).
Each level has two methods: one without an error parameter (Info, Warning, Debug, Trace) and one with an error parameter (InfoE, WarningE, DebugE, TraceE). The Error is, obviously, singular and has the error parameter, though it is okay to provide nil as the error.
Child Loggers and Context ¶
Child loggers allow you to build up contextual information that's automatically included in all subsequent log entries. The parent logger remains unaffected:
// Add fields to a child logger
requestLogger := lgr.WithFields(
fields.F("request_id", "abc123"),
fields.F("user_id", 42),
)
requestLogger.Info("processing request") // includes request_id and user_id
lgr.Info("other operation") // does not include request fields
// Add a logger name for component identification
dbLogger := lgr.WithName("database")
dbLogger.Info("connection established") // includes logger-name: database
// Build hierarchical names by chaining WithName calls
// By default, names are joined with ":" separator
serviceLogger := lgr.WithName("service")
handlerLogger := serviceLogger.WithName("handler")
handlerLogger.Info("processing") // includes logger-name: service:handler
// Attach stack trace to debug issues
lgr.WithStackTrace(0).Error("unexpected error", err)
Child logger methods can be chained to combine multiple contexts:
apiLogger := lgr.
WithName("api").
WithFields(fields.F("version", "v2")).
WithStackTrace(0)
Adapters ¶
Adapters bridge the logger abstraction to concrete logging implementations. The logger package doesn't include any output mechanism itself - all log output is delegated to adapters.
Common adapters:
- bufferadapter: In-memory buffer for testing
- zapadapter: Integration with uber-go/zap
- slogadapter: Integration with log/slog (Go 1.21+)
To create a custom adapter, implement the logger.Adapter interface.
Customization with Mappers and Formatters ¶
Though the logger supports concepts of logger names, errors logging and stack traces, etc. - different logging backends may have different conventions for how these values are represented in logs or lack of such functionality at all. For that reason logger abstracts these concepts passing it to the adapters as fields, with the help of mappers.
The logger provides four types of mappers:
Name Mapper: Converts logger names (from WithName) to fields. By default, creates a field with key "logger-name":
lgr := logger.New(adapter, logger.WithNameMapper(func(name string) fields.Field {
return fields.F("component", name) // Use "component" instead of "logger-name"
}))
Name Formatter: Controls how logger names are combined when WithName is called multiple times. By default, uses NameFormatterHierarchical which joins names with ":" separator. Use NameFormatterReplaced to replace names instead:
// Hierarchical naming (default): "service:handler:method"
lgr := logger.New(adapter)
lgr.WithName("service").WithName("handler").WithName("method")
// Replacement naming: only "method"
lgr := logger.New(adapter, logger.WithNameFormatter(logger.NameFormatterReplaced))
lgr.WithName("service").WithName("handler").WithName("method")
Error Mapper: Converts errors to fields. By default, creates a field with key "error" and calls err.Error() to get the string representation. The default mapper includes panic recovery for improperly implemented error types:
lgr := logger.New(adapter, logger.WithErrorMapper(func(err error) fields.Field {
return fields.F("error", fmt.Sprintf("%+v", err)) // Use value of error with %+v
}))
Stack Trace Mapper: Converts stack traces to fields. By default, creates a field with key "stacktrace":
lgr := logger.New(adapter, logger.WithStackTraceMapper(func(st *stacktrace.Stack) fields.Field {
return fields.F("stack", st.String()) // Use "stack" instead of "stacktrace"
}))
Caller Mapper: Converts caller frames (from automatic caller capture) to fields. By default, creates a field with key "caller" formatted as "file:line":
lgr := logger.New(adapter,
logger.WithCallerAtLevel(logger.LevelError), // Enable automatic capture
logger.WithCallerMapper(func(frame stacktrace.Frame) fields.Field {
return fields.F("source", frame.ShortPath()) // Use "source" and short path
}),
)
All mappers and formatters can be customized independently during logger creation.
No-Op Logger ¶
For scenarios where logging is not desired (testing, optional logging), use a no-op logger that performs no operations:
lgr := logger.NewNop()
lgr.Info("this does nothing")
// Check if a logger is no-op
if lgr.IsNop() {
// Skip expensive logging preparation
}
No-op loggers propagate through child logger creation - calling WithFields, WithName, or WithStackTrace on a no-op logger returns another no-op logger.
Context Integration ¶
The logger provides context.Context integration for passing loggers through your application:
import "context"
// Store logger in context
ctx := logger.ToCtx(context.Background(), lgr)
// Retrieve logger from context
lgr, ok := logger.FromCtx(ctx)
if !ok {
// Handle missing logger
}
// Retrieve logger or get no-op if missing
lgr := logger.FromCtxOrNop(ctx)
This allows passing loggers to functions without explicit parameters while maintaining type safety.
Error Logging Adapter ¶
For integration with interfaces that expect a simple error logging function, use NewErrorLogger:
import "dev.gaijin.team/go/golib/e"
// Create error logger with specific level
errorLogger := logger.NewErrorLogger(lgr, logger.LevelError)
// Use as e.ErrorLogger
var logFn e.ErrorLogger = errorLogger
logFn("operation failed", err, fields.F("operation", "save"))
This is useful when working with libraries that accept error logging callbacks or when implementing interfaces with logging requirements.
Flushing ¶
Some logging backends are buffered and require flushing in order to ensure all log entries are written out. Logger provides a Flush method to trigger backend flush, though it is application responsibility to call it at appropriate times:
defer lgr.Flush()
For unbuffered adapters, Flush is a no-op. It's safe-by-contract to call Flush even if the adapter doesn't buffer its output.
Index ¶
- Constants
- func DefaultCallerMapper(frame stacktrace.Frame) fields.Field
- func DefaultErrorMapper(err error) (f fields.Field)
- func DefaultNameMapper(name string) fields.Field
- func DefaultStackTraceMapper(st *stacktrace.Stack) fields.Field
- func IsEqual(l1, l2 Logger) bool
- func NameFormatterHierarchical(prev, next string) string
- func NameFormatterReplaced(_, next string) string
- func NewErrorLogger(lgr Logger, level int) e.ErrorLogger
- func ToCtx(ctx context.Context, logger Logger) context.Context
- type Adapter
- type CtxKey
- type Logger
- func (l Logger) Debug(msg string, fs ...fields.Field)
- func (l Logger) DebugE(msg string, err error, fs ...fields.Field)
- func (l Logger) Error(msg string, err error, fs ...fields.Field)
- func (l Logger) Flush() error
- func (l Logger) Info(msg string, fs ...fields.Field)
- func (l Logger) InfoE(msg string, err error, fs ...fields.Field)
- func (l Logger) IsNop() bool
- func (l Logger) IsZero() bool
- func (l Logger) Log(level int, msg string, err error, fs ...fields.Field)
- func (l Logger) Trace(msg string, fs ...fields.Field)
- func (l Logger) TraceE(msg string, err error, fs ...fields.Field)
- func (l Logger) Warning(msg string, fs ...fields.Field)
- func (l Logger) WarningE(msg string, err error, fs ...fields.Field)
- func (l Logger) WithFields(fs ...fields.Field) Logger
- func (l Logger) WithName(name string) Logger
- func (l Logger) WithStackTrace(skip int) Logger
- type Option
- func WithCallerAtLevel(level int) Option
- func WithCallerMapper(fn func(frame stacktrace.Frame) fields.Field) Option
- func WithErrorMapper(fn func(err error) fields.Field) Option
- func WithLevel(level int) Option
- func WithNameFormatter(fn func(prev, next string) string) Option
- func WithNameMapper(fn func(name string) fields.Field) Option
- func WithStackTraceMapper(fn func(st *stacktrace.Stack) fields.Field) Option
Constants ¶
const ( LevelError = iota*10 + 10 LevelWarning LevelInfo LevelDebug LevelTrace )
Variables ¶
This section is empty.
Functions ¶
func DefaultCallerMapper ¶ added in v0.8.0
func DefaultCallerMapper(frame stacktrace.Frame) fields.Field
DefaultCallerMapper is the default mapper for converting caller frames to fields. It creates a field with key "caller" and formats the frame as "file:line".
Example output: "/path/to/logger.go:123".
func DefaultErrorMapper ¶ added in v0.8.0
DefaultErrorMapper is the default mapper for converting errors to fields. It creates a field with key "error" and the error message string as value.
The mapper calls err.Error() to obtain the string representation, which may panic for improperly implemented error types (e.g., nil pointer with value receiver). When this occurs, the mapper recovers and logs either "<nil>" or "<PANIC=...>" with the panic details.
Example output:
err := nil // "<nil>"
err := errors.New("failed") // "failed"
err := (*CustomErr)(nil) // "<nil>" (typed nil)
err := &badErr{} // "<PANIC=...>" if Error() panics
func DefaultNameMapper ¶ added in v0.8.0
DefaultNameMapper is the default mapper for converting logger names to fields. It creates a field with key "logger-name" and the name as value.
func DefaultStackTraceMapper ¶ added in v0.8.0
func DefaultStackTraceMapper(st *stacktrace.Stack) fields.Field
DefaultStackTraceMapper is the default mapper for converting stack traces to fields. It creates a field with key "stacktrace" and the stack trace string representation as value.
The mapper calls st.String() to obtain the formatted stack trace string.
func IsEqual ¶ added in v0.8.0
IsEqual returns true if two loggers are functionally equal. Two loggers are considered equal if they have the same maxLevel, adapter, mappers, name, and nameFormatter.
func NameFormatterHierarchical ¶ added in v0.8.0
NameFormatterHierarchical is the default formatter for combining new logger name with existing, using `:` separator (e.g., "parent:child").
func NameFormatterReplaced ¶ added in v0.8.0
NameFormatterReplaced is a name formatter that always replaces the previous name with the new name.
func NewErrorLogger ¶ added in v0.6.0
func NewErrorLogger(lgr Logger, level int) e.ErrorLogger
NewErrorLogger creates a new e.ErrorLogger that logs errors with the given log-level.
This function is useful for scenarios where the log level is configured after logger creation, such as in middleware or when adapting to interfaces that expect an error logging function rather than a full logger.
Types ¶
type Adapter ¶
type Adapter interface {
// Log logs a message with the provided level, message, and fields.
//
// The level parameter is one of the logger level constants (LevelError,
// LevelWarning, LevelInfo, LevelDebug, LevelTrace) or a custom level value.
//
// The fs parameter contains zero or more fields that should be attached to the
// log entry. This may include both fields from WithFields calls and fields
// passed directly to the Log call.
Log(level int, msg string, fs ...fields.Field)
// WithFields returns a new adapter instance with the given fields attached.
// The returned adapter should include these fields in all subsequent Log calls.
//
// This method must return a new adapter instance and must not modify the
// original adapter.
//
// Rationale:
//
// Although it might look unnecessary to alter the adapter instead of carrying
// fields in the logger itself, this design allows adapters to optimize field
// handling based on their backend capabilities.
//
// Some logging backends may support efficient field storage and reuse, while
// others may require fields to be passed with each log call. For example zap
// pre-encodes fields and then reuses encoded values, instead of encoding them
// each time a log entry is created.
//
// By attaching fields to the adapter, we enable adapters to implement the
// most efficient strategy for their backend.
WithFields(fs ...fields.Field) Adapter
// Flush flushes any buffered log entries to the output. This is a no-op for
// non-buffered loggers.
//
// It is the application's responsibility to call [Logger.Flush] before exiting
// to ensure all log entries are written. Adapters should return any errors
// that occur during flushing.
Flush() error
}
Adapter is an interface that allows to encapsulate any logger backend inside Logger.
Adapters provide a bridge between the logger abstraction and concrete logging implementations (zap, logrus, slog, etc.). They handle the translation of logger's generic API calls to backend-specific operations.
For examples of adapter implementations, see the adapters subpackages.
type Logger ¶
type Logger struct {
// contains filtered or unexported fields
}
func FromCtx ¶
FromCtx attempts to extract logger from the context. In case logger is not present, or it is not of the correct type, it returns empty logger and false.
func FromCtxOrNop ¶
FromCtxOrNop extracts logger from the context. In case logger is not present, or it is not of the correct type, it returns a nop logger.
func New ¶
New creates new Logger with maximum log-level set to LevelInfo and default mappers. To change log level and other behaviours use options.
Panics if adapter is nil. To create no-op logger use NewNop.
func NewNop ¶
func NewNop() Logger
NewNop creates a new logger that does nothing. Methods creating child-loggers will also create no-op loggers.
func (Logger) Debug ¶
Debug logs a message with the LevelDebug log-level.
Use Debug to log detailed information that is useful during development and debugging.
func (Logger) DebugE ¶
DebugE logs a message with the LevelDebug log-level and the provided error.
Use DebugE to log detailed information that is useful during development and debugging along with an error.
func (Logger) Error ¶
Error logs a message with the LevelError log-level.
Use Error to log any unrecoverable error, such as a database query failure where the application cannot continue. It's OK to pass nil as the error. To attach a stack trace, use Logger.WithStackTrace.
func (Logger) Flush ¶
Flush flushes the underlying logger adapter, allowing buffered adapters to write logs to the output.
It is the application's responsibility to call Logger.Flush before exiting.
func (Logger) Info ¶
Info logs a message with the LevelInfo log-level.
Use Info to log informational messages that highlight the progress of the application.
func (Logger) InfoE ¶
InfoE logs a message with the LevelInfo log-level and the provided error.
Use InfoE to log informational messages that highlight the progress of the application along with an error.
func (Logger) IsNop ¶
IsNop returns true if the logger is a no-op logger.
A no-op logger is created via NewNop and performs no operations. All logging methods and child logger creation methods return immediately without calling the underlying adapter. This is useful for testing or when a logger is required but logging is not desired.
func (Logger) IsZero ¶
IsZero returns true if the logger is a zero-value structure.
A zero-value logger is an uninitialized Logger struct that has not been created via New or NewNop. Zero-value loggers should not be used, but in rare cases it is required to check if value is not initialized.
func (Logger) Log ¶
Log logs a message with the given log-level, optional error, and fields.
The level parameter expected to be one of the logger level constants (LevelError, LevelWarning, LevelInfo, LevelDebug, LevelTrace). Usage of custom levels is also supported, but heavily depends on the underlying adapter capabilities.
If the level is higher than the logger's maximum level (set via WithLevel), the message is not logged. If err is not nil, it is converted to a field using the error mapper and appended to the provided fields.
For no-op loggers, this method returns immediately without any operation.
func (Logger) Trace ¶
Trace logs a message with the LevelTrace log-level.
Use Trace to log very detailed information, typically of interest only when diagnosing problems.
func (Logger) TraceE ¶
TraceE logs a message with the LevelTrace log-level and the provided error.
Use TraceE to log very detailed information, typically of interest only when diagnosing problems along with an error.
func (Logger) Warning ¶
Warning logs a message with the LevelWarning log-level.
Use Warning to log any recoverable error or concerning situation that doesn't prevent the application from continuing, such as a deprecated API usage or a retry-able failure. For warnings with an error, use Logger.WarningE.
func (Logger) WarningE ¶
WarningE logs a message with the LevelWarning log-level and the provided error.
Use WarningE to log any recoverable error, such as an error during a remote API call where the service did not respond and the application will retry.
func (Logger) WithFields ¶
WithFields returns a new child logger with the given fields attached to it.
The returned child logger will include these fields in all subsequent log entries, in addition to any fields already attached to the parent logger. The parent logger remains unaffected.
For no-op loggers, this method returns the same no-op logger.
func (Logger) WithName ¶
WithName returns a new child logger with the given name assigned to it.
The name is converted to a field using the name mapper (by default, creates a field with key "logger-name") and attached to all subsequent log entries from this logger. This is useful for identifying which component or module generated a log entry.
When called multiple times on a logger chain, the name formatter (set via WithNameFormatter) determines how names are combined. By default, names are concatenated with a `:` separator (e.g., "parent:child").
The parent logger remains unaffected. For no-op loggers, this method returns the same no-op logger.
func (Logger) WithStackTrace ¶
WithStackTrace returns a new child-logger with the stack trace attached to it. The skip parameter defines how many stack frames to skip when capturing the stack trace. skip=0 means the caller of WithStackTrace is included in the stack trace.
For no-op loggers, this method returns the same no-op logger.
type Option ¶ added in v0.8.0
type Option func(*Logger)
Option is a functional option for configuring Logger behavior.
func WithCallerAtLevel ¶ added in v0.8.0
WithCallerAtLevel enables automatic caller information capture for log entries with level less or equal passed threshold.
When enabled, the logger will automatically capture and include caller information (formatted as "/path/to/file:line") as a field in log entries whose level is less or equal the threshold. For example, WithCallerAtLevel(LevelWarning) will add caller information to Error and Warning logs, but no others.
By default, automatic caller capture is disabled. Use this option when you need to track the source location of important log messages like errors and warnings.
func WithCallerMapper ¶ added in v0.8.0
func WithCallerMapper(fn func(frame stacktrace.Frame) fields.Field) Option
WithCallerMapper sets a custom caller mapper for the logger.
The caller mapper controls how caller frames (captured via automatic caller capture with WithCallerAtLevel) are converted to fields. By default, DefaultCallerMapper is used, which creates fields with the key "caller" and formats frames as "file:line".
Custom mappers can change the field key, format caller information differently, or include additional frame details like function names.
func WithErrorMapper ¶ added in v0.8.0
WithErrorMapper sets a custom error mapper for the logger.
The error mapper controls how errors passed to logging methods are converted to fields. By default, DefaultErrorMapper is used, which creates fields with the key "error" and calls err.Error() to get the string representation.
Custom mappers can change the field key, format errors differently, or add additional error information (such as error types or stack traces from error implementations that include them).
func WithLevel ¶ added in v0.8.0
WithLevel sets the maximum log-level for the logger.
Log messages with levels higher than the specified level will be ignored (not passed to the underlying adapter).
func WithNameFormatter ¶ added in v0.8.0
WithNameFormatter sets a custom name formatter for the logger.
The name formatter controls how logger names are combined when Logger.WithName is called multiple times on a logger chain. By default, NameFormatterHierarchical is used.
Custom formatters can implement hierarchical naming (e.g., "parent:child") or other naming strategies. The formatter receives the previous name (empty string if no name was set) and the next name, and returns the final name.
func WithNameMapper ¶ added in v0.8.0
WithNameMapper sets a custom name mapper for the logger.
The name mapper controls how logger names (set via Logger.WithName) are converted to fields. By default, DefaultNameMapper is used, which creates fields with the key "logger-name".
func WithStackTraceMapper ¶ added in v0.8.0
func WithStackTraceMapper(fn func(st *stacktrace.Stack) fields.Field) Option
WithStackTraceMapper sets a custom stack trace mapper for the logger.
The stack trace mapper controls how stack traces (captured via Logger.WithStackTrace) are converted to fields. By default, DefaultStackTraceMapper is used, which creates fields with the key "stacktrace" and formats the stack as a string.
Custom mappers can change the field key, format stack traces differently, or limit the number of frames included.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package bufferadapter provides an in-memory logger adapter for testing.
|
Package bufferadapter provides an in-memory logger adapter for testing. |
|
Package logrusadapter provides a logger adapter for the logrus logging library.
|
Package logrusadapter provides a logger adapter for the logrus logging library. |
|
Package slogadapter provides a logger adapter for the standard library's slog logging package.
|
Package slogadapter provides a logger adapter for the standard library's slog logging package. |
|
Package zapadapter provides a logger adapter for the zap logging library.
|
Package zapadapter provides a logger adapter for the zap logging library. |