闲社服务运行正常AI智能体自动化平台
g

Go错误处理

技能标识:golang-samber-oops

Structured error handling in Golang with samber/oops — error builders, stack traces, error codes, error context, error wrapping, error attributes, user-facing vs developer messages, panic recovery, and logger integration. Apply when using or adopting samber/oops, or when the codebase already imports github.com/samber/oops.

作者:admin | 来源记录:ClawHub
登记来源
ClawHub
版本
V 1.1.2
检测标记
后台标记通过
免费
免费获取
0
收藏
来源与检测标记为平台登记信息,并不代表已展示可复核的检测报告。使用前请核对版本、依赖和所需权限,建议先在隔离环境中运行。
概述
安装方式
版本历史

Go错误处理

技能名称: golang-samber-oops
详细描述:
角色定位: 你是一位将错误视为结构化数据的 Go 工程师。每个错误都携带足够的上下文——领域、属性、追踪——以便值班工程师无需询问开发者即可诊断问题。

samber/oops 结构化错误处理

samber/oops 是 Go 标准错误处理的即插即用替代方案,增加了结构化上下文、堆栈追踪、错误码、公开消息和恐慌恢复。可变数据放入 .With() 属性中(而非消息字符串),因此 APM 工具(Datadog、Loki、Sentry)可以正确地对错误进行分组。与标准库方法(在日志记录点添加 slog 属性)不同,oops 属性会随错误一起在调用栈中传递。

为什么使用 samber/oops

标准的 Go 错误缺乏上下文——你只能看到 connection failed,但不知道是哪个用户触发的、正在运行什么查询、或者完整的调用栈。samber/oops 提供了:

  • - 结构化上下文 — 任何错误上的键值属性
  • 堆栈追踪 — 自动捕获调用栈
  • 错误码 — 机器可读的标识符
  • 公开消息 — 与技术细节分离的用户安全消息
  • 低基数消息 — 可变数据放在 .With() 属性中,而非消息字符串,以便 APM 工具正确地对错误进行分组

此技能并非详尽无遗。请参考库文档和代码示例以获取更多信息。Context7 可以作为发现平台提供帮助。

核心模式:错误构建器链

所有 oops 错误都使用流畅的构建器模式:

go
err := oops.
In(user-service). // 领域/功能
Tags(database, postgres). // 分类
Code(network_failure). // 机器可读标识符
User(user-123, email, foo@bar.com). // 用户上下文
With(query, query). // 自定义属性
Errorf(failed to fetch user: %s, timeout)

终止方法:

  • - .Errorf(format, args...) — 创建一个新错误
  • .Wrap(err) — 包装现有错误
  • .Wrapf(err, format, args...) — 带消息包装
  • .Join(err1, err2, ...) — 组合多个错误
  • .Recover(fn) / .Recoverf(fn, format, args...) — 将恐慌转换为错误

错误构建器方法

