Goエラーハンドリングを簡単にするOSS作った
Goのエラーハンドリングを簡単にするOSS
こちら思い付きで即興で作りました。
少しずつ手直ししたりテスト書いたりしてます。
MITライセンスで公開しています
興味ある方は使ってみてください
issue待ってます!
アドバイス等もお待ちしております!
作った背景
エラーが発生した際に開発者に必要な情報を提供しつつ、クライアント(ユーザーやフロントエンド)に対して適した情報を提供するためのツールです。
例えば dial tcp 127.0.0.1:3306: connect: connection refused
のようなmysqlのエラーが発生した際にはそれをクライアントに露出させたくはないです。
クライアントに伝えるべきは Internal Server Error
です。
逆にログが Internal Server Error
だけだと何が原因なのか一目でわかりません。
つまりエラーにクライアントのための顔と、開発者のための顔を用意してあげると色々と楽になれるのではないかと思いました。
そこでユングのペルソナになぞらえてperrという名前にしました。
加えて
- エラーハンドリングを簡単にするために比較メソッド
Is()
- ログや通知のための
Level()
メソッド - 解析のためにスタックトレース出力メソッド
Traces()
- 構造的に保存できるように
Map()
とJson()
メソッド
を用意しました。
使い方
ザックリと解説していきます。
基本的には相手に見せるものとログ用のエラーを指定します。
基本
p := perr.Wrap(err, perr.BadRequest)
p := perr.New("new error", perr.BadRequest)
どちらもperr.Errのポインタを返します。perr.ErrはError() string
インターフェースを満たしているのでerror
インターフェースとして扱うことが可能となっています。
WrapとNewの細かい説明はいかにします。
既存のエラーをラップする
Wrap
メソッドを使います。
第一引数に既存のエラーを、第二引数にどういうエラーとして扱うか?(クライアント向け)を指定します。
第一引数がnilであればnilを返します。
_, err := strconv.Atoi("sample")
p := perr.Wrap(err, perr.BadRequest) # ラップする
fmt.Printf("Client: %v\n", p.Output().Error())
fmt.Printf("Developer: %v\n", p.Error())
// 出力結果
// Client: Bad Request
// Developer: strconv.Atoi: parsing "sample": invalid syntax
WrapithLevel()
を使うと独自にレベルを付与できます。
l := perr.ErrLevel("Dangerous")
_, err := strconv.Atoi("sample")
// 普通
p := perr.Wrap(err, perr.BadRequest)
fmt.Printf("Default Level: %v\n", p.Level())
// レベル付与
pl := perr.WrapWithLevel(err, perr.BadRequest, l)
fmt.Printf("With Level: %v\n", pl.Level())
// 出力結果
// Default Level: EXTERNAL ERROR
// With Level: Dangerous
新規エラー
New
メソッドを使います
第一引数にstringを指定します。この文字列がerrors.New()されてログ用のエラーとなります。
第二引数にどういうエラーとして扱うか?(クライアント向け)を指定します。
第一引数がブランクであれば開発者向けとクライアント向けが同じになります。
第二引数がnilであれば上と同様です。
第一引数がブランクかつ第二引数がnilであればnilを返します。
err := perr.New("Someone pour coffee into a tea cup", perr.BadRequest)
fmt.Printf("Client: %v\n", err.Output().Error())
fmt.Printf("Developer: %v\n", err.Error())
// 出力結果
// Client: I'm a teapot
// Developer: Someone pour coffee into tea cup.
またオプショナルな第三引数を使うとクライアントへのメッセージを変えられます。
err := perr.New("Someone pour coffee into a tea cup", perr.BadRequest, "Don't pour coffee!")
fmt.Printf("Client: %v\n", err.Output().Error())
fmt.Printf("Developer: %v\n", err.Error())
// 出力結果
// Client: Don't pour coffee!
// Developer: Someone pour coffee into tea cup.
こちらもラップと同様NewWithLevel()
を使うと独自にレベルを付与できます。
stacktrace
スタックトレースを取得できます
_, err := strconv.Atoi("sample")
p := perr.Wrap(err, perr.BadRequest) # ラップする
fmt.Print(p.Traces().String())
// 出力結果
// /secret/stack.go:81 ===> main
// p.Traces()でStackTrace
// StackTracesを取得してます
Map & Json
それぞれ構造体とJson([]byte)を用意してくれます。
保存等するのに便利だと思います。
細かくは省略しますが、それぞれMap()
とJson()
メソッドを使用します。
今後の展望
本当に思いつきで作っただけなのでブラッシュアップしていきたいです。
ただ小回りの効かないものにはしたくないのでできる限り最小限にしようとは思っています。
あとまだだいぶ荒いのでちょっとずつ改善していきます。
階層化
ラップ時に対象のerror
がPerror
インターフェースを満たしていればstacktraceを追加する
test や CI
タイトル通り
Discussion