asgi-request-duration


Nameasgi-request-duration JSON
Version 1.0.6 PyPI version JSON
download
home_pageNone
SummaryA asgi middleware to measure the request duration
upload_time2025-07-12 13:36:38
maintainerFabio Greco
docs_urlNone
authorFabio Greco
requires_python<4.0,>=3.11
licenseGNU GPLv3
keywords asgi async connexion fastapi middleware request-duration starlette timing
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            [![PyPI - Downloads](https://img.shields.io/pypi/dm/asgi-request-duration.svg)](https://pypi.org/project/asgi-request-duration/)
[![PyPI - License](https://img.shields.io/pypi/l/asgi-request-duration)](https://www.gnu.org/licenses/gpl-3.0)
[![PyPI - Version](https://img.shields.io/pypi/v/asgi-request-duration.svg)](https://pypi.org/project/asgi-request-duration/)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/asgi-request-duration)](https://pypi.org/project/asgi-request-duration/)
[![PyPI - Status](https://img.shields.io/pypi/status/asgi-request-duration)](https://pypi.org/project/asgi-request-duration/)
[![Dependencies](https://img.shields.io/librariesio/release/pypi/asgi-request-duration)](https://libraries.io/pypi/asgi-request-duration)
[![Last Commit](https://img.shields.io/github/last-commit/feteu/asgi-request-duration)](https://github.com/feteu/asgi-request-duration/commits/main)
[![Build Status build/testpypi](https://img.shields.io/github/actions/workflow/status/feteu/asgi-request-duration/publish-testpypi.yaml?label=publish-testpypi)](https://github.com/feteu/asgi-request-duration/actions/workflows/publish-testpypi.yaml)
[![Build Status build/pypi](https://img.shields.io/github/actions/workflow/status/feteu/asgi-request-duration/publish-pypi.yaml?label=publish-pypi)](https://github.com/feteu/asgi-request-duration/actions/workflows/publish-pypi.yaml)
[![Build Status test](https://img.shields.io/github/actions/workflow/status/feteu/asgi-request-duration/test.yaml?label=test)](https://github.com/feteu/asgi-request-duration/actions/workflows/test.yaml)

# ASGI Request Duration ⏱️

ASGI Request Duration is a middleware for ASGI applications that measures the duration of HTTP requests and integrates this information into response headers and log records. This middleware is designed to be easy to integrate and configure, providing valuable insights into the performance of your ASGI application.

> **Note:** If you find this project useful, please consider giving it a star ⭐ on GitHub. This helps prioritize its maintenance and development. If you encounter any typos, bugs 🐛, or have new feature requests, feel free to open an issue. I will be happy to address them.

## Table of Contents 📚

1. [Features ✨](#features-✨)
2. [Installation 🛠️](#installation-🛠️)
3. [Usage 🚀](#usage-🚀)
    1. [Middleware 🧩](#middleware-🧩)
    2. [Logging Filter 📝](#logging-filter-📝)
    3. [Configuration ⚙️](#configuration-⚙️)
      1. [Middleware Configuration 🔧](#middleware-configuration-🔧)
      2. [Logging Filter Configuration 🔍](#logging-filter-configuration-🔍)
4. [Examples 📖](#examples-📖)
    1. [Example with Starlette 🌟](#example-with-starlette-🌟)
5. [Contributing 🤝](#contributing-🤝)
6. [License 📜](#license-📜)

## Features ✨

- Measure the duration of each HTTP request.
- Add the request duration to response headers.
- Integrate the request duration into log records.
- Configurable header name and precision.
- Exclude specific paths from timing.

## Installation 🛠️

You can install the package using pip:

```bash
pip install asgi-request-duration
```

## Usage 🚀

### Middleware 🧩

To use the middleware, add it to your ASGI application:

```python
from asgi_request_duration.middleware import RequestDurationMiddleware
from starlette.applications import Starlette

app = Starlette()
app.add_middleware(RequestDurationMiddleware)
```

### Logging Filter 📝

To use the logging filter, configure your logger to use the `RequestDurationFilter`:

```python
import logging
from asgi_request_duration.filters import RequestDurationFilter

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("myapp")
logger.addFilter(RequestDurationFilter())
```

### Configuration ⚙️

#### Middleware Configuration 🔧

You can configure the middleware by passing parameters to the `RequestDurationMiddleware`:

- `excluded_paths`: List of paths to exclude from timing.
- `header_name`: The name of the header to store the request duration.
- `precision`: The precision of the recorded duration.
- `skip_validate_header_name`: Flag to skip header name validation.
- `skip_validate_precision`: Flag to skip precision validation.
- `time_granularity`: Specifies the unit of time measurement (default: `Seconds`).

Example:

```python
app.add_middleware(
    RequestDurationMiddleware,
    excluded_paths=["^/health/?$"],
    header_name="x-request-duration",
    precision=3,
    skip_validate_header_name=False,
    skip_validate_precision=False,
    time_granularity=TimeGranularity.MILLISECONDS,
)
```

#### Logging Filter Configuration 🔍

You can configure the logging filter by passing parameters to the `RequestDurationFilter`:

- `context_key`: The key to retrieve the request duration context value.
- `default_value`: The default value if the request duration context key is not found.

Example:

```python
logger.addFilter(RequestDurationFilter(context_key="request_duration", default_value="-"))
```

## Examples 📖

Here is a complete example of how to use the middleware with the Starlette framework. For more examples and detailed usage, please refer to the [examples](https://github.com/feteu/asgi-request-duration/tree/main/examples) folder in the repository.

### Example with Starlette 🌟

```python
from asgi_request_duration import RequestDurationMiddleware, TimeGranularity
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route
from uvicorn import run


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(
    RequestDurationMiddleware,
    excluded_paths=["/excluded"],
    header_name="x-request-duration",
    precision=4,
    skip_validate_header_name=False,
    skip_validate_precision=False,
    time_granularity=TimeGranularity.MILLISECONDS,
)

if __name__ == "__main__":
    run(app, host='127.0.0.1', port=8000)
```

## Contributing 🤝
Contributions are welcome! Please refer to the [CONTRIBUTING.md](CONTRIBUTING.md) file for guidelines on how to contribute to this project.

## License 📜
This project is licensed under the GNU GPLv3 License. See the [LICENSE](LICENSE) file for more details.


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "asgi-request-duration",
    "maintainer": "Fabio Greco",
    "docs_url": null,
    "requires_python": "<4.0,>=3.11",
    "maintainer_email": "fabio.greco.github@gmail.com",
    "keywords": "asgi, async, connexion, fastapi, middleware, request-duration, starlette, timing",
    "author": "Fabio Greco",
    "author_email": "fabio.greco.github@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/85/81/4617df3941a06a85331f108879f7746253171301f496a3feceb6e14b6a8b/asgi_request_duration-1.0.6.tar.gz",
    "platform": null,
    "description": "[![PyPI - Downloads](https://img.shields.io/pypi/dm/asgi-request-duration.svg)](https://pypi.org/project/asgi-request-duration/)\n[![PyPI - License](https://img.shields.io/pypi/l/asgi-request-duration)](https://www.gnu.org/licenses/gpl-3.0)\n[![PyPI - Version](https://img.shields.io/pypi/v/asgi-request-duration.svg)](https://pypi.org/project/asgi-request-duration/)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/asgi-request-duration)](https://pypi.org/project/asgi-request-duration/)\n[![PyPI - Status](https://img.shields.io/pypi/status/asgi-request-duration)](https://pypi.org/project/asgi-request-duration/)\n[![Dependencies](https://img.shields.io/librariesio/release/pypi/asgi-request-duration)](https://libraries.io/pypi/asgi-request-duration)\n[![Last Commit](https://img.shields.io/github/last-commit/feteu/asgi-request-duration)](https://github.com/feteu/asgi-request-duration/commits/main)\n[![Build Status build/testpypi](https://img.shields.io/github/actions/workflow/status/feteu/asgi-request-duration/publish-testpypi.yaml?label=publish-testpypi)](https://github.com/feteu/asgi-request-duration/actions/workflows/publish-testpypi.yaml)\n[![Build Status build/pypi](https://img.shields.io/github/actions/workflow/status/feteu/asgi-request-duration/publish-pypi.yaml?label=publish-pypi)](https://github.com/feteu/asgi-request-duration/actions/workflows/publish-pypi.yaml)\n[![Build Status test](https://img.shields.io/github/actions/workflow/status/feteu/asgi-request-duration/test.yaml?label=test)](https://github.com/feteu/asgi-request-duration/actions/workflows/test.yaml)\n\n# ASGI Request Duration \u23f1\ufe0f\n\nASGI Request Duration is a middleware for ASGI applications that measures the duration of HTTP requests and integrates this information into response headers and log records. This middleware is designed to be easy to integrate and configure, providing valuable insights into the performance of your ASGI application.\n\n> **Note:** If you find this project useful, please consider giving it a star \u2b50 on GitHub. This helps prioritize its maintenance and development. If you encounter any typos, bugs \ud83d\udc1b, or have new feature requests, feel free to open an issue. I will be happy to address them.\n\n## Table of Contents \ud83d\udcda\n\n1. [Features \u2728](#features-\u2728)\n2. [Installation \ud83d\udee0\ufe0f](#installation-\ud83d\udee0\ufe0f)\n3. [Usage \ud83d\ude80](#usage-\ud83d\ude80)\n    1. [Middleware \ud83e\udde9](#middleware-\ud83e\udde9)\n    2. [Logging Filter \ud83d\udcdd](#logging-filter-\ud83d\udcdd)\n    3. [Configuration \u2699\ufe0f](#configuration-\u2699\ufe0f)\n      1. [Middleware Configuration \ud83d\udd27](#middleware-configuration-\ud83d\udd27)\n      2. [Logging Filter Configuration \ud83d\udd0d](#logging-filter-configuration-\ud83d\udd0d)\n4. [Examples \ud83d\udcd6](#examples-\ud83d\udcd6)\n    1. [Example with Starlette \ud83c\udf1f](#example-with-starlette-\ud83c\udf1f)\n5. [Contributing \ud83e\udd1d](#contributing-\ud83e\udd1d)\n6. [License \ud83d\udcdc](#license-\ud83d\udcdc)\n\n## Features \u2728\n\n- Measure the duration of each HTTP request.\n- Add the request duration to response headers.\n- Integrate the request duration into log records.\n- Configurable header name and precision.\n- Exclude specific paths from timing.\n\n## Installation \ud83d\udee0\ufe0f\n\nYou can install the package using pip:\n\n```bash\npip install asgi-request-duration\n```\n\n## Usage \ud83d\ude80\n\n### Middleware \ud83e\udde9\n\nTo use the middleware, add it to your ASGI application:\n\n```python\nfrom asgi_request_duration.middleware import RequestDurationMiddleware\nfrom starlette.applications import Starlette\n\napp = Starlette()\napp.add_middleware(RequestDurationMiddleware)\n```\n\n### Logging Filter \ud83d\udcdd\n\nTo use the logging filter, configure your logger to use the `RequestDurationFilter`:\n\n```python\nimport logging\nfrom asgi_request_duration.filters import RequestDurationFilter\n\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(\"myapp\")\nlogger.addFilter(RequestDurationFilter())\n```\n\n### Configuration \u2699\ufe0f\n\n#### Middleware Configuration \ud83d\udd27\n\nYou can configure the middleware by passing parameters to the `RequestDurationMiddleware`:\n\n- `excluded_paths`: List of paths to exclude from timing.\n- `header_name`: The name of the header to store the request duration.\n- `precision`: The precision of the recorded duration.\n- `skip_validate_header_name`: Flag to skip header name validation.\n- `skip_validate_precision`: Flag to skip precision validation.\n- `time_granularity`: Specifies the unit of time measurement (default: `Seconds`).\n\nExample:\n\n```python\napp.add_middleware(\n    RequestDurationMiddleware,\n    excluded_paths=[\"^/health/?$\"],\n    header_name=\"x-request-duration\",\n    precision=3,\n    skip_validate_header_name=False,\n    skip_validate_precision=False,\n    time_granularity=TimeGranularity.MILLISECONDS,\n)\n```\n\n#### Logging Filter Configuration \ud83d\udd0d\n\nYou can configure the logging filter by passing parameters to the `RequestDurationFilter`:\n\n- `context_key`: The key to retrieve the request duration context value.\n- `default_value`: The default value if the request duration context key is not found.\n\nExample:\n\n```python\nlogger.addFilter(RequestDurationFilter(context_key=\"request_duration\", default_value=\"-\"))\n```\n\n## Examples \ud83d\udcd6\n\nHere is a complete example of how to use the middleware with the Starlette framework. For more examples and detailed usage, please refer to the [examples](https://github.com/feteu/asgi-request-duration/tree/main/examples) folder in the repository.\n\n### Example with Starlette \ud83c\udf1f\n\n```python\nfrom asgi_request_duration import RequestDurationMiddleware, TimeGranularity\nfrom starlette.applications import Starlette\nfrom starlette.requests import Request\nfrom starlette.responses import JSONResponse\nfrom starlette.routing import Route\nfrom uvicorn import run\n\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    RequestDurationMiddleware,\n    excluded_paths=[\"/excluded\"],\n    header_name=\"x-request-duration\",\n    precision=4,\n    skip_validate_header_name=False,\n    skip_validate_precision=False,\n    time_granularity=TimeGranularity.MILLISECONDS,\n)\n\nif __name__ == \"__main__\":\n    run(app, host='127.0.0.1', port=8000)\n```\n\n## Contributing \ud83e\udd1d\nContributions are welcome! Please refer to the [CONTRIBUTING.md](CONTRIBUTING.md) file for guidelines on how to contribute to this project.\n\n## License \ud83d\udcdc\nThis project is licensed under the GNU GPLv3 License. See the [LICENSE](LICENSE) file for more details.\n\n",
    "bugtrack_url": null,
    "license": "GNU GPLv3",
    "summary": "A asgi middleware to measure the request duration",
    "version": "1.0.6",
    "project_urls": {
        "Homepage": "https://github.com/feteu/asgi-request-duration",
        "Repository": "https://github.com/feteu/asgi-request-duration"
    },
    "split_keywords": [
        "asgi",
        " async",
        " connexion",
        " fastapi",
        " middleware",
        " request-duration",
        " starlette",
        " timing"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d0b7db3ea4d054da00f37b6f7be76e8376e9473104c3006b5239ab748560ee0b",
                "md5": "6cd1b9bc5a908b99cdf6d1582f72fe54",
                "sha256": "4543c8b6feb33ad1004f4c9213db8c3e8429dbab9286c63f0279164979b3d7dd"
            },
            "downloads": -1,
            "filename": "asgi_request_duration-1.0.6-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "6cd1b9bc5a908b99cdf6d1582f72fe54",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.11",
            "size": 21147,
            "upload_time": "2025-07-12T13:36:36",
            "upload_time_iso_8601": "2025-07-12T13:36:36.776836Z",
            "url": "https://files.pythonhosted.org/packages/d0/b7/db3ea4d054da00f37b6f7be76e8376e9473104c3006b5239ab748560ee0b/asgi_request_duration-1.0.6-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "85814617df3941a06a85331f108879f7746253171301f496a3feceb6e14b6a8b",
                "md5": "ad0cd297545f43455ced588eb976439f",
                "sha256": "7359dd163d737469327baaff639f1eaa4f164f7f53db091d1b7fd24e490ff5ab"
            },
            "downloads": -1,
            "filename": "asgi_request_duration-1.0.6.tar.gz",
            "has_sig": false,
            "md5_digest": "ad0cd297545f43455ced588eb976439f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.11",
            "size": 18343,
            "upload_time": "2025-07-12T13:36:38",
            "upload_time_iso_8601": "2025-07-12T13:36:38.384990Z",
            "url": "https://files.pythonhosted.org/packages/85/81/4617df3941a06a85331f108879f7746253171301f496a3feceb6e14b6a8b/asgi_request_duration-1.0.6.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-12 13:36:38",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "feteu",
    "github_project": "asgi-request-duration",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "asgi-request-duration"
}
        
Elapsed time: 0.51804s