def test_default(): app = create_app() Instrumentator().add(metrics.default()).instrument(app).expose(app) client = TestClient(app) client.get("/", data="fefeef") client.get("/") _ = get_response(client, "/metrics") assert (REGISTRY.get_sample_value( "http_requests_total", { "handler": "/", "method": "GET", "status": "2xx" }, ) > 0) assert (REGISTRY.get_sample_value( "http_request_size_bytes_sum", {"handler": "/"}, ) > 0) assert (REGISTRY.get_sample_value( "http_response_size_bytes_sum", {"handler": "/"}, ) > 0) assert (REGISTRY.get_sample_value( "http_request_duration_highr_seconds_sum", {}, ) > 0) assert (REGISTRY.get_sample_value( "http_request_duration_seconds_sum", {"handler": "/"}, ) > 0)
def test_default_should_only_respect_2xx_for_highr(): app = create_app() Instrumentator(excluded_handlers=["/metrics"]).add( metrics.default(should_only_respect_2xx_for_highr=True)).instrument( app).expose(app) client = TestClient(app) client.get("/efefewffe", data="fefeef") client.get("/ffe04904nfiuo-ni") response = get_response(client, "/metrics") assert b"http_request_duration_highr_seconds_count 0.0" in response.content
def instrument(self, app: FastAPI): """Performs the instrumentation by adding middleware. The middleware iterates through all `instrumentations` and execute them. Args: app: FastAPI app instance. Raises: e: Only raised if FastAPI itself throws an exception. Returns: self: Instrumentator. Builder Pattern. """ if (self.should_respect_env_var and os.environ.get(self.env_var_name, "false") != "true"): return self if len(self.instrumentations) == 0: self.instrumentations.append(metrics.default()) self.inprogress = None if self.should_instrument_requests_inprogress: labels = (( "method", "handler", ) if self.inprogress_labels else ()) self.inprogress = Gauge( name=self.inprogress_name, documentation="Number of HTTP requests in progress.", labelnames=labels, multiprocess_mode="livesum", ) @app.middleware("http") async def dispatch_middleware(request: Request, call_next) -> Response: start_time = default_timer() handler, is_templated = self._get_handler(request) is_excluded = self._is_handler_excluded(handler, is_templated) handler = ("none" if not is_templated and self.should_group_untemplated else handler) if not is_excluded and self.should_instrument_requests_inprogress: if self.inprogress_labels: inprogress = self.inprogress.labels( request.method, handler) else: inprogress = self.inprogress inprogress.inc() try: response = None response = await call_next(request) status = str(response.status_code) except Exception as e: if response is None: status = "500" raise e from None finally: if not is_excluded: duration = max(default_timer() - start_time, 0) if self.should_instrument_requests_inprogress: inprogress.dec() if self.should_round_latency_decimals: duration = round(duration, self.round_latency_decimals) if self.should_group_status_codes: status = status[0] + "xx" info = metrics.Info( request=request, response=response, method=request.method, modified_handler=handler, modified_status=status, modified_duration=duration, ) for instrumentation in self.instrumentations: instrumentation(info) return response return self
def instrument(self, app: FastAPI) -> "self": """Performs the instrumentation by adding middleware. The middleware iterates through all `instrumentations` and execute them. Args: app: FastAPI app instance. Raises: e: Only raised if FastAPI itself throws an exception. Returns: self: Instrumentator. Builder Pattern. """ if ( self.should_respect_env_var and os.environ.get(self.env_var_name, "false") != "true" ): return self if len(self.instrumentations) == 0: self.instrumentations.append(metrics.default()) @app.middleware("http") async def dispatch_middleware(request: Request, call_next) -> Response: start_time = default_timer() try: response = None response = await call_next(request) status = str(response.status_code) except Exception as e: if response is None: status = "500" raise e from None finally: handler, is_templated = self._get_handler(request) if not self._is_handler_excluded(handler, is_templated): duration = max(default_timer() - start_time, 0) if self.should_round_latency_decimals: duration = round(duration, self.round_latency_decimals) if is_templated is False and self.should_group_untemplated: handler = "none" if self.should_group_status_codes: status = status[0] + "xx" info = metrics.Info( request=request, response=response, method=request.method, modified_handler=handler, modified_status=status, modified_duration=duration, ) for instrumentation in self.instrumentations: instrumentation(info) return response return self