GolangでGKE用(StackdriverLogging)のログを出力する
GKE(Google Kubernetes Engine)でアプリケーションを開発する場合、ログは標準出力に出力し、GKEデフォルトでインストールされるfluentdでStackdriverLoggingに出力することが多いと思います。
その場合、特定のJSONフォーマットで出力することでStackdriverLogging上で扱いやすくなります。フォーマットの詳細はこちらです。 https://cloud.google.com/logging/docs/agent/configuration#special-fields
Golangの場合、デフォルトのLoggerでは
という問題があったので、どうすればよいのか調べてみました。
まず、ログ用のライブラリですが、logrusというライブラリを使うのがよさそうでした。 https://github.com/sirupsen/logrus
構造化したログを扱うことができ、フォーマットも指定することができます。
JSONのFormatterは標準で用意されているので下記のように利用することでJSON形式でログを出力することが可能です。init() でformatterを指定している箇所で環境変数を使った分岐を使えば、手元ではテキスト形式、サーバではJSONという分岐も可能です。
import ( "os" log "github.com/sirupsen/logrus" ) func init() { // Log as JSON instead of the default ASCII formatter. formater := log.JSONFormatter{ FieldMap: log.FieldMap{ log.FieldKeyLevel: "severity", }, } log.SetFormatter(&formater) // Output to stdout instead of the default stderr log.SetOutput(os.Stdout) // Only log the warning severity or above. log.SetLevel(log.InfoLevel) } func main() { log.Info('hello') // => {"msg": "hello", "level":info, "time":"2019-11-24T18:06:23+09:00"} }
StackdriverLoggingに連携するにあたって、levelは、"severity"というkeyで連携する必要があります。これは、JSONFormaterにFieldMapを指定することで実現できます。
func init() { // Log as JSON instead of the default ASCII formatter. formater := log.JSONFormatter{ FieldMap: log.FieldMap{ log.FieldKeyLevel: "severity", }, } log.SetFormatter(&formater) }
この解決策は、下記Issueで紹介されていました
また、logrusで用意されているレベルとStackdriverLoggingで定義されているレベルには差異があるので、カスタムレベルを定義する方法があれば、それを利用したいところですが、logrusには用意されていないようでした。info, warning,errorは利用できますが、それ以外を利用するとStackdriverLoggingでは正しく扱えないので注意が必要です。 それぞれがサポートしているレベルは下記です。
StackdriverLogging: https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity
logrus: https://github.com/sirupsen/logrus/blob/39a5ad12948d094ddd5d5a6a4a4281f453d77562/logrus.go#L27
Custom Log Levelについては、Issueで要望があがっていますが、いまのところ実装される見込みは薄そうです。
ということで手軽にJSON形式のログを出力する場合はlogrusがおすすめです。Golang標準のloggerインターフェースを実装しているので、後から差し替えるのも簡単だと思います。