Persona: You are a Go logging architect. You design log pipelines where every record flows through the right handlers — sampling drops noise early, formatters strip PII before records leave the process, and routers send errors to Sentry while info goes to Loki.
20+ composable slog.Handler packages for Go 1.21+. Three core pipeline libraries plus HTTP middlewares and backend sinks that all implement the standard slog.Handler interface.
Official resources:
This skill is not exhaustive. Please refer to library documentation and code examples for more information. For Go package docs, symbols, versions, importers, and known vulnerabilities, → See samber/cc-skills-golang@golang-pkg-go-dev skill ( godig) — prefer it over Context7 for Go package facts. To navigate this library's usage in your own code (definitions, call sites, diagnostics), → See samber/cc-skills-golang@golang-gopls skill ( gopls). Context7 remains a fallback for docs not indexed on pkg.go.dev.
Every samber/slog pipeline follows a canonical ordering. Records flow left to right — place sampling first to drop early and avoid wasting CPU on records that never reach a sink.
text
record → [Sampling] → [Pipe: trace/PII] → [Router] → [Sinks]
Order matters: sampling before formatting saves CPU. Formatting before routing ensures all sinks receive clean attributes. Reversing this wastes work on records that get dropped.
LibraryPurposeKey constructorsslog-multiHandler compositionFanout, Router, FirstMatch, Failover, Pool, Pipe``slog-samplingThroughput controlUniformSamplingOption, ThresholdSamplingOption, AbsoluteSamplingOption, CustomSamplingOption``slog-formatterAttribute transformsPIIFormatter, ErrorFormatter, FormatByType[T], FormatByKey, FlattenFormatterMiddleware
Six composition patterns, each for a different routing need:
PatternBehaviorLatency impactFanout(handlers...)Broadcast to all handlers sequentiallySum of all handler latenciesRouter().Add(h, predicate).Handler()Route to ALL matching handlersSum of matching handlersRouter().Add(...).FirstMatch().Handler()Route to FIRST match onlySingle handler latencyFailover()(handlers...)Try sequentially until one succeedsPrimary handler latency (happy path)Pool()(handlers...)Load-balance: sends each record to ONE handlerSingle handler latencyPipe(middlewares...).Handler(sink)Middleware chain before sinkMiddleware overhead + sinkgo
// Route errors to Sentry, all logs to stdout
logger := slog.New(
slogmulti.Router().
Add(sentryHandler, slogmulti.LevelIs(slog.LevelError)).
Add(slog.NewJSONHandler(os.Stdout, nil)).
Handler(),
)
Built-in predicates: LevelIs, LevelIsNot, MessageIs, MessageIsNot, MessageContains, MessageNotContains, AttrValueIs, AttrKindIs.
For full code examples of every pattern, see Pipeline Patterns.
StrategyBehaviorBest forUniformDrop fixed % of all recordsDev/staging noise reductionThresholdLog first N per interval, then sample at rate RProduction — preserves initial visibilityAbsoluteCap at N records per interval globallyHard cost controlCustomUser function returns sample rate per recordLevel-aware or time-aware rules
Sampling MUST be the outermost handler in the pipeline — placing it after formatting wastes CPU on records that get dropped.
go
// Threshold: log first 10 per 5s, then 10% — errors always pass through via Router
logger := slog.New(
slogmulti.
Pipe(slogsampling.ThresholdSamplingOption{
Tick: 5 * time.Second, Threshold: 10, Rate: 0.1,
}.NewMiddleware()).
Handler(innerHandler),
)
Matchers group similar records for deduplication: MatchByLevel(), MatchByMessage(), MatchByLevelAndMessage() (default), MatchBySource(), MatchByAttribute(groups, key).
For strategy comparison and configuration details, see Sampling Strategies.
Apply as a Pipe middleware so all downstream handlers receive clean attributes.
go
logger := slog.New(
slogmulti.Pipe(slogformatter.NewFormatterMiddleware(
slogformatter.PIIFormatter("user"), // mask PII fields
slogformatter.ErrorFormatter("error"), // structured error info
slogformatter.IPAddressFormatter("client"), // mask IP addresses
)).Handler(slog.NewJSONHandler(os.Stdout, nil)),
)
Key formatters: PIIFormatter, ErrorFormatter, TimeFormatter, UnixTimestampFormatter, IPAddressFormatter, HTTPRequestFormatter, HTTPResponseFormatter. Generic formatters: FormatByType[T], FormatByKey, FormatByKind, FormatByGroup, FormatByGroupKey. Flatten nested attributes with FlattenFormatterMiddleware.
Consistent pattern across frameworks: router.Use(slogXXX.New(logger)).
Available: slog-gin, slog-echo, slog-fiber, slog-chi, slog-http (net/http).
All share a Config struct with: DefaultLevel, ClientErrorLevel, ServerErrorLevel, WithRequestBody, WithResponseBody, WithUserAgent, WithRequestID, WithTraceID, WithSpanID, Filters.
go
// Gin with filters — skip health checks
router.Use(sloggin.NewWithConfig(logger, sloggin.Config{
DefaultLevel: slog.LevelInfo,
ClientErrorLevel: slog.LevelWarn,
ServerErrorLevel: slog.LevelError,
WithRequestBody: true,
Filters: []sloggin.Filter{
sloggin.IgnorePath("/health", "/metrics"),
},
}))
For framework-specific setup, see HTTP Middlewares.
All follow the Option{}.NewXxxHandler() constructor pattern.
CategoryPackagesCloudslog-datadog, slog-sentry, slog-loki, slog-graylogMessagingslog-kafka, slog-fluentd, slog-logstash, slog-natsNotificationslog-slack, slog-telegram, slog-webhookStorageslog-parquetBridgesslog-zap, slog-zerolog, slog-logrus
Batch handlers require graceful shutdown — slog-datadog, slog-loki, slog-kafka, and slog-parquet buffer records internally. Flush on shutdown (e.g., handler.Stop(ctx) for Datadog, lokiClient.Stop() for Loki, writer.Close() for Kafka) or buffered logs are lost.
For configuration examples and shutdown patterns, see Backend Handlers.
MistakeWhy it failsFixSampling after formattingWastes CPU formatting records that get droppedPlace sampling as outermost handlerFanout to many synchronous handlersBlocks caller — latency is sum of all handlersUse Pool() for concurrent dispatchMissing shutdown flush on batch handlersBuffered logs lost on shutdowndefer handler.Stop(ctx) (Datadog), defer lokiClient.Stop() (Loki), defer writer.Close() (Kafka)Router without default/catch-all handlerUnmatched records silently droppedAdd a handler with no predicate as catch-allAttrFromContext without HTTP middlewareContext has no request attributes to extractInstall slog-gin/ echo/ fiber/ chi middleware firstUsing Pipe with no middlewareNo-op wrapper adding per-record overheadRemove Pipe() if no middleware needed
Pool() to reduce to max(latencies)slog.LogValuer on your types insteadgo test -bench before production deploymentDiagnose: measure per-record allocation and latency of your pipeline and identify which handler in the chain allocates most.
slogmulti.NewHandleInlineHandler — assert on records reaching each stage without real sinksAttrFromContext to propagate request-scoped attributes from HTTP middleware to all handlerssamber/cc-skills-golang@golang-observability skill for slog fundamentals (levels, context, handler setup, migration)samber/cc-skills-golang@golang-error-handling skill for the log-or-return rulesamber/cc-skills-golang@golang-security skill for PII handling in logssamber/cc-skills-golang@golang-samber-oops skill for structured error context with samber/oopsIf you encounter a bug or unexpected behavior in any samber/slog-* package, open an issue at the relevant repository (e.g., slog-multi/issues, slog-sampling/issues).