プログラマー探偵の事件簿:エラーを隠蔽するrecover
プログラム言語にはエラーを無かったことにしてくれる魔法の呪文が存在する。try / catch、rescureといったものである。GO言語の場合はrecoverである。今回は私自身がrecoverを使ってTWSNMPのエラーの証拠を隠蔽することに悩んだ話である。助手の猫は「ベッドやトイレを汚した時は隠さず正直に知らせろ!」と言っている。
事件の始まり
Raspberry PiでNetflowに対応する実験をしていた時である。
この記事の最後にバグ発見として書いた問題である。Netflow(ipfix形式)のデータを受けている途中でTWSNMPが異常終了した。
異常終了の原因
GO言語で異常終了する時にはpanicというメッセージを表示する。今回は、
panic: interface conversion: interface {} is nil, not uint16
である。メッセージにはソースコード上場所も記録されている。
SrcPort: int(record["sourceTransportPort"].(uint16)),
送信元ポート番号がないNetflowのデータを処理する時に発生していることまでわかった。このデータはGO言語のNetflow処理パッケージから送られているものでブラックボックスである。
安易な解決策
送信元ポート番号がないNetflowのデータを処理する時に発生しているのだから、送信元ポート番号がないNetflowのデータは無視するようにすればよい!
if _,ok := record["sourceTransportPort"];ok {
あれば処理する
}
のようにする。
ここで、処理するデータは他にもあり、それらが存在しないことも考えなければと心配になる。安易に全部のデータの存在をチェックする処理を組み込んだ。とりあえず問題は起きないが、はたしてそれで良いのだろうか?
助手の猫は無言でこっちを見ている。「もう少し考えろ!」という意味か?
隠蔽する解決策
GO言語のpanicを回避する方法をGoogleに聞いてみた。
を見つけた。panicが起こっても無かったことにしてくれる方法である。これはよい呪文を見つけた。さっきの対策をやめて、さっそく試してみる。
defer func() {
if r := recover(); r != nil {
astiLogger.Errorf("logIPFIX err=%v", r)
}
}()
問題の処理
問題の処理のところで発生したエラーはdeferの中のrecoverが隠蔽してくれる。うまくいったように思えるがログを見るとlogIPFIX err=の行が頻繁に出ている。まるで、猫が「ベッドを汚した」、「トイレを汚した」と文句を言っているような感じである。はたしてそれで良いのだろうか?
正直な解決策
悩んだすえ、やっぱり正直に直すことにした。
問題の原点に戻って、送信元ポート番号がないNetflowのデータがどういうものか調べることにした。問題が発生した時にデータをログに記録してみた。
defer func() {
if r := recover(); r != nil {
astiLogger.Errorf("logIPFIX err=%v", r)
//データをログに出力
for k, v := range record {
astiLogger.Errorf("%v=%v", k, v)
}
}
}()
調べてみるとICMPのデータの場合にエラーになっている。ICMPにはポート番号はないので当然である。
ICMPの場合はポート番号の代わりにコードとタイプを利用する処理を追加した。recoverの処理はそのままに組み込んだ状態である。エラーのログも記録されなくなった。
いつか、recoverの魔法で異常終了を免れることがあるかもしれない。
助手の猫はトイレもベッドも、かみさんのおかげでキレイなので、すやすや寝ている。
開発のための諸経費(機材、Appleの開発者、サーバー運用)に利用します。 ソフトウェアのマニュアルをnoteの記事で提供しています。 サポートによりnoteの運営にも貢献できるのでよろしくお願います。