The structured logger follows a pipeline architecture where log events flow through multiple stages before reaching their destination.
┌─────────────┐
│ Application │
└──────┬──────┘
│
▼
┌─────────────────────────────────────┐
│ Logger API │
│ Debug/Info/Warn/Error/Fatal │
└──────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Entry Builder │
│ - Timestamp │
│ - Level │
│ - Message │
│ - Fields (key/value) │
│ - Caller (optional) │
└──────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Level Filter │
│ Skip if level < threshold │
└──────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Formatter │
│ Convert Entry → []byte │
│ (JSON, Text, Custom) │
└──────┬──────────────────────────────┘
│
├─── Sync Mode ───┐
│ │
│ ▼
│ ┌───────────────┐
│ │ Sink Dispatch │
│ └───────┬───────┘
│ │
└─ Async Mode ────┤
│
▼
┌───────────────┐
│ Async Worker │
│ (Queue) │
└───────┬───────┘
│
▼
┌───────────────┐
│ Sink Dispatch │
└───────┬───────┘
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│Console │ │ File │ │ Custom │
│ Sink │ │ Sink │ │ Sink │
└────────┘ └────────┘ └────────┘
- Public interface for application code
- Methods:
Debug(),Info(),Warn(),Error(),Fatal() - Accepts message and variadic key/value pairs
- Manages lifecycle (initialization, close)
- Creates structured log entry
- Captures timestamp (RFC3339)
- Converts level to string
- Parses key/value fields into map
- Optionally captures caller info (file:line)
- Compares entry level against logger threshold
- Skips processing if below minimum level
- Enables efficient filtering without formatting overhead
- Converts Entry struct to byte array
- Default: JSON formatter
- Extensible via Formatter interface
- Handles field serialization
- Buffered channel queue
- Background goroutine processes entries
- Non-blocking enqueue
- Graceful shutdown with flush
- Fan-out to multiple sinks
- Writes formatted data to each sink
- Error handling per sink (doesn't fail all on one error)
- Final output destinations
- Console: stdout
- File: append mode with file handle
- Custom: implement Sink interface
log.Info("msg", "key", "val")
→ Entry created
→ Level checked
→ Formatted to JSON
→ Written to all sinks immediately
→ Returns to caller
log.Info("msg", "key", "val")
→ Entry created
→ Level checked
→ Formatted to JSON
→ Enqueued to channel
→ Returns to caller immediately
Background worker:
→ Dequeues entry
→ Writes to all sinks
Formatterinterface allows custom output formatsSinkinterface allows custom destinations- Easy to add new implementations without modifying core
- Entry creation separate from formatting
- Formatting separate from output
- Each component has single responsibility
Configstruct provides unified configuration- Easier to customize than multiple constructor parameters
- Backward compatible with simple constructors
- Caller tracing: disabled by default (performance)
- Async mode: opt-in for high throughput
- Multiple sinks: single sink by default
- Invalid fields logged as warnings, not errors
- Sink write failures don't crash logger
- Async mode flushes queue on close
- Latency: Blocking I/O on every log call
- Throughput: ~68K logs/sec
- Use case: Low-volume logging, simple applications
- Latency: Non-blocking (enqueue only)
- Throughput: ~100K logs/sec
- Use case: High-volume logging, performance-critical paths
- Trade-off: Logs may be lost if process crashes before flush
- Sync: Minimal (no buffering)
- Async: BufferSize × average entry size
- Typical: 100-500 buffer size = ~50-250KB
type MyFormatter struct{}
func (f *MyFormatter) Format(entry *logger.Entry) ([]byte, error) {
// Custom formatting logic
}type MySink struct{}
func (s *MySink) Write(data []byte) error {
// Custom output logic
}
func (s *MySink) Close() error {
// Cleanup
}config := logger.Config{
Formatter: &MyFormatter{},
Sinks: []logger.Sink{&MySink{}},
}
log := logger.NewWithConfig(config)- Logger: Safe for concurrent use (no shared mutable state)
- Async Worker: Channel-based synchronization
- Sinks: Responsibility of sink implementation
- ConsoleSink: Safe (stdout is synchronized)
- FileSink: Safe (os.File operations are atomic)
- Custom: Must implement own synchronization
- Invalid field keys → Warning to stderr, skip pair
- Sink write failure → Warning to stderr, continue to next sink
- Format failure → Warning to stderr, skip log
log.Fatal()→ Logs message then callsos.Exit(1)- File sink creation failure → Returns error to caller
-
Contextual Logging: Child loggers with inherited fields
With()method creates immutable child loggers- Fields merge at log time
- Nested contexts supported
-
Log Rotation: Size-based automatic file rotation
- Configurable max size and backup count
- Thread-safe rotation
- Automatic backup shifting
-
Performance Benchmarks: Comprehensive benchmark suite
- Measures all major operations
- Identifies performance characteristics
- Guides optimization decisions
- Sampling: Log only N% of entries at high volume
- Batching: Group multiple entries for efficient I/O
- Compression: Compress old log files
- Remote Sinks: HTTP, Kafka, CloudWatch, etc.
- Context Integration: Extract fields from context.Context
- Structured Errors: Better error type handling