🍣

httprouterでpprofを使う

2023/02/04に公開

はじめに

メモリリークの調査のためにhttprouterを使っているアプリケーションでpprofを使おうとしたら手こずったので、その時に調べた内容の備忘録になります。

https://pkg.go.dev/net/http/pprof

https://github.com/julienschmidt/httprouter

うまくいかなかったこと

公式ドキュメントを参考にアプリケーションで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のコードを見ると分かります。

https://cs.opensource.google/go/go/+/refs/tags/go1.19.5:src/net/http/pprof/pprof.go

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のコメントを参考にさせていただきました。

https://github.com/julienschmidt/httprouter/issues/236#issuecomment-377326986

まとめ

公式ドキュメントをちゃんと読もうと反省しました。

Discussion