mtlog provides comprehensive context support including context-aware logging methods, scoped properties via LogContext, and automatic deadline detection.
mtlog provides context-aware variants of all logging methods that accept a context.Context parameter:
// Standard methods
logger.Info("User logged in")
// Context-aware methods
logger.InfoContext(ctx, "User logged in")
logger.ErrorContext(ctx, "Operation failed: {Error}", err)All levels supported: VerboseContext, DebugContext, InfoContext, WarnContext, ErrorContext, FatalContext
Attach properties to a context that automatically flow to all loggers:
import "github.com/willibrandon/mtlog"
// Add properties to context
ctx = mtlog.PushProperty(ctx, "RequestId", "abc-123")
ctx = mtlog.PushProperty(ctx, "UserId", 456)
// All logs using this context include these properties
logger.InfoContext(ctx, "Processing request")
// Output includes: RequestId=abc-123, UserId=456Properties flow through nested contexts:
func handleRequest(ctx context.Context) {
ctx = mtlog.PushProperty(ctx, "RequestId", generateRequestId())
processUser(ctx, userId)
}
func processUser(ctx context.Context, userId int) {
ctx = mtlog.PushProperty(ctx, "UserId", userId)
// Logs include both RequestId and UserId
logger.InfoContext(ctx, "Processing user")
}Higher priority overrides lower:
- LogContext properties (lowest)
- ForContext properties
- Event properties (highest)
ctx = mtlog.PushProperty(ctx, "UserId", 123)
logger.ForContext("UserId", 456).InfoContext(ctx, "Test") // UserId=456
logger.InfoContext(ctx, "User {UserId}", 789) // UserId=789Automatically detect and warn when operations approach context deadlines:
// Warn when within 100ms of deadline
logger := mtlog.New(
mtlog.WithConsole(),
mtlog.WithContextDeadlineWarning(100*time.Millisecond),
)
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
logger.InfoContext(ctx, "Starting operation")
time.Sleep(350 * time.Millisecond)
logger.InfoContext(ctx, "Still processing...") // WARNING: Deadline approaching!// Warn when 20% of time remains (clean API)
logger := mtlog.New(
mtlog.WithDeadlinePercentageOnly(0.2), // 20% threshold
)
// Or with both absolute and percentage thresholds
logger := mtlog.New(
mtlog.WithDeadlinePercentageThreshold(
10*time.Millisecond, // Min absolute threshold
0.2, // 20% threshold
),
)Note: Percentage-based thresholds require the context to be logged early in its lifetime for accurate calculations. If a context is first seen when already near its deadline, percentage calculations may be less accurate.
When approaching deadline:
{
"deadline.approaching": true,
"deadline.remaining_ms": 95,
"deadline.at": "2024-01-15T10:30:45Z",
"deadline.first_warning": true
}When deadline exceeded:
{
"deadline.exceeded": true,
"deadline.exceeded_by_ms": 150
}import "github.com/willibrandon/mtlog/internal/enrichers"
logger := mtlog.New(
mtlog.WithContextDeadlineWarning(50*time.Millisecond,
// Custom handler for deadline events
enrichers.WithDeadlineCustomHandler(func(event *core.LogEvent, remaining time.Duration) {
metrics.RecordDeadlineApproaching(remaining)
event.Properties["alert.team"] = "platform"
}),
// Configure cache
enrichers.WithDeadlineCacheSize(1000),
enrichers.WithDeadlineCacheTTL(5*time.Minute),
),
)func timeoutMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Add timeout to all requests
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
defer cancel()
// Add request properties
ctx = mtlog.PushProperty(ctx, "request_id", generateRequestID())
ctx = mtlog.PushProperty(ctx, "method", r.Method)
ctx = mtlog.PushProperty(ctx, "path", r.URL.Path)
logger.InfoContext(ctx, "Request started")
next.ServeHTTP(w, r.WithContext(ctx))
logger.InfoContext(ctx, "Request completed")
})
}- Context methods: ~2ns overhead vs standard methods
- LogContext: No overhead when not used, efficient for <10 properties
- Deadline awareness: 2.7ns when no deadline, ~5ns with deadline check
- Cache: O(1) lookup, bounded memory (default 1000 contexts)
- Use context methods when you have a context
// Good
func process(ctx context.Context) {
logger.InfoContext(ctx, "Processing")
}
// Avoid
func process(ctx context.Context) {
logger.Info("Processing") // Missing context benefits
}- Configure deadline awareness at startup
logger := mtlog.New(
mtlog.WithContextDeadlineWarning(100*time.Millisecond),
// Other options...
)- Use LogContext for cross-cutting concerns
// Add once at request boundary
ctx = mtlog.PushProperty(ctx, "request_id", requestID)
ctx = mtlog.PushProperty(ctx, "tenant_id", tenantID)
// Properties flow through all operations- Set appropriate thresholds
// Fast APIs (100ms target)
mtlog.WithContextDeadlineWarning(20*time.Millisecond)
// Batch jobs (5 minute target)
mtlog.WithContextDeadlineWarning(30*time.Second)
// Mixed workloads
mtlog.WithDeadlinePercentageThreshold(10*time.Millisecond, 0.1)If percentage-based thresholds aren't triggering as expected:
- Ensure early context logging - The context must be seen early in its lifetime for accurate percentage calculation:
// Good - log immediately after creating context
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
logger.InfoContext(ctx, "Starting operation") // Prime the cache
// Bad - first log near deadline
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
time.Sleep(4*time.Second)
logger.InfoContext(ctx, "Almost done") // Too late for percentage calculation- Check cache statistics:
if stats := logger.DeadlineStats(); stats != nil {
s := stats.(core.DeadlineStats)
fmt.Printf("Cache hit rate: %.2f%%\n",
float64(s.CacheSize)/float64(s.CacheCapacity)*100)
}Adjust thresholds based on your operation SLAs:
// Fast APIs (100ms SLA) - warn at 20ms
logger := mtlog.New(
mtlog.WithContextDeadlineWarning(20*time.Millisecond),
)
// Medium operations (1s SLA) - warn at 20% remaining
logger := mtlog.New(
mtlog.WithDeadlinePercentageOnly(0.2),
)
// Long batch jobs (5min SLA) - warn at 30s
logger := mtlog.New(
mtlog.WithContextDeadlineWarning(30*time.Second),
)Enable deadline metrics to track patterns:
logger := mtlog.New(
mtlog.WithContextDeadlineWarning(50*time.Millisecond,
enrichers.WithDeadlineMetrics(true), // Logs to selflog
),
)Different types of operations require different deadline warning strategies:
// Warn at 20% of deadline or 20ms, whichever comes first
logger := mtlog.New(
mtlog.WithDeadlinePercentageThreshold(20*time.Millisecond, 0.2),
)// Percentage-based for better scaling
logger := mtlog.New(
mtlog.WithDeadlinePercentageOnly(0.15), // Warn at 15% remaining
)// Absolute threshold for predictable warnings
logger := mtlog.New(
mtlog.WithContextDeadlineWarning(500*time.Millisecond),
)// Large absolute threshold
logger := mtlog.New(
mtlog.WithContextDeadlineWarning(30*time.Second),
)// Use ForContext and WithDeadlineWarning to create specialized loggers
baseLogger := mtlog.New(mtlog.WithConsole())
apiLogger := baseLogger.
ForContext("component", "api").
WithDeadlineWarning(50*time.Millisecond)
batchLogger := baseLogger.
ForContext("component", "batch").
WithDeadlineWarning(30*time.Second)
// Each logger has its own deadline configuration
// API requests warn at 50ms, batch jobs warn at 30s