以前書いた記事ではしれっとHTTPステータスを int
に詰め直していたのですが、他のところで実装していたときにハマったのでメモを残しておきます。
def on_finish(self) -> None:
# レイテンシを記録する
histogram.labels(
method=self.request.method,
uri=self.request.path,
status=int(self.get_status()) # ここの話です
).observe(self.request.request_time())
背景
Python 3.5で HTTPStatus
列挙子が http モジュールに追加されました。 HTTPStatus
は IntEnum を継承していて、int のサブクラスでもあるため、RequestHandler
の get_status()
で
が返ってくる可能性があります。HTTPStatus
status=self.get_status()
というコードにしていた場合、prometheus/client_python – metrics.py#L161 で文字列に変換されるときに列挙子のメンバー名に変換されます。
そのため、Prometheus のラベルには HTTP ステータスコードではなく列挙子のメンバー名が設定されてしまいます。
>>> from http import HTTPStatus
>>> str(HTTPStatus.BAD_REQUEST)
'HTTPStatus.BAD_REQUEST'
HTTP ステータスコードにするにはどうするか
これを防ぐには、一般的にはラベルの値に Prometheus に表示したい文字列をちゃんと渡してやる必要があります。
def on_finish(self) -> None:
# 文字列に変換する
if isinstance(status, HTTPStatus):
status = str(self.get_status().value)
else:
status = str(self.get_status())
# レイテンシを記録する
histogram.labels(
method=self.request.method,
uri=self.request.path,
status=status
).observe(self.request.request_time())
今回のケースでは、HTTPStatus
を int に詰め直してやれば、文字列に変換したときに HTTP ステータスコードになるので、実装をシンプルにできます。
>>> int(HTTPStatus.BAD_REQUEST)
400
>>> str(int(HTTPStatus.BAD_REQUEST))
'400'
def on_finish(self) -> None:
# レイテンシを記録する
histogram.labels(
method=self.request.method,
uri=self.request.path,
status=int(self.get_status())
).observe(self.request.request_time())
結論
Prometheus のラベル値にオブジェクトを渡す場合は意図しない文字列に変換されないかを確認することが必要です。意図を明確にするには文字列に変換して渡してやるのが無難です。