{"id":15020971,"url":"https://github.com/kozhushman/prometheusrock","last_synced_at":"2025-10-26T20:32:18.840Z","repository":{"id":49827785,"uuid":"302382029","full_name":"kozhushman/prometheusrock","owner":"kozhushman","description":"Prometheus middleware for Starlette and FastAPI","archived":false,"fork":false,"pushed_at":"2021-04-02T11:02:05.000Z","size":58,"stargazers_count":38,"open_issues_count":3,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-10-30T06:24:16.081Z","etag":null,"topics":["custom-metrics","fastapi","metrics","middleware","prometheus","prometheus-middleware","starlette"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kozhushman.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-10-08T15:19:28.000Z","updated_at":"2024-04-20T06:00:16.000Z","dependencies_parsed_at":"2022-08-25T18:12:05.882Z","dependency_job_id":null,"html_url":"https://github.com/kozhushman/prometheusrock","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kozhushman%2Fprometheusrock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kozhushman%2Fprometheusrock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kozhushman%2Fprometheusrock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kozhushman%2Fprometheusrock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kozhushman","download_url":"https://codeload.github.com/kozhushman/prometheusrock/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238397226,"owners_count":19465136,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["custom-metrics","fastapi","metrics","middleware","prometheus","prometheus-middleware","starlette"],"created_at":"2024-09-24T19:55:57.691Z","updated_at":"2025-10-26T20:32:18.486Z","avatar_url":"https://github.com/kozhushman.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PrometheusRock\n![Python package](https://github.com/kozhushman/prometheusrock/workflows/Python%20package/badge.svg?branch=main)\n![CodeQL](https://github.com/kozhushman/prometheusrock/workflows/CodeQL/badge.svg?branch=main)\n\nPrometheus middleware for Starlette and FastAPI\n\nThis middleware collects couple of basic metrics and allow you to add your own ones.\n\n**Basic metrics**:\n* Counter: requests_total\n* Histogram: request_processing_time\n\n\nBasic labels for them:\n* method\n* path\n* status_code\n* `User-Agent` and `Host` headers \n* application name\n\nExample:  \n```sh\nrequest_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\n```\n\nMetrics include labels for the HTTP method, the path, and the response status code.\n\nSet for path `/metrics` handler `metrics_route` and your metrics will be exposed on that url for Prometheus further use.\n\n## Usage\n\n### 1. I don't want anything custom, just give me the basics!\nIf you don't want nothing extra, this is for you. Grab the code and run to paste it!\n\nFor **starlette** and **FastAPI** init part pretty similar.\n\n1. First:\n    ```\n    pip install prometheusrock\n    ```\n2. Second:\n\n    Choose your fighter!\n    If you're using starlette:\n    ```python\n    from starlette.applications import Starlette\n   ```\n   And if you're using FastAPI:\n   ```python\n    from fastapi import FastAPI\n   ```\n   Moving further:\n   ```python\n    from prometheusrock import PrometheusMiddleware, metrics_route\n    \n    app = # Starlette() or FastAPI()\n    app.add_middleware(PrometheusMiddleware)\n    app.add_route(\"/metrics\", metrics_route)\n    ...\n    ```\n    \n    And that's it! Now go on `/metrics` and see your logs!\n    \n### 2. Custom you say? Let me see...but just a little!\nIf you want to configure basic metrics let me show you how!\n\nWhen you declare middleware, you can pass following args:\n* `app_name` - the name you want to show in metrics as the name of your app. Default - \"ASGIApp\",\n* `additional_headers` - if you want to track additional headers (aside of default ones - `user-agent` and `host`)\nyou can pass `list` (that's important!) with names of that headers. They all cast to lowercase, so casing doesn't matters.\n* `remove_labels` - by default basic metrics labels are following: `method`, `path`, `status_code`, `headers`, `app_name`.\nIf you don't wanna some of them - pass `list` with their names here. And their gone!\n* `skip_paths` - sometimes you don't wanna log some of the endpoint. \n(Fore example you don't wanna log accesses to `/metrics` in your metrics).\nIf you want to exclude this paths from metric - pass here `list` with their urls.\nBy default this middleware ignores `/metrics` route, \nso if you initially moved your metric route to some other url - pass it here.\nIf you want to log all routes (even the default `/metrics` - pass an empty list!)\n* `disable_default_counter` - if you want to disable default Counter metric - pass `True` value to this optional param.\n* `disable_default_histogram` - if you want to disable default Histogram metric - pass `True` value to this optional param.\n* `custom_base_labels` - if you want change default labels to yours - pass them here.\n  **REWRITES DEFAULT LABLES**. Args `remove_labels` **WILL BE IGNORED**.   \n  example - `['path','method']` - and you have metric, that contains only `path` and `method` labels.\n* `custom_base_headers` - if you want change default headers to yours - pass them here.\n  **REWRITES DEFAULT HEADERS**. Args `additional_headers` **WILL BE IGNORED**.\n  If you use `custom_base_labels`, don't forget to pass `headers` in it, \n  otherwise `custom_base_headers` will have no effect.  \n  example - `['content-type','x-api-client']` - and now you write only these two headers.\n* `aggregate_paths` - if you have endpoints like `/item/{id}`, then, by default,\nyour logs will quickly overflow, showing you huge amount of numbers, when, in fact,\nendpoint is one. So pass here list of endpoints path to aggregate by.\nexample - `['/item/']`\n\nBut a picture is worth a thousand words, right? Let's see some code!\nFor example, we want our middleware to have a following settings:\nwe 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),\nand we don't want url `/_healthcheck` to be tracked.\n```python\napp.add_middleware(\n    PrometheusMiddleware,\n    app_name='this_is_my_app',\n    additional_headers=['accept-encoding'],\n    remove_labels=['path'],\n    skip_paths=['/_healthcheck']\n)\n```\n\nAnd after that, our metric will look something like that:\n```sh\nrequests_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\n```\n\n## Let's go deeper! Add your own custom metric!\n\nAnd the star of the evening - custom metrics!\nSo, lets suppose you want to check how many are rows in your Database after each request. Let's explore this:\n\nFirst, we do all the same things - we initiate the app, we add PrometheusMiddleware.\nAnd the next steps are:\n1. We must decide what type of metric we want - [choose one from here](https://github.com/prometheus/client_python). Basically, you will need pass one of the types - `info, gauge, counter, histogram, summary, enum`.\n2. We declare the function that will act like our metric logic:\n   ```python\n   # async here isn't necessary, you can use ordinary function\n    async def query(middleware_proxy):\n        res = await db.execute_query(\n            \"SELECT COUNT(*) as count from MyTable\"\n        )\n        middleware_proxy.metric.labels(**res)\n    ```\n   Function **MUST** accept this argument. Obviously you can name it however you want,\n   as long is it still there. If you want to know what's inside - \n   `from prometheusrock import Metric`. I strongly recommend to pass it as typehinting:\n   ```python\n   from prometheusrock import Metric\n   ...\n   async def query(middleware_proxy: Metric):\n    ```\n   Metric have 3 attributes:\n   * metric - instance of `prometheus_client` metric object.\n   * metric_type - string with type.\n   * spent_time - time, that was spent on request. You may need it if you, for example, implementing Histogram metric.\n   * request - request object from app.\n   \n   And now **IMPORTANT** remark - you *must* correctly invoke metric! \n   So if you, for example, chose `Counter` metric, in your custom function you must do `middleware_proxy.metric.labels(**res).inc()`,\n   or if you chose Histogram - `middleware_proxy.metric.labels(**res).observe(middleware_proxy.spent_time)` and so on,\n   according to [this docs](https://github.com/prometheus/client_python).\n   Value that you're passing there - `res` (or however you called it) must be a sequence of the parameters, \n   that you set as lables for your metric. For example, if your metric have labels `count` and `id`, `res` must be\n   a dictionary `{\"count\": count, \"id\": id}` or list with right positioning - `[count, id]`.\n   \n3. And finally we tell our middleware about our custom metric:\n    ```python\n    from prometheusrock import AddMetric, PrometheusMiddleware\n    ...\n    \n    app.add_middleware(PrometheusMiddleware)\n    ...\n    \n   # async here isn't necessary, you can use ordinary function\n    async def query(middleware_proxy):\n        res = await db.execute_query(\n            \"SELECT COUNT(*) as count from MyTable\"\n        )\n        middleware_proxy.metric.labels(**res)\n   \n    AddMetric(\n        function=query,  \n        metric_name='my_precious', \n        metric_type='info',  \n        labels=['row_count']\n    )\n    ```\n    AddMetric accept following params:\n    * function - function that will work as your metric logic\n    * metric_name - unique metric name, must be ONE-WORDED (e.g. unique_metric_name). Default - \"user_metric\".\n    * metric_description- description of your metric. Default- \"description of user metric\".\n    * labels - list of lables that you want your metric to contain. Default - [\"info\"].\n    * metric_type - one of `prometheus_client` metric types - described in paragraph 1.\n    \n## Links and dependencies\n\nDependencies:\n[Starlette](https://github.com/encode/starlette), \n[client_python](https://github.com/prometheus/client_python)\n\nAdditional links:\n[FastAPI](https://github.com/tiangolo/fastapi)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkozhushman%2Fprometheusrock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkozhushman%2Fprometheusrock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkozhushman%2Fprometheusrock/lists"}