Goでエラーハンドリングを書いていると、スタックトレースが欲しくなるのですが、いい感じにだせるライブラリが意外とありません。

pkg/errorserrors.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"
		//     ]
		//   }
		// }
	}
}

内部的には、一度 WithStackNew を通した時点でスタックトレースを埋め込み、以降はWrapしてもそれを引き継ぐ形になっています。

比較表(簡易版)

ライブラリ 特徴
Go標準 Is/Asは便利。スタックトレースは自前になる
pkg/errors 互換APIは便利。ただし開発停止・スタック重複しがち
stackprune/errors スタック重複回避。slogにも対応

インストール

1
go get github.com/stackprune/errors

今後考えていること

  • GoDocの整理
  • バージョンを安定させる

まとめ

エラーハンドリング周りは普段コードを書いているとだんだん気になってくる部分ですが、まだ良い落とし所が少ない印象もあります。 今回作った stackprune/errors もまずは自分用に作り始めたものですが、もし同じようなニーズがある方がいれば試してもらえると嬉しいです。