Design Logger (LLD)
1. Requirements
-
Functional
- Accept log records with level, message, timestamp, and structured fields.
- Route output to multiple sinks (console, file, remote) with independent formats.
- Filter below configured minimum level per sink or globally.
-
Non-Functional
- Thread-safe emission from many goroutines or async tasks without corrupt lines.
- Optional async offload so callers rarely block (bounded queue).
-
Assumptions / Out of Scope
- Log aggregation at cluster level is separate; focus on library API.
2. Core Entities
| Entity | Responsibility | Key Attributes |
|---|---|---|
| Logger | Public API | minimumLevel, handlers |
| LogRecord | One event | level, message, timestamp, fields |
| Handler | Output pipeline stage | next filter |
| Formatter | Layout string | pattern |
| Sink | IO destination | writer |
| LevelFilter | Drops low-priority records | threshold |
3. Class Diagram
Loading diagram…
4. State / Sequence Diagram (where relevant)
Loading diagram…
5. Design Patterns Applied
- Chain of Responsibility — Handlers chained or iterated with filters. Chain of Responsibility pattern.
- Decorator — Add context fields wrapping inner logger. Decorator pattern.
- Abstract Factory — Build standard handler sets for prod vs dev. Abstract Factory pattern.
6. Implementation
Go
package logging
type Level int
type LogRecord struct {
Level Level
Message string
Timestamp time.Time
Fields map[string]any
}
type Handler interface {
Handle(record LogRecord)
}
type Logger struct {
MinLevel Level
Handlers []Handler
}
func (logger *Logger) Log(level Level, message string, fields map[string]any) { /* ... */ }JavaScript
class LogRecord {
constructor({ level, message, timestamp, fields }) {
this.level = level;
this.message = message;
this.timestamp = timestamp;
this.fields = fields;
}
}
class Logger {
constructor({ minimumLevel, handlers }) {
this.minimumLevel = minimumLevel;
this.handlers = handlers;
}
log({ level, message, fields }) { /* ... */ }
}7. Concurrency / Thread Safety
- Collisions: Concurrent
Logcalls writing same file or stdout interleaving bytes. - Granularity: Mutex inside each
Sinkor channel feeding single writer goroutine in Go. - JavaScript: Async sinks should serialize writes with a queue.
8. Extensibility & Followups
- Sampling for high-volume debug lines.
- Ring buffer for recent errors attached to crash reports.
- Edge cases: sink backpressure, disk full, circular log rotation policy.
Last updated on
Spotted something unclear or wrong on this page?