goで書いたwebappでもApache形式のログを出したい!ということでgo-apache-logformatを書きました。

最初はこのツールを使って毎回ServeHTTP()関数の中でログを出力する関数を呼び出すようにしようと思ったんだけど簡単なラッパーを用意したので多分かなり簡単にApacheログを出力できるようになるはず

まず最初にやらないといけないのがApacheLog構造体を作るところ。出力先のio.Writerと出力フォーマットを指定して作成するんだけど、いわゆるcombined logフォーマットだったらもうすでに作ってあるので、それをClone()しちゃえば簡単:

import (
"github.com/lestrrat/go-apache-logformat"
)

....
logger := apachelog.CombinedLog.Clone()
logger.SetOutput(...) // io.Writer. os.FileとかでもOK. デフォルトはos.Stderr

で、あとはそれをhttp.HandlerFuncで使用されるようにすればいい。もしServeHTTP()関数を実装した構造体があればそれをラップするだけでもOK:

http.ListenAndServe(":8080", apachelog.WrapLoggingWriter(handler.ServeHTTP, logger)

もし色々自分でやりたいなら、ServeHTTP関数の中で自分で以下のようにフックしてもいい:

func ServeHTTP(w http.ResponseWriter, r *http.Request) {
...
lw := apachelog.NewLoggingWriter(w, r, logger)
defer lw.EmitLog() ...
}

ここでひとつ気をつけないといけないのが、上記のServeHTTPの中ではw ではなく lw (LoggingWriter)のほうを使う事。そうしないと諸々必要な値が取れない。

最後にもしこれをログファイルにはき出しつつローテーションも行いたいならgo-file-rotatelogsを最初のloggerにかませばそれでおしまい:

import (
"github.com/lestrrat/go-file-rotatelogs"
"github.com/lestrrat/go-apache-logformat"
) ...
logfile := rotatelogs.NewRotateLogs("/path/to/accesslog.%Y%m%d")
logfile.LinkName = "/path/to/accesslog" logger := apachelog.CombinedLog.Clone()
logger.SetOutput(logfile)

というわけで是非お試しください。ツッコミ、PR、お待ちしております