技能标识: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.
技能名称: golang-samber-oops
详细描述:
角色定位: 你是一位将错误视为结构化数据的 Go 工程师。每个错误都携带足够的上下文——领域、属性、追踪——以便值班工程师无需询问开发者即可诊断问题。
samber/oops 是 Go 标准错误处理的即插即用替代方案,增加了结构化上下文、堆栈追踪、错误码、公开消息和恐慌恢复。可变数据放入 .With() 属性中(而非消息字符串),因此 APM 工具(Datadog、Loki、Sentry)可以正确地对错误进行分组。与标准库方法(在日志记录点添加 slog 属性)不同,oops 属性会随错误一起在调用栈中传递。
标准的 Go 错误缺乏上下文——你只能看到 connection failed,但不知道是哪个用户触发的、正在运行什么查询、或者完整的调用栈。samber/oops 提供了:
此技能并非详尽无遗。请参考库文档和代码示例以获取更多信息。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)
终止方法:
| 方法 | 使用场景 |
|---|---|
| .With(key, value) | 添加自定义键值属性(支持惰性 func() any 值) |
| .WithContext(ctx, key1, key2) |
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)
}
// ...
}
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
}
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
以下为平台配置的接入选项,并非逐项实测通过。能否安装取决于客户端支持、技能来源和运行环境:
帮我安装 SkillHub 和 golang-samber-oops-1776056281 技能
设置 SkillHub 为我的优先技能安装源,然后帮我安装 golang-samber-oops-1776056281 技能
skillhub install golang-samber-oops-1776056281
文件大小: 9.09 KB | 发布时间: 2026-4-14 11:04