方法使用场景
.With(key, value)添加自定义键值属性(支持惰性 func() any 值)
.WithContext(ctx, key1, key2)
从 Go 上下文中提取值作为属性(支持惰性值) | | .In(domain) | 设置功能/服务/领域 | | .Tags(auth, sql) | 添加分类标签(通过 err.HasTag(tag) 查询) | | .Code(iamauthzmissing_permission) | 设置机器可读的错误标识符/标识 | | .Public(Could not fetch user.) | 设置用户安全消息(与技术细节分离) | | .Hint(Runbook: https://doc.acme.org/doc/abcd.md) | 为开发者添加调试提示 | | .Owner(team/slack) | 标识负责的团队/所有者 | | .User(id, k, v) | 添加用户标识符和属性 | | .Tenant(id, k, v) | 添加租户/组织上下文和属性 | | .Trace(id) | 添加追踪/关联 ID(默认:ULID) | | .Span(id) | 添加表示工作/操作单元的范围 ID(默认:ULID) | | .Time(t) | 覆盖错误时间戳(默认:time.Now()) | | .Since(t) | 设置自 t 以来的持续时间(通过 err.Duration() 暴露) | | .Duration(d) | 设置显式错误持续时间 | | .Request(req, includeBody) | 附加 *http.Request(可选包含请求体) | | .Response(res, includeBody) | 附加 *http.Response(可选包含响应体) | | oops.FromContext(ctx) | 从存储在 Go 上下文中的 OopsErrorBuilder 开始 |

常见场景

数据库/仓库层

go
func (r UserRepository) FetchUser(id string) (User, error) {
query := SELECT * FROM users WHERE id = $1
row, err := r.db.Query(query, id)
if err != nil {
return nil, oops.
In(user-repository).
Tags(database, postgres).
With(query, query).
With(user_id, id).
Wrapf(err, failed to fetch user from database)
}
// ...
}

HTTP 处理器层

go
func (h Handler) CreateUser(w http.ResponseWriter, r http.Request) {
userID := getUserID(r)

err := h.service.CreateUser(r.Context(), userID)
if err != nil {
return oops.
In(http-handler).
Tags(endpoint, /users).
Request(r, false).
User(userID).
Wrapf(err, create user failed)
}

w.WriteHeader(http.StatusCreated)
}

带可复用构建器的服务层

go
func (s *UserService) CreateOrder(ctx context.Context, req CreateOrderRequest) error {
builder := oops.
In(order-service).
Tags(orders, checkout).
Tenant(req.TenantID, plan, req.Plan).
User(req.UserID, email, req.UserEmail)

product, err := s.catalog.GetProduct(ctx, req.ProductID)
if err != nil {
return builder.
With(product_id, req.ProductID).
Wrapf(err, product lookup failed)
}

if product.Stock < req.Quantity {
return builder.
Code(insufficient_stock).
Public(Not enough items in stock.).
With(requested, req.Quantity).
With(available, product.Stock).
Errorf(insufficient stock for product %s, req.ProductID)
}

return nil
}

错误包装最佳实践

应该:直接包装,无需 nil 检查

go
// ✓ 好 — 如果 err 为 nil,Wrap 返回 nil
return oops.Wrapf(err, operation failed)

// ✗ 差 — 不必要的 nil 检查
if err != nil {
return oops.Wrapf(err, operation failed)
}
return nil

应该:在每一层添加上下文

每个架构层都应该通过 Wrap/Wrapf 添加上下文——至少在每次包边界处(不一定是每个函数调用)。

go
// ✓ 好 — 每一层都添加相关上下文
func Controller() error {
return oops.In(controller).Trace(traceID).Wrapf(Service(), user request failed)
}

func Service() error {
return oops.In(service).With(op, create_user).Wrapf(Repository(), db operation failed)
}

func Repository() error {
return oops.In(repository).Tags(database, postgres).Errorf(connection timeout)
}

应该:保持错误消息低基数

错误消息必须是低基数的,以便进行 APM 聚合。将可变数据插入消息字符串会破坏 Datadog、Loki、Sentry 中的分组。

go
// ✗ 差 — 高基数,破坏 APM 分组
oops.Errorf(failed to process user %s in tenant %s, userID, tenantID)

// ✓ 好 — 静态消息 + 结构化属性
oops.With(userid, userID).With(tenantid, tenantID).Errorf(failed to process user)

恐慌恢复

oops.Recover() 必须在 goroutine 边界使用。将恐慌转换为结构化错误:

go
func ProcessData(data string) (err error) {
return oops.
In(data-processor).
Code(panic_recovered).
Hint(Check input data format and dependencies).
With(panic_value, r).
Recover(func() {
riskyOperation(data)
})
}

访问错误信息

samber/oops 错误实现了标准的 error 接口。访问额外信息:

go
if oopsErr, ok := err.(oops.OopsError); ok {
fmt.Println(Code:, oopsErr.Code())
fmt.Println(Domain:, oopsErr.Domain())
fmt.Println(Tags:, oopsErr.Tags())
fmt.Println(Context:, oopsErr.Context())
fmt.Println(Stacktrace:, oopsErr.Stacktrace())
}

// 获取面向公众的消息,带后备值
publicMsg

标签

skill ai

通过对话安装

以下为平台配置的接入选项,并非逐项实测通过。能否安装取决于客户端支持、技能来源和运行环境:

OpenClaw WorkBuddy QClaw Kimi Claude

方式一:安装 SkillHub 和技能

帮我安装 SkillHub 和 golang-samber-oops-1776056281 技能

方式二:设置 SkillHub 为优先技能安装源

设置 SkillHub 为我的优先技能安装源,然后帮我安装 golang-samber-oops-1776056281 技能

通过命令行安装

skillhub install golang-samber-oops-1776056281

下载

⬇ 下载 golang-samber-oops v1.1.2(免费)

文件大小: 9.09 KB | 发布时间: 2026-4-14 11:04

v1.1.2 最新 2026-4-14 11:04
- Updated version metadata to 1.1.2 in SKILL.md.
- Fixed a minor typo: "informations" → "information" in the documentation.
返回顶部