[](https://pypi.org/project/asgi-request-id/)
[](https://pypi.org/project/asgi-request-id/)
[](https://pypi.org/project/asgi-request-id/)
[](https://pypi.org/project/asgi-request-id/)
[](https://pypi.org/project/asgi-request-id/)
[](https://libraries.io/pypi/asgi-request-id/)
[](https://github.com/arni-inaba/asgi-request-id/commits/main)
# asgi-request-id 🌟
`asgi-request-id` is a middleware for ASGI applications that provides a unique request identifier for each incoming request. This identifier can be used for logging, tracing, and debugging purposes, making it easier to track requests as they flow through your application. The middleware is highly configurable, allowing you to customize the request ID generation, specify headers for incoming and outgoing request IDs, and exclude certain paths from request ID handling. It is compatible with popular ASGI frameworks like Starlette and FastAPI, and can be easily integrated into your existing application with minimal changes.
## Table of Contents 📚
- [Installation 📦](#installation)
- [Usage 🚀](#usage)
- [Middleware 🛠️](#middleware)
- [Example 🔍](#example)
- [Logging 📝](#logging)
## Installation 📦
```
pip install asgi-request-id
```
## Usage 🚀
The `asgi-request-id` middleware performs the following actions:
- Searches for an incoming request identifier using the `incoming_request_id_header` attribute and uses it as the request ID if found.
- Generates a unique request ID with an optional prefix if no incoming request identifier is found.
- Stores the request ID in a context variable, making it accessible to the logging context through a filter.
- Includes the request ID in the response headers. If the `outgoing_request_id_header` attribute is set, its value will be used as the response header name. Ensure that the chosen header name complies with HTTP header naming conventions.
For Python 3.6 compatibility, install the backported [contextvars](https://github.com/MagicStack/contextvars) package.
## Middleware 🛠️
The `RequestIdMiddleware` class is used to handle the request ID header. It has the following attributes:
- `app`: The ASGI application.
- `excluded_paths`: List of paths to exclude from middleware processing.
- `incoming_request_id_header`: Optional incoming request ID header.
- `outgoing_request_id_header`: Optional outgoing request ID header.
- `prefix`: Optional prefix to add to the request ID.
- `skip_validate_header_name`: Optional flag to skip header name validation.
- `uuid_generator`: Optional UUID generator.
### Example 🔍
Here is a minimal example demonstrating how to use the `asgi-request-id` middleware. Additional examples with more detailed use cases and configurations can be found in the `examples` folder of the repository.
```python
import os
import uvicorn
from asgi_request_id import RequestIdMiddleware
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route
from uuid import uuid4
async def info_endpoint(request: Request) -> JSONResponse:
return JSONResponse({"message": "info"})
async def excluded_endpoint(request: Request) -> JSONResponse:
return JSONResponse({"message": "excluded"})
routes = [
Route("/info", info_endpoint, methods=["GET"]),
Route("/excluded", excluded_endpoint, methods=["GET"]),
]
app = Starlette(routes=routes)
app.add_middleware(
RequestIdMiddleware,
excluded_paths=["/excluded"],
incoming_request_id_header="x-request-id",
outgoing_request_id_header="x-request-id",
prefix="my-special-prefix-",
uuid_generator=lambda: uuid4().hex,
)
if __name__ == "__main__":
log_config = f"{os.path.dirname(__file__)}{os.sep}conf{os.sep}logging.yaml"
config = uvicorn.Config("app:app", host="127.0.0.1", port=8000, log_config=log_config)
server = uvicorn.Server(config)
server.run()
```
## Logging 📝
To integrate the request ID into your logging, you can use the `RequestIdFilter` class. Here is an example `logging.yaml` configuration:
```yaml
---
version: 1
filters:
request_id:
(): 'asgi_request_id.RequestIdFilter'
default_value: '-'
formatters:
default:
(): 'uvicorn.logging.DefaultFormatter'
fmt: '%(levelprefix)s [%(asctime)s] %(message)s'
access:
(): 'uvicorn.logging.AccessFormatter'
fmt: '%(levelprefix)s [%(asctime)s] {%(request_id)s} %(client_addr)s - "%(request_line)s" %(status_code)s'
handlers:
default:
class: logging.StreamHandler
formatter: default
stream: ext://sys.stderr
access:
class: logging.StreamHandler
filters: [request_id]
formatter: access
stream: ext://sys.stdout
loggers:
uvicorn:
level: INFO
handlers:
- default
uvicorn.error:
level: INFO
uvicorn.access:
level: INFO
propagate: False
handlers:
- access
```
Raw data
{
"_id": null,
"home_page": "https://github.com/arni-inaba/asgi-request-id",
"name": "asgi-request-id",
"maintainer": "Arni Inaba Kjartansson",
"docs_url": null,
"requires_python": "<4.0,>=3.11",
"maintainer_email": "arni@inaba.is",
"keywords": "asgi, async, connexion, fastapi, middleware, request-id, starlette, x-request-id",
"author": "Arni Inaba Kjartansson",
"author_email": "arni@inaba.is",
"download_url": "https://files.pythonhosted.org/packages/20/ed/fd68a10ab8982a0aea9b283aaa5e707b7fb6580f91ad251247f63677421d/asgi_request_id-1.0.0.tar.gz",
"platform": null,
"description": "[](https://pypi.org/project/asgi-request-id/)\n[](https://pypi.org/project/asgi-request-id/)\n[](https://pypi.org/project/asgi-request-id/)\n[](https://pypi.org/project/asgi-request-id/)\n[](https://pypi.org/project/asgi-request-id/)\n[](https://libraries.io/pypi/asgi-request-id/)\n[](https://github.com/arni-inaba/asgi-request-id/commits/main)\n\n# asgi-request-id \ud83c\udf1f\n\n`asgi-request-id` is a middleware for ASGI applications that provides a unique request identifier for each incoming request. This identifier can be used for logging, tracing, and debugging purposes, making it easier to track requests as they flow through your application. The middleware is highly configurable, allowing you to customize the request ID generation, specify headers for incoming and outgoing request IDs, and exclude certain paths from request ID handling. It is compatible with popular ASGI frameworks like Starlette and FastAPI, and can be easily integrated into your existing application with minimal changes.\n\n## Table of Contents \ud83d\udcda\n\n- [Installation \ud83d\udce6](#installation)\n- [Usage \ud83d\ude80](#usage)\n- [Middleware \ud83d\udee0\ufe0f](#middleware)\n - [Example \ud83d\udd0d](#example)\n- [Logging \ud83d\udcdd](#logging)\n\n## Installation \ud83d\udce6\n\n```\npip install asgi-request-id\n```\n\n## Usage \ud83d\ude80\n\nThe `asgi-request-id` middleware performs the following actions:\n\n- Searches for an incoming request identifier using the `incoming_request_id_header` attribute and uses it as the request ID if found.\n- Generates a unique request ID with an optional prefix if no incoming request identifier is found.\n- Stores the request ID in a context variable, making it accessible to the logging context through a filter.\n- Includes the request ID in the response headers. If the `outgoing_request_id_header` attribute is set, its value will be used as the response header name. Ensure that the chosen header name complies with HTTP header naming conventions.\n\nFor Python 3.6 compatibility, install the backported [contextvars](https://github.com/MagicStack/contextvars) package.\n\n## Middleware \ud83d\udee0\ufe0f\n\nThe `RequestIdMiddleware` class is used to handle the request ID header. It has the following attributes:\n\n- `app`: The ASGI application.\n- `excluded_paths`: List of paths to exclude from middleware processing.\n- `incoming_request_id_header`: Optional incoming request ID header.\n- `outgoing_request_id_header`: Optional outgoing request ID header.\n- `prefix`: Optional prefix to add to the request ID.\n- `skip_validate_header_name`: Optional flag to skip header name validation.\n- `uuid_generator`: Optional UUID generator.\n\n### Example \ud83d\udd0d\n\nHere is a minimal example demonstrating how to use the `asgi-request-id` middleware. Additional examples with more detailed use cases and configurations can be found in the `examples` folder of the repository.\n\n```python\nimport os\nimport uvicorn\nfrom asgi_request_id import RequestIdMiddleware\nfrom starlette.applications import Starlette\nfrom starlette.requests import Request\nfrom starlette.responses import JSONResponse\nfrom starlette.routing import Route\nfrom uuid import uuid4\n\nasync def info_endpoint(request: Request) -> JSONResponse:\n return JSONResponse({\"message\": \"info\"})\n\nasync def excluded_endpoint(request: Request) -> JSONResponse:\n return JSONResponse({\"message\": \"excluded\"})\n\nroutes = [\n Route(\"/info\", info_endpoint, methods=[\"GET\"]),\n Route(\"/excluded\", excluded_endpoint, methods=[\"GET\"]),\n]\n\napp = Starlette(routes=routes)\napp.add_middleware(\n RequestIdMiddleware,\n excluded_paths=[\"/excluded\"],\n incoming_request_id_header=\"x-request-id\",\n outgoing_request_id_header=\"x-request-id\",\n prefix=\"my-special-prefix-\",\n uuid_generator=lambda: uuid4().hex,\n)\n\nif __name__ == \"__main__\":\n log_config = f\"{os.path.dirname(__file__)}{os.sep}conf{os.sep}logging.yaml\"\n config = uvicorn.Config(\"app:app\", host=\"127.0.0.1\", port=8000, log_config=log_config)\n server = uvicorn.Server(config)\n server.run()\n```\n\n## Logging \ud83d\udcdd\n\nTo integrate the request ID into your logging, you can use the `RequestIdFilter` class. Here is an example `logging.yaml` configuration:\n\n```yaml\n---\nversion: 1\nfilters:\n request_id:\n (): 'asgi_request_id.RequestIdFilter'\n default_value: '-'\nformatters:\n default:\n (): 'uvicorn.logging.DefaultFormatter'\n fmt: '%(levelprefix)s [%(asctime)s] %(message)s'\n access:\n (): 'uvicorn.logging.AccessFormatter'\n fmt: '%(levelprefix)s [%(asctime)s] {%(request_id)s} %(client_addr)s - \"%(request_line)s\" %(status_code)s'\nhandlers:\n default:\n class: logging.StreamHandler\n formatter: default\n stream: ext://sys.stderr\n access:\n class: logging.StreamHandler\n filters: [request_id]\n formatter: access\n stream: ext://sys.stdout\nloggers:\n uvicorn:\n level: INFO\n handlers:\n - default\n uvicorn.error:\n level: INFO\n uvicorn.access:\n level: INFO\n propagate: False\n handlers:\n - access\n```",
"bugtrack_url": null,
"license": "MIT",
"summary": "ASGI request id middleware",
"version": "1.0.0",
"project_urls": {
"Homepage": "https://github.com/arni-inaba/asgi-request-id",
"Repository": "https://github.com/arni-inaba/asgi-request-id"
},
"split_keywords": [
"asgi",
" async",
" connexion",
" fastapi",
" middleware",
" request-id",
" starlette",
" x-request-id"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "99ad663254d416654dec5b6cc9ec148920d7de48b963868c7432daac8c2b2b23",
"md5": "c7ffaa2a9d99bb169d941d4eb4b12605",
"sha256": "f7fd8387ce0b517fc86da86e3e9006595a59f5dffc73a365dfbee22995cfbf1d"
},
"downloads": -1,
"filename": "asgi_request_id-1.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "c7ffaa2a9d99bb169d941d4eb4b12605",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.11",
"size": 8430,
"upload_time": "2025-02-11T15:00:02",
"upload_time_iso_8601": "2025-02-11T15:00:02.163839Z",
"url": "https://files.pythonhosted.org/packages/99/ad/663254d416654dec5b6cc9ec148920d7de48b963868c7432daac8c2b2b23/asgi_request_id-1.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "20edfd68a10ab8982a0aea9b283aaa5e707b7fb6580f91ad251247f63677421d",
"md5": "127a5b7f1d022b22baad7d5fb94211aa",
"sha256": "b1555094199f92d394a856902a5e70ba388b84df9164e31b4f0df022fe01a45d"
},
"downloads": -1,
"filename": "asgi_request_id-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "127a5b7f1d022b22baad7d5fb94211aa",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.11",
"size": 8070,
"upload_time": "2025-02-11T15:00:04",
"upload_time_iso_8601": "2025-02-11T15:00:04.070328Z",
"url": "https://files.pythonhosted.org/packages/20/ed/fd68a10ab8982a0aea9b283aaa5e707b7fb6580f91ad251247f63677421d/asgi_request_id-1.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-02-11 15:00:04",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "arni-inaba",
"github_project": "asgi-request-id",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "asgi-request-id"
}