golangのpanic発生しても処理を続行したい
golangでのエラーとかって
基本的にはエラーオブジェクトのチェックしかしていない
panic発生したら
落ちます
package main import ( "log" ) func main() { log.Println("start") // 意図的にpanicを起こす panic("aaaa") //var a *interface{} //*a = 0 //x := 10 //y := 0 //b := x / y //log.Println(b) log.Println("end") }
↓
2019/08/14 10:04:05 start panic: aaaa goroutine 1 [running]: main.main() /sample2-b.go:12 +0x79 exit status 2
そうならないようにerrオブジェクトや変数のチェックで抑制していますが
まれになんらかの理由でオブジェクトにnullが挿入される自体が発生してpanicがおきたらマズい
javaのtry-catchとかあればよいがそういうのはないため、
deferとrecoverを併用することで対応できるとのこと
ドキュメントより以下、引用(http://golang.jp/effective_go#recover)
panicが呼び出されたとき(これには、配列のインデックスが範囲外であるときや、型アサーションに失敗したような暗黙的なものも含む)は、すぐさま、カレントの関数を停止し、ゴルーチンのスタックの巻き戻しを開始します。その途中、遅延指定されている関数をすべて実行します。この巻き戻しがゴルーチンのスタックの先頭にたどり着くと、プログラムは終了します。ただし、組み込み関数recoverを使うことで、ゴルーチンの制御を取り戻し、通常の実行を再開させることが可能です。
書き直し
package main import ( "log" "fmt" "runtime" ) func main() { log.Println("start") // 今回は即時関数にしているがerrorの戻り値を取得できるならなんでもよい err := func() (errRes error) { // panic発生時に補足するやつ defer func() { if err := recover(); err != nil { // エラー情報を戻り値のerrResへ設定 errRes = fmt.Errorf("error: %s", err) // stack trace for i:=0;;i++ { _, file, line, ok := runtime.Caller(i) if ok == false { break } log.Printf("depth: %d, line: %4d, file:%v\n", i, line, file) } } }() // 意図的にpanicを起こす //panic("aaaa") //var a *interface{} //*a = 0 x := 10 y := 0 b := x / y log.Println(b) // ここまできたら正常 return nil }() // errorチェック if err != nil { log.Println(err) } log.Println("end") }
↓
2019/08/14 10:13:38 start 2019/08/14 10:13:38 depth: 0, line: 22, file: /work/golang/sample2.go 2019/08/14 10:13:38 depth: 1, line: 522, file:/usr/local/Cellar/go/1.12.7/libexec/src/runtime/panic.go 2019/08/14 10:13:38 depth: 2, line: 61, file:/usr/local/Cellar/go/1.12.7/libexec/src/runtime/panic.go 2019/08/14 10:13:38 depth: 3, line: 37, file:/work/golang/sample2.go 2019/08/14 10:13:38 depth: 4, line: 42, file:/work/golang/sample2.go 2019/08/14 10:13:38 depth: 5, line: 200, file:/usr/local/Cellar/go/1.12.7/libexec/src/runtime/proc.go 2019/08/14 10:13:38 depth: 6, line: 1337, file:/usr/local/Cellar/go/1.12.7/libexec/src/runtime/asm_amd64.s 2019/08/14 10:13:38 error: runtime error: integer divide by zero 2019/08/14 10:13:38 end
Webフレームワークとかは
API配下のpanicは拾ってくれるが、内部でgoroutineなどを任意で定義した場合にpanicを起こすと
Webフレームワークのアプリ自体が停止する可能性がある・・・
参考
Go言語のエラーハンドリングについて ~panic編~ - Qiita
golangでrecoverしたときの戻り値 - Qiita
http://golang.jp/effective_go#recover
golangでpanicをrecoverしたときにスタックトレースを表示する - code.alone
所感
なんかスマートじゃない感じ…
現状、これしか見つからないからこの方法で対応を試みます。