A Prometheus Python client library for asyncio-based applications

Overview
https://github.com/claws/aioprometheus/workflows/Python%20Package%20Workflow/badge.svg?branch=master https://readthedocs.org/projects/aioprometheus/badge/?version=latest

aioprometheus

aioprometheus is a Prometheus Python client library for asyncio-based applications. It provides metrics collection and serving capabilities, supports multiple data formats and pushing metrics to a gateway.

The project documentation can be found on ReadTheDocs.

Install

$ pip install aioprometheus

A Prometheus Push Gateway client and ASGI service are also included, but their dependencies are not installed by default. You can install them alongside aioprometheus by running:

$ pip install aioprometheus[aiohttp]

Prometheus 2.0 removed support for the binary protocol, so in version 20.0.0 the dependency on prometheus-metrics-proto, which provides binary support, is now optional. If you want binary response support, for use with an older Prometheus, you will need to specify the 'binary' optional extra:

$ pip install aioprometheus[binary]

Multiple optional dependencies can be listed at once, such as:

$ pip install aioprometheus[aiohttp,binary]

Example

The example below shows a single Counter metric collector being created and exposed via the optional aiohttp service endpoint.

#!/usr/bin/env python
"""
This example demonstrates how a single Counter metric collector can be created
and exposed via a HTTP endpoint.
"""
import asyncio
import socket
from aioprometheus import Counter, Service


if __name__ == "__main__":

    async def main(svr: Service) -> None:

        events_counter = Counter(
            "events", "Number of events.", const_labels={"host": socket.gethostname()}
        )
        svr.register(events_counter)
        await svr.start(addr="127.0.0.1", port=5000)
        print(f"Serving prometheus metrics on: {svr.metrics_url}")

        # Now start another coroutine to periodically update a metric to
        # simulate the application making some progress.
        async def updater(c: Counter):
            while True:
                c.inc({"kind": "timer_expiry"})
                await asyncio.sleep(1.0)

        await updater(events_counter)

    loop = asyncio.get_event_loop()
    svr = Service()
    try:
        loop.run_until_complete(main(svr))
    except KeyboardInterrupt:
        pass
    finally:
        loop.run_until_complete(svr.stop())
    loop.close()

In this simple example the counter metric is tracking the number of while loop iterations executed by the updater coroutine. In a realistic application a metric might track the number of requests, etc.

Following typical asyncio usage, an event loop is instantiated first then a metrics service is instantiated. The metrics service is responsible for managing metric collectors and responding to metrics requests.

The service accepts various arguments such as the interface and port to bind to. A collector registry is used within the service to hold metrics collectors that will be exposed by the service. The service will create a new collector registry if one is not passed in.

A counter metric is created and registered with the service. The service is started and then a coroutine is started to periodically update the metric to simulate progress.

This example and demonstration requires some optional extra to be installed.

$ pip install aioprometheus[aiohttp,binary]

The example script can then be run using:

(venv) $ cd examples
(venv) $ python simple-example.py
Serving prometheus metrics on: http://127.0.0.1:5000/metrics

In another terminal fetch the metrics using the curl command line tool to verify they can be retrieved by Prometheus server.

By default metrics will be returned in plan text format.

$ curl http://127.0.0.1:5000/metrics
# HELP events Number of events.
# TYPE events counter
events{host="alpha",kind="timer_expiry"} 33

Similarly, you can request metrics in binary format, though the output will be hard to read on the command line.

$ curl http://127.0.0.1:5000/metrics -H "ACCEPT: application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited"

The metrics service also responds to requests sent to its / route. The response is simple HTML. This route can be useful as a Kubernetes /healthz style health indicator as it does not incur any overhead within the service to serialize a full metrics response.

$ curl http://127.0.0.1:5000/
<html><body><a href='/metrics'>metrics</a></body></html>

The aioprometheus package provides a number of convenience decorator functions that can assist with updating metrics.

The examples directory contains many examples showing how to use the aioprometheus package. The app-example.py file will likely be of interest as it provides a more representative application example than the simple example shown above.

