おそらくはそれさえも平凡な日々

Goの任意のLoggerをログローテート対応できるreplaceablewriter

https://github.com/Songmu/replaceablewriter

表題の通りですが、io.Writer をラップして io.WriteCloser として振る舞い、その内部に保持した io.Writer を差し替え可能にするライブラリを書いた。

例えば、Goの標準logをログローテートしたい場合には以下のようにします。

f, _ := os.OpenFile("20191001.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
w := replaceablewriter.New(f)
log.SetOutput(w)
// 翌日になったら差し替える
f2, _ := os.OpenFile("20191002.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
w.Replace(f2) // 以降、20191002.logにログが書き込まれる

io.Writer の差し替えはシグナル受けたときにやっても良いし、タイマーを回しても良いでしょう。ファイル名を同じにしたい場合は、事前にファイルを os.Rename してからファイルを開き直して、Replaceを呼べばOKです。例えば以下のような形。

logname := "log.log"
f, _ := os.OpenFile(logname, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
w := replaceablewriter.New(f)
log.SetOutput(w)
// 翌日になったら差し替える
os.Rename(logname, "log-20191001.log")
f2, _ := os.OpenFile(logname, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
w.Replace(f2)

単なる io.WriteCloser なので、io.Writer を指定できるloggerであればなんでも対応可能です。ざっと書いたので、まだ適当クオリティですが、フィードバックあると嬉しいです。

作った動機とか

個人的に多機能なロガーが好きじゃない。ロガーは呼び出しも多く、IOを伴う処理でもあるため、地味にパフォーマンスのボトルネックになりやすい。なので、ロガーはシンプルにストリームに書くことに集中して欲しい。プラガブルな機能とかログローテート機能は別に必要ない。実際、多機能で知られるlogrusとかもパフォーマンスが悪いことで有名です。

ログローテートも、アプリケーション自体はシンプルに標準エラー出力や標準出力に吐いておいて、後続のlograotate, clonolog, multilog等に任せるのが好みです。kazuhoさんもこう言っています。

そもそもファイルに書くという発想が時代遅れになりつつあるわけです。それでもログローテートしたい場合、ファットなロガーがオールインワンでそれを提供するのではなくて、Goにはio.Writer という素晴らしい抽象があるので、このレイヤーで書き出し先を差し替えれば良いんじゃないかと思ったのが、このreplaceablewriterを作った動機です。

ログローテートについて熱く語ってしまいましたが、別にログローテートに限らず、書き出し先を切り替えられるのは便利だと思うので、よろしければご活用ください。

created at
last modified at

2019-10-09T01:24:09+0900

comments powered by Disqus