Goでログを取る。 システムの選択と使用

You’re relatively new to the Go language. おそらく、Web アプリケーションまたはサーバーを書くために Go を使用していて、ログ ファイルを作成する必要があります。 そこで、Web ですばやく検索し、Go でログを記録するための多数のオプションがあることを知りました。 どれを選べばいいのか、どうやって判断すればいいのでしょうか? この記事では、その質問に答えるための情報を提供します。

組み込みの log パッケージを見て、Go エコシステムで普及している他のロギング ソリューションを調べる前に、それがどのプロジェクトに向いているかを判断します。 ログはすべての本番 Web アプリケーションで使用され、開発者と運用に役立ちます:

  • アプリケーションのコードでバグを発見する
  • パフォーマンスの問題を発見する
  • 停止とセキュリティ インシデントの事後分析を行う

実際にログするデータは、構築中のアプリケーションの種類に依存します。 たいていの場合、以下のようなバリエーションがあるでしょう。

  • イベントが発生した、またはログが生成されたときのタイムスタンプ
  • デバッグ、エラー、または情報などのログ レベル
  • 何が起こったかを理解し、状況を簡単に再現するためのコンテキスト データ

ログしてはいけないもの

一般的には、機密のビジネス データまたは個人の識別ができる情報のいかなる形式もログしてはなりません。 これには、

  • Names
  • IP Address
  • Credit Card Number

これらの制限により、エンジニアリングの観点からログの有用性が低くなりますが、アプリケーションはよりセキュアになっています。 多くの場合、GDPR や HIPAA などの規制により、個人データのログを取ることが禁止されている場合があります。

log パッケージの導入

Go 標準ライブラリには、ほとんどの基本的なログ機能を提供する組み込みの log パッケージがあります。 デバッグ、警告、エラーなどのログ レベルはありませんが、基本的なロギング戦略をセットアップするのに必要なものはすべて提供されます。「というテキストを標準エラーに出力しますが、日付と時刻も出力します。これは、日付でログメッセージをフィルタリングするのに便利です。

Logging to a file

ログメッセージをファイルに保存する必要がある場合、新しいファイルを作成するか既存のファイルを開いて、それをログの出力として設定することで行なえます。 以下はその例です。