Examples in the examples/frameworks directory show how aioprometheus can be used within various web application frameworks without needing to create a separate aioprometheus.Service endpoint to handle metrics. The FastAPI example is shown below.

#!/usr/bin/env python
"""
Sometimes you may not want to expose Prometheus metrics from a dedicated
Prometheus metrics server but instead want to use an existing web framework.

This example uses the registry from the aioprometheus package to add
Prometheus instrumentation to a FastAPI application. In this example a registry
and a counter metric is instantiated and gets updated whenever the "/" route
is accessed. A '/metrics' route is added to the application using the standard
web framework method. The metrics route renders Prometheus metrics into the
appropriate format.

Run:

  $ pip install fastapi uvicorn
  $ uvicorn fastapi_example:app

"""

from aioprometheus import render, Counter, Registry
from fastapi import FastAPI, Header, Response
from typing import List


app = FastAPI()
app.registry = Registry()
app.events_counter = Counter("events", "Number of events.")
app.registry.register(app.events_counter)


@app.get("/")
async def hello():
    app.events_counter.inc({"path": "/"})
    return "hello"


@app.get("/metrics")
async def handle_metrics(response: Response, accept: List[str] = Header(None)):
    content, http_headers = render(app.registry, accept)
    return Response(content=content, media_type=http_headers["Content-Type"])

License

aioprometheus is released under the MIT license.

aioprometheus originates from the (now deprecated) prometheus python package which was released under the MIT license. aioprometheus continues to use the MIT license and contains a copy of the original MIT license from the prometheus-python project as instructed by the original license.

