errs

API

Az errs csomag API-ja: New, Wrap, Define, attribútumok és accessorok.

Az errs teljes felülete: öt konstruktor, négy attribútumfüggvény, három accessor, és a Define, amivel egy hibamódot egyszer nevezel el és mindenhol újrahasználod. Minden itt leírt dolog együttműködik a sima errors.Is/errors.As-szal, mert az *errs.Error úgy wrappel, mint bármely más Go hiba. Lásd az Áttekintés oldalt a telepítéshez és a kétüzenetes tervezési elvhez, amire ez az API épül.

import "github.com/gp-system/errs"

Konstruktorok

func New(msg string, attrs ...Attr) *Error
func Errorf(format string, args ...any) *Error
func Wrap(err error, msg string, attrs ...Attr) *Error
func Wrapf(err error, format string, args ...any) *Error
func NewPanic(recovered any, attrs ...Attr) *Error
  • New egy friss hibát indít, a hívási pontnál capture-ölve a stacket.
  • Errorf a New fmt.Sprintf-stílusú formázással, attribútumok nélkül.
  • Wrap egy új üzenetet fűz egy meglévő hibához, az eredetit okként megtartva; nil-re nil, tehát az errs.Wrap(err, "...") feltétel nélkül biztonságosan hívható egy potenciálisan nil err-re.
  • Wrapf a Wrap formázással.
  • NewPanic egy elkapott panic-értéket alakít *errs.Error-rá, aminek a stackje a panic helyére mutat, recover() kezelőben használatra.
if err := repo.Save(ctx, order); err != nil {
    return errs.Wrap(err, "orders: save", errs.With("order_id", order.ID))
}

if err := validateQuantity(qty); err != nil {
    return errs.Errorf("invalid quantity %d: %w", qty, err)
}

Attribútumok

Az Attr egy func(*Error), sorrendben alkalmazva bármelyik konstruktorra:

type Attr func(*Error)

func Code(code string) Attr    // gépi olvasású azonosító, üzenetátfogalmazás közben is stabil
func Public(msg string) Attr   // az egyetlen szöveg, amit a kliens valaha láthat
func Status(code int) Attr     // a HTTP-formájú státusz, amire ez a hiba leképeződik
func With(key string, value any) Attr // strukturált metadata, az adott wrap-szinten rögzítve
var ErrOutOfStock = errs.Define("out_of_stock",
    errs.Public("A termék elfogyott."),
    errs.Status(http.StatusConflict))

err := ErrOutOfStock.New("orders: decrement stock", errs.With("product_id", "prod_42"))

A Code, a Public és a Status jellemzően egyszer van beállítva, egy Define-olt definitionön; a With az egyetlen attribútum, amihez minden egyes wrap-helyen nyúlsz, hogy az adott híváshoz tartozó metadata-t (egy ID-t, egy darabszámot, egy query-nevet) rögzítsd.

Accessorok

func CodeOf(err error) string
func PublicOf(err error) string
func StatusOf(err error) int

Mindegyik végigmegy a hiba-láncon (az errors.As-en keresztül), keresve az első *errs.Error-t, ami beállította a megfelelő attribútumot, így ugyanúgy működik, akár magát az *errs.Error-t adod át, akár valamit, ami feljebb wrappeli. A StatusOf 0-t ad vissza, ha semmi a láncban nem állított státuszt, így egy renderelő explicit eshet vissza 500-ra, találgatás helyett.

Introspekció: Frames, FullFrames, Chain

type Frame struct {
    File     string
    Line     int
    Function string
}

type Step struct {
    Msg      string
    File     string
    Line     int
    Function string
    Code     string
    Meta     map[string]any
}

func Frames(err error) []Frame     // a keletkezés pontjában capture-ölt stack
func FullFrames(err error) []Frame // a Frames, plusz a közbeeső, nem wrappelő hívási frame-ek
func Chain(err error) []Step       // egy Step wrap-szintenként, a root cause utoljára

