Slog sampling policy

A middleware that samples incoming records which caps the CPU and I/O load of logging while attempting to preserve a representative subset of your logs.
Sampling fixes throughput by dropping repetitive log entries.
See also:
🚀 Install
go get github.com/samber/slog-sampling
Compatibility: go >= 1.20.3
This library is v0 and follows SemVer strictly. On slog
final release (go 1.21), this library will go v1.
No breaking changes will be made to exported APIs before v1.0.0.
💡 Usage
GoDoc: https://pkg.go.dev/github.com/samber/slog-sampling
The sampling middleware can be used standalone or with slog-multi
helper.
3 strategies are available:
type UniformSamplingOption struct {
// This will log the first `Threshold` log entries with the same level and message
// in a `Tick` interval as-is. Following that, it will allow `Rate` in the range [0.0, 1.0].
Tick time.Duration
Threshold uint64
Rate float64
// Optional hooks
OnAccepted func(context.Context, slog.Record)
OnDropped func(context.Context, slog.Record)
}
Using slog-multi
:
import (
slogmulti "github.com/samber/slog-multi"
slogsampling "github.com/samber/slog-sampling"
"golang.org/x/exp/slog"
)
// Will print 30% of entries.
option := slogsampling.UniformSamplingOption{
// The sample rate for sampling traces in the range [0.0, 1.0].
Rate: 0.33,
OnAccepted: func(context.Context, slog.Record) {
// ...
},
OnDropped: func(context.Context, slog.Record) {
// ...
},
}
logger := slog.New(
slogmulti.
Pipe(option.NewMiddleware()).
Handler(slog.NewJSONHandler(os.Stdout)),
)
Treshold sampling
type ThresholdSamplingOption struct {
// This will log the first `Threshold` log entries with the same level and message
// in a `Tick` interval as-is. Following that, it will allow `Rate` in the range [0.0, 1.0].
Tick time.Duration
Threshold uint64
Rate float64
// Optional hooks
OnAccepted func(context.Context, slog.Record)
OnDropped func(context.Context, slog.Record)
}
If Rate
is zero, the middleware will drop all log entries after the first Threshold
records in that interval.
Using slog-multi
:
import (
slogmulti "github.com/samber/slog-multi"
slogsampling "github.com/samber/slog-sampling"
"golang.org/x/exp/slog"
)
// Will print the first 10 entries having the same level+message, then every 10th messages until next interval.
option := slogsampling.ThresholdSamplingOption{
Tick: 5 * time.Second,
Threshold: 10,
Rate: 10,
OnAccepted: func(context.Context, slog.Record) {
// ...
},
OnDropped: func(context.Context, slog.Record) {
// ...
},
}
logger := slog.New(
slogmulti.
Pipe(option.NewMiddleware()).
Handler(slog.NewJSONHandler(os.Stdout)),
)
Custom sampler
type CustomSamplingOption struct {
// The sample rate for sampling traces in the range [0.0, 1.0].
Sampler func(context.Context, slog.Record) float64
// Optional hooks
OnAccepted func(context.Context, slog.Record)
OnDropped func(context.Context, slog.Record)
}
Using slog-multi
:
import (
slogmulti "github.com/samber/slog-multi"
slogsampling "github.com/samber/slog-sampling"
"golang.org/x/exp/slog"
)
// Will print 100% of log entries during the night, or 50% of errors, 20% of warnings and 1% of lower levels.
option := slogsampling.CustomSamplingOption{
Sampler: func(ctx context.Context, record slog.Record) float64 {
if record.Time.Hour() < 6 || record.Time.Hour() > 22 {
return 1
}
switch record.Level {
case slog.LevelError:
return 0.5
case slog.LevelWarn:
return 0.2
default:
return 0.01
}
},
OnAccepted: func(context.Context, slog.Record) {
// ...
},
OnDropped: func(context.Context, slog.Record) {
// ...
},
}
logger := slog.New(
slogmulti.
Pipe(option.NewMiddleware()).
Handler(slog.NewJSONHandler(os.Stdout)),
)
🤝 Contributing
Don't hesitate ;)
# Install some dev dependencies
make tools
# Run tests
make test
# or
make watch-test
👤 Contributors

💫 Show your support
Give a ⭐️ if this project helped you!

📝 License
Copyright © 2023 Samuel Berthe.
This project is MIT licensed.