Comments
  • Use orjson

    Use orjson

    • Use orjson as its much faster than the standard python json library.
    • Drop object_pairs_hook when decoding keys as orjson interface does not support it.

    A simple benchmark (there are plenty of others available on the internet):

    In [1]: import json, orjson
    
    In [2]: test_dict = {'bfdfsd': 223, 'afdsfds': '1freresr', '1':4, '3': '434w3', 'gfdgd': 'trttdrfg' }
    
    In [3]: timeit json.loads(json.dumps(test_dict, sort_keys=True))
    4.98 µs ± 78.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
    
    In [4]: timeit orjson.loads(orjson.dumps(test_dict, option=(orjson.OPT_SORT_KEYS|orjson.OPT_NON_STR_KEYS)))
    778 ns ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
    
    In [5]: timeit orjson.loads(orjson.dumps(test_dict, option=orjson.OPT_SORT_KEYS))
    692 ns ± 13.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
    
    opened by binary0111 6
  • Use specified registry in middleware collectors

    Use specified registry in middleware collectors

    The user-specified registry was not passed to the middleware collectors, so the default registry was always used.

    The user-specified registry is now passed to the collectors. If the user does not specify a registry the default registry will be used.

    Fixes #70

    opened by JacobHenner 5
  • Aiohttp 3+ start/shutdown changes

    Aiohttp 3+ start/shutdown changes

    Hey, aiohttp3 introduced advanced site runners https://docs.aiohttp.org/en/stable/web_advanced.html#application-runners

    I am going to make PR to adjust aioprometheus to use it, but it will force to support aiohttp3+. Is it acceptable for You @claws ?

    opened by hellysmile 5
  • metrics handler to existing server

    metrics handler to existing server

    hello, would be nice to include some documentation on how to do something like:

    from aiohttp import web
    from aioprometheus import Service
    
    prometheus_service = Service()
    
    async def handle(request):
        text = "Hello aiohttp"
        return web.Response(text=text)
    
    app = web.Application()
    app.router.add_get('/', handle)
    app.router.add_get('/metrics', prometheus_service.handle_metrics)
    
    web.run_app(app)
    
    opened by benitogf 4
  • Middleware const_labels argument

    Middleware const_labels argument

    This minor addition to the Middleware class allows const_labels to be provided and passed to the counters created at construction.

    Usage:

    app = FastAPI()
    app.add_middleware(MetricsMiddleware, const_labels={"app": "webapi"})
    
    opened by ngerakines 3
  • Make aiohttp dependency optional

    Make aiohttp dependency optional

    Includes:

    • Documentation update calling out the aiohttp extra for both the Service and Pusher features
    • Import guards in pusher and service
    • Runtime checks in the Pusher and Service initializers, to retain consistency in the top-level API
    • Quoted type annotations where aiohttp.web.* might not be available
    • Version bump to 19.11.0

    @claws let me know if there is anything I missed or if you have any concerns, thanks! :)

    opened by pirogoeth 3
  • Adding a /healthz method

    Adding a /healthz method

    There are cases where aioprometheus will be the only http-serving port an application has, and in these instances it's helpful to use it for an orchestrator (AWS ECS, Kubernetes etc) to check the application's health.

    Now it tolerates the lack of an Accept: header it's possible to use /metrics for this. But generating the textual metrics list only to throw the output away is somewhat wasteful.

    To help out in this case would you be open to a PR adding a /healthz method that does nothing but return a 200 code?

    opened by alexmbird 3
  • prometheus_metrics_proto not present in requirements.txt

    prometheus_metrics_proto not present in requirements.txt

    Hi,

    aioprometheus imports prometheus_metrics_proto yet the package is not present in requirements.txt. A colleague of mine has spent some time finding out what is wrong, so it would be cool to fix this. Not sure what version to put there, so just opening an issue, not sending a pull request...

    Cheers!

    opened by tchap 3
  • Middleware attempts to create conflicting Counters if not added last

    Middleware attempts to create conflicting Counters if not added last

    In Starlette, every middleware class that has been added to an application is called each time an additional middleware class is added.

    Unless aioprometheus's MetricsMiddleware is added as the last piece of middleware, it's __init__(...) will be called more than once. This causes an Exception when the Counter metrics are redefined with the same name in the same registry.

    opened by JacobHenner 2
  • Update RTD config to version 2 format

    Update RTD config to version 2 format

    Update the RTD configuration file to v2. List the development requirements file so that RTD installs those items which should allow the build to complete as sphinx_material will then be found.

    opened by claws 2
  • Documentation seems to be outdated

    Documentation seems to be outdated

    Dear all,

    the example is showing an outdated example due to the recent changes to the API, that introduced automatic registeration of metrics when they are created. Could you adjust the documentation accordingly?

    Thanks, Manuel

    opened by giffels 2
  • Update Documentation

    Update Documentation

    Greetings,

    I copied and ran the @inprogress decorator example, and when viewing the metrics endpoint, I observed the metric did not increment. It stayed at 1

    # HELP request_in_progress Number of requests in progress
    # TYPE request_in_progress gauge
    request_in_progress{route="/"} 1
    # HELP request_total Total number of requests
    # TYPE request_total counter
    request_total{route="/"} 338
    

    Using python 3.9.11, using this requirements.in:

    aioprometheus[aiohttp,starlette,quart]
    asgiref
    gunicorn
    icmplib
    orjson
    pbr
    psutil
    starlette
    twine
    ujson
    uvicorn[standard]
    wheel
    
    opened by justinreddick 0
  • platform collector - report details on python

    platform collector - report details on python

    prints python information

    $ curl http://localhost:8000/metrics
    
    # HELP python_info Python platform information
    # TYPE python_info gauge
    python_info{implementation="cpython",major="3",minor="10",patchlevel="5",system="linux",version="3.10.5 (main, Jun  9 2022, 00:00:00) [GCC 12.1.1 20220507 (Red Hat 12.1.1-1)]"} 1
    
    
    opened by ritzk 0
  • Using timer decoration with fastapi causes high cpu usage and slows down metric endpoint

    Using timer decoration with fastapi causes high cpu usage and slows down metric endpoint

    Hi,

    I'm using aioprometheus with a fastapi and uvicorn. I'm attempting to add request time metrics by using the @timer decorator, as provided in the examples.

    This works, but if calling the endpoint which has the timer decoration frequently for a while, the metrics endpoint provided by aioprometheus gets really slow, and the service is consuming lots of CPU when calling the metrics-endpoint. I'm guessing as more and more requests gets counted, the metric calculations becomes more and more heavy. Should the metrics registry be reset at some interval to prevent this?

    Minimal code to reproduce;

    import uvicorn
    from fastapi import FastAPI, Response, status
    from aioprometheus import timer, Summary, MetricsMiddleware
    from aioprometheus.asgi.starlette import metrics
    
    app = FastAPI()
    
    app.add_middleware(MetricsMiddleware)
    app.add_route("/metrics", metrics)
    
    # Create prometheus metrics to track time spent and requests made etc.
    app.state.metric_request_time = Summary(
        "request_processing_seconds",
        "Time spent processing request")
    
    # Primary routes
    @app.get("/cluster/primary-v1")
    @timer(app.state.metric_request_time, {"route": "/cluster/primary-v1"})
    async def primary_v1(response: Response):
        return {'state': 'testing'}
    
    # Grab event loop
    loop = asyncio.new_event_loop()
    
    server = uvicorn.Server(uvicorn.Config(app=app, loop=loop, log_config=None,
                                           access_log=None, host='0.0.0.0',
                                           port=7006))
    
    future_uvicorn = loop.create_task(server.serve())
    
    try:
        loop.run_until_complete(future_uvicorn)
    except asyncio.exceptions.CancelledError:
        print("uvicorn was cancelled, exiting!")
    
    

    Using python 3.10.4, with this requirements.txt

    fastapi==0.75.1
    uvicorn[standard]==0.17.6
    aioprometheus[starlette]==22.3.0
    

    I reproduce the issue by running ali for 1 hour: ali --rate=500 --duration=60m http://127.0.0.1:7006/cluster/primary-v1

    When ali finished, a GET to /metrics endpoint take around 500ms, and uses 100% cpu. Maybe I'm doing something wrong, or could it be a bug?

    opened by MannerMan 5
  • Deprecate support for binary format metrics

    Deprecate support for binary format metrics

    Prometheus stopped supporting the binary metric format a while ago. To simplify maintaining this library it is recommended that the support for binary format metrics be deprecated and eventually removed.

    opened by claws 1
  • ProcessCollector support

    ProcessCollector support

    Hi. It would be nice to have something like ProcessCollector. Using ProcessCollector from prometheus_client raises an error:

    Traceback (most recent call last):
      File "prom.py", line 5, in <module>
        prometheus_service.register(ProcessCollector(namespace='mydaemon'))
      File "/usr/local/lib/python3.7/site-packages/aioprometheus/service.py", line 177, in register
        self.registry.register(collector)
      File "/usr/local/lib/python3.7/site-packages/aioprometheus/registry.py", line 32, in register
        raise TypeError("Invalid collector type: {}".format(collector))
    TypeError: Invalid collector type: <prometheus_client.process_collector.ProcessCollector object at 0x7f2887ca0c10>
    

    Code:

    from prometheus_client import ProcessCollector
    from aioprometheus import Service
    
    prometheus_service = Service()
    prometheus_service.register(ProcessCollector(namespace='mydaemon'))
    
    enhancement 
    opened by easysugar 0
