Overview
The Logger interface provides convenience methods for each log level, plus core methods for flexible logging. All methods are thread-safe and perform fast-path filtering.
Core Logging Methods
Log
The fundamental logging method that all other methods are built upon.
func (l Logger) Log(level Level, msg string, fields ...Field)
The severity level: TraceLevel (10), DebugLevel (20), InfoLevel (30), WarnLevel (40), ErrorLevel (50), FatalLevel (60)
The log message (can be empty string)
Structured key-value pairs using String(), Int(), Err(), etc.
Performance: Fast-path filtering checks level BEFORE any allocations or processing. Filtered messages cost < 1 ns.
Example:
logger.Log(go_logs.InfoLevel, "User logged in",
go_logs.String("username", "john"),
go_logs.String("ip", "192.168.1.1"),
)
Test Example (from logger_test.go:52):
buf := &bytes.Buffer{}
logger, _ := go_logs.New(go_logs.WithOutput(buf))
logger.Log(go_logs.InfoLevel, "test message", go_logs.String("key", "value"))
output := buf.String()
// Output contains: "test message" and "key=value"
LogCtx
Logs a message with context support for distributed tracing.
func (l Logger) LogCtx(ctx context.Context, level Level, msg string, fields ...Field)
Context containing tracing information (trace_id, span_id, request_id, user_id)
Additional structured fields
Behavior: Automatically extracts fields from context and includes them in the log entry.
Example:
ctx := go_logs.WithTraceID(context.Background(), "trace-789")
ctx = go_logs.WithSpanID(ctx, "span-456")
logger.LogCtx(ctx, go_logs.InfoLevel, "Processing request",
go_logs.String("endpoint", "/api/users"),
)
// Output includes: trace_id=trace-789 span_id=span-456 endpoint=/api/users
Test Example (from logger_test.go:226):
buf := &bytes.Buffer{}
logger, _ := go_logs.New(
go_logs.WithOutput(buf),
go_logs.WithLevel(go_logs.InfoLevel),
)
ctx := context.Background()
logger.LogCtx(ctx, go_logs.InfoLevel, "context message", go_logs.String("key", "value"))
output := buf.String()
// Contains: "context message" and "key=value"
Level-Specific Methods
These convenience methods provide a simpler API for common log levels. Each method internally calls Log() with the appropriate level.
Trace
Logs at TraceLevel (10) for extremely detailed debugging.
func (l Logger) Trace(msg string, fields ...Field)
Use Case: Function entry/exit, loop iterations, extremely verbose debugging.
Example:
logger.Trace("Function called",
go_logs.String("function", "processData"),
go_logs.String("params", "user=123"),
)
Test Example (from logger_test.go:76):
buf := &bytes.Buffer{}
logger, _ := go_logs.New(
go_logs.WithOutput(buf),
go_logs.WithLevel(go_logs.TraceLevel),
)
logger.Trace("test message")
output := buf.String()
// Contains: "TRACE"
Debug
Logs at DebugLevel (20) for diagnostic information.
func (l Logger) Debug(msg string, fields ...Field)
Use Case: Detailed application flow, variable values, debugging.
Example:
logger.Debug("Processing request",
go_logs.String("endpoint", "/api/users"),
go_logs.String("method", "GET"),
go_logs.Int("user_id", 123),
)
Test Example (from logger_test.go:77):
logger.Debug("test message")
output := buf.String()
// Contains: "DEBUG"
Info
Logs at InfoLevel (30) for general informational messages.
func (l Logger) Info(msg string, fields ...Field)
Use Case: Normal application events, startup/shutdown, major milestones.
Example:
logger.Info("Server started",
go_logs.Int("port", 8080),
go_logs.String("version", "1.0.0"),
)
Test Example (from logger_test.go:78):
logger.Info("test message")
output := buf.String()
// Contains: "INFO"
Structured Fields Example (from logger_test.go:244):
err := errors.New("test error")
logger.Info("structured message",
go_logs.String("str", "value"),
go_logs.Int("int", 42),
go_logs.Int64("int64", int64(1234567890)),
go_logs.Float64("float", 3.14),
go_logs.Bool("bool", true),
go_logs.Err(err),
)
output := buf.String()
// Contains: str=value int=42 int64=1234567890 float=3.14 bool=true error=test error
Warn
Logs at WarnLevel (40) for potentially harmful situations.
func (l Logger) Warn(msg string, fields ...Field)
Use Case: Deprecated features, unexpected but handled conditions, high resource usage.
Example:
logger.Warn("High memory usage",
go_logs.Float64("usage_percent", 85.5),
go_logs.Float64("threshold_percent", 80.0),
)
Test Example (from logger_test.go:79):
logger.Warn("test message")
output := buf.String()
// Contains: "WARN"
Error
Logs at ErrorLevel (50) for error events.
func (l Logger) Error(msg string, fields ...Field)
Structured fields (commonly includes Err(error))
Use Case: Recoverable errors, failed operations that don’t crash the app.
Behavior:
- By default, automatically captures caller information (file:line function)
- Can capture stack traces if configured with
WithStackTrace(true)
- Does NOT terminate the application (use Fatal for that)
Example:
logger.Error("Database connection failed",
go_logs.Err(err),
go_logs.String("host", "localhost"),
go_logs.Int("port", 5432),
go_logs.Int("retry_count", 3),
)
Test Example (from logger_test.go:80):
logger.Error("test message")
output := buf.String()
// Contains: "ERROR"
Fatal
Logs at FatalLevel (60) and terminates the application.
func (l Logger) Fatal(msg string, fields ...Field)
Behavior:
- Logs the message with FatalLevel
- Calls
os.Exit(1) to terminate the application
- Deferred functions in the current goroutine are NOT executed
Use Case: Unrecoverable errors that require immediate termination (config load failure, critical resource unavailable).
Fatal() calls os.Exit(1) after logging, terminating the program immediately. Use Error() if you want to continue execution.
Example:
config, err := loadConfig("app.yaml")
if err != nil {
logger.Fatal("Cannot load configuration",
go_logs.Err(err),
go_logs.String("config_file", "app.yaml"),
)
// Program terminates here - this line never executes
}
Source (from logger_impl.go:247):
func (l *LoggerImpl) Fatal(msg string, fields ...Field) {
l.Log(FatalLevel, msg, fields...)
// Terminate application after fatal log
os.Exit(1)
}
Level Filtering Behavior
All logging methods check the current log level threshold before processing:
Example (from logger_test.go:96):
buf := &bytes.Buffer{}
logger, _ := go_logs.New(
go_logs.WithOutput(buf),
go_logs.WithLevel(go_logs.WarnLevel), // Only Warn and above
)
// These are filtered out (no output)
logger.Debug("debug message")
logger.Info("info message")
// These are logged
logger.Warn("warn message")
logger.Error("error message")
output := buf.String()
// Does NOT contain: "debug message" or "info message"
// Contains: "warn message" and "error message"
Structured Fields
All logging methods accept structured fields:
Field Types
go_logs.String("key", "value") // String field
go_logs.Int("count", 42) // Integer field
go_logs.Int64("id", 1234567890) // Int64 field
go_logs.Float64("rate", 3.14) // Float field
go_logs.Bool("enabled", true) // Boolean field
go_logs.Err(err) // Error field (key="error")
go_logs.Any("data", complexStruct) // Any type (uses fmt.Sprintf)
Field Examples
Multiple Fields:
logger.Info("User login",
go_logs.String("username", "john"),
go_logs.String("ip", "192.168.1.1"),
go_logs.Int("login_count", 5),
go_logs.Bool("first_time", false),
)
Error Fields:
if err != nil {
logger.Error("Operation failed",
go_logs.Err(err),
go_logs.String("operation", "database_query"),
go_logs.String("query", "SELECT * FROM users"),
)
}
Fast-Path Filtering
Benchmark (from benchmark_test.go:375):
logger, _ := go_logs.New(
go_logs.WithOutput(io.Discard),
go_logs.WithLevel(go_logs.ErrorLevel), // Filter out Info
)
// Benchmark result: 0.32 ns/op, 0 allocs/op
logger.Info("filtered message")
Result: Messages below the threshold are filtered in < 1 nanosecond with zero allocations.
Without Fields (from benchmark_test.go:354):
logger, _ := go_logs.New(go_logs.WithOutput(io.Discard))
// Benchmark: ~220 ns/op
logger.Info("test message")
With Fields (from benchmark_test.go:363):
// Benchmark: ~250 ns/op
logger.Info("test message",
go_logs.String("key1", "value1"),
go_logs.Int("key2", 42),
go_logs.Float64("key3", 3.14),
)
Thread Safety
All logging methods are thread-safe and can be called concurrently:
Test Example (from logger_test.go:323):
buf := &bytes.Buffer{}
logger, _ := go_logs.New(
go_logs.WithOutput(buf),
go_logs.WithLevel(go_logs.InfoLevel),
)
// Log from 10 concurrent goroutines
for i := 0; i < 10; i++ {
go func(id int) {
logger.Info("goroutine message", go_logs.Int("id", id))
}(i)
}
// All 10 messages are safely written
Dynamic Level Changes
You can change the log level at runtime:
Test Example (from logger_test.go:129):
buf := &bytes.Buffer{}
logger, _ := go_logs.New(
go_logs.WithOutput(buf),
go_logs.WithLevel(go_logs.ErrorLevel),
)
// Filtered at ErrorLevel
logger.Info("initial info")
// Change level to InfoLevel
logger.SetLevel(go_logs.InfoLevel)
// Now logged
logger.Info("new info")
Empty Messages
Logging methods handle empty messages:
Test Example (from logger_test.go:463):
logger.Info("") // Empty message
output := buf.String()
// Still logs with level: "INFO"
No Fields
Logging methods work without any fields:
Test Example (from logger_test.go:477):
logger.Info("message without fields")
output := buf.String()
// Contains: "message without fields"
Common Patterns
Error Handling Pattern
func (s *Service) ProcessData(data []byte) error {
if len(data) == 0 {
s.logger.Error("Empty data received",
go_logs.String("source", "ProcessData"),
)
return errors.New("empty data")
}
s.logger.Debug("Processing data",
go_logs.Int("size_bytes", len(data)),
)
// Process data...
s.logger.Info("Data processed successfully",
go_logs.Int("records", 100),
)
return nil
}
HTTP Request Logging
func (s *Service) LogRequest(r *http.Request, statusCode int, duration time.Duration) {
s.logger.Info("HTTP request",
go_logs.String("method", r.Method),
go_logs.String("path", r.URL.Path),
go_logs.Int("status", statusCode),
go_logs.Float64("duration_ms", duration.Seconds()*1000),
go_logs.String("user_agent", r.UserAgent()),
)
}
Conditional Debug Logging
if logger.GetLevel() >= go_logs.DebugLevel {
// Only perform expensive operation if debug is enabled
debugData := collectExpensiveDebugInfo()
logger.Debug("Debug information",
go_logs.Any("data", debugData),
)
}
Panic Recovery with Fatal
func main() {
logger, err := go_logs.New()
if err != nil {
log.Fatal(err)
}
defer func() {
if r := recover(); r != nil {
logger.Fatal("Panic recovered",
go_logs.Any("panic", r),
go_logs.String("stack", string(debug.Stack())),
)
}
}()
// Application code...
}
See Also