Goでエラーハンドリングを書いていると、スタックトレースが欲しくなるのですが、いい感じにだせるライブラリが意外とありません。
pkg/errors
は errors.Wrap
を複数回やるとスタックトレースが重複してしまう。メンテナンスも止まっているし、 errors.Join
などが未実装。
cockroachdb/errors
はいい感じなのだけど、出てくるログの量ががちょっとおおくて加工する必要がでてくる。
そういう経緯もあって、今回 stackprune/errors というエラーハンドリングライブラリを自作してみました。
目的と方針
pkg/errors
互換のAPI (Wrap
, WithStack
など) を用意
- Wrapを何回呼んでもスタックが重複しないようにする
- 標準
errors
と組み合わせても動作する
- slog などの構造化ログにも統合しやすくする
Goのエラーハンドリングは長らく議論が多い領域ですが、「現場で使いやすいちょうどいい感じ」を目指しています。
使い方
コード例はこんな感じです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
package main
import (
"fmt"
"log/slog"
"os"
"github.com/stackprune/errors"
)
func doSomething() error {
return errors.New("something went wrong")
}
func handler() error {
err := doSomething()
if err != nil {
return errors.Wrap(err, "failed to handle request")
}
return nil
}
func main() {
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
if err := handler(); err != nil {
fmt.Printf("error: %+v\n", err)
// 出力例:
// error: failed to handle request: something went wrong
// main.doSomething
// /app/main.go:12
// main.handler
// /app/main.go:16
// main.main
// /app/main.go:27
logger.Error("error:", slog.Any("error", err))
// 出力例: (実際は1行のJSONで出力されます)
// {
// "time": "2025-06-17T09:43:54.625751251+09:00",
// "level": "ERROR",
// "msg": "error:",
// "error": {
// "message": "failed to handle request: something went wrong",
// "kind": "*errors.Error",
// "stack": [
// "main.doSomething at /app/main.go:12",
// "main.handler at /app/main.go:16",
// "main.main at /app/main.go:27"
// ]
// }
// }
}
}
|
内部的には、一度 WithStack
や New
を通した時点でスタックトレースを埋め込み、以降はWrapしてもそれを引き継ぐ形になっています。
比較表(簡易版)
ライブラリ |
特徴 |
Go標準 |
Is /As は便利。スタックトレースは自前になる |
pkg/errors |
互換APIは便利。ただし開発停止・スタック重複しがち |
stackprune/errors |
スタック重複回避。slogにも対応 |
インストール
1
|
go get github.com/stackprune/errors
|
今後考えていること
まとめ
エラーハンドリング周りは普段コードを書いているとだんだん気になってくる部分ですが、まだ良い落とし所が少ない印象もあります。
今回作った stackprune/errors
もまずは自分用に作り始めたものですが、もし同じようなニーズがある方がいれば試してもらえると嬉しいです。