Owner
null
api versioning for fastapi web applications

fastapi-versioning api versioning for fastapi web applications Installation pip install fastapi-versioning Examples from fastapi import FastAPI from f

Dean Way 472 Jan 2, 2023
Backend, modern REST API for obtaining match and odds data crawled from multiple sites. Using FastAPI, MongoDB as database, Motor as async MongoDB client, Scrapy as crawler and Docker.

Introduction Apiestas is a project composed of a backend powered by the awesome framework FastAPI and a crawler powered by Scrapy. This project has fo

Fran Lozano 54 Dec 13, 2022
Async and Sync wrapper client around httpx, fastapi, date stuff

lazyapi Async and Sync wrapper client around httpx, fastapi, and datetime stuff. Motivation This library is forked from an internal project that works

null 2 Apr 19, 2022
FastAPI Admin Dashboard based on FastAPI and Tortoise ORM.

FastAPI ADMIN 中文文档 Introduction FastAPI-Admin is a admin dashboard based on fastapi and tortoise-orm. FastAPI-Admin provide crud feature out-of-the-bo

long2ice 1.6k Dec 31, 2022
JSON-RPC server based on fastapi

Description JSON-RPC server based on fastapi: https://fastapi.tiangolo.com Motivation Autogenerated OpenAPI and Swagger (thanks to fastapi) for JSON-R

