go-code-review
Use when reviewing Go code or checking code against community style standards. Also use proactively before submitting a Go PR or…
Use when choosing a logging approach, configuring slog, writing structured log statements, or deciding log levels in Go. Also use when setting up production logging, adding request-scoped context to logs, or migrating from log to slog, even if the user doesn't explicitly mention
$ npx -y skills add cxuu/golang-skills --skill go-logging --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/go-loggingContext preview
The summary Claude sees to decide when to auto-load this skill.
Use when choosing a logging approach, configuring slog, writing structured log statements, or deciding log levels in Go. Also use when setting up production logging, adding request-scoped context to logs, or migrating from log to slog, even if the user doesn't explicitly mention
name: go-logging description: Use when choosing a logging approach, configuring slog, writing structured log statements, or deciding log levels in Go. Also use when setting up production logging, adding request-scoped context to logs, or migrating from log to slog, even if the user doesn't explicitly mention logging. Does not cover error handling strategy (see go-error-handling).
> Compatibility: `log/slog` requires Go 1.21+; `testing/slogtest` requires Go 1.22+.
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/slog` for 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_case` for 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("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](../go-error-handling/SKILL.md). 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](../go-error-handling/SKILL.md) 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 |
---
AI Agent Skills for writing idiomatic, production-quality Go code. 20 modular skills teach AI coding assistants Go best practices derived from: Google Go Style Guide Effective Go Uber Go Style Guide Go Wiki CodeReviewComments Skills are tuned following
Use when reviewing Go code or checking code against community style standards. Also use proactively before submitting a Go PR or…
Use when writing concurrent Go code — goroutines, channels, mutexes, or thread-safety guarantees. Also use when parallelizing…
Use when working with context.Context in Go — placement in signatures, propagating cancellation and deadlines, and storing values…
Use when writing conditionals, loops, or switch statements in Go — including if with initialization, early returns, for loop…
Use when working with Go slices, maps, or arrays — choosing between new and make, using append, declaring empty slices (nil vs…
Use when declaring or initializing Go variables, constants, structs, or maps — including var vs :=, reducing scope with if-init,…