夏休みの自由課題です。
今作っているサーバにログ出力の機能をつけたいと思っていて、ログライブラリを検討していました。
必要な要件は以下です。
- ファイルには、構造化ログを出力したい(JSONでもLTSVでもなんでもいい)
- コンソール(
STDOUT
)には、人間にある程度見やすいログを出力したい(色がつく必要はない)- ファイルログとコンソールログの情報は一緒で良い。見え方を変えたいだけ
複数の io.Writer
に同時に出力するというところでは io.MultiWriter
が思いつくのですが、
io.MultiWriter
では io.Stdout
とファイルに同時に出力することはできても、別のフォーマットにはできません。
実は、最初に採用しようと考えていたログライブラリでもいろいろ試行錯誤したのですが、そのライブラリでの解決方法は見つけられませんでした。
で、結局、zap というLoggerで綺麗に解決できたので紹介します。
zap
Goの世界ではとても有名なLoggerなのですね。高速というのも魅力です。
ちなみにzapの使い方や設定の説明は以下のエントリが最高です。
io.Stdoutとファイルのそれぞれに別々のフォーマットでログを出力する
上のエントリにもあるように普通のzapの使い方だと zap.Config
を作成-> Build()
して zap.Logger
を作るのですが、今回は面倒なことをするので、zap.New()
を利用して zap.Logger
を作成します。
zap.New()
は zapcore.Core
を引数に必要としますので、まずはzapcore.Core
を作成します。
zapcore.Core
は zapcore.NewCore()
を使って作成します。
zapcore.NewCore()
は、zapcore.Encoder
、zapcore.WriteSyncer
、zapcore.LevelEnabler
の3つの引数を必要とします。
それぞれの引数の概要は以下のとおりです。
引数 | 概要 |
---|---|
zapcore.Encoder |
ログフォーマット形式 |
zapcore.WriteSyncer |
出力先 |
zapcore.LevelEnabler |
ログレベル |
ようは、ログフォーマットと出力先の異なる複数の zapcore.NewCore()
を持つ zapcore.Logger
を作れればいいのですが、zapのソースコードを読んでいたらありました。
というわけで具体的なコードです。
package logger import ( "log" "os" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) // NewLogger returns logger func NewLogger() *zap.Logger { encoderConfig := zapcore.EncoderConfig{ TimeKey: "time", LevelKey: "level", NameKey: "name", CallerKey: "caller", MessageKey: "msg", StacktraceKey: "stacktrace", EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.StringDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, } file, _ := os.Create("app.log") consoleCore := zapcore.NewCore( zapcore.NewConsoleEncoder(encoderConfig), # コンソールで見やすい形式 zapcore.AddSync(os.Stdout), # io.Writerをzapcore.WriteSyncerに変換 zapcore.DebugLevel, ) logCore := zapcore.NewCore( zapcore.NewJSONEncoder(encoderConfig), # 構造化ログ(JSON) zapcore.AddSync(file), zapcore.InfoLevel, ) logger := zap.New(zapcore.NewTee( consoleCore, logCore, )) return logger }
zapcore.NewTee()
を使って zapcore.Core
をまとめているのがポイントです。
というわけで
zapでやりたかったログ出力の形を実現できました。
構造化ログのフォーマットもltsvのエンコーダもあることを確認していますので、良さそうです。