From golang-skills
Guides Go developers on structured logging with slog: choosing log levels, configuring handlers, writing key-value pairs, and adding request-scoped context for production observability.
How this skill is triggered — by the user, by Claude, or both
Slash command
/golang-skills:go-loggingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Compatibility: `log/slog` requires Go 1.21+; `testing/slogtest` requires Go 1.22+.
Compatibility:
log/slogrequires Go 1.21+;testing/slogtestrequires Go 1.22+.
references/LEVELS-AND-CONTEXT.md - Read when choosing log levels, deciding logger-in-context versus explicit parameters, or excluding sensitive fields.references/LOGGING-PATTERNS.md - Read when configuring slog handlers, logging HTTP requests, testing handlers, or migrating from log.Printf.Logs are for operators, not developers. Every log line should help someone diagnose a production issue. If it doesn't serve that purpose, it's noise.
Normative: Use
log/slogfor new Go code.
slog is structured, leveled, and in the standard library (Go 1.21+). It
covers the vast majority of production logging needs.
Which logger?
├─ New production code → log/slog
├─ Trivial CLI / one-off → log (standard)
└─ Measured perf bottleneck → zerolog or zap (benchmark first)
Do not introduce a third-party logging library unless profiling shows slog
is a bottleneck in your hot path. When you do, keep the same structured
key-value style.
Normative: Always use key-value pairs. Never interpolate values into the message string.
The message is a static description of what happened. Dynamic data goes in key-value attributes:
// Good: static message, structured fields
slog.Info("order placed", "order_id", orderID, "total", total)
// Bad: dynamic data baked into the message string
slog.Info(fmt.Sprintf("order %d placed for $%.2f", orderID, total))
Advisory: Use
snake_casefor log attribute keys.
Keys should be lowercase, underscore-separated, and consistent across the
codebase: user_id, request_id, elapsed_ms.
For performance-critical paths, use typed constructors to avoid allocations:
slog.LogAttrs(ctx, slog.LevelInfo, "request handled",
slog.String("method", r.Method),
slog.Int("status", code),
slog.Duration("elapsed", elapsed),
)
Advisory: Follow these level semantics consistently.
| Level | When to use | Production default |
|---|---|---|
| Debug | Developer-only diagnostics, tracing internal state | Disabled |
| Info | Notable lifecycle events: startup, shutdown, config loaded | Enabled |
| Warn | Unexpected but recoverable: deprecated feature used, retry succeeded | Enabled |
| Error | Operation failed, requires operator attention | Enabled |
Rules of thumb:
slog.Error should always include an "err" attributeslog.Error("payment failed", "err", err, "order_id", id)
slog.Warn("retry succeeded", "attempt", n, "endpoint", url)
slog.Info("server started", "addr", addr)
slog.Debug("cache lookup", "key", key, "hit", hit)
Advisory: Derive loggers from context to carry request-scoped fields.
Use middleware to enrich a logger with request ID, user ID, or trace ID, then pass the enriched logger downstream via context or as an explicit parameter. Keep the full context-key and middleware implementation in the logging patterns reference so request-scoped logging has one owner.
The handle-once rule belongs to go-error-handling. In logging work, apply it by choosing either a local log-and-recover path or a return path with context, not both for the same error.
Exception: HTTP handlers and other top-of-stack boundaries may log detailed errors server-side while returning a sanitized message to the client:
if err != nil {
slog.Error("checkout failed", "err", err, "user_id", uid)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
See go-error-handling for the full handle-once pattern and error wrapping guidance.
Normative: Never log secrets, credentials, PII, or high-cardinality unbounded data.
| Do | Don't |
|---|---|
slog.Info("msg", "key", val) | log.Printf("msg %v", val) |
| Static message + structured fields | fmt.Sprintf in message |
snake_case keys | camelCase or inconsistent keys |
| Log OR return errors | Log AND return the same error |
| Derive logger from context | Create a new logger per call |
Use slog.Error with "err" attr | slog.Info for errors |
Pre-check Enabled() on hot paths | Always allocate log args |
npx claudepluginhub cxuu/golang-skills --plugin golang-skillsBuilds structured logging pipelines in Go with samber/slog-*: handler composition, sampling, formatting, HTTP middleware, and backend routing (Datadog, Sentry, Loki, syslog).
Writes or reviews structured logging in Go services using github.com/xgodev/boost/wrapper/log. Handles logger creation, enrichment with fields, and migration from standard Go loggers.
Implements structured logging with levels, request context, PII sanitization using Winston in Node.js. Covers Python structlog, Go zap, ELK/CloudWatch integration for log bloat, PII exposure, missing context.