{"id":"ab450a1b-714e-4e46-92a3-fb7ed691ee6a","shortId":"YcuVpE","kind":"skill","title":"go-logging","tagline":"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 ","description":"# Go Logging\n\n## Core Principle\n\nLogs are for **operators**, not developers. Every log line should help someone\ndiagnose a production issue. If it doesn't serve that purpose, it's noise.\n\n---\n\n## Choosing a Logger\n\n> **Normative**: Use `log/slog` for new Go code.\n\n`slog` is structured, leveled, and in the standard library (Go 1.21+). It\ncovers the vast majority of production logging needs.\n\n```\nWhich logger?\n├─ New production code      → log/slog\n├─ Trivial CLI / one-off    → log (standard)\n└─ Measured perf bottleneck → zerolog or zap (benchmark first)\n```\n\nDo not introduce a third-party logging library unless profiling shows `slog`\nis a bottleneck in your hot path. When you do, keep the same structured\nkey-value style.\n\n> Read [references/LOGGING-PATTERNS.md](references/LOGGING-PATTERNS.md) when setting up slog handlers, configuring JSON/text output, or migrating from log.Printf to slog.\n\n---\n\n## Structured Logging\n\n> **Normative**: Always use key-value pairs. Never interpolate values into the message string.\n\nThe message is a **static description** of what happened. Dynamic data goes in\nkey-value attributes:\n\n```go\n// Good: static message, structured fields\nslog.Info(\"order placed\", \"order_id\", orderID, \"total\", total)\n\n// Bad: dynamic data baked into the message string\nslog.Info(fmt.Sprintf(\"order %d placed for $%.2f\", orderID, total))\n```\n\n### Key Naming\n\n> **Advisory**: Use `snake_case` for log attribute keys.\n\nKeys should be lowercase, underscore-separated, and consistent across the\ncodebase: `user_id`, `request_id`, `elapsed_ms`.\n\n### Typed Attributes\n\nFor performance-critical paths, use typed constructors to avoid allocations:\n\n```go\nslog.LogAttrs(ctx, slog.LevelInfo, \"request handled\",\n    slog.String(\"method\", r.Method),\n    slog.Int(\"status\", code),\n    slog.Duration(\"elapsed\", elapsed),\n)\n```\n\n> Read [references/LEVELS-AND-CONTEXT.md](references/LEVELS-AND-CONTEXT.md) when optimizing log performance or pre-checking with Enabled().\n\n---\n\n## Log Levels\n\n> **Advisory**: Follow these level semantics consistently.\n\n| Level | When to use | Production default |\n|-------|-------------|--------------------|\n| Debug | Developer-only diagnostics, tracing internal state | Disabled |\n| Info  | Notable lifecycle events: startup, shutdown, config loaded | Enabled |\n| Warn  | Unexpected but recoverable: deprecated feature used, retry succeeded | Enabled |\n| Error | Operation failed, requires operator attention | Enabled |\n\n**Rules of thumb**:\n- If nobody should act on it, it's not Error — use Warn or Info\n- If it's only useful with a debugger attached, it's Debug\n- `slog.Error` should always include an `\"err\"` attribute\n\n```go\nslog.Error(\"payment failed\", \"err\", err, \"order_id\", id)\nslog.Warn(\"retry succeeded\", \"attempt\", n, \"endpoint\", url)\nslog.Info(\"server started\", \"addr\", addr)\nslog.Debug(\"cache lookup\", \"key\", key, \"hit\", hit)\n```\n\n> Read [references/LEVELS-AND-CONTEXT.md](references/LEVELS-AND-CONTEXT.md) when choosing between Warn and Error or defining custom verbosity levels.\n\n---\n\n## Request-Scoped Logging\n\n> **Advisory**: Derive loggers from context to carry request-scoped fields.\n\nUse middleware to enrich a logger with request ID, user ID, or trace ID, then\npass the enriched logger downstream via context or as an explicit parameter:\n\n```go\nfunc middleware(next http.Handler) http.Handler {\n    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n        logger := slog.With(\"request_id\", requestID(r))\n        ctx := context.WithValue(r.Context(), loggerKey, logger)\n        next.ServeHTTP(w, r.WithContext(ctx))\n    })\n}\n```\n\nAll subsequent log calls in that request carry `request_id` automatically.\n\n> Read [references/LOGGING-PATTERNS.md](references/LOGGING-PATTERNS.md) when implementing logging middleware or passing loggers through context.\n\n---\n\n## Log or Return, Not Both\n\n> **Normative**: Handle each error exactly once — either log it or return it.\n\nLogging an error and then returning it causes duplicate noise as callers up the\nstack also handle the error.\n\n```go\n// Bad: logged here AND by every caller up the stack\nif err != nil {\n    slog.Error(\"query failed\", \"err\", err)\n    return fmt.Errorf(\"query: %w\", err)\n}\n\n// Good: wrap and return — let the caller decide\nif err != nil {\n    return fmt.Errorf(\"query: %w\", err)\n}\n```\n\n**Exception**: HTTP handlers and other top-of-stack boundaries may log detailed\nerrors server-side while returning a sanitized message to the client:\n\n```go\nif err != nil {\n    slog.Error(\"checkout failed\", \"err\", err, \"user_id\", uid)\n    http.Error(w, \"internal error\", http.StatusInternalServerError)\n    return\n}\n```\n\nSee [go-error-handling](../go-error-handling/SKILL.md) for the full\nhandle-once pattern and error wrapping guidance.\n\n---\n\n## What NOT to Log\n\n> **Normative**: Never log secrets, credentials, PII, or high-cardinality unbounded data.\n\n- Passwords, API keys, tokens, session IDs\n- Full credit card numbers, SSNs\n- Request/response bodies that may contain user data\n- Entire slices or maps of unbounded size\n\n> Read [references/LEVELS-AND-CONTEXT.md](references/LEVELS-AND-CONTEXT.md) when deciding what data is safe to include in log attributes.\n\n---\n\n## Quick Reference\n\n| Do | Don't |\n|----|-------|\n| `slog.Info(\"msg\", \"key\", val)` | `log.Printf(\"msg %v\", val)` |\n| Static message + structured fields | `fmt.Sprintf` in message |\n| `snake_case` keys | camelCase or inconsistent keys |\n| Log OR return errors | Log AND return the same error |\n| Derive logger from context | Create a new logger per call |\n| Use `slog.Error` with `\"err\"` attr | `slog.Info` for errors |\n| Pre-check `Enabled()` on hot paths | Always allocate log args |\n\n---\n\n## Related Skills\n\n- **Error handling**: See [go-error-handling](../go-error-handling/SKILL.md) when deciding whether to log or return an error, or for the handle-once pattern\n- **Context propagation**: See [go-context](../go-context/SKILL.md) when passing request-scoped values (including loggers) through context\n- **Performance**: See [go-performance](../go-performance/SKILL.md) when optimizing hot-path logging or reducing allocations in log calls\n- **Code review**: See [go-code-review](../go-code-review/SKILL.md) when reviewing logging practices in Go PRs","tags":["logging","golang","skills","cxuu","agent-skills","ai-agent","ai-assistant","claude","claude-code","codex","cursor","llm"],"capabilities":["skill","source-cxuu","skill-go-logging","topic-agent-skills","topic-ai-agent","topic-ai-assistant","topic-claude","topic-claude-code","topic-codex","topic-cursor","topic-golang","topic-llm"],"categories":["golang-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/cxuu/golang-skills/go-logging","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add cxuu/golang-skills","source_repo":"https://github.com/cxuu/golang-skills","install_from":"skills.sh"}},"qualityScore":"0.491","qualityRationale":"deterministic score 0.49 from registry signals: · indexed on github topic:agent-skills · 82 github stars · SKILL.md body (6,256 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-02T12:55:18.727Z","embedding":null,"createdAt":"2026-04-18T22:13:18.126Z","updatedAt":"2026-05-02T12:55:18.727Z","lastSeenAt":"2026-05-02T12:55:18.727Z","tsv":"'/go-code-review/skill.md':857 '/go-context/skill.md':821 '/go-error-handling/skill.md':656,798 '/go-performance/skill.md':837 '1.21':100 '2f':240 'across':262 'act':367 'ad':29 'addr':416,417 'advisori':245,314,443 'alloc':283,786,846 'also':22,564 'alway':182,392,785 'api':685 'approach':9 'arg':788 'attach':386 'attempt':409 'attent':359 'attr':774 'attribut':211,251,272,396,722 'automat':519 'avoid':282 'bad':226,569 'bake':229 'benchmark':129 'bodi':696 'bottleneck':125,146 'boundari':617 'cach':419 'call':512,769,849 'caller':560,575,598 'camelcas':746 'card':692 'cardin':681 'carri':449,516 'case':248,744 'caus':556 'check':309,780 'checkout':638 'choos':6,80,429 'cli':117 'client':632 'code':89,114,295,850,855 'codebas':264 'config':341 'configur':10,170 'consist':261,319 'constructor':280 'contain':699 'context':33,447,475,531,763,815,820,831 'context.withvalue':501 'core':52 'cover':102 'creat':764 'credenti':676 'credit':691 'critic':276 'ctx':286,500,508 'custom':436 'd':237 'data':205,228,683,701,715 'debug':326,389 'debugg':385 'decid':17,599,713,800 'default':325 'defin':435 'deprec':348 'deriv':444,760 'descript':200 'detail':620 'develop':59,328 'developer-on':327 'diagnos':66 'diagnost':330 'disabl':334 'doesn':46,72 'downstream':473 'duplic':557 'dynam':204,227 'either':543 'elaps':269,297,298 'enabl':311,343,353,360,781 'endpoint':411 'enrich':457,471 'entir':702 'err':395,401,402,580,585,586,591,601,607,635,640,641,773 'error':354,373,433,540,551,567,621,648,654,665,753,759,777,791,796,807 'even':42 'event':338 'everi':60,574 'exact':541 'except':608 'explicit':48,479 'fail':356,400,584,639 'featur':349 'field':217,453,739 'first':130 'fmt.errorf':588,604 'fmt.sprintf':235,740 'follow':315 'full':659,690 'func':482,489 'go':2,21,50,88,99,212,284,397,481,568,633,653,795,819,835,854,863 'go-code-review':853 'go-context':818 'go-error-handl':652,794 'go-log':1 'go-perform':834 'goe':206 'good':213,592 'guidanc':667 'handl':289,538,565,655,661,792,797,812 'handle-onc':660,811 'handler':169,610 'happen':203 'help':64 'high':680 'high-cardin':679 'hit':423,424 'hot':149,783,841 'hot-path':840 'http':609 'http.error':645 'http.handler':485,486 'http.handlerfunc':488 'http.request':493 'http.responsewriter':491 'http.statusinternalservererror':649 'id':222,266,268,404,405,462,464,467,497,518,643,689 'implement':524 'includ':393,719,828 'inconsist':748 'info':335,377 'intern':332,647 'interpol':189 'introduc':133 'issu':69 'json/text':171 'keep':154 'key':159,185,209,243,252,253,421,422,686,730,745,749 'key-valu':158,184,208 'let':596 'level':19,93,313,317,320,438 'librari':98,139 'lifecycl':337 'line':62 'load':342 'log':3,8,14,18,28,35,39,51,54,61,108,121,138,180,250,304,312,442,511,525,532,544,549,570,619,671,674,721,750,754,787,803,843,848,860 'log.printf':176,732 'log/slog':85,115 'logger':82,111,445,459,472,494,504,529,761,767,829 'loggerkey':503 'lookup':420 'lowercas':256 'major':105 'map':705 'may':618,698 'measur':123 'mention':49 'messag':193,196,215,232,629,737,742 'method':291 'middlewar':455,483,526 'migrat':37,174 'ms':270 'msg':729,733 'n':410 'name':244 'need':109 'never':188,673 'new':87,112,766 'next':484 'next.servehttp':505 'nil':581,602,636 'nobodi':365 'nois':79,558 'normat':83,181,537,672 'notabl':336 'number':693 'one':119 'one-off':118 'oper':57,355,358 'optim':303,839 'order':219,221,236,403 'orderid':223,241 'output':172 'pair':187 'paramet':480 'parti':137 'pass':469,528,823 'password':684 'path':150,277,784,842 'pattern':663,814 'payment':399 'per':768 'perf':124 'perform':275,305,832,836 'performance-crit':274 'pii':677 'place':220,238 'practic':861 'pre':308,779 'pre-check':307,778 'principl':53 'product':27,68,107,113,324 'profil':141 'propag':816 'prs':864 'purpos':76 'queri':583,589,605 'quick':723 'r':492,499 'r.context':502 'r.method':292 'r.withcontext':507 'read':162,299,425,520,709 'recover':347 'reduc':845 'refer':724 'references/levels-and-context.md':300,301,426,427,710,711 'references/logging-patterns.md':163,164,521,522 'relat':789 'request':31,267,288,440,451,461,496,515,517,825 'request-scop':30,439,450,824 'request/response':695 'requestid':498 'requir':357 'retri':351,407 'return':487,534,547,554,587,595,603,626,650,752,756,805 'review':851,856,859 'rule':361 'safe':717 'sanit':628 'scope':32,441,452,826 'secret':675 'see':651,793,817,833,852 'semant':318 'separ':259 'serv':74 'server':414,623 'server-sid':622 'session':688 'set':25,166 'show':142 'shutdown':340 'side':624 'size':708 'skill':790 'skill-go-logging' 'slice':703 'slog':11,41,90,143,168,178 'slog.debug':418 'slog.duration':296 'slog.error':390,398,582,637,771 'slog.info':218,234,413,728,775 'slog.int':293 'slog.levelinfo':287 'slog.logattrs':285 'slog.string':290 'slog.warn':406 'slog.with':495 'snake':247,743 'someon':65 'source-cxuu' 'ssns':694 'stack':563,578,616 'standard':97,122 'start':415 'startup':339 'state':333 'statement':15 'static':199,214,736 'status':294 'string':194,233 'structur':13,92,157,179,216,738 'style':161 'subsequ':510 'succeed':352,408 'third':136 'third-parti':135 'thumb':363 'token':687 'top':614 'top-of-stack':613 'topic-agent-skills' 'topic-ai-agent' 'topic-ai-assistant' 'topic-claude' 'topic-claude-code' 'topic-codex' 'topic-cursor' 'topic-golang' 'topic-llm' 'total':224,225,242 'trace':331,466 'trivial':116 'type':271,279 'uid':644 'unbound':682,707 'underscor':258 'underscore-separ':257 'unexpect':345 'unless':140 'url':412 'use':4,23,84,183,246,278,323,350,374,382,454,770 'user':45,265,463,642,700 'v':734 'val':731,735 'valu':160,186,190,210,827 'vast':104 'verbos':437 'via':474 'w':490,506,590,606,646 'warn':344,375,431 'whether':801 'wrap':593,666 'write':12 'zap':128 'zerolog':126","prices":[{"id":"17aee283-f0b5-4919-a927-d050920651e6","listingId":"ab450a1b-714e-4e46-92a3-fb7ed691ee6a","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"cxuu","category":"golang-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T22:13:18.126Z"}],"sources":[{"listingId":"ab450a1b-714e-4e46-92a3-fb7ed691ee6a","source":"github","sourceId":"cxuu/golang-skills/go-logging","sourceUrl":"https://github.com/cxuu/golang-skills/tree/main/skills/go-logging","isPrimary":false,"firstSeenAt":"2026-04-18T22:13:18.126Z","lastSeenAt":"2026-05-02T12:55:18.727Z"}],"details":{"listingId":"ab450a1b-714e-4e46-92a3-fb7ed691ee6a","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cxuu","slug":"go-logging","github":{"repo":"cxuu/golang-skills","stars":82,"topics":["agent-skills","ai-agent","ai-assistant","claude","claude-code","codex","cursor","go","golang","llm"],"license":"apache-2.0","html_url":"https://github.com/cxuu/golang-skills","pushed_at":"2026-03-15T19:32:10Z","description":"AI Agent Skills for idiomatic, production-ready Go code, distilled from Google, Uber, Community","skill_md_sha":"5c4554fbd26a9bacfc5c4f0ea8a5d98bceb82517","skill_md_path":"skills/go-logging/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cxuu/golang-skills/tree/main/skills/go-logging"},"layout":"multi","source":"github","category":"golang-skills","frontmatter":{"name":"go-logging","license":"Apache-2.0","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":"slog requires Go 1.21+; slog/slogtest requires Go 1.22+"},"skills_sh_url":"https://skills.sh/cxuu/golang-skills/go-logging"},"updatedAt":"2026-05-02T12:55:18.727Z"}}