一键导入
serr-structured-error-wrapper
Serr allows us to enrich our Go application errors with location, etc, and bubble them up to the caller where we only have to log once
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Serr allows us to enrich our Go application errors with location, etc, and bubble them up to the caller where we only have to log once
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | serr-structured-error-wrapper |
| description | Serr allows us to enrich our Go application errors with location, etc, and bubble them up to the caller where we only have to log once |
SErr enriches errors with contextual attributes and metadata at each stack frame as errors bubble up to the caller. Rather than logging at every level, wrap errors with attributes throughout the call stack, then extract all accumulated context at the top level for structured logging.
import "github.com/rohanthewiz/serr"
// Basic error with message
err := serr.New("something went wrong")
// Error with key-value attributes
err := serr.New("database error", "table", "users", "operation", "insert")
// Returns error type (like New but with format string)
err := serr.NewF("failed to read file: %s", filename)
err := serr.NewF("item %d not found in %s", itemID, collection)
err := serr.F("failed to process item %d: %s", itemID, reason)
se := serr.NewSErr("my error", "att1", "val1", "att2", "val2")
// Wrap with a message
err := serr.Wrap(dbErr, "failed to save user")
// Wrap with key-value pairs
err := serr.Wrap(dbErr, "table", "users", "user_id", userID)
// Each wrap automatically adds location and function context
err := serr.WrapF(baseErr, "processing item %d failed with code %s", itemID, code)
se := serr.WrapAsSErr(err, "context", "additional info")
fields := se.Fields() // Returns []string{key, val, key, val, ...}
Fields minus the reserved keys serr itself writes (location/function
frame context, msg wrap messages, and the user-message fields). Use it
to format an error for user-facing output, where frame context would be
noise:
err := serr.New("wrong number of parameters", "want", "1", "got", "0")
flds := serr.SErrFromErr(err).UserFields() // []string{"want", "1", "got", "0"}
mapFields := se.FieldsMap()
if val, ok := mapFields["user_id"]; ok {
fmt.Println("User:", val)
}
se.AppendAttributes("count", 123, "enabled", true)
mapAny := se.FieldsMapOfAny()
count := mapAny["count"].(int) // 123
// Values of duplicate keys are collected into a slice (caller to callee order)
mapSlice := se.FieldsMapOfSliceOfAny()
locations := mapSlice["location"] // []any of all location values
if val, ok := se.GetAttribute("key"); ok {
// val is of type any
}
se.AppendKeyValPairs("key1", "val1", "key2", "val2")
se.AppendAttributes("count", 42, "ratio", 3.14, "active", true)
errStr := serr.StringFromErr(err)
// Output: base error => location[pkg/service.go:42] function[pkg.DoThing] msg[context message]
str := se.FieldsAsString()
// Output: key1[val1], key2[val2], location[file.go:10], function[pkg.Func]
str := se.FieldsAsCustomString(", ", " -> ")
// attrSep: separator between attributes
// levelSep: separator between values of duplicate keys
se.SetUserMsg("Your payment could not be processed", serr.Severity.Error)
// Available severities:
// serr.Severity.Success
// serr.Severity.Error
// serr.Severity.Warn
// serr.Severity.Info
// From SErr instance
msg, severity := se.UserMsg()
// From any error
msg, severity := serr.UserMsg(err)
// With fallback message
msg, severity := serr.UserMsgFromErr(err, "An unexpected error occurred")
coreErr := se.GetError()
unwrapped := errors.Unwrap(se)
When a key is repeated across wraps, values are concatenated with arrows showing the call order:
se1 := serr.NewSErr("error", "status", "initial")
se2 := serr.WrapAsSErr(se1, "status", "updated")
fields := se2.FieldsMap()
// fields["status"] = "updated - initial" (newest first)
// If err is already a concrete SErr, it is returned as-is
se := serr.NewSerrNoContext(err)
se := serr.SErrFromErr(err) // same behavior, more ergonomic name
serr.AppendAttributesToErr(err, "key", "value", "count", 42)
Wrap safely handles nil errors:
result := serr.Wrap(nil, "some message")
// Returns nil (logs warning internally)
// Database layer
func getUser(id string) (*User, error) {
user, err := db.Query(...)
if err != nil {
return nil, serr.Wrap(err, "user_id", id, "operation", "query")
}
return user, nil
}
// Service layer
func processUser(id string) error {
user, err := getUser(id)
if err != nil {
return serr.Wrap(err, "action", "process_user")
}
// ... processing
return nil
}
// Handler layer - extract all context for logging
func handleRequest(w http.ResponseWriter, r *http.Request) {
err := processUser(r.URL.Query().Get("id"))
if err != nil {
logger.LogErr(err, "Request failed")
// All accumulated context from every layer is logged
}
}
import "github.com/rohanthewiz/logger"
if err != nil {
// All SErr attributes are extracted and logged
logger.LogErr(err, "Operation failed")
}
func handlePayment(amount float64) error {
err := paymentService.Process(amount)
if err != nil {
se := serr.WrapAsSErr(err, "amount", fmt.Sprintf("%.2f", amount))
se.SetUserMsg("Payment could not be processed. Please try again.", serr.Severity.Error)
return se
}
return nil
}
// In handler
if err != nil {
msg, sev := serr.UserMsg(err)
sendResponse(msg, sev)
}
loc := serr.FunctionLoc() // Returns "pkg/file.go:42"
name := serr.FunctionName() // Returns "pkg.FunctionName"
clone := se.Clone()