ステータスコードをログに出力するhttp.Handler

httpパッケージだけでWebアプリケーションを書くとき、アクセスログの出力もhttp.Handlerで実装しようとするけど、ステータスコードを取得する手段がないことに気づく。

そこでhttp.ResponseWriterを独自に実装することで解決できたのでメモに残す。

type logger struct {
  logger *log.Logger
  handler *http.Handler
}

func (l *logger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  lw := &loggingResponseWriter{
    ResponseWriter: w,
    statusCode: http.StatusOK,
  }
  l.handler.ServeHTTP(lw, r)
  log.Printf("status:%d path:%s\n", lw.statusCode, r.URL.Path)
}

type loggingResponseWriter struct {
  http.ResponseWriter
  statusCode int
}

func (w *loggingResponseWriter) WriteHeader(statusCode int) {
  w.statusCode = statusCode
  w.ResponseWriter.WriteHeader(statusCode)
}
  • loggerというhttp.Handlerは他のhttp.Handlerをラップするミドルウェアになっている。ServeHTTPでラップしたhttp.HandlerServeHTTPを呼んだ後で、ログを出力している。
  • loggingResponseWriterという独自のhttp.ResponseWriterを用意する。埋め込みによってinterfaceを実装している。
  • loggingResponseWriterWriteHeaderだけ独自に実装し、受け取ったステータスコードを保持するようにしている。こうすることで後でログに出力できる。それ以外のメソッドは埋め込んだhttp.ResponseWriterに委譲される。