【リッチなerrors】cockroachdb/errorsとは
モチベーション
CockroachDBのcoding guidelinesを読んでいた際に、今回紹介するcockroachdb/errorsパッケージを発見しました。
調べてみたところ、日本語で紹介されている記事等がなかったので今回この記事を書きました。
cockroachdb/errorsとは
cockroachdb/errorsは、CockroachDBを開発しているCockroach Labsが提供しているパッケージです。
READMEには「分散システムに適した方法でのエラーのネットワークポータビリティを提供する。」とあり、CockroahcDB:の実装のためのエラーハンドリング用のパッケージだということがわかります。
他のerrorsパッケージとの比較はcockroachdb/errors
のREADMEの方に書かれているので、ぜひそちらをご覧ください。
主な機能
ここからはcockroachdb/errorsの主な機能について例と共に見ていきます。
errors.New系
標準パッケージでもサポートされているerrors.New()
はもちろん使えます。
err := errors.New("create a new error")
fmt.Println(err)
output:
create a new error
そしてerrors.Newf()
という、fmt.Sprintf
のように使える関数が備えられています。
i := 101
err := errors.Newf("%d is over than 100", i)
fmt.Println(err)
output:
101 is over than 100
wrap処理
標準パッケージだとfmt.Errorf()
を用いてエラーをラップしていくと思いますが、cockroachdb/errors
では、errors.Wrap
でラップすることが可能です。
fmt.Errorf()
よりもなにをしているのか感覚的にわかりやすくなっていると思います。
下の例はどちらとも同じエラーメッセージが返されます。
// 標準パッケージのerrors
func f() error {
return errors.New("error happen")
}
func hoge1() error {
if err := f(); err != nil {
return fmt.Errorf("hoge: %w", err)
}
}
// cockroachdb/errors
func hoge2() error {
if err := f(); err != nil {
return errors.Wrap(err, "hoge")
}
}
func main() {
err1 := hoge1()
err2 := hoge2()
fmt.Println(err1.Error())
fmt.Println(err2.Error())
}
output:
hoge: error happen
hoge: error happen
errors.Is系
errors.Is()
も標準パッケージと同じように使えます。
var ErrHoge = erros.New("hoge")
func happenHoge() error {
return ErrHoge
}
func main() {
err := happenHoge()
if errors.Is(err, ErrHoge) {
fmt.Println("this is hoge error!")
}
}
output:
this is hoge error!
errors.IsAny()
というのも存在します。
これは個人的にcockroachdb/errors
の一番の特徴だと思います。
errors.Is()の2番目の引数を可変長で複数渡すことができます。
var (
ErrHoge = errors.New("hoge")
ErrHuga = errors.New("huga")
)
func happenHoge() error {
return ErrHoge
}
func main() {
err := happenHoge()
if errors.IsAny(err, ErrHoge, ErrHuga) {
fmt.Println("this is hoge or huga error")
}
}
output:
this is hoge or huga error
errors.As()
errors.As()
ももちろんあります。使い方も標準パッケージと同様です。
type MyError struct{}
func (e *MyError) Error() string {
return "hoge"
}
func Hoge() error {
return &MyError{}
}
func main() {
err := Hoge()
var target *MyError
if errors.As(err, &target) {
fmt.Println("this error is same type")
}
}
output:
this error is same type
errorに情報付与系
エラーにメッセージなどの情報を付加できる機能もあります。
errors.WithMessage
では、sentryのADITIONAL DATA
に表示するメッセージを付加することができます。
func withMessage() error {
err := errors.New("error happen!")
return errors.WithMessage(err, "error report with sentry")
}
errors.WithDetail
とerrors.WithHint
はどちらもエラーにメッセージを含ませることができます。
挙動としてはどちらも同じようになっています。
含ませたメッセージはそのままprintで出力することはできないです。
func containHint() error {
return errors.WithHint(errros.New("error happen!"), "this error has a hint")
}
func main() {
if err := containHint(); err != nil {
fmt.Println(err)
}
}
output:
error happen!
fmt.Printf()
などのフォーマットを使用できるもので"+v"
を用いるとヒントがスタックトレースと共に出力されます。
func main() {
if err := containHint(); err != nil {
fmt.Printf("%+v", err)
}
}
output:
error happen!
(1) this error has a hint
Wraps: (2) attached stack trace
-- stack trace:
| main.init
| /usr/local/go/src/kimuson/main.go:11
| runtime.doInit
| /usr/local/go/src/runtime/proc.go:6498
| runtime.main
| /usr/local/go/src/runtime/proc.go:238
| runtime.goexit
| /usr/local/go/src/runtime/asm_amd64.s:1581
Wraps: (3) error happen!
Error types: (1) *hintdetail.withHint (2) *withstack.withStack (3) *errutil.leafError
これだけではなく、errors.GetAllHints
とerrors.FlattenHints
というHintのメッセージだけ取り出せるものもあります。
func main() {
if err := containHint(); err != nil {
fmt.Println(errors.GetAllHints(err))
fmt.Println(errors.FlattenHints(err))
}
}
output:
[this error has a hint]
this error has a hint
同じようなものとして、issueのURLと詳細を含めることができるerrors.WithIssueLink
という関数も存在します。
sentry利用系
sentryを使っている場合は、ReportError
という関数にerrorを渡すと、そのままsentryに送ることができます。
func main() {
if err := f(); err != nil {
errors.ReportError(err)
}
}
このようにすることで、スタックトレースもつけてsentryにeventとして送ってくれます。
これらの他にも多くの機能がcockroachdb/errors
には実装されているので、興味ある方はぜひリポジトリなり、go.pkg.devで見てみてください。
まとめ
いかがだったでしょうか。cockroachdb/errors
が機能がリッチなerrorsになっていることがわかったと思います。
個人的に気になった点としては、errors.Is()
の内部実装がcockroachdb/errors
と標準パッケージで異なっていたところです。
今回は省略しますが、興味ある方は比較してみてください。
errを引数にいれるだけでsentryに送ってくれるReportError
関数と、スタックトレースも出してくれるerrors.WithHint
, errors.WithDetail
が便利そうだな感じました。
CockroachDB
自体のリポジトリも今後見てみる予定なので、その際にこのcockroachdb/errors
だからこそうまくerr処理できているものがあれば追記していきます。
Discussion