Skip to main content

PrometheusRock

Python package CodeQL

Prometheus middleware for Starlette and FastAPI

This middleware collects couple of basic metrics and allow you to add your own ones.

Basic metrics:

  • Counter: requests_total
  • Histogram: request_processing_time

Basic labels for them:

  • method
  • path
  • status_code
  • User-Agent and Host headers
  • application name

Example:

request_processing_time_sum{app_name="test_app",headers="{'host': '127.0.0.1:8020', 'user-agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:81.0) Gecko/20100101 Firefox/81.0'}",path="/test",status_code="200"} 0.00036406517028808594

Metrics include labels for the HTTP method, the path, and the response status code.

Set for path /metrics handler metrics_route and your metrics will be exposed on that url for Prometheus further use.

Usage

1. I don't want anything custom, just give me the basics!

If you don't want nothing extra, this is for you. Grab the code and run to paste it!

For starlette and FastAPI init part pretty similar.

  1. First:

    pip install prometheusrock
    
  2. Second:

    Choose your fighter! If you're using starlette:

    from starlette.applications import Starlette
    

    And if you're using FastAPI:

     from fastapi import FastAPI
    

    Moving further:

     from prometheusrock import PrometheusMiddleware, metrics_route
    
     app = # Starlette() or FastAPI()
     app.add_middleware(PrometheusMiddleware)
     app.add_route("/metrics", metrics_route)
     ...
    

    And that's it! Now go on /metrics and see your logs!

2. Custom you say? Let me see...but just a little!

If you want to configure basic metrics let me show you how!

When you declare middleware, you can pass following args:

  • app_name - the name you want to show in metrics as the name of your app. Default - "ASGIApp",
  • additional_headers - if you want to track additional headers (aside of default ones - user-agent and host) you can pass list (that's important!) with names of that headers. They all cast to lowercase, so casing doesn't matters.
  • remove_labels - by default basic metrics labels are following: method, path, status_code, headers, app_name. If you don't wanna some of them - pass list with their names here. And their gone!
  • skip_paths - sometimes you don't wanna log some of the endpoint. (Fore example you don't wanna log accesses to /metrics in your metrics). If you want to exclude this paths from metric - pass here list with their urls. By default this middleware ignores /metrics route, so if you initially moved your metric route to some other url - pass it here. If you want to log all routes (even the default /metrics - pass an empty list!)
  • disable_default_counter - if you want to disable default Counter metric - pass True value to this optional param.
  • disable_default_histogram - if you want to disable default Histogram metric - pass True value to this optional param.
  • custom_base_labels - if you want change default labels to yours - pass them here. REWRITES DEFAULT LABLES. Args remove_labels WILL BE IGNORED.
    example - ['path','method'] - and you have metric, that contains only path and method labels.
  • custom_base_headers - if you want change default headers to yours - pass them here. REWRITES DEFAULT HEADERS. Args additional_headers WILL BE IGNORED. If you use custom_base_labels, don't forget to pass headers in it, otherwise custom_base_headers will have no effect.
    example - ['content-type','x-api-client'] - and now you write only these two headers.
  • aggregate_paths - if you have endpoints like /item/{id}, then, by default, your logs will quickly overflow, showing you huge amount of numbers, when, in fact, endpoint is one. So pass here list of endpoints path to aggregate by. example - ['/item/']

But a picture is worth a thousand words, right? Let's see some code! For example, we want our middleware to have a following settings: we want a name this_is_my_app, we want to track header accept-encoding, we don't wanna label path (if you have one endpoint for example), and we don't want url /_healthcheck to be tracked.

app.add_middleware(
    PrometheusMiddleware,
    app_name='this_is_my_app',
    additional_headers=['accept-encoding'],
    remove_labels=['path'],
    skip_paths=['/_healthcheck']
)

And after that, our metric will look something like that:

requests_total{app_name="this_is_my_app",headers="{'host': '127.0.0.1:8000', 'user-agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:81.0) Gecko/20100101 Firefox/81.0', 'accept-encoding': 'gzip, deflate'}",method="GET",status_code="200"} 1.0

