zerologを標準logの代用にするための設定


ログレベルのない標準logでは辛くなってきたので、高速構造化LoggerであるzerologのPretty loggingで代用するための設定について調べました。構造化ログを知らなかった無知が書いております。

公式で紹介されている例

zerolog公式でもPretty loggingの設定方法が軽く紹介されているのですが、その出力は

2006-01-02T15:04:05+09:00 | INFO  | ***Hello World**** foo:BAR

で、個人的にマイクロ秒とファイル名、行番号が欲しかったのですが(あとタイムゾーンは不要)検索では見つけられず、godocを眺めつつの試行錯誤で探しました。

設定方法

以下の手順でできました。なお移行中に標準logと混在しても困らないようにzerolog/logはzlogにエリアスしています。またglobal loggerを書き換えています。

import (
    "fmt"
    "log"
    "path/filepath"
    "strings"
    "time"

    "github.com/rs/zerolog"
    zlog "github.com/rs/zerolog/log"
)

func initLog() {
    zerolog.SetGlobalLevel(zerolog.InfoLevel)
    zerolog.TimeFieldFormat = time.RFC3339Nano
    output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: "2006-01-02 15:04:05.000000"}
    output.FormatLevel = func(i interface{}) string {
        return fmt.Sprintf("%-6s", i)
    }
    output.FormatMessage = func(i interface{}) string {
        return fmt.Sprintf(" %s ", i)
    }
    output.FormatFieldName = func(i interface{}) string {
        return fmt.Sprintf("%s:", i)
    }
    output.FormatFieldValue = func(i interface{}) string {
        return fmt.Sprintf("%s", i)
    }
    output.FormatCaller = func(i interface{}) string {
        t := fmt.Sprintf("%s", i)
        s := strings.Split(t, ":")
        if 2 != len(s) {
            return t
        }
        f := filepath.Base(s[0])
        return f + ":" + s[1]
    }
    zlog.Logger = zerolog.New(output).With().Timestamp().Caller().Logger()
    zlog.Info().Str("foo", "bar").Int("baz", 123).Msg("Hello World")
}

// 2019-01-20 18:48:25.743394 info   main.go:257  Hello World  baz:123 foo:bar

なお複数のpackageで構成するプログラムの場合にはglobal loggerを書き換えるよりもファイル先頭で

var Logger zerolog.Logger

としておき、

    Logger = zerolog.New(output).With().Timestamp().Caller().Logger()
    Logger.Info().Str("foo", "bar").Int("baz", 123).Msg("Hello World")

と設定して使う方が、packageごとに異なるログレベルを設定したりできるようになるので良さそうです。

使い方補足

zerologは本来機械可読な構造化ログを出力するためのものなので、表示したいパラメータは一つずつStr()やInt()といった型ごとのメソッドに名前と一緒に渡し、それをメソッドチェーンで繋ぐという思想の様です。自作のstructもInterface()で表示できます(リフレクションを使うので遅くはなるはずです)。

後づけの狙い

開発時にはログレベル付Loggerとして利用、出荷時にはPretty loggingをやめれば超高速Loggerであるzerologの恩恵にあやかれるはずだと思います。

ログローテーション

lumberjackを使うのが簡単そうです。

writer := &lumberjack.Logger{
    Filename:   "/var/log/myapp/foo.log",
    MaxSize:    10, // megabytes
    MaxBackups: 5,
    MaxAge:     28, //days
    Compress:   true, // disabled by default
}

としておいて、

output := zerolog.ConsoleWriter{Out: writer, ... 

これでできました。

※動作確認バージョン go 1.8.1 / zerolog v1.11.0
※当初zapを試そうとしましたが、最新2バージョンのみサポートだそうで、2019/1現在すでにgo 1.8.1では動きませんでした