From 2b09e08668a62f176dd66db067f4de3a84dee32e Mon Sep 17 00:00:00 2001 From: Deimos Date: Fri, 18 Jan 2019 19:12:40 -0700 Subject: [PATCH] Add a simple tween to gather request metrics --- tildes/tildes/__init__.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tildes/tildes/__init__.py b/tildes/tildes/__init__.py index ac85f4f..6c348d1 100644 --- a/tildes/tildes/__init__.py +++ b/tildes/tildes/__init__.py @@ -3,14 +3,17 @@ """Configure and initialize the Pyramid app.""" +from time import time from typing import Any, Callable, Dict, Optional, Tuple from marshmallow.exceptions import ValidationError from paste.deploy.config import PrefixMiddleware +from prometheus_client import Histogram from pyramid.config import Configurator from pyramid.httpexceptions import HTTPTooManyRequests from pyramid.registry import Registry from pyramid.request import Request +from pyramid.response import Response from redis import Redis import sentry_sdk from sentry_sdk.integrations.pyramid import PyramidIntegration @@ -42,6 +45,7 @@ def main(global_config: Dict[str, str], **settings: str) -> PrefixMiddleware: config.scan("tildes.views") config.add_tween("tildes.http_method_tween_factory") + config.add_tween("tildes.metrics_tween_factory") config.add_request_method(is_safe_request_method, "is_safe_method", reify=True) @@ -100,6 +104,33 @@ def http_method_tween_factory(handler: Callable, registry: Registry) -> Callable return method_override_tween +def metrics_tween_factory(handler: Callable, registry: Registry) -> Callable: + # pylint: disable=unused-argument + """Return a tween function that gathers metrics for Prometheus.""" + + request_histogram = Histogram( + "tildes_pyramid_requests_seconds", + "Request processing times", + labelnames=["route", "status_code", "method"], + ) + + def metrics_tween(request: Request) -> Response: + """Gather metrics for each request.""" + start_time = time() + response = handler(request) + duration = time() - start_time + + request_histogram.labels( + route=request.matched_route.name, + status_code=response.status_code, + method=request.method, + ).observe(duration) + + return response + + return metrics_tween + + def get_redis_connection(request: Request) -> Redis: """Return a connection to the Redis server.""" socket = request.registry.settings["redis.unix_socket_path"]