参考文献: client_python/metrics.py at master · prometheus/client_python · GitHub
あなたは弁当販売を始めることにしました。A か B の type を指定してリクエストしてもらい、それに応じてA弁当(バターチキンカレー)かB弁当(ローストビーフ)を渡したいと思います。以下のようにすれば、localhost:8080/bentou?type=A にアクセスするとバターチキンカレーが返却されます。
import asyncio from aiohttp import web class BentouHandler: async def handle(self, request): params = {k: v for k, v in request.query.items() if v} if 'type' not in params: raise ValueError('type がありません') type_ = params['type'] bentou = None if type_ == 'A': await asyncio.sleep(2) bentou = {'主食': 'ナン', 'おかず': 'バターチキンカレー'} elif type_ == 'B': await asyncio.sleep(1) bentou = {'主食': 'ごはん', 'おかず': 'ローストビーフ'} else: raise ValueError('type が不正です') return web.json_response(bentou, status=200) handler = BentouHandler() app = web.Application() app.add_routes([web.get(f'/bentou', handler.handle)]) if __name__ == '__main__': web.run_app(app, port=8080)
import asyncio from aiohttp import web from prometheus_client import Counter, Histogram, generate_latest class BentouHandler: async def handle(self, request): params = {k: v for k, v in request.query.items() if v} if 'type' not in params: raise ValueError('type がありません') type_ = params['type'] bentou = None if type_ == 'A': await asyncio.sleep(2) bentou = {'主食': 'ナン', 'おかず': 'バターチキンカレー'} elif type_ == 'B': await asyncio.sleep(1) bentou = {'主食': 'ごはん', 'おかず': 'ローストビーフ'} else: raise ValueError('type が不正です') return web.json_response(bentou, status=200) class BentouMetrics: def __init__(self): # 必要なカウンタを用意 self.get_req_counter = Counter('get_request_count', 'リクエスト数') self.resp_time_hist = Histogram('resp_time_hist', 'レスポンスタイム', buckets=[0.5, 1.5, 2.5]) @web.middleware async def wrapper(self, request, handler): # ハンドラをラップして各種メトリクスを計測する # 目的のエンドポイント以外へのリクエストは計測対象外 if request.path not in ['/bentou']: return await handler(request) # 各種メトリクスを計測する self.get_req_counter.inc() # リクエスト数をインクリメント with self.resp_time_hist.time(): # レスポンス時間を計測 response = await handler(request) return response async def exposer(self, request): # 現在のメトリクスを返却する return web.Response(body=generate_latest(), content_type='text/plain') @classmethod def setup(cls, app): metrics = cls() app.middlewares.append(metrics.wrapper) app.router.add_get('/metrics', metrics.exposer) handler = BentouHandler() app = web.Application() BentouMetrics.setup(app) app.add_routes([web.get(f'/bentou', handler.handle)])
... # HELP get_request_count_total リクエスト数 # TYPE get_request_count_total counter get_request_count_total 2.0 # HELP resp_time_hist レスポンスタイム # TYPE resp_time_hist histogram resp_time_hist_bucket{le="0.5"} 0.0 resp_time_hist_bucket{le="1.5"} 1.0 resp_time_hist_bucket{le="2.5"} 2.0 resp_time_hist_bucket{le="+Inf"} 2.0 ...
import time class BentouMetrics: def __init__(self): # 必要なカウンタを用意 self.get_req_counter = Counter('get_request_count', '正常に返せたリクエスト数') self.get_err_req_counter = Counter('get_error_request_count', '正常に返せなかったリクエスト数') self.resp_time_hist = Histogram('resp_time_hist', '正常レスポンスタイム', buckets=[0.5, 1.5, 2.5]) @web.middleware async def wrapper(self, request, handler): # ハンドラをラップして各種メトリクスを計測する # 目的のエンドポイント以外へのリクエストは計測対象外 if request.path not in ['/bentou']: return await handler(request) # 各種メトリクスを計測する time_0 = time.perf_counter() try: response = await handler(request) self.get_req_counter.inc() # リクエスト数をインクリメント self.resp_time_hist.observe(time.perf_counter() - time_0) # レスポンス時間を記録 except Exception as e: self.get_err_req_counter.inc() # 正常に返せなかったリクエスト数をインクリメント raise e return response