Let's go deeper! Add your own custom metric!

And the star of the evening - custom metrics! So, lets suppose you want to check how many are rows in your Database after each request. Let's explore this:

First, we do all the same things - we initiate the app, we add PrometheusMiddleware. And the next steps are:

  1. We must decide what type of metric we want - choose one from here. Basically, you will need pass one of the types - info, gauge, counter, histogram, summary, enum.

  2. We declare the function that will act like our metric logic:

    # async here isn't necessary, you can use ordinary function
     async def query(middleware_proxy):
         res = await db.execute_query(
             "SELECT COUNT(*) as count from MyTable"
         )
         middleware_proxy.metric.labels(**res)
    

    Function MUST accept this argument. Obviously you can name it however you want, as long is it still there. If you want to know what's inside - from prometheusrock import Metric. I strongly recommend to pass it as typehinting:

    from prometheusrock import Metric
    ...
    async def query(middleware_proxy: Metric):
    

    Metric have 3 attributes:

    • metric - instance of prometheus_client metric object.
    • metric_type - string with type.
    • spent_time - time, that was spent on request. You may need it if you, for example, implementing Histogram metric.
    • request - request object from app.

    And now IMPORTANT remark - you must correctly invoke metric! So if you, for example, chose Counter metric, in your custom function you must do middleware_proxy.metric.labels(**res).inc(), or if you chose Histogram - middleware_proxy.metric.labels(**res).observe(middleware_proxy.spent_time) and so on, according to this docs. Value that you're passing there - res (or however you called it) must be a sequence of the parameters, that you set as lables for your metric. For example, if your metric have labels count and id, res must be a dictionary {"count": count, "id": id} or list with right positioning - [count, id].

  3. And finally we tell our middleware about our custom metric:

    from prometheusrock import AddMetric, PrometheusMiddleware
    ...
    
    app.add_middleware(PrometheusMiddleware)
    ...
    
    # async here isn't necessary, you can use ordinary function
    async def query(middleware_proxy):
        res = await db.execute_query(
            "SELECT COUNT(*) as count from MyTable"
        )
        middleware_proxy.metric.labels(**res)
    
    AddMetric(
        function=query,  
        metric_name='my_precious', 
        metric_type='info',  
        labels=['row_count']
    )
    

    AddMetric accept following params:

    • function - function that will work as your metric logic
    • metric_name - unique metric name, must be ONE-WORDED (e.g. unique_metric_name). Default - "user_metric".
    • metric_description- description of your metric. Default- "description of user metric".
    • labels - list of lables that you want your metric to contain. Default - ["info"].
    • metric_type - one of prometheus_client metric types - described in paragraph 1.

Links and dependencies

Dependencies: Starlette, client_python

Additional links: FastAPI

Metadata

Release files for prometheusrock 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for prometheusrock 0.2.0
File Size Uploaded
prometheusrock-0.2.0.tar.gz 11.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for prometheusrock 0.2.0
File Interpreter ABI Platform
prometheusrock-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 21.1 kB

Release files / prometheusrock-0.2.0.tar.gz

Download URL prometheusrock-0.2.0.tar.gz
Size 11.2 kB
Tags Source
SHA-256 checksum
How to use checksums
8855f2f1fea43ff7570c39b49755b9eb9f1b193395da241d805f1819eda0efe7
BLAKE2b-256 checksum
How to use checksums
c13eab798d882544f0c69990c56fe1f8dfed8d1f6fce1b34294207bf9f63cd18
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/50.3.0 requests-toolbelt/0.9.1 tqdm/4.50.2 CPython/3.8.5

Release files / prometheusrock-0.2.0-py3-none-any.whl

Download URL prometheusrock-0.2.0-py3-none-any.whl
Size 10.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e98723a30ab2cfcfd173cc5fd25c2dd3a492e53ca07ee1fc21b0bf986a97944e
BLAKE2b-256 checksum
How to use checksums
388d310fdf76cb960416f6df34b5930f102666ddf996165e648a9e560eed7f9d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/50.3.0 requests-toolbelt/0.9.1 tqdm/4.50.2 CPython/3.8.5

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page