Golangでエラー時にスタックトレースを表示する

  • 67
    いいね
  • 0
    コメント

Golangのerrorにはスタックトレース情報はない.基本は必要ないと思うし困ったこともない.一方で新しくプロジェクトに入りかつコードベースに慣れてない場合はどこが問題かアタリをつけるためにあっても良いとも思う.pkg/errorsを使ってるならそれはすぐに実現できる.

pkg/errorsの基本的な考え方についてなどは GoCon 2016 springの後に "Golangのエラー処理とpkg/error"にまとめた(作者のDaveのブログ記事は "Don’t just check errors, handle them gracefully").ここで強調して書いたのはちゃんとエラーをアノテートする(何をしてそのエラーが発生したかの状況を付加する)ことである.特に外部のパッケージとやり取りする場合,つまりまともなエラーメッセージが返ってこないかもしれない場合,pkg/errorsWrapを使えば最終的に表示されるエラーが理解でないものになってしまうことを防ぐことができる.例えば以下のように使う.

f, err := os.Open(path)
if err != nil {
        return errors.Wrapf(err, "failed to open %q", path)
}

これだけではなくpkg/errorsWrapNewを呼び出すときにそのファイル名と行といったトレース情報も一緒に記録する.もともとはPrintFprintといった関数が提供されておりそれを使うとトレース情報が表示可能だったが 0.7.0後に削除された(経緯は"Stack traces and the errors package"が詳しい).現在は https://golang.org/pkg/fmt/#Formatter を実装しているので SprintfPrintfの verbで表示の切り替えができるようになっている.

例えば以下のように%+vを使えばスタックトレースを含めた表示が可能になる.

func main() {
    err := errors.New("error")
    err = errors.Wrap(err, "open failed")
    err = errors.Wrap(err, "read config failed")

    fmt.Printf("%+v\n”, err)
}

自分は普段は鬱陶しいので表示しない.TRACE環境変数を準備してそこに値がセットされていれば %+vで出力しそれ以外の場合は %s で通常の表示を行うようにしている.

ちなみにデバッグ情報は以下のような関数を準備してDEBUG環境変数で切り替えを行ったりすると便利.

func Debugf(format string, args ...interface{}) {
     if Debug {
         fmt.Fprintf(os.Stdout, "[DEBUG] "+format+"\n", args...)
     }
}