营销安装
- 作者仓库星标 0
- 作者更新于 实时读取
- 作者仓库 skills-registry
- 领域
- AI 智能
- 兼容 Agent
-
- Claude Code
- Cursor
- Cline
- Codex
- Windsurf
- Gemini CLI
- +20
- 信任分
- 88 / 100 · 社区维护
- 作者 / 版本 / 许可
- @tomevault-io · 未声明 license
- Token 消耗评级
- 中等消耗
- 接入复杂程度
- 需简单配置
- 是否需要外部 API Key
- 不需要
- 兼容的系统
- 未声明(默认跨平台)
- 底层运行要求
- 无特殊要求
- 文件与系统权限
-
- 只读
- 允许写入 / 修改
- 读取环境变量
- 网络行为
- 允许外网请求
- 安装命令数
- 26 条
档案由构建时根据 SKILL.md 与安装命令自动衍生,可能与作者实际意图存在差异。
需要注意: 未限定 allowed-tools,默认拥有全部工具权限。
---
name: golang-slog
description: Implement structured logging in Go with log/slog, including logger setup, common attributes, con…
category: AI 智能
runtime: 无特殊运行时
---
# golang-slog 输出预览
## PART A: 任务判断
- 适用问题:提示词、Agent 工作流、模型评估或自动化推理。
- 输入要求:目标材料、限制条件、期望输出和验收方式。
- 证据边界:围绕“When to Use / Default Stance / Version Notes”读取原文规则,不把推断写成作者承诺。
## PART B: 执行结果
- **01** 任务判断:确认你的需求是否属于提示词、Agent 工作流、模型评估或自动化推理,并标出输入、限制和预期结果。
- **02** 执行计划:优先按“When to Use / Default Stance / Version Notes”拆成步骤,说明每一步会读取什么、修改什么、产出什么。
- **03** 交付结果:给出可复制的命令、文件改动、检查清单或内容草稿,并说明如何继续迭代。
- **04** 风险边界:结合 读取文件、写入/修改文件、读取环境变量、会按任务需要访问外部网络、通常不需要额外 API Key 给出执行前确认项。
## Running Rules
- 读取文件、写入/修改文件、读取环境变量;会按任务需要访问外部网络;通常不需要额外 API Key。
- 先小样例验证,再放大到真实任务。
- 交付时同时给结果、检查口径和下一步迭代建议。 原文没有稳定的斜杠命令要求。安装验证后通常全局生效,直接在对话里点名这个 Skill 并描述任务即可。
告诉 Agent 目标文件或材料、期望结果、不可改范围、是否允许联网或执行命令。本 Skill 的权限画像是:读取文件、写入/修改文件、读取环境变量。
先用一个小任务确认它会围绕“When to Use / Default Stance / Version Notes”工作;涉及文件或命令时,先看 diff、日志、预览或测试结果。
检查最终产物是否包含明确结果、必要证据和下一步动作;如果输出泛泛而谈,就补充输入、边界和验收标准后重跑。
---
name: golang-slog
description: Implement structured logging in Go with log/slog, including logger setup, common attributes, con…
category: AI 智能
source: tomevault-io/skills-registry
---
# golang-slog
## 什么时候使用
- 把 AI / Agent方向的常用动作沉淀成 Agent 可调用的技能 适合处理AI Agent、提示词、模型评估与自动化推理,核心价值是把输入、判断、执行、验证和交付边界固定下来,避免 Agent 泛泛回答。 把任务拆成可执行、可检查…
- 面向提示词、Agent 工作流、模型评估或自动化推理,优先处理能明确输入、步骤和验收标准的工作。
## 需要提供什么
- 目标材料、目录范围、期望结果和不可改动内容。
- 是否允许联网、执行命令、读写文件或调用外部服务。
## 执行规则
- 围绕「When to Use / Default Stance / Version Notes」组织步骤,不把推断写成作者事实。
- 读取文件、写入/修改文件、读取环境变量;会按任务需要访问外部网络;通常不需要额外 API Key。
- 先跑小样例,确认结果可检查后再扩大任务范围。
## 输出要求
- 给出最终产物、关键证据、验证方式和下一步动作。
- 信息不足时标记 unknown,不编造命令、平台或依赖。 作者原文负责流程事实;仓库文件负责来源和命令;流狐只补充适用场景、限制和质量判断。
skill "golang-slog" {
输入层 -> 用户目标 + 目标文件 + 禁止范围 + 验收标准
上下文层 -> When to Use / Default Stance / Version Notes
规则层 -> SKILL.md 触发条件 / 执行顺序 / 输出格式
运行层 -> 无特殊运行时 | 读取文件、写入/修改文件、读取环境变量 | 会按任务需要访问外部网络
安全层 -> 通常不需要额外 API Key + 小任务验证 + diff / 日志复核
输出层 -> 可复制结果 + 检查清单 + 下一步迭代
} Golang slog
Use this skill when implementing structured logging in Go with the standard library log/slog package.
Use it alongside the structured-logging skill:
structured-loggingdefines the cross-language schema, field naming, safety, and observability rulesgolang-slogshows how to implement those rules idiomatically in Go withlog/slog
When to Use
- Adding structured logging to a Go service or CLI
- Replacing ad-hoc
log.Printfcalls with structured events - Standardising common log fields across Go services
- Building request-scoped, worker-scoped, or job-scoped loggers
- Implementing redaction, log schema normalisation, or dynamic log levels
Default Stance
Unless there is a clear reason not to, prefer these defaults:
- Use the standard library
log/slogfor Go 1.21+ - Prefer a
Formatconfig such asjsonortext, rather than a bareJSONboolean - Use
JSONHandlerin production andTextHandlerlocally - Construct the logger once during application startup
- Attach stable application metadata once with
Logger.With(...) - Keep the schema aligned with the
structured-loggingskill - Prefer typed attrs like
slog.String,slog.Int,slog.Int64,slog.Duration,slog.Bool - Prefer
InfoContext/ErrorContext/LogAttrswhen acontext.Contextis available - Use
WithGrouporslog.Group(...)for nested structured context - Use
ReplaceAttrandLogValuerfor schema normalisation and redaction - Pass
*slog.Loggeras a dependency instead of hiding it in global state when possible
Version Notes
log/slogis in the Go standard library starting with Go 1.21slog.MultiHandleris a newer addition; only use it if your project's Go version supports it- If your codebase targets older Go versions, use your existing logging stack instead of forcing
slog
Schema Alignment with structured-logging
The structured-logging skill recommends canonical fields like:
timestamplevelmessageserviceenvironmentapplication_versionapplication_commitrequest_idtrace_idspan_iduser_idoperationoutcomeduration_mserror_kinderror_codeerror_message
By default, slog emits built-in keys such as:
timelevelmsgsource
If you want Go logs to match the shared schema exactly, rename built-in keys with HandlerOptions.ReplaceAttr.
Recommended Logger Bootstrap
Centralise logger construction in main or a small bootstrap package.
package logging
import (
"fmt"
"io"
"log/slog"
)
type Format string
const (
FormatJSON Format = "json"
FormatText Format = "text"
)
type config struct {
environment string
service string
applicationVersion string
applicationCommit string
format Format
addSource bool
level slog.Leveler
replaceAttr func(groups []string, a slog.Attr) slog.Attr
}
type Option func(*config)
func WithEnvironment(v string) Option {
return func(c *config) { c.environment = v }
}
func WithService(v string) Option {
return func(c *config) { c.service = v }
}
func WithApplicationVersion(v string) Option {
return func(c *config) { c.applicationVersion = v }
}
func WithApplicationCommit(v string) Option {
return func(c *config) { c.applicationCommit = v }
}
func WithFormat(v Format) Option {
return func(c *config) { c.format = v }
}
func WithAddSource(v bool) Option {
return func(c *config) { c.addSource = v }
}
func WithLevel(v slog.Leveler) Option {
return func(c *config) { c.level = v }
}
func WithReplaceAttr(fn func(groups []string, a slog.Attr) slog.Attr) Option {
return func(c *config) { c.replaceAttr = fn }
}
func LevelVarFromString(v string) (*slog.LevelVar, error) {
var level slog.LevelVar
if err := level.UnmarshalText([]byte(v)); err != nil {
return nil, fmt.Errorf("parse log level %q: %w", v, err)
}
return &level, nil
}
func New(w io.Writer, opts ...Option) (*slog.Logger, error) {
cfg := config{
format: FormatJSON,
level: slog.LevelInfo,
replaceAttr: func(groups []string, a slog.Attr) slog.Attr {
switch a.Key {
case slog.TimeKey:
a.Key = "timestamp"
case slog.MessageKey:
a.Key = "message"
}
return a
},
}
for _, opt := range opts {
opt(&cfg)
}
handlerOptions := &slog.HandlerOptions{
AddSource: cfg.addSource,
Level: cfg.level,
ReplaceAttr: cfg.replaceAttr,
}
var handler slog.Handler
switch cfg.format {
case FormatJSON:
handler = slog.NewJSONHandler(w, handlerOptions)
case FormatText:
handler = slog.NewTextHandler(w, handlerOptions)
default:
return nil, fmt.Errorf("unsupported log format: %q", cfg.format)
}
logger := slog.New(handler).With(
slog.String("service", cfg.service),
slog.String("environment", cfg.environment),
slog.String("application_version", cfg.applicationVersion),
slog.String("application_commit", cfg.applicationCommit),
)
return logger, nil
}
Example use:
level, err := logging.LevelVarFromString("info")
if err != nil {
return err
}
logger, err := logging.New(os.Stdout,
logging.WithService("users-api"),
logging.WithEnvironment("production"),
logging.WithApplicationVersion("1.8.0"),
logging.WithApplicationCommit("a1b2c3d4"),
logging.WithFormat(logging.FormatJSON),
logging.WithLevel(level),
)
if err != nil {
return err
}
Guidance:
- Build one base logger at startup
- Prefer functional options when the bootstrap needs several optional settings
- Add
service,environment,application_version, andapplication_committhere - Prefer a format enum/string like
jsonortextover aJSON bool - Use
JSONHandlerfor production ingestion - Use
TextHandleronly when human readability is more important than strict machine parsing
Global vs Injected Logger
Prefer:
- Constructing the logger at startup
- Injecting
*slog.Loggerinto servers, services, workers, and repositories that need it
Avoid:
- Scattering calls to
slog.Default()throughout the codebase without a reason - Using package globals for everything
SetDefault is acceptable at the process boundary if you explicitly want top-level slog.Info(...) and legacy log.Print(...) calls to flow through the same handler, but keep that decision near startup.
Common Attribute Strategy
Stable Application Metadata
Attach these once to the base logger:
baseLogger := logger.With(
slog.String("service", "users-api"),
slog.String("environment", "production"),
slog.String("application_version", "1.8.0"),
slog.String("application_commit", "a1b2c3d4"),
)
Request- or Job-Scoped Metadata
Attach these at the request/job boundary:
requestLogger := baseLogger.With(
slog.String("request_id", requestID),
slog.String("trace_id", traceID),
slog.String("span_id", spanID),
slog.String("user_id", userID),
)
Prefer deriving child loggers with With(...) rather than repeating the same attrs on every line.
Context Usage
If a context.Context is available, prefer the ...Context forms:
logger.InfoContext(ctx, ...)logger.ErrorContext(ctx, ...)logger.LogAttrs(ctx, ...)
Why:
- custom handlers may extract trace/span metadata from the context
- it keeps logs aligned with request-scoped and tracing-aware systems
Rules:
- pass context when you already have one
- do not create fake contexts just to log
- do not treat context as a generic service locator
HTTP Request Pattern
Derive a request logger at the boundary and reuse it for the whole flow.
func (s *Server) handleGetUser(w http.ResponseWriter, r *http.Request) {
start := time.Now()
logger := s.logger.With(
slog.String("request_id", requestIDFromRequest(r)),
slog.String("trace_id", traceIDFromContext(r.Context())),
slog.String("http_method", r.Method),
slog.String("http_route", "/users/{id}"),
)
user, err := s.userService.GetByID(r.Context(), chi.URLParam(r, "id"))
if err != nil {
logger.ErrorContext(r.Context(), "get user failed",
slog.String("operation", "get_user"),
slog.String("outcome", "failure"),
slog.String("error_kind", "user_lookup_failed"),
slog.String("error_message", "failed to fetch user"),
slog.Int64("duration_ms", time.Since(start).Milliseconds()),
)
return
}
logger.InfoContext(r.Context(), "http request completed",
slog.String("operation", "get_user"),
slog.String("outcome", "success"),
slog.Int("http_status_code", http.StatusOK),
slog.Int64("duration_ms", time.Since(start).Milliseconds()),
)
_ = user
}
Keep boundary logs summary-oriented. Avoid logging every internal function call in production.
Prefer Typed Attrs
Prefer:
logger.Info("user authenticated",
slog.String("user_id", userID),
slog.String("operation", "login"),
slog.Int64("duration_ms", duration.Milliseconds()),
)
Over large alternating key/value calls when they get dense or performance-sensitive.
Notes:
- alternating key/value syntax is valid and ergonomic for small calls
- typed attrs are clearer in larger calls
- typed attrs also avoid mistakes that produce
!BADKEY
Hot Paths and LogAttrs
If profiling shows logging overhead matters, prefer LogAttrs with typed attrs.
logger.LogAttrs(ctx, slog.LevelInfo, "cache refresh completed",
slog.String("operation", "refresh_cache"),
slog.String("outcome", "success"),
slog.Int64("duration_ms", duration.Milliseconds()),
)
Use this especially in high-frequency code paths.
Grouping Attributes
Use groups when nested context improves clarity or prevents key collisions.
logger.Info("request finished",
slog.Group("request",
slog.String("method", r.Method),
slog.String("route", "/users/{id}"),
),
slog.Group("auth",
slog.String("user_id", userID),
slog.String("actor_type", "user"),
),
)
Use WithGroup("http") or slog.Group("http", ...) when multiple subsystems would otherwise reuse keys like id, status, or method.
Error Logging
Log errors as structured events, not only as formatted strings.
Prefer:
logger.ErrorContext(ctx, "invoice payment failed",
slog.String("operation", "charge_invoice"),
slog.String("outcome", "failure"),
slog.String("error_kind", "payment_gateway_error"),
slog.String("error_code", "card_declined"),
slog.String("error_message", "payment provider declined charge"),
slog.String("user_id", userID),
slog.Int64("duration_ms", duration.Milliseconds()),
)
Guidance:
- prefer stable domain fields like
error_kindanderror_code - keep
error_messagesafe and sanitised - include the raw
errorvalue only when useful and safe - avoid logging the same failure at every layer
When you do need the raw error object:
logger.ErrorContext(ctx, "dependency call failed",
slog.Any("error", err),
slog.String("dependency", "billing-api"),
)
JSONHandler formats error-valued attrs as strings.
Redaction and Sensitive Data
Never log secrets directly.
Use LogValuer for type-level redaction:
type Token string
func (Token) LogValue() slog.Value {
return slog.StringValue("REDACTED")
}
Use ReplaceAttr for cross-cutting redaction or key normalisation:
opts := &slog.HandlerOptions{
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
if a.Key == "authorization" || a.Key == "token" {
return slog.String(a.Key, "REDACTED")
}
return a
},
}
Prefer type-local redaction with LogValuer when the data type itself is sensitive. Use ReplaceAttr when you need a handler-wide policy.
Performance Guidance
- Use
Logger.With(...)for common attrs instead of repeating them - Prefer
LogAttrsin hot paths - Use typed attrs for common scalar values
- Avoid expensive string formatting before checking whether a level is enabled
- Pass values directly when possible instead of eagerly calling
.String() - Use
LogValuerto defer expensive computation for debug logs
Example:
if logger.Enabled(ctx, slog.LevelDebug) {
logger.DebugContext(ctx, "computed plan",
slog.Any("plan", buildDebugPlan(input)),
)
}
Avoid:
logger.DebugContext(ctx, "computed plan",
slog.String("plan", buildDebugPlan(input).String()),
)
if buildDebugPlan is expensive and debug logging may be disabled.
Dynamic Levels
Use slog.LevelVar when the level may change at runtime.
var level = new(slog.LevelVar)
level.Set(slog.LevelInfo)
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: level,
}))
If your level comes from config, flags, or environment variables, a small helper is reasonable:
func LevelVarFromString(v string) (*slog.LevelVar, error) {
var level slog.LevelVar
if err := level.UnmarshalText([]byte(v)); err != nil {
return nil, fmt.Errorf("parse log level %q: %w", v, err)
}
return &level, nil
}
This is appropriate for CLIs, services with admin-configurable verbosity, or temporary incident debugging.
Bridging the Old log Package
If you still have legacy log.Print / log.Printf calls, you can centralise output with:
slog.SetDefault(logger)
That allows the default log package to emit through the configured slog handler.
Use this only as a deliberate application-wide choice, not as a substitute for dependency injection.
Testing Guidance
For tests around logging:
- write to a
bytes.Buffer - use
ReplaceAttrto remove or normalise time/source fields - assert on structured output, not on fragile formatting details
- prefer JSON output in tests when machine-readable assertions are easier
Example:
var buf bytes.Buffer
logger := slog.New(slog.NewJSONHandler(&buf, &slog.HandlerOptions{
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
if a.Key == slog.TimeKey {
return slog.Attr{}
}
return a
},
}))
Review Checklist
When reviewing Go slog usage, check for:
log/slogused consistently instead of mixed ad-hoc logging styles- base logger created centrally at startup
- stable common attrs attached with
With(...) - shared schema aligned with the
structured-loggingskill ReplaceAttrused when canonical key renaming is neededInfoContext/ErrorContextused when context already existsLogAttrsused in hot paths where it matters- structured error fields instead of only formatted strings
- redaction for secrets and sensitive values
- no repeated noisy logs for the same failure across layers
- JSON logs used for production ingestion
Anti-Patterns
Avoid these unless there is a strong reason:
- logging with
fmt.Sprintfinto one giant string - using
slog.Anyfor everything when typed attrs are clearer - eagerly computing expensive debug fields when debug is disabled
- forcing every function to call
slog.Default() - burying the logger in context as a universal service locator
- logging secrets, tokens, or raw auth headers
- leaving built-in keys as
time/msgwhen the rest of the platform expectstimestamp/message - emitting text logs in production when downstream systems expect JSON
- logging every internal function call instead of boundary summaries
Quick Template
level := new(slog.LevelVar)
level.Set(slog.LevelInfo)
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: level,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
switch a.Key {
case slog.TimeKey:
a.Key = "timestamp"
case slog.MessageKey:
a.Key = "message"
}
return a
},
})).With(
slog.String("service", "users-api"),
slog.String("environment", "production"),
slog.String("application_version", "1.0.0"),
slog.String("application_commit", "abcdef12"),
)
logger.Info("user authenticated",
slog.String("request_id", requestID),
slog.String("trace_id", traceID),
slog.String("user_id", userID),
slog.String("operation", "login"),
slog.String("outcome", "success"),
slog.Int64("duration_ms", duration.Milliseconds()),
)
Source: brpaz/agent-skills — distributed by TomeVault.
先判断是否适合
作者设计意图
作者的方法与取舍
边界和复核