分享一个处理结构化 error 的库 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
Gota
V2EX    Go 编程语言

分享一个处理结构化 error 的库

  •  
  •   Gota
    gota33 2021-12-17 20:47:54 +08:00 2295 次点击
    这是一个创建于 1401 天前的主题,其中的信息可能已经有所发展或是发生改变。

    和大家分享一个自己写的库 github.com/gota33/errors

    主要用于结构化 error 的生成、编码和解码。目前自用下来还可以,欢迎试用及提出建议。

    错误的描述信息是遵循 Google API design 设计的。

    这里贴一个输出到控制台的例子,当然也是可以编码成 JSON 在 RESTful 服务间传递的。

    更多的例子可以参考项目首页的文档。

    // ... err := Annotate( context.DeadlineExceeded, DeadlineExceeded, StackTrace("heavy job"), RequestInfo{RequestId: "<uuid>"}, LocalizedMessage{Local: "en-US", Message: "Background task timeout"}, LocalizedMessage{Local: "zh-CN", Message: "后台任务超时"}, ) fmt.Printf("%+v", err) // Output: // status: "504 DEADLINE_EXCEEDED" // message: "context deadline exceeded" // detail[0]: // type: "type.googleapis.com/google.rpc.DebugInfo" // detail: "heavy job" // stack: // goroutine 1 [running]: // runtime/debug.Stack(0xc00005e980, 0x40, 0x40) // /home/user/go/src/runtime/debug/stack.go:24 +0xa5 // github.com/gota33/errors.StackTrace.Annotate(0xfe36af, 0x9, 0x1056490, 0xc00005e980) // /home/user/github/gota33/errors/detail.go:368 +0x2d // github.com/gota33/errors.Annotate(0x1051780, 0x1257e60, 0xc00010fc00, 0x5, 0x5, 0xc00010fba8, 0x10) // /home/user/github/gota33/errors/errors.go:79 +0x97 // github.com/gota33/errors.ExampleAnnotate() // /home/user/github/gota33/errors/example_test.go:10 +0x251 // testing.runExample(0xfe589a, 0xf, 0xfff6c0, 0xfead08, 0x1a, 0x0, 0x0) // /home/user/go/src/testing/run_example.go:63 +0x222 // testing.runExamples(0xc00010fed0, 0x120aee0, 0x3, 0x3, 0x0) // /home/user/go/src/testing/example.go:44 +0x185 // testing.(*M).Run(0xc000114100, 0x0) // /home/user/go/src/testing/testing.go:1419 +0x27d // main.main() // _testmain.go:71 +0x145 // // detail[1]: // type: "type.googleapis.com/google.rpc.RequestInfo" // request_id: "<uuid>" // serving_data: "" // detail[2]: // type: "type.googleapis.com/google.rpc.LocalizedMessage" // local: "en-US" // message: "Background task timeout" // detail[3]: // type: "type.googleapis.com/google.rpc.LocalizedMessage" // local: "zh-CN" // message: "后台任务超时" 
    第 1 条附言    2021-12-18 13:54:26 +08:00
    如果想了解错误结构设计的思路,可以先看一下 Google API Design 的文档: https://cloud.google.com/apis/design/errors
    6 条回复    2021-12-20 11:25:20 +08:00
    godlovesxcjtest
        1
    godlovesxcjtest  
       2021-12-17 22:14:28 +08:00
    挺好的,但是,老哥你不觉得这样处理错误太麻烦了吗。要写好多,go 语言的错误处理本来就麻烦的有要死,如果按照你这种用法,岂不是处理错误的代码都要写好多
    Gota
        2
    Gota  
    OP
       2021-12-17 22:47:02 +08:00
    @godlovesxcjtest 谢谢评价。

    这个库主要在微服务的场景下使用。这时候需要考虑到错误的传递,比如不同的 status code 和 http code ,以及不同的错误类型需要有不同的 payload 来描述错误细节,再如 request_id 这样的字段。

    当然简单的用法也有,比如只附加 status code 而不加具体的 detail ,但这里为了演示就把所有的字段都加上了。
    godlovesxcjtest
        3
    godlovesxcjtest  
       2021-12-17 22:57:38 +08:00
    @Gota request_id 没必要放到 error 里面。可以放到 context 里面,打印日志的时候,传入 context ,这样每条日志都可以有 request_id 。

    我没仔细看源码,想问一下,底层使用你的库抛出错误后,调用的上层想加上一些错误信息,类似 errors.Withmessage 这种,使用什么方法,上层是不是还需要加上错误吗呢
    Gota
        4
    Gota  
    OP
       2021-12-17 23:05:37 +08:00
    @godlovesxcjtest 是这样的,由于服务之间传递错误需要一个固定的格式,所以这里采用的是 Google API design 里的建议格式。所以里面的 request_id 不是用来记录日志的,而是错误格式本身所定义的。

    关于错误码,任意层级都是可以设置的,以最后设置的为准。如果是增加非结构化的错误信息可以直接用 fmt.Errorf(),如果是结构化错误信息就需要用 errors.Annotate() 了。
    eudore
        5
    eudore  
       2021-12-20 09:14:28 +08:00
    我都直接 logger 把 error 发送到 jaeger 面板上去了。
    Gota
        6
    Gota  
    OP
       2021-12-20 11:25:20 +08:00
    @eudore 只是记录 error 是不够的,应尽量在系统内消化错误。所以需要一种方式来标准化错误格式,这样就能传递和处理跨服务的 error 了。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2637 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 09:26 PVG 17:26 LAX 02:26 JFK 05:26
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86