[Python] ログレベル別にログ出力回数を計測する

概要

エラーの発生件数を計測するため、ログレベル別にログ出力を計測する処理をPythonで実装しました。

環境

この記事では、Python 3.9.1 で以下のパッケージを使って動作確認をしました。

tornado==6.1
prometheus_client==0.8.0

実装

ログを出力するときに意識しなくてもいいように、フィルタオブジェクトでカウントする処理を行います。フィルタではカウント処理だけを行うので、常にTrueを返します。

動作確認用にWebアプリケーションにしています。

import logging
import random

import tornado.ioloop
import tornado.web
from prometheus_client.metrics import Counter
from prometheus_client.registry import REGISTRY
from prometheus_client.exposition import choose_encoder


class LogLevelCountFilter(logging.Filter):
    counter = Counter(
        name='log_events',
        documentation='Number of error level events that made it to the logs',
        labelnames=('level',))

    def filter(self, record: logging.LogRecord) -> bool:
        LogLevelCountFilter.counter.labels(
            level=record.levelname
        ).inc()
        return True


class MetricsHandler(tornado.web.RequestHandler):
    def get(self):
        encoder, content_type = choose_encoder(
            self.request.headers.get('Accept'))
        self.set_header('Content-Type', content_type)
        self.write(encoder(REGISTRY))


class TestHandler(tornado.web.RequestHandler):

    def get(self) -> None:
        # ランダムにステータスコードを返す
        self.set_status(random.choice([200, 400, 500]))

    def on_finish(self) -> None:
        message = (f'{self.request.method} {self.request.uri} '
                   f'{self.get_status()}')
        if self.get_status() == 200:
            logging.info(message)
        elif self.get_status() == 400:
            logging.warning(message)
        elif self.get_status() == 500:
            logging.error(message)


def main():
    logging.basicConfig()
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    # フィルタを設定
    logger.addFilter(LogLevelCountFilter())  

    app = tornado.web.Application([
        (r'/test', TestHandler),
        (r'/metrics', MetricsHandler),
    ])
    app.listen(8080)
    tornado.ioloop.IOLoop.current().start()


if __name__ == '__main__':
    main()

動作確認

# 適当にリクエストして、レスポンスステータスの数をカウントする
$ for i in $(seq 100); 
do
  curl -o /dev/null -w '%{http_code}\n' -s http://localhost:8080/test
done | sort | uniq -c
   30 200
   35 400
   35 500

# メトリクスを確認
$ curl http://localhost:8080/metrics
...
# HELP log_events_total Number of error level events that made it to the logs
# TYPE log_events_total counter
log_events_total{level="WARNING"} 35.0
log_events_total{level="ERROR"} 35.0
log_events_total{level="INFO"} 30.0
# HELP log_events_created Number of error level events that made it to the logs
# TYPE log_events_created gauge
log_events_created{level="WARNING"} 1.617544123928205e+09
log_events_created{level="ERROR"} 1.6175441239456499e+09
log_events_created{level="INFO"} 1.617544123958624e+09

コメントする