{"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":"python","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,"search_vec":"'0.3.2':49 'activ':45 'asgi':2,5,8,14,54 'asgi3':42 'automat':12 'base':32 'cloud':33 'current':40 'datadog':37 'design':10 'develop':20 'emit':17 'endpoint':15 'fastapi':61 'grid':22 'instrument':13 'integr':28 'latest':52 'librari':39 'like':36 'maintain':46 'metric':19,56 'middlewar':9,55 'monitor':34 'particular':25 'perform':59 'releas':53 'servic':35 'starlett':60 'statsd':31,57 'statsd-bas':30 'support':41 'time':1,4,18,58 'timing-asgi':3 'use':26 'version':48","created_at":"2026-04-12T03:48:40.239802+00:00","updated_at":"2026-04-16T22:58:03.107914+00:00","problems":[{"fix":"pip install timing-asgi","cause":"The 'timing-asgi' library is not installed in the current Python environment.","error":"ModuleNotFoundError: No module named 'timing_asgi'"},{"fix":"app.add_middleware(TimingMiddleware, recorder=DatadogRecorder(client=statsd_client), channel=StarletteInfo())","cause":"The TimingMiddleware requires both 'recorder' and 'channel' arguments during initialization, but they were not provided.","error":"TypeError: __init__ missing 2 required positional arguments: 'recorder' and 'channel'"},{"fix":"statsd_client = statsd.StatsClient(host='localhost', port=8125)\nrecorder=DatadogRecorder(client=statsd_client)","cause":"The DatadogRecorder requires a 'statsd.StatsClient' instance to be passed via the 'client' argument during initialization.","error":"TypeError: __init__ missing 1 required positional argument: 'client'"}],"ecosystem":"pypi","meta_description":null,"install_score":null,"quickstart_score":null,"quickstart_tag":null,"pypi_latest":"0.3.2","cli_name":"","cli_version":null,"type":"library","homepage":null,"github":"https://github.com/steinnes/timing-asgi","docs":null,"changelog":null,"pypi":"https://pypi.org/project/timing-asgi/","npm":null,"openapi_spec":null,"status_page":null,"smithery":null,"categories":["observability","web-framework","devops"],"base_url":null,"auth_type":null,"provenance":{"verified_status":"passing","verified_at":"2026-06-28","last_verified":"2026-08-29","next_check":"2026-07-28","install_tag":null}}