httprouterでpprofを使う
はじめに
メモリリークの調査のためにhttprouterを使っているアプリケーションでpprofを使おうとしたら手こずったので、その時に調べた内容の備忘録になります。
うまくいかなかったこと
公式ドキュメントを参考にアプリケーションでpprofを有効にしてみました。
package main
import (
//...
"net/http"
_ "net/http/pprof"
)
ですが、プロファイルを取得しようとしてもできない状態でした。
$ go tool pprof http://localhost:xxxx/debug/pprof/heap
原因
公式ドキュメントにちゃんと書いてあったのですが、DefaultServeMuxを使用していない場合は独自でハンドラーを登録しないといけないようです。
If you are not using DefaultServeMux, you will have to register handlers with the mux you are using.
「DefaultServeMuxを使用していない」とはどういう状況でしょうか。
DefaultServeMuxとはnet/httpで定義されている変数です。
http.ListenAndServeでlistenするときにHandlerをnilで設定すると、DefaultServeMuxが使用されます。
log.Fatal(http.ListenAndServe(":8080", nil))
httprouterを使っている場合はhttp.ListenAndServeのHandlerに値を渡しており、DefaultServeMuxは使用しない状態になっていることが分かります。
log.Fatal(http.ListenAndServe(":8080", router))
DefaultServeMuxを使用している場合に自動で利用可能になる理由はpprofのコードを見ると分かります。
init()ではhttp.HandleFuncでIndex、Cmdlineなどのハンドラーを登録しています。
http.HandleFuncはDefaultServeMuxにハンドラーを登録する関数です。
func init() {
http.HandleFunc("/debug/pprof/", Index)
http.HandleFunc("/debug/pprof/cmdline", Cmdline)
http.HandleFunc("/debug/pprof/profile", Profile)
http.HandleFunc("/debug/pprof/symbol", Symbol)
http.HandleFunc("/debug/pprof/trace", Trace)
}
なのでDefaultServeMuxでhttp.ListenAndServeでlistenしている場合は、pprofをimportするだけで利用可能になるんですね。
httprouterでのpprofの設定方法
httprouterのプロファイルを取得するために必要なパスをDefaultServeMuxに登録してあげればOKです。
router.Handler(http.MethodGet, "/debug/pprof/*item", http.DefaultServeMux)
まとめてパスを設定する書き方は以下のissueのコメントを参考にさせていただきました。
まとめ
公式ドキュメントをちゃんと読もうと反省しました。
Discussion