Skip to main content

PyPI - Downloads PyPI - License PyPI - Version PyPI - Python Version PyPI - Status Dependencies Last Commit Build Status build/testpypi Build Status build/pypi Build Status test

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 ✨
  2. Installation 🛠️
  3. Usage 🚀
    1. Middleware 🧩
    2. Logging Filter 📝
    3. Configuration ⚙️
    4. Middleware Configuration 🔧
    5. Logging Filter Configuration 🔍
  4. Examples 📖
    1. Example with Starlette 🌟
  5. Contributing 🤝
  6. 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:

pip install asgi-request-duration

Usage 🚀

Middleware 🧩

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

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:

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:

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:

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 folder in the repository.

Example with Starlette 🌟

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 file for guidelines on how to contribute to this project.

License 📜

This project is licensed under the GNU GPLv3 License. See the LICENSE file for more details.

Metadata

Release files for asgi-request-duration 1.0.6

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

Source distribution (sdist)

Source distribution for asgi-request-duration 1.0.6
File Size Uploaded
asgi_request_duration-1.0.6.tar.gz 18.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for asgi-request-duration 1.0.6
File Interpreter ABI Platform
asgi_request_duration-1.0.6-py3-none-any.whl Python 3 none any Details

Total release size: 39.5 kB

Release files / asgi_request_duration-1.0.6.tar.gz

Download URL asgi_request_duration-1.0.6.tar.gz
Size 18.3 kB
Tags Source
SHA-256 checksum
How to use checksums
7359dd163d737469327baaff639f1eaa4f164f7f53db091d1b7fd24e490ff5ab
BLAKE2b-256 checksum
How to use checksums
85814617df3941a06a85331f108879f7746253171301f496a3feceb6e14b6a8b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.0.1 CPython/3.12.8

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 12, 2025.

Transparency log

Release files / asgi_request_duration-1.0.6-py3-none-any.whl

Download URL asgi_request_duration-1.0.6-py3-none-any.whl
Size 21.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4543c8b6feb33ad1004f4c9213db8c3e8429dbab9286c63f0279164979b3d7dd
BLAKE2b-256 checksum
How to use checksums
d0b7db3ea4d054da00f37b6f7be76e8376e9473104c3006b5239ab748560ee0b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.0.1 CPython/3.12.8

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 12, 2025.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.6 This release

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

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