agentsclimarketplace

Go logging

Skill muratmirgun/gophers/skills/go-logging

26 production-grade Go skills for Claude Code, Gemini CLI, and opencode.

Install
npx -y skills add muratmirgun/gophers --skill go-logging

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 8 stars8 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when choosing a Go logger, configuring slog, writing structured log statements, picking log levels, or attaching request-scoped fields. Apply proactively whenever code calls log/fmt to emit operational information, migrates off log/logrus/zap/zerolog, or sets up production logging. Covers structured logging only — metrics, traces, profiling, and RUM belong to a separate observability skill.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

8.7 KB, as published. Nobody here has run it

Go Logging

Logs are written for operators — the human who will be paged at 3 a.m. and needs to know what happened. Every log line either helps diagnose a production issue or it is noise. log/slog from the standard library is the default; reach for anything else only after measuring.

This skill covers logging only. Metrics, distributed tracing, profiling, and RUM are a separate concern — they belong to a future go-observability skill. Do not confuse them with logging here.

Core Rules

  1. Use log/slog for new code. Structured, leveled, in the standard library since Go 1.21.
  2. Static message, structured fields. The message describes what happened; data goes in key-value attributes.
  3. Log or return, never both. Logging a wrapped error makes the same failure appear at every layer.
  4. Log at the boundary. HTTP handlers, job runners, and main log. Library code wraps and returns.
  5. Use snake_case keys consistently across the codebase (user_id, request_id, elapsed_ms).
  6. slog.Error always carries an "err" attribute. Without it, you logged a sentence, not an error.
  7. Never log secrets, PII, or unbounded data. Tokens, full credit cards, request bodies — none of it.

Choosing a Logger

SituationUse
New production servicelog/slog
Trivial CLI / one-off scriptlog (the standard package)
Measured hot-path bottleneck where slog dominates the flame graphzap or zerolog, but keep the structured style
Existing zap/logrus/zerolog codeMigrate to slog with a bridge handler; see references/slog-handler-ecosystem.md

slog's API is stable, the ecosystem has consolidated around it, and JSON output works with every log shipper. Do not introduce a third-party logger without a benchmark showing the win.

Structured Logging

Build log messages from a static message plus typed fields:

// Good — static message, structured fields
slog.Info("order placed", "order_id", orderID, "total_cents", totalCents)

// Bad — dynamic data baked into the message string
slog.Info(fmt.Sprintf("order %d placed for $%.2f", orderID, total))

The aggregator (Loki, Elastic, CloudWatch) can index order_id. It cannot index a sprintf'd sentence.

For hot paths, typed constructors avoid allocations:

slog.LogAttrs(ctx, slog.LevelInfo, "request handled",
    slog.String("method", r.Method),
    slog.Int("status", code),
    slog.Duration("elapsed", elapsed),
)

Log Levels

LevelWhenDefault
DebugDeveloper-only diagnostics; tracing internal stateDisabled in prod
InfoNotable lifecycle events: startup, shutdown, config loadedEnabled
WarnUnexpected but recoverable: retry succeeded, deprecated flag usedEnabled
ErrorOperation failed; someone should lookEnabled

Rules of thumb:

  • If nobody should act on it, it is not Error — use Warn or Info.
  • If it is only useful with a debugger attached, it is Debug.
  • slog.Error must include an "err" attribute.
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)

Read references/levels-and-context.md when choosing between Warn and Error, defining custom verbosity levels, or pre-checking Enabled() on hot paths.

Request-Scoped Logging

Derive a logger per request that carries the fields every downstream call should include:

func middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log := slog.With("request_id", requestID(r))
        ctx := context.WithValue(r.Context(), loggerKey{}, log)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

func FromContext(ctx context.Context) *slog.Logger {
    if l, ok := ctx.Value(loggerKey{}).(*slog.Logger); ok { return l }
    return slog.Default()
}

Use the Context-aware variants (slog.InfoContext, slog.ErrorContext) so handlers that read trace IDs from the context can stamp them into the record:

slog.InfoContext(ctx, "order placed", "order_id", id)

Read references/request-scope-and-middleware.md when wiring request IDs, building logging middleware, or choosing between context-stored loggers and explicit parameters.

Log or Return — Not Both

Logging an error and then returning it produces the same failure at every layer, and three log records for one bug:

// Bad — every caller up the stack logs it again
if err != nil {
    slog.Error("query failed", "err", err)
    return fmt.Errorf("query: %w", err)
}

// Good — wrap and return; the boundary logs once
if err != nil {
    return fmt.Errorf("loading user %d: %w", id, err)
}

The only layer that logs is the one that finishes the work: the HTTP handler, the job runner, main. That layer may log a detailed record server-side while returning a sanitised message to the client:

if err := checkout(ctx); err != nil {
    slog.ErrorContext(ctx, "checkout failed", "err", err, "user_id", uid)
    http.Error(w, "internal error", http.StatusInternalServerError)
    return
}

See the go-error-handling skill for the full handle-once pattern.

What Not to Log

  • Passwords, API keys, tokens, session IDs.
  • Full credit card numbers, SSNs, government IDs.
  • Request or response bodies that may contain user data.
  • Whole slices or maps of unbounded size (log lengths instead).
  • Anything you would not want appearing in a customer support screenshot.

Use a redacting slog.Handler (or wrap your own) so sensitive keys are blanked at the handler level, not at every call site. See references/slog-handler-ecosystem.md.

Anti-Patterns

Anti-patternWhy it hurtsDo this instead
log.Printf("msg %v", v)Unstructured; impossible to indexslog.Info("msg", "key", v)
fmt.Sprintf inside the messageData is now part of the stringStatic message + key/value attrs
Logging and returning the same errorDuplicate log records, noisy alertsWrap and return; log at the boundary
slog.Info("err: %v", err)Drops level semantics and structureslog.Error("op failed", "err", err)
New logger per callLoses request-scoped fieldsDerive once in middleware, pass via context
Mixed key styles (userId, user_id, UserID)Aggregators index them as different fieldsPick snake_case and stick to it
Logging the whole request bodyLeaks PII; explodes log volumeLog lengths and content type only
Introducing zap/zerolog without a benchmarkExtra dependency for no measured winStay on slog; benchmark before switching

Verification Checklist

Before finishing a logging change:

  • All new log calls use log/slog, not log.Printf
  • Each call has a static message and key-value attributes
  • slog.Error calls carry an "err" attribute
  • No call both logs and returns the same error
  • Keys use snake_case and match existing keys in the codebase
  • Handlers use *Context variants so trace correlation works
  • No secrets, PII, or unbounded values appear in attribute values
  • Request-scoped fields are added in middleware, not at each call site

References

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.