{"id":4281,"library":"timing-asgi","title":"Timing ASGI","description":"Timing-asgi is an ASGI middleware designed to automatically instrument ASGI endpoints and emit timing metrics. Developed by GRID, it's particularly useful for integrating with statsd-based cloud monitoring services like Datadog. The library currently supports ASGI3 and is actively maintained, with version 0.3.2 being the latest release.","status":"active","version":"0.3.2","language":"en","source_language":"en","source_url":"https://github.com/steinnes/timing-asgi","tags":["asgi","middleware","metrics","statsd","timing","performance","starlette","fastapi"],"install":[{"cmd":"pip install timing-asgi","lang":"bash","label":"Install latest version"}],"dependencies":[],"imports":[{"symbol":"TimingMiddleware","correct":"from timing_asgi import TimingMiddleware"},{"symbol":"TimingClient","correct":"from timing_asgi import TimingClient"},{"symbol":"StarletteScopeToName","correct":"from timing_asgi.integrations import StarletteScopeToName"},{"note":"`PathToName` is in `timing_asgi.utils`, not directly under `timing_asgi`.","wrong":"from timing_asgi import PathToName","symbol":"PathToName","correct":"from timing_asgi.utils import PathToName"}],"quickstart":{"code":"import logging\nimport uvicorn\nfrom starlette.applications import Starlette\nfrom starlette.responses import PlainTextResponse\nfrom timing_asgi import TimingMiddleware, TimingClient\nfrom timing_asgi.integrations import StarletteScopeToName\n\n\nclass PrintTimings(TimingClient):\n    \"\"\"A simple TimingClient that prints metrics to stdout.\"\"\"\n    def timing(self, metric_name: str, timing: float, tags: list[str]) -> None:\n        print(f\"Metric: {metric_name}, Time: {timing:.6f}, Tags: {tags}\")\n\n\napp = Starlette()\n\n@app.route(\"/\")\nasync def homepage(request):\n    return PlainTextResponse(\"hello world\")\n\napp.add_middleware(\n    TimingMiddleware,\n    client=PrintTimings(),\n    metric_namer=StarletteScopeToName(prefix=\"myapp\", starlette_app=app)\n)\n\nif __name__ == \"__main__\":\n    logging.basicConfig(level=logging.INFO)\n    print(\"Running Uvicorn on http://127.0.0.1:8000. Access / to see timing metrics.\")\n    uvicorn.run(app, host=\"127.0.0.1\", port=8000)","lang":"python","description":"This example demonstrates how to integrate `timing-asgi` with a Starlette application. It defines a custom `TimingClient` that prints the collected metrics to the console. The `TimingMiddleware` is added to the Starlette application, configured with the custom client and a `StarletteScopeToName` metric namer to generate meaningful metric names based on the Starlette routes."},"warnings":[{"fix":"Ensure your ASGI server and application framework are ASGI3 compliant. If not, downgrade to `timing-asgi==0.1.2`.","message":"As of version 0.2.0, `timing-asgi` exclusively supports ASGI3. Applications requiring ASGI2 compatibility must use `timing-asgi` version 0.1.2.","severity":"breaking","affected_versions":">=0.2.0"},{"fix":"Currently, there is no direct fix within `timing-asgi` for lifespan event instrumentation. Consider external tools or custom instrumentation for application startup/shutdown timings.","message":"The middleware logs a debug message indicating that 'ASGI scope of type lifespan is not supported yet'. This means lifecycle events (startup/shutdown) of your ASGI application will not be instrumented by this middleware.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Adhere strictly to the `TimingClient` interface by implementing the `timing` method as specified in the base class.","message":"When implementing a custom `TimingClient`, ensure it provides a `timing` method with the signature `(self, metric_name: str, timing: float, tags: list[str]) -> None`. The middleware performs a runtime check for this method, and its absence or incorrect signature will lead to errors.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Explicitly pass an appropriate `metric_namer` to `TimingMiddleware`. For Starlette, use `StarletteScopeToName(prefix=\"your_app_name\", starlette_app=app)`.","message":"By default, if no `metric_namer` is provided, `PathToName` is used, which might generate less descriptive metric names (e.g., based on URL path). For framework-specific routing, a specialized namer like `StarletteScopeToName` is often preferred for more accurate metric names.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-11T00:00:00.000Z","next_check":"2026-07-10T00:00:00.000Z"}