Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mtlog is a high-performance structured logging library for Go, inspired by [Seri
- **ForType logging** with automatic SourceContext from Go types and intelligent caching
- **LogContext scoped properties** that flow through operation contexts
- **Source context enrichment** with intelligent caching for automatic logger categorization
- **Context deadline awareness** with automatic timeout warnings and deadline tracking
- **Pipeline architecture** for clean separation of concerns
- **Type-safe generics** for better compile-time safety
- **LogValue interface** for safe logging of sensitive data
Expand Down Expand Up @@ -560,6 +561,53 @@ This is particularly useful for:
- Service-oriented architectures
- Multi-tenant applications requiring cache isolation

## Context Deadline Awareness

mtlog can automatically detect and warn when operations are approaching their context deadlines, helping catch timeout-related issues before they fail:

```go
// Configure deadline awareness
logger := mtlog.New(
mtlog.WithConsole(),
mtlog.WithContextDeadlineWarning(100*time.Millisecond), // Warn within 100ms
)

// Use context-aware logging methods
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: approaching deadline!

// Percentage-based thresholds
logger := mtlog.New(
mtlog.WithDeadlinePercentageThreshold(
1*time.Millisecond, // Minimum absolute threshold
0.2, // Warn when 20% of time remains
),
)

// HTTP handler example
func handler(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 200*time.Millisecond)
defer cancel()

logger.InfoContext(ctx, "Processing request")
// ... perform operations ...
logger.InfoContext(ctx, "Response ready") // Warns if close to timeout
}
```

Features:
- **Zero overhead** when no deadline is present (2.7ns, 0 allocations)
- **Automatic level upgrading** - Info logs become Warnings when deadline approaches
- **OTEL-style properties** - `deadline.remaining_ms`, `deadline.at`, `deadline.approaching`
- **First warning tracking** - Marks the first warning for each context
- **Deadline exceeded detection** - Tracks operations that continue past deadline
- **LRU cache with TTL** - Efficient tracking with automatic cleanup
- **Custom handlers** - Add metrics, alerts, or custom logic when deadlines approach

## Filters

Control which events are logged with powerful filtering:
Expand Down Expand Up @@ -922,6 +970,7 @@ See the [examples](./examples) directory and [OTEL examples](./adapters/otel/exa
- [Context logging](./examples/context/main.go)
- [Type-based logging](./examples/fortype/main.go)
- [LogContext scoped properties](./examples/logcontext/main.go)
- [Deadline awareness](./examples/deadline-awareness/main.go)
- [Advanced filtering](./examples/filtering/main.go)
- [Conditional logging](./examples/conditional/main.go)
- [Sampling basics](./examples/sampling/main.go)
Expand Down Expand Up @@ -1324,6 +1373,7 @@ For comprehensive guides and examples, see the [docs](./docs) directory:

- **[Quick Reference](./docs/quick-reference.md)** - Quick reference for all features
- **[Template Syntax](./docs/template-syntax.md)** - Guide to message template syntaxes
- **[Context Guide](./docs/context-guide.md)** - Context logging, LogContext, and deadline awareness
- **[Sampling Guide](./docs/sampling-guide.md)** - Comprehensive per-message sampling documentation
- **[Sinks Guide](./docs/sinks.md)** - Complete guide to all output destinations
- **[Routing Patterns](./docs/routing-patterns.md)** - Advanced event routing patterns and best practices
Expand Down
26 changes: 26 additions & 0 deletions adapters/middleware/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,4 +361,30 @@ func (n *noOpLogger) SampleProfile(profileName string) core.Logger { r
func (n *noOpLogger) SampleAdaptive(targetEventsPerSecond uint64) core.Logger { return n }
func (n *noOpLogger) SampleAdaptiveWithOptions(targetEventsPerSecond uint64, minRate, maxRate float64, adjustmentInterval time.Duration) core.Logger {
return n
}

// Context-aware methods
func (n *noOpLogger) VerboseContext(ctx context.Context, template string, args ...any) {}
func (n *noOpLogger) DebugContext(ctx context.Context, template string, args ...any) {}
func (n *noOpLogger) InfoContext(ctx context.Context, template string, args ...any) {}
func (n *noOpLogger) WarnContext(ctx context.Context, template string, args ...any) {}
func (n *noOpLogger) ErrorContext(ctx context.Context, template string, args ...any) {}
func (n *noOpLogger) FatalContext(ctx context.Context, template string, args ...any) {}
func (n *noOpLogger) WriteContext(ctx context.Context, level core.LogEventLevel, template string, args ...any) {}

func (n *noOpLogger) EnableSamplingSummaryWithCleanup(period time.Duration) (core.Logger, func()) {
return n, func() {}
}

func (n *noOpLogger) GetSamplingMetrics() core.SamplingMetrics {
return core.SamplingMetrics{}
}

// New methods for deadline awareness
func (n *noOpLogger) DeadlineStats() interface{} {
return nil
}

func (n *noOpLogger) WithDeadlineWarning(threshold time.Duration, opts ...interface{}) core.Logger {
return n
}
46 changes: 46 additions & 0 deletions adapters/otel/bridge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,52 @@ func (m *mockLogger) SampleAdaptiveWithOptions(targetEventsPerSecond uint64, min
return m
}

// Context-aware methods
func (m *mockLogger) VerboseContext(ctx context.Context, template string, args ...any) {
m.Verbose(template, args...)
}

func (m *mockLogger) DebugContext(ctx context.Context, template string, args ...any) {
m.Debug(template, args...)
}

func (m *mockLogger) InfoContext(ctx context.Context, template string, args ...any) {
m.Information(template, args...)
}

func (m *mockLogger) WarnContext(ctx context.Context, template string, args ...any) {
m.Warning(template, args...)
}

func (m *mockLogger) ErrorContext(ctx context.Context, template string, args ...any) {
m.Error(template, args...)
}

func (m *mockLogger) FatalContext(ctx context.Context, template string, args ...any) {
m.Fatal(template, args...)
}

func (m *mockLogger) WriteContext(ctx context.Context, level core.LogEventLevel, template string, args ...any) {
m.Write(level, template, args...)
}

func (m *mockLogger) EnableSamplingSummaryWithCleanup(period time.Duration) (core.Logger, func()) {
return m, func() {}
}

func (m *mockLogger) GetSamplingMetrics() core.SamplingMetrics {
return core.SamplingMetrics{}
}

// New methods for deadline awareness
func (m *mockLogger) DeadlineStats() interface{} {
return nil
}

func (m *mockLogger) WithDeadlineWarning(threshold time.Duration, opts ...interface{}) core.Logger {
return m
}

func TestBridge(t *testing.T) {
// Create a mock logger
logger := &mockLogger{level: core.InformationLevel}
Expand Down
26 changes: 26 additions & 0 deletions adapters/otel/fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,4 +304,30 @@ func (m *mockFuzzLogger) SampleProfile(profileName string) core.Logger {
func (m *mockFuzzLogger) SampleAdaptive(targetEventsPerSecond uint64) core.Logger { return m }
func (m *mockFuzzLogger) SampleAdaptiveWithOptions(targetEventsPerSecond uint64, minRate, maxRate float64, adjustmentInterval time.Duration) core.Logger {
return m
}

// Context-aware methods
func (m *mockFuzzLogger) VerboseContext(ctx context.Context, template string, args ...any) {}
func (m *mockFuzzLogger) DebugContext(ctx context.Context, template string, args ...any) {}
func (m *mockFuzzLogger) InfoContext(ctx context.Context, template string, args ...any) {}
func (m *mockFuzzLogger) WarnContext(ctx context.Context, template string, args ...any) {}
func (m *mockFuzzLogger) ErrorContext(ctx context.Context, template string, args ...any) {}
func (m *mockFuzzLogger) FatalContext(ctx context.Context, template string, args ...any) {}
func (m *mockFuzzLogger) WriteContext(ctx context.Context, level core.LogEventLevel, template string, args ...any) {}

func (m *mockFuzzLogger) EnableSamplingSummaryWithCleanup(period time.Duration) (core.Logger, func()) {
return m, func() {}
}

func (m *mockFuzzLogger) GetSamplingMetrics() core.SamplingMetrics {
return core.SamplingMetrics{}
}

// New methods for deadline awareness
func (m *mockFuzzLogger) DeadlineStats() interface{} {
return nil
}

func (m *mockFuzzLogger) WithDeadlineWarning(threshold time.Duration, opts ...interface{}) core.Logger {
return m
}
12 changes: 12 additions & 0 deletions core/deadline_stats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package core

import "time"

// DeadlineStats provides statistics about deadline tracking.
type DeadlineStats struct {
CacheSize int // Current number of contexts in the deadline cache
CacheCapacity int // Maximum capacity of the deadline cache
FirstWarningCount int // Number of contexts that have received first warnings
FirstWarningCapacity int // Maximum capacity of first warning set
CacheTTL time.Duration // Time-to-live for cache entries
}
28 changes: 28 additions & 0 deletions core/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@ type Logger interface {
// Warn writes a warning-level log event (alias for Warning).
Warn(messageTemplate string, args ...any)

// Context-aware logging methods (following Go idioms like slog)

// VerboseContext writes a verbose-level log event with context awareness.
VerboseContext(ctx context.Context, messageTemplate string, args ...any)

// DebugContext writes a debug-level log event with context awareness.
DebugContext(ctx context.Context, messageTemplate string, args ...any)

// InfoContext writes an information-level log event with context awareness.
InfoContext(ctx context.Context, messageTemplate string, args ...any)

// WarnContext writes a warning-level log event with context awareness.
WarnContext(ctx context.Context, messageTemplate string, args ...any)

// ErrorContext writes an error-level log event with context awareness.
ErrorContext(ctx context.Context, messageTemplate string, args ...any)

// FatalContext writes a fatal-level log event with context awareness.
FatalContext(ctx context.Context, messageTemplate string, args ...any)

// Sampling methods for per-message control

// Sample creates a logger that samples every nth message.
Expand Down Expand Up @@ -95,4 +115,12 @@ type Logger interface {

// SampleAdaptiveWithOptions creates a logger with adaptive sampling and custom parameters.
SampleAdaptiveWithOptions(targetEventsPerSecond uint64, minRate, maxRate float64, adjustmentInterval time.Duration) Logger

// DeadlineStats returns deadline tracking statistics if deadline awareness is enabled.
// Returns nil if deadline awareness is not configured.
DeadlineStats() interface{}

// WithDeadlineWarning creates a logger with modified deadline warning threshold.
// This allows creating derived loggers with different deadline configurations.
WithDeadlineWarning(threshold time.Duration, opts ...interface{}) Logger
}
Loading
Loading