- Published on
Go の Web サーバで Response Body と Request Body をロギングする
- Authors
- Name
- ko-da-k
- @ko_da_k
旧ブログ(除却予定)から記事を移行しました
TL;DR
- こんな感じの middleware を実装すれば Request Body も Response Body もロギングできるようになる
// loggingWriter http.ResponseWriter の wrapper
type loggingWriter struct {
http.ResponseWriter
multi io.Writer
}
// Write はmultiwriter に書き込むことで response body を記録
func (w *loggingWriter) Write(b []byte) (int, error) {
return w.multi.Write(b)
}
// extraLog なんかいい感じの構造体に突っ込んどく
// Log は極力 Json 化しておいたほうがいい (入門 監視 等の書籍に書いてある)
type extraLog struct {
RequestBody interface{} `json:"requestBody"`
ResponseBody interface{} `json:"responseBody"`
}
// loggerMiddleware negroni 等を利用した middleware の実装
func loggerMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
var logBuf bytes.Buffer
multiWriter := io.MultiWriter(w, &logBuf)
rspWriter := &loggingWriter{w, multiWriter}
// request body のロギング
reqBody, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Warnf("Failed to read request body: %s\n", err)
}
r.Body = ioutil.NopCloser(bytes.NewBuffer(reqBody)) // 次のハンドラでも使えるように
// loggingWriter を渡してあげる
next(rspWriter, r)
// あとはこれをいい感じのロギングライブラリで表示してあげるだけ
extra := extraLog{
RequestBody: string(reqBody),
ResponseBody: logBuf.String(),
}
}
背景
最近、仕事で Go を書くことが多く、最近は API Server の実装を行っていることが多いです。
その中で、サーバログとして Request Body と Response Body を出力することがあったので備忘録的にまとめておきます (セキュリティ的な観点もあるので、Request Body と Response Body をログ出力するのはあまり良しとは言えませんが...)
Request Body は割とすんなり扱えたのですが、Response Body を Middleware を挟んでログ出力するのは少し工夫が必要だったので、まとめておきます
やっていること
http.ResponseWriter
の Interface は、 以下のような定義になっています、
type ResponseWriter Interface {
Header() Header
Write([]byte) (int, error)
WriteHeader(statusCode int)
}
https://golang.org/pkg/net/http/#ResponseWriter
上記で作成した例は、Writeを Overwrite する形で、ログ出力用の MultiWriter
に書き込むようにしています
また、middlewareで r.Body
を読み取ってしまうと、次の Handler に渡せなくなってしまうため、ioutil.NopCloser
を利用しています