web-dev-qa-db-ja.com

Goパッケージはlog.Fatalをいつ使用する必要がありますか?

私はこれまでlog.Fatalの使用を避けてきましたが、最近偶然にこれらの質問を発見しました。 code-coverage および tests-using-log-fatal

100コードカバレッジの質問からのコメントの1つは次のように述べています。

...ほとんどの場合、log.Fatalはmain関数またはinit関数でのみ使用する必要があります(または、関数から直接のみ呼び出すことを意図したものもあります) "

考えてみたので、Goに付属の標準ライブラリコードを検討し始めました。ライブラリのtestコードがlog.Fatalを使用しているように見える多くの例があります。以下に示す net/http のように、テストコードの外にいくつかの例があります。

// net/http/transport.go 
func (t *Transport) putIdleConn(pconn *persistConn) bool {
    ...
    for _, exist := range t.idleConn[key] {
        if exist == pconn {
            log.Fatalf("dup idle pconn %p in freelist", pconn)
        }
    }
    ...
}

log.Fatalの使用を避けることがベストプラクティスである場合、なぜ標準ライブラリでまったく使用されないのであれば、エラーが返されることを期待していました。ライブラリのユーザーにos.Exitが呼び出され、アプリケーションがクリーンアップする機会を提供しないのは不当に思われます。

私は世間知らずかもしれないので、より良い実践としての私の質問はlog.Panicを呼び出すことです。

では、Goのベストプラクティスは何をログに記録する必要があるかということです。

26
miltonb

それは私だけかもしれませんが、_log.Fatal_の使い方を次に示します。 UNIXの規則に従って、エラーが発生したプロセスは、ゼロ以外の終了コードでできるだけ早く失敗するはずです。これにより、次の場合に_log.Fatal_を使用するためのガイドラインが表示されます…

  1. …インポートが処理されるとき、またはメインのfuncが呼び出される前にそれぞれ発生するため、私のfunc init()のいずれかでエラーが発生します。逆に言えば、ライブラリーまたはcmdが行うことになっている作業単位に直接影響を与えないことだけを行います。たとえば、ロギングを設定し、正常な環境とパラメータがあるかどうかを確認します。無効なフラグがあればmainを実行する必要はありませんよね?適切なフィードバックを提供できない場合は、早期に通知する必要があります。
  2. …エラーが発生し、そのエラーは回復不能です。コマンドラインで指定された画像ファイルのサムネイルを作成するプログラムがあるとします。このファイルが存在しないか、権限が不十分なために読み取れない場合は、続行する理由がなく、このエラーから回復できません。だから私たちは慣習を守って失敗します。
  3. …処理中にエラーが発生し、元に戻せない場合があります。これは一種のソフトな定義です。それを説明しましょう。 cpの実装があり、非インタラクティブでディレクトリを再帰的にコピーすることが開始されたとしましょう。ここで、コピー先のファイルと同じ名前(ただし内容が異なる)のファイルがターゲットディレクトリにあると仮定します。ユーザーに何をするかを決定するように求めることができず、このファイルをコピーできないため、問題があります。終了コード0で終了すると、ユーザーはソースディレクトリとターゲットディレクトリが正確なコピーであると想定するため、問題のファイルを単にスキップすることはできません。ただし、情報を破壊する可能性があるため、単純に上書きすることはできません。これは、ユーザーの明示的な要求ごとに回復できない状況なので、_log.Fatal_を使用して状況を説明し、できるだけ早く失敗するという原則に従います。
32

マーカス、私はあなたの反応に出くわしました、そして私はそれが素晴らしくて非常に洞察力があると思います、そして私はあなたの内訳に同意する傾向があります。一般化するのは非常に難しいですが、私はこれについてもう少し考えていて、初心者として行っています。理論的なレベルでは、OS、パッケージフレームワーク、ライブラリに関係なく、コンピューティングのベストプラクティスを検討している場合、ロガーの責任は単純にログを記録することです。どのレベルでも、ロガーの責任:

  • 選択したチャネルに合わせて情報をフォーマットし、一貫して印刷します。
  • さまざまなログレベルの分類、フィルタリング、表示[デバッグ、情報、警告、エラー]
  • 非同期および並列ジョブ全体のログエントリの処理と集計

ロギングパッケージまたは任意のパッケージは、プログラムが適切に動作している場合、プログラムをクラッシュさせる権限を持っていません。ミドルウェアやライブラリはスロー/キャッチパターンに従う必要があり、呼び出し側がキャッチするall例外がスローされる機会があります。これは、アプリケーション内で従うのに適したパターンでもあります。アプリケーションのさまざまな部分、および場合によっては他のアプリケーションを強化する基盤とパッケージを構築するときに、アプリケーションが直接クラッシュすることはありません。むしろ、プログラムが処理できるように、致命的な例外をスローする必要があります。これは、マーカスのいくつかの点にも対処していると思います。キャッチされなかった場合、致命的なクラッシュとして発信者にすぐに警告するためです。

ほとんどの場合、log.Fatalを直接使用して、直接ユーザーが直面するCLIツールの目的で楽しむことができます。これは、本当に意図されたシンプルさだと思います。パッケージ全体で致命的なエラーを処理する長期的なアプローチとしては意味がないと思います。

1
moniecodes