Goの標準logの問題
Goの標準ライブラリには、logパッケージ(標準log)というものがあり、単純なログ出力であれば、これで十分です。
標準logは、Javaのslf4jのような、よくあるログライブラリのようにDebug
とかInfo
といった、ログレベル別の関数を持ちません。あるのは以下の3系統のみです。
- Print系
- ログを出力する。
- Panic系
- ログを出力してから
panic
を発生させる
- ログを出力してから
- Fatal系
- ログを出力してから
os.Exit(1)
を呼んで、アプリケーションを終了させる
- ログを出力してから
これで分かるとおり、通常のログ出力には、Print系しか使えないため、ある程度大規模化、複雑化したアプリケーションで、ログレベルに応じた出力をしようとすると、標準logでは機能不足になります1。
標準以外のログライブラリを使った場合の問題
この標準logの機能不足を解決するために、いくつものログライブラリが作られました。代表的なものとしてはDockerでも使われているlogrusでしょうか。
ですが、このような標準以外のログライブラリを使うと、以下のような問題が起きます。
- 標準logを含め、利用するライブラリが混在しないように気を使う
- 共通ライブラリを作った場合、そこでも同じログライブラリを使わないと面倒
- 強い言い方をすると関連するもの全てがログライブラリに汚染されてしまう
- そのログライブラリの使い方を覚える必要がある
- チーム開発の場合、全員に周知する必要がある
レビューで弾けとか、それくらい覚えろという意見もあるかと思いますが、Webアプリでさえシンプルにnet/http
だけで済ませたい自分としては、ログ出力についても同様にシンプルに済ませたいのです。
cologを使う
そこでcologの登場です。
cologはprefix-based leveled execution log(colog作者の造語?)なログライブラリで、出力される文字列のプリフィクスをログレベルに利用するログライブラリです。
似たような仕組みのライブラリにはhashicorp/logutilsがありますが、cologの方が拡張性に優れています。
使い方
cologの使い方は簡単で、colog.Register()
を呼び出した後から、cologが有効になり、標準logの出力をフックして、出力するようになります。
ログレベルの指定は、出力されるログの先頭にerror:
のようにプリフィクスを置くことで行います。
デフォルトで規定されているログレベルは以下の6つです。
- trace
- debug
- info
- warn
- error
- alert
プリフィクス無しの場合のログレベルはcolog.SetDefaultLevel(colog.Level)
で指定できます。またcolog.SetMinLevel(colog.Level)
で、出力対象となるログレベルの最小値が指定できます。
出力フォーマットはcolog.Formatter
インターフェースの実装で決定されます。デフォルトではStdFormatterとJSONFormatterが提供されていますが、当然自分で書くことも可能です。colog.Formatter
には標準logのSetFlagで指定出来るものと同じ設定値が渡せるようになっており、これで日時などの出力制御が出来ます。
コード例は以下の通りです。ログ出力そのものは標準logを使っていることに注目してください。
package main
import (
"log"
"github.com/comail/colog"
)
func main() {
colog.SetDefaultLevel(colog.LDebug)
colog.SetMinLevel(colog.LTrace)
colog.SetFormatter(&colog.StdFormatter{
Colors: true,
Flag: log.Ldate | log.Ltime | log.Lshortfile,
})
colog.Register()
log.Printf("trace: this is a trace log.")
log.Printf("debug: this is a debug log.")
log.Printf("info: this is an info log.")
log.Printf("warn: this is a warning log.")
log.Printf("error: this is an error log.")
log.Printf("alert: this is an alert log.")
log.Printf("this is a default level log.")
}
これを実行すると、このように出力されます。
例なので、デフォルトのログレベルを全て色付きで出力してみましたが、通常の本番時には色を無くしたり、SetMinLevelを変更することになるでしょう。
hook
cologには特定のログレベルに対して、フックを設定することができます。
これを使えば、他の関数を呼び出したり、外部サービスにログを飛ばすことが出来ます。
colog作者によって作られた、cologのログをlogrusに渡すcologrusなんてものもあります。
まとめ
cologを使うと、標準logを使ってログを出力するだけで、それなりにリッチなログ出力を行えます。
ログレベルに対応したプリフィクスを覚えておく必要がありますが、特に変わった語彙ではないので、実際には覚えるまでもないでしょう。この程度のプリフィクスであれば、ログが汚れるということもないと思います2。
cologの設定は、具体的なアプリケーションで行うだけなので、共通ライブラリはcologに依存させる必要はなく、外部ライブラリへの依存性を抑えることができます。
作者のブログ記事にはベンチマークもあるので、こちらも参考にしてください。
https://texlution.com/post/colog-prefix-based-logging-in-golang/
それでは良いGo & logライフを。