m_shige1979のときどきITブログ

プログラムの勉強をしながら学習したことや経験したことをぼそぼそと書いていきます

Github(変なおっさんの顔でるので気をつけてね)

https://github.com/mshige1979

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フレームワークのアプリ自体が停止する可能性がある・・・

所感

なんかスマートじゃない感じ…
現状、これしか見つからないからこの方法で対応を試みます。