Golang Samber Slog
Samuel BertheCommunityCreated by samberDistributed by Samuel Berthe
- Skill
- Tool integration
- Software development
- MIT
- Medium risk
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
The samber/slog collection provides over 20 composable packages that extend the standard Go slog library. It includes tools for handler composition, log sampling, attribute formatting, HTTP middleware for common web frameworks, and various backend sinks for log delivery. The library follows a canonical pipeline model where records flow through sampling, formatting, and routing stages to reach their final destinations.
What it does
- Composes multiple log handlers using fanout, routing, and failover patterns.
- Controls log throughput via uniform, threshold, or absolute sampling strategies.
- Transforms log attributes using formatters for PII masking, error handling, and data flattening.
- Integrates with web frameworks like Gin, Echo, Fiber, and Chi to log HTTP requests.
- Routes logs to various backends including Datadog, Sentry, Loki, Kafka, and others.
- Provides middleware for injecting request-scoped attributes into log records.
When to use it
- When you need to route logs to multiple destinations simultaneously.
- When you need to reduce log volume using sampling strategies.
- When you need to scrub PII or format attributes before logs leave your application.
- When you need to integrate structured logging into Gin, Echo, Fiber, or Chi web applications.
- When you need to send logs to external services like Datadog, Sentry, or Loki.
Advantages
- Implements the standard slog.Handler interface for compatibility.
- Supports complex pipeline architectures with modular components.
- Provides consistent configuration patterns across different HTTP frameworks.
- Includes built-in predicates for conditional log routing.
- Offers specialized formatters for common tasks like error and IP address handling.
Usage examples
- “How do I route error logs to Sentry while sending all logs to stdout?”
- The agent provides a code snippet using slogmulti.Router to add a Sentry handler with a LevelIs(slog.LevelError) predicate alongside a standard JSON handler.
- “How can I mask PII in my logs?”
- The agent demonstrates using slogformatter.NewFormatterMiddleware with PIIFormatter to automatically mask sensitive fields before they reach the log handler.
- “How do I add logging to my Gin router?”
- The agent shows how to use sloggin.NewWithConfig to integrate structured logging into a Gin application, including options for filtering paths.
- “How do I ensure logs are not lost when using Datadog?”
- The agent explains the requirement to call the Stop method on the handler during application shutdown to flush buffered logs.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/samber/golang-samber-slog/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip golang-samber-slog.zip -d .agents/skills
Documentation by samber
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.
samber/slog-**** — Structured Logging Pipeline for Go
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:
github.com/samber/slog-multi — handler composition
github.com/samber/slog-sampling — throughput control
github.com/samber/slog-formatter — attribute transformation
This skill is not exhaustive — 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-devskill (godig), preferred 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-goplsskill (gopls).Context7 remains a fallback for docs not indexed on pkg.go.dev.
The Pipeline Model
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.
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.
Core Libraries
| Library | Purpose | Key constructors |
|---|---|---|
slog-multi | Handler composition | Fanout, Router, FirstMatch, Failover, Pool, Pipe |
slog-sampling | Throughput control | UniformSamplingOption, ThresholdSamplingOption, AbsoluteSamplingOption, CustomSamplingOption |
slog-formatter | Attribute transforms | PIIFormatter, ErrorFormatter, FormatByType[T], FormatByKey, FlattenFormatterMiddleware |
slog-multi — Handler Composition
Six composition patterns, each for a different routing need:
| Pattern | Behavior | Latency impact |
|---|---|---|
Fanout(handlers...) | Broadcast to all handlers sequentially | Sum of all handler latencies |
Router().Add(h, predicate).Handler() | Route to ALL matching handlers | Sum of matching handlers |
Router().Add(...).FirstMatch().Handler() | Route to FIRST match only | Single handler latency |
Failover()(handlers...) | Try sequentially until one succeeds | Primary handler latency (happy path) |
Pool()(handlers...) | Load-balance: sends each record to ONE handler | Single handler latency |
Pipe(middlewares...).Handler(sink) | Middleware chain before sink | Middleware overhead + sink |
// 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.
slog-sampling — Throughput Control
| Strategy | Behavior | Best for |
|---|---|---|
| Uniform | Drop fixed % of all records | Dev/staging noise reduction |
| Threshold | Log first N per interval, then sample at rate R | Production — preserves initial visibility |
| Absolute | Cap at N records per interval globally | Hard cost control |
| Custom | User function returns sample rate per record | Level-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.
// 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.
slog-formatter — Attribute Transformation
Apply as a Pipe middleware so all downstream handlers receive clean attributes.
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.
HTTP Middlewares
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.
// 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.
Backend Sinks
All follow the Option{}.NewXxxHandler() constructor pattern.
| Category | Packages |
|---|---|
| Cloud | slog-datadog, slog-sentry, slog-loki, slog-graylog |
| Messaging | slog-kafka, slog-fluentd, slog-logstash, slog-nats |
| Notification | slog-slack, slog-telegram, slog-webhook |
| Storage | slog-parquet |
| Bridges | slog-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.
Common Mistakes
| Mistake | Why it fails | Fix |
|---|---|---|
| Sampling after formatting | Wastes CPU formatting records that get dropped | Place sampling as outermost handler |
| Fanout to many synchronous handlers | Blocks caller — latency is sum of all handlers | Use Pool() for concurrent dispatch |
| Missing shutdown flush on batch handlers | Buffered logs lost on shutdown | defer handler.Stop(ctx) (Datadog), defer lokiClient.Stop() (Loki), defer writer.Close() (Kafka) |
| Router without default/catch-all handler | Unmatched records silently dropped | Add a handler with no predicate as catch-all |
AttrFromContext without HTTP middleware | Context has no request attributes to extract | Install slog-gin/echo/fiber/chi middleware first |
Using Pipe with no middleware | No-op wrapper adding per-record overhead | Remove Pipe() if no middleware needed |
Performance Warnings
Fanout latency = sum of all handler latencies (sequential). With 5 handlers at 10ms each, every log call costs 50ms. Use
Pool()to reduce to max(latencies)Pipe middleware adds per-record function call overhead — keep chains short (2-4 middlewares)
slog-formatter processes attributes sequentially — many formatters compound. For hot-path attribute formatting, prefer implementing
slog.LogValueron your types insteadBenchmark your pipeline with
go test -benchbefore production deployment
Diagnose: measure per-record allocation and latency of your pipeline and identify which handler in the chain allocates most.
Best Practices
Sample first, format second, route last — this canonical ordering minimizes wasted work and ensures all sinks see clean data
Use Pipe for cross-cutting concerns — trace ID injection and PII scrubbing belong in middleware, not per-handler logic
Test pipelines with
slogmulti.NewHandleInlineHandler— assert on records reaching each stage without real sinksUse
AttrFromContextto propagate request-scoped attributes from HTTP middleware to all handlersPrefer Router over Fanout when handlers need different record subsets — Router evaluates predicates and skips non-matching handlers
Cross-References
→ See
samber/cc-skills-golang@golang-observabilityskill for slog fundamentals (levels, context, handler setup, migration)→ See
samber/cc-skills-golang@golang-error-handlingskill for the log-or-return rule→ See
samber/cc-skills-golang@golang-securityskill for PII handling in logs→ See
samber/cc-skills-golang@golang-samber-oopsskill for structured error context withsamber/oops
If 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).
Files
- ATTRIBUTION.mdAdded by AutoGineer911 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md12.0 KiB
- evals/evals.json15.7 KiB
- references/backend-handlers.md8.2 KiB
- references/http-middlewares.md5.5 KiB
- references/pipeline-patterns.md8.8 KiB
- references/sampling-strategies.md7.0 KiB
references/pipeline-patterns.md
Pipeline Patterns
Complete code examples for every slog-multi composition pattern.
Table of Contents
Fanout — Broadcast to All
Router — Predicate-Based Routing
FirstMatch — Short-Circuit Routing
Failover — Sequential Fallback
Pool — Load-Balanced Dispatch
Pipe — Middleware Chain
Inline Handlers and Middleware
AttrFromContext — Request-Scoped Attributes
Full Production Pipeline
Fanout — Broadcast to All
Sends every record to every handler sequentially. Latency = sum of all handler latencies.
import slogmulti "github.com/samber/slog-multi"
logger := slog.New(
slogmulti.Fanout(
slog.NewJSONHandler(os.Stdout, nil), // stdout
slog.NewTextHandler(logFile, nil), // file
slogsentry.Option{Level: slog.LevelError}.NewSentryHandler(), // Sentry
),
)When to use: Every destination must receive every record (audit logs, compliance). When NOT to use: Handlers have different record needs (use Router) or high latency (use Pool).
Router — Predicate-Based Routing
Routes records to ALL handlers whose predicate matches. Unmatched records go nowhere unless a default handler is added.
logger := slog.New(
slogmulti.Router().
Add(sentryHandler, slogmulti.LevelIs(slog.LevelError)).
Add(slackHandler, slogmulti.LevelIs(slog.LevelWarn)).
Add(lokiHandler, slogmulti.LevelIs(slog.LevelInfo, slog.LevelDebug)).
Add(slog.NewJSONHandler(os.Stdout, nil)). // catch-all: no predicate
Handler(),
)Built-in predicates:
slogmulti.LevelIs(slog.LevelError) // match specific levels
slogmulti.LevelIsNot(slog.LevelDebug) // exclude levels
slogmulti.MessageIs("payment processed") // exact message match
slogmulti.MessageIsNot("healthcheck") // exclude exact message
slogmulti.MessageContains("timeout") // partial message match
slogmulti.MessageNotContains("debug") // exclude partial message
slogmulti.AttrValueIs("module", "billing") // match attribute value
slogmulti.AttrKindIs(slog.KindString) // match attribute kindCustom predicate:
func recordMatchRegion(region string) func(ctx context.Context, r slog.Record) bool {
return func(ctx context.Context, r slog.Record) bool {
match := false
r.Attrs(func(attr slog.Attr) bool {
if attr.Key == "region" && attr.Value.String() == region {
match = true
return false
}
return true
})
return match
}
}
logger := slog.New(
slogmulti.Router().
Add(slackUS, recordMatchRegion("us")).
Add(slackEU, recordMatchRegion("eu")).
Handler(),
)Warning: Records matching no predicate are silently dropped. Always add a catch-all handler (no predicate) unless you intentionally want to discard unmatched records.
FirstMatch — Short-Circuit Routing
Like Router but stops at the first matching handler. Each record goes to exactly one destination.
logger := slog.New(
slogmulti.Router().
Add(queryHandler, matchQueryLogs). // priority 1
Add(requestHandler, matchRequestLogs). // priority 2
Add(defaultHandler). // fallback
FirstMatch().
Handler(),
)When to use: Priority-based routing where each record should be processed exactly once. Order matters — put the most specific handlers first.
Failover — Sequential Fallback
Tries handlers in order until one succeeds (returns nil error). If primary fails, falls through to secondary.
logger := slog.New(
slogmulti.Failover()(
slogloki.Option{Level: slog.LevelDebug, Client: lokiClient}.NewLokiHandler(),
slog.NewJSONHandler(localFile, nil), // fallback to local file
slog.NewTextHandler(os.Stderr, nil), // last resort
),
)When to use: Network sinks that may be unreliable (Loki, Logstash, remote syslog). Primary handles 99.9% of traffic; fallback catches the rest.
Pool — Load-Balanced Dispatch
Randomly distributes each record to one handler from the pool. Useful when you have equivalent handlers and want to spread load.
logger := slog.New(
slogmulti.Pool()(
lokiHandler1, // shard 1
lokiHandler2, // shard 2
lokiHandler3, // shard 3
),
)When to use: Multiple equivalent sinks where you want throughput distribution. Latency = single handler latency (not sum like Fanout).
Pipe — Middleware Chain
Chains middleware functions that intercept, transform, or enrich records before they reach the final handler.
logger := slog.New(
slogmulti.
Pipe(samplingMiddleware). // step 1: drop noise
Pipe(piiScrubbingMiddleware). // step 2: mask PII
Pipe(traceInjectionMiddleware). // step 3: add trace_id
Pipe(slogmulti.RecoverHandlerError( // step 4: catch handler panics
func(ctx context.Context, record slog.Record, err error) {
log.Println("handler error:", err)
},
)).
Handler(slog.NewJSONHandler(os.Stdout, nil)),
)Inline Handlers and Middleware
Create quick handlers without defining a full struct.
// Inline handler — for testing or simple consumers
handler := slogmulti.NewHandleInlineHandler(
func(ctx context.Context, groups []string, attrs []slog.Attr, record slog.Record) error {
fmt.Printf("LOG: %s %s\n", record.Level, record.Message)
return nil
},
)
// Inline middleware — intercept and transform records
middleware := slogmulti.NewHandleInlineMiddleware(
func(ctx context.Context, record slog.Record, next func(context.Context, slog.Record) error) error {
record.AddAttrs(slog.String("service", "my-api"))
return next(ctx, record)
},
)AttrFromContext — Request-Scoped Attributes
HTTP middlewares (slog-gin, slog-echo, etc.) inject request attributes into context. Backend handlers extract them via AttrFromContext.
// Backend handler extracts trace_id from context
handler := slogsentry.Option{
Level: slog.LevelError,
AttrFromContext: []func(ctx context.Context) []slog.Attr{
func(ctx context.Context) []slog.Attr {
if traceID := ctx.Value("trace_id"); traceID != nil {
return []slog.Attr{slog.String("trace_id", traceID.(string))}
}
return nil
},
},
}.NewSentryHandler()Important: AttrFromContext only works when the context actually contains the expected values. This requires an HTTP middleware (like slog-gin) to populate the context first. Without the middleware, AttrFromContext silently returns nil.
Full Production Pipeline
Canonical ordering: sampling → middleware (PII, trace) → routing → sinks.
import (
slogmulti "github.com/samber/slog-multi"
slogsampling "github.com/samber/slog-sampling"
slogformatter "github.com/samber/slog-formatter"
slogsentry "github.com/samber/slog-sentry/v2"
slogloki "github.com/samber/slog-loki/v3"
)
// 1. Sampling: first 20 per 5s, then 10%
sampling := slogsampling.ThresholdSamplingOption{
Tick: 5 * time.Second, Threshold: 20, Rate: 0.1,
}.NewMiddleware()
// 2. PII scrubbing
pii := slogformatter.NewFormatterMiddleware(
slogformatter.PIIFormatter("user"),
slogformatter.IPAddressFormatter("client_ip"),
)
// 3. Error recovery
recovery := slogmulti.RecoverHandlerError(func(ctx context.Context, r slog.Record, err error) {
log.Printf("slog handler error: %v", err)
})
// 4. Sinks
sentryHandler := slogsentry.Option{Level: slog.LevelError}.NewSentryHandler()
lokiHandler := slogloki.Option{Level: slog.LevelDebug, Client: lokiClient}.NewLokiHandler()
defer lokiClient.Stop() // flush buffered logs
// 5. Compose — errors bypass sampling, everything else is sampled
logger := slog.New(
slogmulti.
Pipe(pii). // scrub PII on all records
Pipe(recovery). // catch panics
Handler(
slogmulti.Router().
Add(sentryHandler, slogmulti.LevelIs(slog.LevelError)). // errors: no sampling
Add(slogmulti. // everything else: sampled
Pipe(sampling).
Handler(lokiHandler),
).
FirstMatch(). // stop at first matching route — errors won't fall through to sampled path
Handler(),
),
)
slog.SetDefault(logger)Requirements and compatibility
Written for: Claude Code, OpenAI Codex
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Declares allowed tools, Claude Code frontmatter
The author says: Designed for Claude Code, Codex or similar harness, and for projects using Golang.
Security
What our scanner noticed· Medium risk
- Possible data-exfiltration endpoints
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- samber
- Publisher
- Samuel Berthe (Community)
- Distributed by
- Samuel Berthe
- Source
- samber/cc-skills-golang · skills/golang-samber-slog · commit bb1d43d
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by samber or Samuel Berthe.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download golang-samber-slog.zip