LoginSignup
1
0

More than 1 year has passed since last update.

【Go】chi の組み込み middleware にカスタムロガーを設定しようと思ってやめた話

Last updated at Posted at 2022-12-13

はじめに

  • go の web フレームワークライブラリとして chi がある
  • http リクエスト/レスポンスをインターセプトして共通処理をする middleware が差し込めるようになっている
  • よく使われるものは chi が用意してくれており、自前で書かなくても使えるようになっている

経緯

  • リクエスト処理中に panic した時用に、panic ハンドリングをする middleware を差し込みたかった
  • chi が用意してくれているものがあるので、それを使おうとした
  • これが内部的にエラーログを出力するが、デフォルトだと改行されてズラズラ出力されるので、それを構造化された形(json)で出力したかった
    • アプリケーション内では zap を使っており、middleware 内でもそのカスタムロガーを使ってログを吐きたかった

tl;dr

  • ロガーをカスタムするのが面倒そうだったので、結局 chi の用意してくれたものを使うのではなく、panic ハンドラを自作してそれを差し込んで使うようにした
    • 内容的には大した実装ではない
    • 自分で書くなら、自分で作ったロガーを使って好きにログを吐くようにできる
  • chi が提供している midleware の中でログを吐くのは panic ハンドラーと、アクセスロギングのハンドラーしかなく、後者も自作したかったため、この対応で特に問題なかった
    • アクセスロギングは、提供されている interface に則ると、出力したいものが出力できなかったため自作した

middleware にロガーを設定する

結局使わなかったが、もし設定するならどうやるか

方法1 chi の RequestLogger middleware を使う

そもそも、chi の middleware にはカスタムロガーを設定するというオプションはなく、アクセスログを自動で吐くための RequestLogger という middleware しか用意されていない。

この RequestLogger の引数にロガーを渡すことができ、それは chi の定めたロガー LogFormatter interface を実装している必要がある。

// LogFormatter initiates the beginning of a new LogEntry per request.
// See DefaultLogFormatter for an example implementation.
type LogFormatter interface {
	NewLogEntry(r *http.Request) LogEntry
}

// LogEntry records the final log when a request completes.
// See defaultLogEntry for an example implementation.
type LogEntry interface {
	Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{})
	Panic(v interface{}, stack []byte)
}

以下は chi が用意する example コード。

上記の例では logrus を使い、chi のロガー interface を満たした struct にそれを持たせて使う、という形になっている。
見ての通り、interface を満たすために、多くのコードを書かないといけなく、ちょっと辛い。

で、この middleware を設定すると内部的にはリクエスト context にそのロガーを突っ込んでおり、panic ハンドラーでは context からロガーを取り出して使う実装になっているので、副次的に panic ハンドラーのロガーも更新されるという形になっている(分かりづら...)。

それから、panic ハンドラーのロガーを変えたいだけなのに、アクセスログも出るようになってしまうし、context に勝手にロガーオブジェクトを突っ込まれてしまう。
もちろん、NewLogEntryWrite メソッドの実装を空にしておけば何も出力されないが、ちょっと気持ち悪い。

方法2 go-chi/httplog を使う

readme に書いてある通り、httplog のロガーを作って httplog.RequestLogger(logger) で middleware に設定する。

main.go
// Logger
logger := httplog.NewLogger("httplog-example", httplog.Options{
    JSON: true,
})

// Service
r := chi.NewRouter()
r.Use(httplog.RequestLogger(logger))
  • 単に json にしたいだけならお手軽な方法ではある
    • オプションを指定することで json にしたりログレベルを変えたりなどが可能っぽい
  • しかし、内部的に zerolog が使われるようで、自作のロガーを使うことはできない

よもやま

chi のリクエスト/レスポンスロギング middleware の何が微妙だったか

  • レスポンスのログにパス名を含められない
  • リクエストとレスポンスでそれぞれログを吐かないといけない
    • このリクエストに対してこのレスポンスをした、というのを一つのログで吐きたかった
  • ということで、結局以下のように自分で作った middleware を使っている
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0