null 199 Dec 30, 2022
The template for building scalable web APIs based on FastAPI, Tortoise ORM and other.

FastAPI and Tortoise ORM. Powerful but simple template for web APIs w/ FastAPI (as web framework) and Tortoise-ORM (for working via database without h

prostomarkeloff 95 Jan 8, 2023
ReST based network device broker

The Open API Platform for Network Devices netpalm makes it easy to push and pull state from your apps to your network by providing multiple southbound

null 368 Dec 31, 2022
a lightweight web framework based on fastapi

start-fastapi Version 2021, based on FastAPI, an easy-to-use web app developed upon Starlette Framework Version 2020 中文文档 Requirements python 3.6+ (fo

HiKari 71 Dec 30, 2022
A Jupyter server based on FastAPI (Experimental)

jupyverse is experimental and should not be used in place of jupyter-server, which is the official Jupyter server.

Jupyter Server 122 Dec 27, 2022
python fastapi example connection to mysql

Quickstart Then run the following commands to bootstrap your environment with poetry: git clone https://github.com/xiaozl/fastapi-realworld-example-ap

null 55 Dec 15, 2022
更新 2.0 版本,使用 Python WEB 高性能异步框架 FastAPI 制作的抖音无水印解析下载,采用前后端分离思想!

前言 这个是 2.0 版本,使用现在流行的前后端分离思想重构。 体验网址:https://douyin.bigdataboy.cn 更新日志 2020.05.30:使用 FastAPI 前后端分离重构 2020.05.02:已更新,正常使用 2020.04.27:抖音结构更新,已修复视频有水印。(失

null 64 Nov 25, 2022
📦 Autowiring dependency injection container for python 3

Lagom - Dependency injection container What Lagom is a dependency injection container designed to give you "just enough" help with building your depen

Steve B 146 Dec 29, 2022
TODO aplication made with Python's FastAPI framework and Hexagonal Architecture

FastAPI Todolist Description Todolist aplication made with Python's FastAPI framework and Hexagonal Architecture. This is a test repository for the pu

Giovanni Armane 91 Dec 31, 2022
python template private service

Template for private python service This is a cookiecutter template for an internal REST API service, written in Python, inspired by layout-golang. Th

UrvanovCompany 15 Oct 2, 2022
A Python pickling decompiler and static analyzer

Fickling Fickling is a decompiler, static analyzer, and bytecode rewriter for Python pickle object serializations. Pickled Python objects are in fact

Trail of Bits 162 Dec 13, 2022
Prometheus instrumentation library for Python applications

Prometheus Python Client The official Python 2 and 3 client for Prometheus. Three Step Demo One: Install the client: pip install prometheus-client Tw

Prometheus 3.2k Jan 7, 2023
Prometheus instrumentation library for Python applications

Prometheus Python Client The official Python 2 and 3 client for Prometheus. Three Step Demo One: Install the client: pip install prometheus-client Tw

Prometheus 3.2k Jan 7, 2023
Prometheus exporter for Flask applications

Prometheus Flask exporter This library provides HTTP request metrics to export into Prometheus. It can also track method invocations using convenient

Viktor Adam 535 Dec 23, 2022
A fast PostgreSQL Database Client Library for Python/asyncio.

asyncpg -- A fast PostgreSQL Database Client Library for Python/asyncio asyncpg is a database interface library designed specifically for PostgreSQL a

magicstack 5.8k Dec 31, 2022
Asynchronous HTTP client/server framework for asyncio and Python

Async http client/server framework Key Features Supports both client and server side of HTTP protocol. Supports both client and server Web-Sockets out

aio-libs 13.2k Jan 5, 2023