agentsclimarketplace

Go logging

Skill pngdeity/apm-user-repository/packages/golang-runtime/.apm/skills/go-logging

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.From its SKILL.md

Install
npx -y skills add pngdeity/apm-user-repository --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

  • 0 stars0 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.

SKILL.md

5.3 KB, ~1.2k tokens by cl100k_base, as published. Nobody here has run it

Go Logging

Core Principle

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.


Choosing a Logger

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.

Read references/LOGGING-PATTERNS.md when setting up slog handlers, configuring JSON/text output, or migrating from log.Printf to slog.


Structured Logging

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))

Key Naming

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.

Typed Attributes

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),
)

Read references/LEVELS-AND-CONTEXT.md when optimizing log performance or pre-checking with Enabled().


Log Levels

Advisory: Follow these level semantics consistently.

LevelWhen to useProduction default
DebugDeveloper-only diagnostics, tracing internal stateDisabled
InfoNotable lifecycle events: startup, shutdown, config loadedEnabled
WarnUnexpected but recoverable: deprecated feature used, retry succeededEnabled
ErrorOperation failed, requires operator attentionEnabled

Rules of thumb:

  • If nobody should act on it, it's not Error — use Warn or Info
  • If it's only useful with a debugger attached, it's Debug
  • slog.Error should always 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)

Request-Scoped Logging

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:

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

Read references/LOGGING-PATTERNS.md when implementing logging middleware or passing loggers through context.


Log or Return, Not Both

Normative: Handle each error exactly once — either log it or return it.

// Bad: logged here AND by every caller up the stack
if err != nil {
    slog.Error("query failed", "err", err)
    return fmt.Errorf("query: %w", err)
}

// Good: wrap and return — let the caller decide
if err != nil {
    return fmt.Errorf("query: %w", err)
}

Exception: HTTP handlers may log detailed errors while returning sanitized messages:

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

What NOT to Log

Normative: Never log secrets, credentials, PII, or high-cardinality unbounded data.

  • Passwords, API keys, tokens, session IDs
  • Full credit card numbers, SSNs
  • Request/response bodies that may contain user data

Quick Reference

DoDon't
slog.Info("msg", "key", val)log.Printf("msg %v", val)
Static message + structured fieldsfmt.Sprintf in message
snake_case keyscamelCase or inconsistent keys
Log OR return errorsLog AND return the same error
Derive logger from contextCreate a new logger per call
Use slog.Error with "err" attrslog.Info for errors

What ships with it: 2 files

14.8 KB alongside SKILL.md

Keep looking

Skills are one crate of 325,949. 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.