Goのエラーはシンプルで、基本的にエラーメッセージのみを返します。
シンプルゆえに、僕からするとメッセージだけ見ても「このエラーはどこで発生しているんだ?」と、まず発生箇所を探すことから始めないといけません。
エラー原因をスムーズに取り除きたいですが、結構モタモタしてしまいます。
なので今回はエラー自体にファイル名と行番号を組み込み、素早く発生箇所にたどり着けるようにしたいと思います。
目次
エラーの発生箇所を取得しよう
これは runtime パッケージを使うだけでOKです。
runtime.Caller を使うことで、発生箇所のファイル名と行番号を取得できます。関数名なんかも取得できますが今回はスルーします。
import (
"runtime"
)
// getCallerInfo は呼び出し元のファイル名と行番号を返します。
func getCallerInfo(dept int) (filename string, line int) {
// 関数名も取得したい場合は、Caller(dept) の1つ目の返り値も使います。
_, filename, line, ok := runtime.Caller(dept)
if !ok {
return "failed to get file and line information", 0
}
return filename, line
}
これだけでエラーが発生した箇所の情報を取得できます。
あとはこれを組み込んだカスタムエラーを実装するだけです。
カスタムエラー実装
シンプルなエラーを実装します。
package main
import (
"fmt"
"runtime"
)
// MyError は、エラーメッセージ、発生したファイル名、および行番号を保持します。
type MyError struct {
msg string
file string
line int
}
func (e MyError) Error() string {
return fmt.Sprintf("file=%s, line=%d, msg=%s", e.file, e.line, e.msg)
}
// New は新しい MyError を作成します。
// エラーメッセージと、発生箇所のファイル名と行番号を取得します。
func New(msg string) *MyError {
filename, line := getCallerInfo(2)
return &MyError{
msg: msg,
file: filename,
line: line,
}
}
// getCallerInfo は呼び出し元のファイル名と行番号を返します。
func getCallerInfo(dept int) (filename string, line int) {
// 関数名も取得したい場合は、Caller(dept) の1つ目の返り値も使います。
_, filename, line, ok := runtime.Caller(dept)
if !ok {
return "failed to get file and line information", 0
}
return filename, line
}
// 確認用。
func doSomething() error {
err := New("failed to do something")
return err
}
func main() {
err := doSomething()
fmt.Println(err) // file=xxx/main.go, line=40, msg=failed to do something
}
終わり
以上です。
これで発生箇所がすぐにわかるので、対処しやすくなりました!
コメント