A Frames a hívási stack abban a pillanatban, amikor a hiba először létrejött (New, Errorf vagy a láncban az első Wrap); a FullFrames ezen felül a közönséges, a hibát sem nem létrehozó, sem nem wrappelő függvényhívások frame-jeit is tartalmazza, megadva a teljes hívási utat. A Chain a wrap-történet: egy Step minden Wrap/Wrapf híváshoz, a saját üzenetével, a wrap helyével és azzal, amit ott With/Code-dal rögzítettek, a legbelső okkal a végén. Ez a három függvény az, amiből a logolás és a Sentry a strukturált kimenetét építi; ezeken kívül ritkán hívod közvetlenül.

Stack-szemantika: capture a keletkezéskor

A stack trace egyszer capture-ölődik, a New/Errorf/első-Wrap/NewPanic hívásnál, ami az *errs.Error-t létrehozza, runtime.Callers-szel. A későbbi Wrap hívások ugyanazon a hibán egy Step-et adnak a lánchoz, de nem capture-ölik újra a stacket: a stack mindig a keletkezés pontjára mutat, nem arra a hívási helyre, ami épp logolja. Ez a gyakorlatban számít: ha a stacket akkor capture-öznéd, amikor logolsz, nem amikor létrehozod a hibát, egy mélyen wrappelt hiba mindig a logoló hívásra mutatna, ugyanarra a sorra, függetlenül attól, hogy a hiba ténylegesen hol kezdődött. A keletkezéskori capture azt jelenti, hogy a stack pont akkor értelmes, amikor számít, és ingyenes minden más alkalommal, amikor a hibát továbbadják, wrappelik vagy errors.Is-szel ellenőrzik.

errs.Define: elnevezett hibamódok

type Definition struct { /* ... */ }

func Define(code string, attrs ...Attr) *Definition

func (d *Definition) New(msg string, attrs ...Attr) *Error
func (d *Definition) Newf(format string, args ...any) *Error
func (d *Definition) Wrap(err error, msg string, attrs ...Attr) *Error
func (d *Definition) Wrapf(err error, format string, args ...any) *Error

A Define egyszer deklarál egy újrahasználható hibamódot, csomagszintű var-ként, ugyanúgy, ahogy egy sentinel hibát deklarálnál errors.New-val. Megköveteli egy Status attribútum beállítását egy érvényes 4xx vagy 5xx HTTP státuszkóddal, és panicol init-időben, ha ez hiányzik vagy tartományon kívüli:

var ErrOutOfStock = errs.Define("out_of_stock",
    errs.Public("A termék elfogyott."),
    errs.Status(http.StatusConflict)) // kötelező, különben a Define panicol

Ez az init-időben panicoló viselkedés azért van, mert egy Definition érvényes státusz nélkül olyan kontraktus, amit senki nem tud biztonságosan renderelni, és a hibát a bináris indulásának pillanatában kell elkapni, nem az első kérésen, ami épp átfut rajta. Egy Definition értéke maga a sentinel: az errors.Is(err, ErrOutOfStock) közvetlenül működik ellene, mert minden hiba, amit a .New/.Wrap belőle gyárt, saját Is-t hordoz, ami a definitionre illeszkedik. A .New/.Newf/.Wrap/.Wrapf a csomagszintű konstruktorokat tükrözi, előre feltöltve a definition kódjával, publikus üzenetével és státuszával, így az out_of_stock minden előfordulása a kódbázisban azonosan renderelődik, anélkül hogy ezt a három attribútumot minden hívási helyen meg kellene ismételni.

Önálló példa

main.go
package main

import (
    "errors"
    "fmt"
    "net/http"

    "github.com/gp-system/errs"
)

var ErrNotFound = errs.Define("not_found",
    errs.Public("A keresett elem nem létezik."),
    errs.Status(http.StatusNotFound))

func lookup(id string) (string, error) {
    if id != "known" {
        return "", ErrNotFound.New("lookup", errs.With("id", id))
    }
    return "value", nil
}

func main() {
    _, err := lookup("missing")

    fmt.Println(errors.Is(err, ErrNotFound)) // true
    fmt.Println(errs.CodeOf(err))            // "not_found"
    fmt.Println(errs.StatusOf(err))          // 404
    fmt.Println(errs.PublicOf(err))          // "A keresett elem nem létezik."

    for _, step := range errs.Chain(err) {
        fmt.Printf("%s (%s:%d)\n", step.Msg, step.File, step.Line)
    }
}

Kapcsolódó oldalak

Copyright © 2026