The EventLogger Module provides structured logging capabilities for Observer pattern events in Modular applications. It acts as an Observer that can be registered with any Subject to log events to various output targets including console, files, and syslog.
- Multiple Output Targets: Support for console, file, and syslog outputs
- Configurable Log Levels: DEBUG, INFO, WARN, ERROR with per-target configuration
- Multiple Output Formats: Text, JSON, and structured formats
- Event Type Filtering: Log only specific event types
- Async Processing: Non-blocking event processing with buffering
- Log Rotation: Automatic file rotation for file outputs
- Error Handling: Graceful handling of output target failures
- Observer Pattern Integration: Seamless integration with ObservableApplication
import (
"github.com/GoCodeAlone/modular"
"github.com/GoCodeAlone/modular/modules/eventlogger"
)
// Register the eventlogger module with your Modular application
app.RegisterModule(eventlogger.NewModule())The eventlogger module can be configured using the following options:
eventlogger:
enabled: true # Enable/disable event logging
logLevel: INFO # Minimum log level (DEBUG, INFO, WARN, ERROR)
format: structured # Default output format (text, json, structured)
bufferSize: 100 # Event buffer size for async processing
flushInterval: 5s # How often to flush buffered events
includeMetadata: true # Include event metadata in logs
includeStackTrace: false # Include stack traces for error events
startupSync: false # Emit startup operational events synchronously (no async delay)
shutdownEmitStopped: true # Emit logger.stopped operational event on Stop()
shutdownDrainTimeout: 2s # Max time to drain buffered events on Stop (0 = wait forever)
eventTypeFilters: # Optional: Whitelist - Only log specific event types
- module.registered
- service.registered
- application.started
eventTypeBlacklist: # Optional: Blacklist - Exclude specific event types (overrides whitelist)
- com.modular.eventlogger.event.received
- com.modular.eventlogger.output.success
excludeOwnEvents: false # Automatically exclude EventLogger's own operational events
outputTargets:
- type: console # Console output
level: INFO
format: structured
console:
useColor: true
timestamps: true
- type: file # File output with rotation
level: DEBUG
format: json
file:
path: /var/log/modular-events.log
maxSize: 100 # MB
maxBackups: 5
maxAge: 30 # days
compress: true
- type: syslog # Syslog output
level: WARN
format: text
syslog:
network: unix
address: ""
tag: modular
facility: userimport (
"github.com/GoCodeAlone/modular"
"github.com/GoCodeAlone/modular/modules/eventlogger"
)
func main() {
// Create application with observer support
app := modular.NewObservableApplication(configProvider, logger)
// Register event logger module
app.RegisterModule(eventlogger.NewModule())
// Initialize application - event logger will auto-register as observer
if err := app.Init(); err != nil {
log.Fatal(err)
}
// Now all application events will be logged
app.RegisterModule(&MyModule{}) // Logged as module.registered event
app.Start() // Logged as application.started event
}// Get the event logger service
var eventLogger *eventlogger.EventLoggerModule
err := app.GetService("eventlogger.observer", &eventLogger)
if err != nil {
log.Fatal(err)
}
// Register with any subject for specific event types
err = subject.RegisterObserver(eventLogger, "user.created", "order.placed")
if err != nil {
log.Fatal(err)
}Whitelist Filtering - Only log specific event types:
config := &eventlogger.EventLoggerConfig{
EventTypeFilters: []string{
"module.registered",
"service.registered",
"application.started",
"application.failed",
},
}Blacklist Filtering - Exclude specific event types:
config := &eventlogger.EventLoggerConfig{
// Log everything except these specific events
EventTypeBlacklist: []string{
"com.modular.eventlogger.event.received",
"com.modular.eventlogger.event.processed",
"com.modular.eventlogger.output.success",
},
}Automatic Self-Event Exclusion - Prevent event amplification by excluding EventLogger's own operational events:
config := &eventlogger.EventLoggerConfig{
// Automatically exclude EventLogger operational events
ExcludeOwnEvents: true,
}Combined Filtering - Use whitelist and blacklist together:
config := &eventlogger.EventLoggerConfig{
// First apply whitelist
EventTypeFilters: []string{
"user.*", // Allow all user events
"order.*", // Allow all order events
"application.*", // Allow all application events
},
// Then apply blacklist (takes precedence)
EventTypeBlacklist: []string{
"user.debug", // Exclude user.debug even though user.* is whitelisted
},
}The EventLogger emits operational events about its own processing (event.received, event.processed, output.success, etc.). In high-volume scenarios, these operational events can cause event amplification - where logging 1,000 business events generates 3,000+ operational events, which themselves get logged, creating a feedback loop.
Option 1: Use excludeOwnEvents (Recommended for most cases)
eventlogger:
enabled: true
excludeOwnEvents: true # Simple one-line solutionOption 2: Use Blacklist (For fine-grained control)
eventlogger:
enabled: true
eventTypeBlacklist:
- "com.modular.eventlogger.event.received"
- "com.modular.eventlogger.event.processed"
- "com.modular.eventlogger.output.success"
- "com.modular.eventlogger.buffer.full"
- "com.modular.eventlogger.event.dropped"Human-readable single-line format:
2024-01-15 10:30:15 INFO [module.registered] application Module 'auth' registered (type=AuthModule)
Machine-readable JSON format:
{"timestamp":"2024-01-15T10:30:15Z","level":"INFO","type":"module.registered","source":"application","data":{"moduleName":"auth","moduleType":"AuthModule"},"metadata":{}}Detailed multi-line structured format:
[2024-01-15 10:30:15] INFO module.registered
Source: application
Data: map[moduleName:auth moduleType:AuthModule]
Metadata: map[]
Outputs to stdout with optional color coding and timestamps:
outputTargets:
- type: console
level: INFO
format: structured
console:
useColor: true # ANSI color codes for log levels
timestamps: true # Include timestamps in outputOutputs to files with automatic rotation:
outputTargets:
- type: file
level: DEBUG
format: json
file:
path: /var/log/events.log
maxSize: 100 # MB before rotation
maxBackups: 5 # Number of backup files to keep
maxAge: 30 # Days to keep files
compress: true # Compress rotated filesOutputs to system syslog:
outputTargets:
- type: syslog
level: WARN
format: text
syslog:
network: unix # unix, tcp, udp
address: "" # For tcp/udp: "localhost:514"
tag: modular # Syslog tag
facility: user # Syslog facilityThe module automatically maps event types to appropriate log levels:
- ERROR:
application.failed,module.failed - WARN: Custom warning events
- INFO:
module.registered,service.registered,application.started, etc. - DEBUG:
config.loaded,config.validated
When multiple filtering options are configured, they are evaluated in the following order:
- Whitelist Filter (
eventTypeFilters) - If configured, only events matching the whitelist are considered - ExcludeOwnEvents - If enabled, EventLogger's own operational events are filtered out
- Blacklist Filter (
eventTypeBlacklist) - If configured, events matching the blacklist are excluded (takes precedence over whitelist) - Log Level Filter - Events must meet the minimum log level requirement
Example: If an event type is in both the whitelist AND the blacklist, it will be excluded (blacklist wins).
- Async Processing: Events are processed asynchronously to avoid blocking the application
- Buffering: Events are buffered in memory before writing to reduce I/O overhead
- Error Isolation: Failures in one output target don't affect others
- Graceful Degradation: Buffer overflow results in dropped events with warnings
- Filter Performance: Event type filters use simple string matching for optimal performance
The module supports fine‑grained control over lifecycle behavior:
| Setting | Purpose | Typical Usage |
|---|---|---|
startupSync |
When true, emits operational startup events (config.loaded, output.registered, logger.started) synchronously inside Start() so tests or dependent logic can immediately observe them without arbitrary sleeps. |
Enable in deterministic test suites to remove time.Sleep calls. Leave false in production to minimize startup blocking. |
shutdownEmitStopped |
When true (default), emits a eventlogger.logger.stopped operational event after draining. Set false to suppress if you prefer a silent shutdown or want to avoid any late emissions during teardown. |
Disable in environments where observers are already torn down or to reduce noise in integration tests. |
shutdownDrainTimeout |
Bounded duration to wait for the event queue to drain during Stop(). If the timeout elapses, a warning is logged and shutdown proceeds. Zero (or negative) means wait indefinitely. |
Tune to balance fast shutdown vs. ensuring critical events are flushed (e.g. increase for audit trails, reduce for fast ephemeral jobs). |
Benign framework lifecycle events (e.g. config.loaded, config.validated, module.registered, service.registered) that arrive before the logger has fully started are silently dropped instead of producing event logger not started errors. This prevents noisy, misleading logs during application bootstrapping while keeping genuine misordering issues visible for other event types.
Enable startupSync: true in test configuration to make assertions against startup events immediately after app.Start() without introducing sleeps. Pair with a small shutdownDrainTimeout (e.g. 500ms) to keep CI fast while still flushing most buffered events.
The module handles various error conditions gracefully:
- Output Target Failures: Logged but don't stop other targets
- Buffer Overflow: Oldest events are dropped with warnings
- Configuration Errors: Reported during module initialization
- Observer Errors: Logged but don't interrupt event flow
The EventLogger module complements the existing EventBus module:
- EventBus: Provides pub/sub messaging between modules
- EventLogger: Provides structured logging of Observer pattern events
- Use Together: EventBus for inter-module communication, EventLogger for audit trails
The module includes comprehensive tests:
cd modules/eventlogger
go test ./... -v- Uses Go's
log/syslogpackage for syslog support - File rotation could be enhanced with external libraries like
lumberjack - Async processing uses buffered channels and worker goroutines
- Thread-safe implementation supports concurrent event logging
- Implements the Observer interface for seamless integration