package mainimport ( "log" "os")func main() { // If the file doesn't exist, create it or append to the file file, err := os.OpenFile("logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) if err != nil { log.Fatal(err) } log.SetOutput(file) log.Println("Hello world!")}

このコードを実行すると、次のように logs.txt.

2019/12/09 17:22:47 Hello world!

に書き込まれます。前述のように、基本的にログはio.Writerインターフェイスを実装する任意の宛先に出力できるので、アプリケーションでメッセージを記録する場所を決定する際にはかなり柔軟性が高くなります。

カスタム ロガーの作成

パッケージは標準エラーに書き込む定義済みの logger を実装していますが、log.New() メソッドを使用してカスタム ロガー タイプを作成できます。

新しいロガーを作成する場合、log.New() に 3 つの引数を渡す必要があります:

  • out: ログデータの書き込み先となるio.Writerインターフェイスを実装した任意の型
  • prefix: ログデータの書き込み先となるio.Writerインターフェイスを実装した任意の型。 各ログ行の先頭に付加される文字列
  • flag: logger が生成する各ログエントリに含めるべきロギングプロパティを定義できる定数のセット (これについては次のセクションで説明します)

この機能を利用して、カスタム logger を作成することができます。 以下は、InfoWarningError ロガーを実装した例です。

package mainimport ( "log" "os")var ( WarningLogger *log.Logger InfoLogger *log.Logger ErrorLogger *log.Logger)func init() { file, err := os.OpenFile("logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) if err != nil { log.Fatal(err) } InfoLogger = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) WarningLogger = log.New(file, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile) ErrorLogger = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)}func main() { InfoLogger.Println("Starting the application...") InfoLogger.Println("Something noteworthy happened") WarningLogger.Println("There is something you should know about") ErrorLogger.Println("Something went wrong")}

init 関数の先頭で logs.txt ファイルを作成または開いた後、出力先、プレフィックス文字列、ログフラグを提供して、3 つの定義済みロガーを初期化します。

main関数の中で、Println関数を呼び出してロガーが利用され、新しいログエントリーがログファイルに書き込まれます。 このプログラムを実行すると、次のように logs.txt に書き込まれます。

INFO: 2019/12/09 12:01:06 main.go:26: Starting the application...INFO: 2019/12/09 12:01:06 main.go:27: Something noteworthy happenedWARNING: 2019/12/09 12:01:06 main.go:28: There is something you should know aboutERROR: 2019/12/09 12:01:06 main.go:29: Something went wrong

この例では、1 つのファイルにログを記録していますが、ロガーの作成時に別のファイルを渡せば、各ロガーに別のファイルを使用できます。

Log flags

ログフラグ定数を使って、ファイル、行番号、日付、時間などの追加のコンテキスト情報を提供することによりログ メッセージをより豊かにすることができます。 たとえば、以下に示すフラグの組み合わせで「Something went wrong」というメッセージをロガーに渡すと、

log.Ldate|log.Ltime|log.Lshortfile

will print

2019/12/09 12:01:06 main.go:29: Something went wrong

残念ながら、表示順序や表示形式を制御することはできない。

Introducing logging frameworks

log パッケージを使用することは、リッチで構造化されたログを生成するより、速いフィードバックを得ることが重要なローカル開発では素晴らしいことです。 それ以上では、ほとんどの場合、ロギング フレームワークを使用する方がよいでしょう。

Logging Framework を使用する主な利点は、ログ データを標準化するのに役立つということです。 これは、次のことを意味します。

  • ログ データを読み、理解することがより簡単になります。 7358>

    Choosing a logging framework

    Deciding which framework to use is a challenge, as there are several options to choose from.

    The two most popular logging frameworks for Go seems to beglog and logrus. glog の人気は驚くべきもので、数年間更新されていません。logrus はよりよくメンテナンスされており、Docker などの人気のあるプロジェクトで使用されているので、これに注目することにします。

    Getting started with logrus

    logrus のインストールは、ターミナルで以下のコマンドを実行するだけです:

    go get "github.com/Sirupsen/logrus"

    logrus の素晴らしい点は、標準ライブラリの log パッケージと完全に互換性があるので、あらゆる場所の log インポートを log "github.com/sirupsen/logrus" で置き換えれば、そのまま動作します!

    logrus のインストールは簡単です!

    log パッケージを使用した先ほどの “hello world” の例を修正し、代わりに logrus を使用してみましょう。

    Logging in JSON

    logrus は、JSON での構造化ロギングに適しています – JSON は明確に定義された標準なので、外部サービスがログを解析しやすく、また、以下のように、フィールドの使用によりログ メッセージにコンテキストを比較的容易に追加できます:

    package mainimport ( log "github.com/sirupsen/logrus")func main() { log.SetFormatter(&log.JSONFormatter{}) log.WithFields( log.Fields{ "foo": "foo", "bar": "bar", }, ).Info("Something happened")}

    生成されるログ出力はメッセージ、ログ レベル、タイムスタンプおよびインクルーシブ フィールドを含む JSON オブジェクトとなります。

    {"bar":"bar","foo":"foo","level":"info","msg":"Something happened","time":"2019-12-09T15:55:24+01:00"}

    ログを JSON として出力することに興味がない場合、logrus 用のいくつかのサード パーティ製フォーマッターが存在し、その Github ページで見ることができることに注意してください。 7358>

    ログ レベル

    標準のログ パッケージとは異なり、logrus はログ レベルをサポートします。

    logrus には 7 つのログ レベルがあります。 Trace、Debug、Info、Warn、Error、Fatal、および Panic です。 各レベルの重大度は、リストを下に行くほど高くなります。

    log.Trace("Something very low level.")log.Debug("Useful debugging information.")log.Info("Something noteworthy happened!")log.Warn("You should probably take a look at this.")log.Error("Something failed but I'm not quitting.")// Calls os.Exit(1) after logginglog.Fatal("Bye.")// Calls panic() after logginglog.Panic("I'm bailing.")

    logger にログレベルを設定することにより、環境に応じて必要なエントリのみをログに記録することが可能です。 デフォルトでは、logrus は Info 以上 (Warn、Error、Fatal、または Panic) のログを記録します。

    package mainimport ( log "github.com/sirupsen/logrus")func main() { log.SetFormatter(&log.JSONFormatter{}) log.Debug("Useful debugging information.") log.Info("Something noteworthy happened!") log.Warn("You should probably take a look at this.") log.Error("Something failed but I'm not quitting.")}

    上記のコードを実行すると、次の出力が生成されます:

    {"level":"info","msg":"Something noteworthy happened!","time":"2019-12-09T16:18:21+01:00"}{"level":"warning","msg":"You should probably take a look at this.","time":"2019-12-09T16:18:21+01:00"}{"level":"error","msg":"Something failed but I'm not quitting.","time":"2019-12-09T16:18:21+01:00"}

    デバッグ レベルのメッセージが出力されないことに注意してください。 ログに含めるには、log.Levellog.DebugLevel と同じにします:

    log.SetLevel(log.DebugLevel)

    Wrap up

    この投稿では、組み込みログ パッケージの使用方法を調べ、つまらないアプリケーションや迅速なプロトタイプを構築する場合にのみ使用すべきことを確立しました。 その他のすべてについては、主流のログ記録フレームワークを使用することが必須です。

    また、ログに含まれる情報が一貫しており、特に集中型プラットフォームで集約する場合に、分析が容易であることを保証する方法について考察しました。

Leave a Reply