def __init__(self, get_response=None): if not hasattr(settings, 'DJANGO_SATELLA_METRICS'): self.summary_metric = getMetric('django.summary', 'summary') self.histogram_metric = getMetric('django.histogram', 'histogram') self.status_codes_metric = getMetric('django.status_codes', 'counter') self.monitor_metrics = False self.url_getter = lambda request: request.path else: def try_get(field_name, def_metric_name, def_metric_type): if field_name not in settings.DJANGO_SATELLA_METRICS: setattr(self, field_name, getMetric(def_metric_name, def_metric_type)) else: setattr(self, field_name, settings.DJANGO_SATELLA_METRICS[field_name]) try_get('summary_metric', 'django.summary', 'summary') try_get('histogram_metric', 'django.histogram', 'histogram') try_get('status_codes_metric', 'django.status_codes', 'counter') self.monitor_metrics = settings.DJANGO_SATELLA_METRICS.get( 'monitor_metrics', False) self.url_getter = settings.DJANGO_SATELLA_METRICS.get( 'url_getter', lambda request: request.path) if get_response is not None: super().__init__(get_response)
def SatellaMetricsMiddleware( app: fastapi.FastAPI, summary_metric: tp.Optional[Metric] = None, histogram_metric: tp.Optional[Metric] = None, response_codes_metric: tp.Optional[Metric] = None): """ Install handlers to measure metrics on an application. Metrics will be created by default is not specified. For summary metric it will be called "requests_summary", for histogram metric it will be called "requests_histogram", and for status codes "requests_response_codes". :param app: FastAPI application to monitor :param summary_metric: summary metric to use. Should be of type 'summary' :param histogram_metric: histogram metric to use. Should be of type 'histogram' :param response_codes_metric: Response codes counter to use. Should be of type 'counter' """ summary_metric = summary_metric or getMetric( 'requests_summary', 'summary', quantiles=[0.2, 0.5, 0.9, 0.95, 0.99]) histogram_metric = histogram_metric or getMetric('requests_histogram', 'histogram') response_codes_metric = response_codes_metric or getMetric( 'requests_response_codes', 'counter') @app.middleware('http') async def do_middleware(request: fastapi.Request, call_next): with measure() as measurement: response = await call_next(request) summary_metric.runtime(measurement(), endpoint=str(request.url)) histogram_metric.runtime(measurement(), endpoint=str(request.url)) response_codes_metric.runtime(+1, response_code=response.status_code) return response
def try_get(field_name, def_metric_name, def_metric_type): if field_name not in settings.DJANGO_SATELLA_METRICS: setattr(self, field_name, getMetric(def_metric_name, def_metric_type)) else: setattr(self, field_name, settings.DJANGO_SATELLA_METRICS[field_name])
def export_metrics(request): """A Django view to output the metrics""" root_data = getMetric().to_metric_data() for datum in root_data.values: if datum.internal: root_data.values.remove(datum) if hasattr(settings, 'DJANGO_SATELLA_METRICS'): root_data.add_labels( settings.DJANGO_SATELLA_METRICS.get('extra_labels', {})) return HttpResponse(metric_data_collection_to_prometheus(root_data))
def export_prometheus(): metric = getMetric() metric_data = metric.to_metric_data() new_values = set() for datum in metric_data.values: if not datum.internal: new_values.add(datum) metric_data.values = new_values metric_data.add_labels(labels) return fastapi.Response( content=metric_data_collection_to_prometheus(metric_data), media_type='text/plain')
def SatellaMetricsMiddleware( app: flask.Flask, summary_metric: tp.Optional[Metric] = None, histogram_metric: tp.Optional[Metric] = None, response_codes_metric: tp.Optional[Metric] = None): """ Install handlers to measure metrics on an application :param app: flask application to monitor :param summary_metric: summary metric to use. Should be of type 'summary' :param histogram_metric: histogram metric to use. Should be of type 'histogram' :param response_codes_metric: Response codes counter to use. Should be of type 'counter' """ app.metrics = MetricsContainer( summary_metric or getMetric('requests_summary', 'summary', quantiles=[0.2, 0.5, 0.9, 0.95, 0.99]), histogram_metric or getMetric('requests_histogram', 'histogram'), response_codes_metric or getMetric('requests_response_codes', 'counter')) app.before_request(before_request) app.after_request(after_request)
def test_satella_metrics(self): getMetric('my.internal.metric', 'counter', internal=True) q = requests.get('http://localhost:5000/') self.assertEqual(q.status_code, 200) q = requests.get('http://localhost:5000/value_error') self.assertEqual(q.status_code, 500) root_metric = getMetric().to_metric_data() request_codes = choose( lambda metric: metric.name == 'requests_response_codes' and metric. labels == {'response_code': 200}, root_metric.values) self.assertEqual(request_codes.value, 1) request_codes = choose( lambda metric: metric.name == 'requests_response_codes' and metric. labels == {'response_code': 500}, root_metric.values) self.assertEqual(request_codes.value, 1) q = requests.get('http://localhost:5000/metrics') self.assertEqual(q.status_code, 200) self.assertIn('service_name="my_service"', q.text) self.assertIn('requests_response_codes', q.text) self.assertNotIn('my_internal_metric', q.text)
# Internationalization # https://docs.djangoproject.com/en/3.0/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.0/howto/static-files/ STATIC_URL = '/static/' from satella.instrumentation.metrics import getMetric DJANGO_SATELLA_METRICS = { 'summary_metric': getMetric('django.summary', 'summary'), 'histogram_metric': getMetric('django.histogram', 'histogram'), 'status_codes_metric': getMetric('django.status_codes', 'counter'), 'extra_labels': { 'service_name': 'test' }, 'monitor_metrics': False }
def start(): getMetric('my.internal.metric', 'counter', internal=True) uvicorn.run('tests.test_fastapi_satella_metrics:app', host='127.0.0.1', port=8000, reload=False)
# Copyright 2020 SMOK sp. z o. o. import itertools import os import logging import requests import re import json from flask import Flask, request from flask_json import FlaskJSON, as_json from flask_satella_metrics.prometheus_exporter import PrometheusExporter from satella.instrumentation.metrics import getMetric matched_regexes = getMetric('matched.regex', 'counter') matched_nothing = getMetric('matched.nothing', 'counter') total_entries = getMetric('entries.total', 'counter') calls_made = getMetric('entries.calls', 'counter') entries_dropped = getMetric('entries.dropped', 'counter') seq_successes = getMetric('seq.successes', 'counter') seq_failures = getMetric('seq.failures', 'counter') logger = logging.getLogger(__name__) app = Flask(__name__) FlaskJSON(app) app.register_blueprint(PrometheusExporter()) SERVER_URL = os.environ['SEQ_ADDRESS'] if not SERVER_URL.endswith('/'): SERVER_URL = SERVER_URL + '/'