go-chi/chiでJSON logを手軽に使う方法

go-chiは、あまり素のnet/httpからかけ離れたラッピングを行っては居ないので、goでのweb APIの取扱を考えたりするのに便利なのですが、本番運用のことを考えるとJSON logが欲しくなったりしますね。

go-chiのloggerミドルウェアの出力は綺麗なのですが、これをJSON logにしたい。

httplog

middleware一覧の部分で紹介sれているhttplogが便利です。

github.com

package main

import (
    "log"
    "net/http"
    "os"

    "github.com/go-chi/chi"
    "github.com/go-chi/httplog"
    "github.com/go-chi/render"
)

func main() {
    // これと
    l := httplog.NewLogger("app", httplog.Options{
        JSON: true,
    })

    r := chi.NewRouter()
    // r.Use(middlware.Logger) // これは消す
    r.Use(httplog.RequestLogger(l)) // これ

    r.Get("/api", func(w http.ResponseWriter, r *http.Request) {
        data := map[string]string{
            "message": "hello",
        }
        render.JSON(w, r, data)
    })

    addr := os.Getenv("Addr")
    if addr == "" {
        addr = ":4444"
    }

    log.Printf("listen: %s", addr)
    if err := http.ListenAndServe(addr, r); err != nil {
        log.Fatalf("!! %+v", err)
    }
}

実行例 (formatted)

2020/09/22 21:48:09 listen: :44444
{
  "httpRequest": {
    "header": {
      "accept": "*/*",
      "accept-encoding": "gzip, deflate",
      "connection": "keep-alive",
      "user-agent": "HTTPie/1.0.3"
    },
    "proto": "HTTP/1.1",
    "remoteIP": "127.0.0.1:57785",
    "requestID": "xxx",
    "requestMethod": "GET",
    "requestPath": "/api",
    "requestURL": "http://localhost:44444/api",
    "scheme": "http"
  },
  "level": "info",
  "message": "Request: GET /api",
  "service": "app",
  "timestamp": "2020-09-22T21:48:13.069761+09:00"
}
{
  "httpRequest": {
    "proto": "HTTP/1.1",
    "remoteIP": "127.0.0.1:57785",
    "requestID": "xxx",
    "requestMethod": "GET",
    "requestPath": "/api",
    "requestURL": "http://localhost:44444/api"
  },
  "httpResponse": {
    "bytes": 20,
    "elapsed": 0.062464,
    "header": {
      "content-type": "application/json; charset=utf-8"
    },
    "status": 200
  },
  "level": "info",
  "message": "Response: 200 OK",
  "service": "app",
  "timestamp": "2020-09-22T21:48:13.069966+09:00"
}

はい。

ちなみに裏側ではzerologが使われているようです1

go list -json github.com/go-chi/httplog | jqfpy '[path for path in get()["Deps"] if "." in path and not path.startswith("vendor/")]'  --squash -r
github.com/go-chi/chi
github.com/go-chi/chi/middleware
github.com/rs/zerolog
github.com/rs/zerolog/internal/json
github.com/rs/zerolog/log

github.com


  1. もっと良い取得方法がある気がする。