この記事はHow to collect, standardize, and centralize Golang logs | Datadogを読んでまとめたものです。
ログを使用する際には、次の点に注意する必要があります:
- ログを引数として渡し、必要な場合に引数として渡します。
- コンテキストを統一的にカプセル化し、必要な場合にコンテキストから取得します。
- ロガーをパッケージ化し、他のパッケージが使用できるように公開します。
明らかに、最初の方法はすぐに問題に直面します。例えば:
func (p *Post) CreatePost(name string, content string, logger *log.Logger) {
///
if err != nil {
logger.Fatal("error!")
}
}
CreatePost
メソッドにはロガーを渡す必要があり、引数が非常に冗長になる可能性があります。また、ロガーの連鎖的な渡しも発生する可能性があります。この方法の最大の利点は、依存関係が透明になることです。関数を介してロガーの使用が明確になり、テストもロガーをモックすることが容易です。
2番目の方法では、main
関数内で明示的にcontext
を渡し、その中にロガーを格納します。必要な場合はlog.Logger.FromContext(ctx)
を使用します。現時点では明らかな欠点は見つかっていません。
const (
ContextKeyLogger = "logger"
)
func main() {
ctx := context.Background()
loggerCtx := context.WithValue(ctx, ContextKeyLogger, log.Logger)
}
3番目の方法は、現在の方法です。ロガーをパッケージ化し、他のパッケージが使用できるように公開します。これにより、簡単に使用できるという最大の利点がありますが、テストする際には容易ではありません。パラメータ化されていないため、ロガーをモックするのは困難です。(他の良い解決策もあるかもしれませんが?)
package logging
var Logger *log.Logger
func init() {
Logger = &log.Logger{
// your setting
}
}
次に、この記事での推奨事項を見てみましょう:
- logrusの使用をお勧めします。logrusは非常に使いやすいログパッケージであり、ネイティブの
log
を完全にサポートしています。フック機構を使用することで、簡単にサードパーティのレポートプラットフォーム(例:datadog、sentryなど)と連携することができます。 - ログのフォーマットとしてJSONを使用することをお勧めします。JSONは解析しやすく、主要な言語でサポートされており、サードパーティプラットフォームにとっても解析しやすいです。logrusでは
SetFormatter(&logrus.JSONFormatter{})
を使用して出力を設定できます。 - 統一されたインターフェースを使用します。
- ゴルーチン内でロガーを呼び出さないようにします。並行性の問題に加えて、ロガーの内部実装にもゴルーチンが実行される可能性があるため、メカニズム全体を制御するのが難しくなります。
- ログをローカルファイルに保存します。他のプラットフォームを使用している場合でも、ネットワークの問題によって一部のログが失われることはありませんし、常にログファイルを見つけることができます。
- クラスターなどのアーキテクチャを使用している場合、
syslog
を使用してログを1つのサーバーに集中させる必要があるかもしれません。 - Dockerなどのコンテナサービスを使用している場合、DockerのSTDOUTを監視する必要があるかもしれません。
その他
log.Fatal()
とlog.Panic()
について、Fatal
は暗黙的にos.Exit(1)
を呼び出し、Panic
は暗黙的にpanic
を呼び出します。使用する際には特に注意が必要です。