{"id":16865932,"url":"https://github.com/lukemathwalker/tracing-actix-web","last_synced_at":"2025-05-14T10:12:41.688Z","repository":{"id":39889633,"uuid":"299061213","full_name":"LukeMathWalker/tracing-actix-web","owner":"LukeMathWalker","description":"Structured logging for actix-web applications.","archived":false,"fork":false,"pushed_at":"2025-04-10T13:12:06.000Z","size":193,"stargazers_count":248,"open_issues_count":4,"forks_count":64,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-05-14T00:46:21.667Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LukeMathWalker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-09-27T15:28:20.000Z","updated_at":"2025-05-12T18:28:47.000Z","dependencies_parsed_at":"2024-10-19T11:38:29.368Z","dependency_job_id":"b5675cde-5ce7-4faa-a404-ffbdbcdd3a4b","html_url":"https://github.com/LukeMathWalker/tracing-actix-web","commit_stats":{"total_commits":169,"total_committers":35,"mean_commits":4.828571428571428,"dds":0.514792899408284,"last_synced_commit":"ebd73e98f3a7ce6db8040d717f3a3da0f50bb091"},"previous_names":[],"tags_count":59,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukeMathWalker%2Ftracing-actix-web","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukeMathWalker%2Ftracing-actix-web/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukeMathWalker%2Ftracing-actix-web/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukeMathWalker%2Ftracing-actix-web/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LukeMathWalker","download_url":"https://codeload.github.com/LukeMathWalker/tracing-actix-web/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254120179,"owners_count":22017953,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-13T14:48:54.382Z","updated_at":"2025-05-14T10:12:41.649Z","avatar_url":"https://github.com/LukeMathWalker.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003etracing-actix-web\u003c/h1\u003e\n\u003cdiv align=\"center\"\u003e\n \u003cstrong\u003e\n   Structured diagnostics for actix-web applications.\n \u003c/strong\u003e\n\u003c/div\u003e\n\n\u003cbr /\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003c!-- Crates version --\u003e\n  \u003ca href=\"https://crates.io/crates/tracing-actix-web\"\u003e\n    \u003cimg src=\"https://img.shields.io/crates/v/tracing-actix-web.svg?style=flat-square\"\n    alt=\"Crates.io version\" /\u003e\n  \u003c/a\u003e\n  \u003c!-- Downloads --\u003e\n  \u003ca href=\"https://crates.io/crates/tracing-actix-web\"\u003e\n    \u003cimg src=\"https://img.shields.io/crates/d/tracing-actix-web.svg?style=flat-square\"\n      alt=\"Download\" /\u003e\n  \u003c/a\u003e\n  \u003c!-- docs.rs docs --\u003e\n  \u003ca href=\"https://docs.rs/tracing-actix-web\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square\"\n      alt=\"docs.rs docs\" /\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\u003cbr/\u003e\n\n`tracing-actix-web` provides [`TracingLogger`], a middleware to collect telemetry data from applications built on top of the [`actix-web`] framework.\n\n\u003e `tracing-actix-web` was initially developed for the telemetry chapter of [Zero to Production In Rust](https://zero2prod.com), a hands-on introduction to backend development using the Rust programming language.\n\n# Getting started \n\n## How to install\n\nAdd `tracing-actix-web` to your dependencies:\n\n```toml\n[dependencies]\n# ...\ntracing-actix-web = \"0.7\"\ntracing = \"0.1\"\nactix-web = \"4\"\n```\n\n`tracing-actix-web` exposes these feature flags:\n\n- `opentelemetry_0_13`: attach [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-rust)'s context to the root span using `opentelemetry` 0.13;\n- `opentelemetry_0_14`: same as above but using `opentelemetry` 0.14;\n- `opentelemetry_0_15`: same as above but using `opentelemetry` 0.15;\n- `opentelemetry_0_16`: same as above but using `opentelemetry` 0.16;\n- `opentelemetry_0_17`: same as above but using `opentelemetry` 0.17;\n- `opentelemetry_0_18`: same as above but using `opentelemetry` 0.18;\n- `opentelemetry_0_19`: same as above but using `opentelemetry` 0.19;\n- `opentelemetry_0_20`: same as above but using `opentelemetry` 0.20;\n- `opentelemetry_0_21`: same as above but using `opentelemetry` 0.21;\n- `opentelemetry_0_22`: same as above but using `opentelemetry` 0.22;\n- `opentelemetry_0_23`: same as above but using `opentelemetry` 0.23;\n- `opentelemetry_0_24`: same as above but using `opentelemetry` 0.24;\n- `opentelemetry_0_25`: same as above but using `opentelemetry` 0.25;\n- `opentelemetry_0_26`: same as above but using `opentelemetry` 0.26;\n- `opentelemetry_0_27`: same as above but using `opentelemetry` 0.27;\n- `opentelemetry_0_28`: same as above but using `opentelemetry` 0.28;\n- `opentelemetry_0_29`: same as above but using `opentelemetry` 0.29;\n- `emit_event_on_error`: emit a [`tracing`] event when request processing fails with an error (enabled by default).\n- `uuid_v7`: use the UUID v7 implementation inside [`RequestId`] instead of UUID v4 (disabled by default).\n## Quickstart\n\n```rust,compile_fail\nuse actix_web::{App, web, HttpServer};\nuse tracing_actix_web::TracingLogger;\n\nfn main() {\n    // Init your `tracing` subscriber here!\n\n    let server = HttpServer::new(|| {\n        App::new()\n            // Mount `TracingLogger` as a middleware\n            .wrap(TracingLogger::default())\n            .service( /*  */ )\n    });\n}\n```\n\nCheck out [the examples on GitHub](https://github.com/LukeMathWalker/tracing-actix-web/tree/main/examples) to get a taste of how [`TracingLogger`] can be used to observe and monitor your\napplication.  \n\n# From zero to hero: a crash course in observability\n\n## `tracing`: who art thou?\n\n[`TracingLogger`] is built on top of [`tracing`], a modern instrumentation framework with\n[a vibrant ecosystem](https://github.com/tokio-rs/tracing#related-crates).\n\n`tracing-actix-web`'s documentation provides a crash course in how to use [`tracing`] to instrument an `actix-web` application.  \nIf you want to learn more check out [\"Are we observable yet?\"](https://www.lpalmieri.com/posts/2020-09-27-zero-to-production-4-are-we-observable-yet/) -\nit provides an in-depth introduction to the crate and the problems it solves within the bigger picture of [observability](https://docs.honeycomb.io/learning-about-observability/).\n\n## The root span\n\n[`tracing::Span`] is the key abstraction in [`tracing`]: it represents a unit of work in your system.  \nA [`tracing::Span`] has a beginning and an end. It can include one or more **child spans** to represent sub-unit\nof works within a larger task.\n\nWhen your application receives a request, [`TracingLogger`] creates a new span - we call it the **[root span]**.  \nAll the spans created _while_ processing the request will be children of the root span.\n\n[`tracing`] empowers us to attach structured properties to a span as a collection of key-value pairs.  \nThose properties can then be queried in a variety of tools (e.g. ElasticSearch, Honeycomb, DataDog) to\nunderstand what is happening in your system.  \n\n## Customisation via [`RootSpanBuilder`]\n\nTroubleshooting becomes much easier when the root span has a _rich context_ - e.g. you can understand most of what\nhappened when processing the request just by looking at the properties attached to the corresponding root span.  \n\nYou might have heard of this technique as the [canonical log line pattern](https://stripe.com/blog/canonical-log-lines),\npopularised by Stripe. It is more recently discussed in terms of [high-cardinality events](https://www.honeycomb.io/blog/observability-a-manifesto/)\nby Honeycomb and other vendors in the observability space.\n\n[`TracingLogger`] gives you a chance to use the very same pattern: you can customise the properties attached\nto the root span in order to capture the context relevant to your specific domain.\n\n[`TracingLogger::default`] is equivalent to:\n\n```rust\nuse tracing_actix_web::{TracingLogger, DefaultRootSpanBuilder};\n\n// Two ways to initialise TracingLogger with the default root span builder\nlet default = TracingLogger::default();\nlet another_way = TracingLogger::\u003cDefaultRootSpanBuilder\u003e::new();\n```\n\nWe are delegating the construction of the root span to [`DefaultRootSpanBuilder`].  \n[`DefaultRootSpanBuilder`] captures, out of the box, several dimensions that are usually relevant when looking at an HTTP\nAPI: method, version, route, etc. - check out its documentation for an extensive list.\n\nYou can customise the root span by providing your own implementation of the [`RootSpanBuilder`] trait.  \nLet's imagine, for example, that our system cares about a client identifier embedded inside an authorization header.\nWe could add a `client_id` property to the root span using a custom builder, `DomainRootSpanBuilder`:\n\n```rust\nuse actix_web::body::MessageBody;\nuse actix_web::dev::{ServiceResponse, ServiceRequest};\nuse actix_web::Error;\nuse tracing_actix_web::{TracingLogger, DefaultRootSpanBuilder, RootSpanBuilder};\nuse tracing::Span;\n\npub struct DomainRootSpanBuilder;\n\nimpl RootSpanBuilder for DomainRootSpanBuilder {\n    fn on_request_start(request: \u0026ServiceRequest) -\u003e Span {\n        let client_id: \u0026str = todo!(\"Somehow extract it from the authorization header\");\n        tracing::info_span!(\"Request\", client_id)\n    }\n\n    fn on_request_end\u003cB: MessageBody\u003e(_span: Span, _outcome: \u0026Result\u003cServiceResponse\u003cB\u003e, Error\u003e) {}\n}\n\nlet custom_middleware = TracingLogger::\u003cDomainRootSpanBuilder\u003e::new();\n```\n\nThere is an issue, though: `client_id` is the _only_ property we are capturing.  \nWith `DomainRootSpanBuilder`, as it is, we do not get any of that useful HTTP-related information provided by\n[`DefaultRootSpanBuilder`].  \n\nWe can do better!\n\n```rust\nuse actix_web::body::MessageBody;\nuse actix_web::dev::{ServiceResponse, ServiceRequest};\nuse actix_web::Error;\nuse tracing_actix_web::{TracingLogger, DefaultRootSpanBuilder, RootSpanBuilder};\nuse tracing::Span;\n\npub struct DomainRootSpanBuilder;\n\nimpl RootSpanBuilder for DomainRootSpanBuilder {\n    fn on_request_start(request: \u0026ServiceRequest) -\u003e Span {\n        let client_id: \u0026str = todo!(\"Somehow extract it from the authorization header\");\n        tracing_actix_web::root_span!(request, client_id)\n    }\n\n    fn on_request_end\u003cB: MessageBody\u003e(span: Span, outcome: \u0026Result\u003cServiceResponse\u003cB\u003e, Error\u003e) {\n        DefaultRootSpanBuilder::on_request_end(span, outcome);\n    }\n}\n\nlet custom_middleware = TracingLogger::\u003cDomainRootSpanBuilder\u003e::new();\n```\n\n[`root_span!`] is a macro provided by `tracing-actix-web`: it creates a new span by combining all the HTTP properties tracked\nby [`DefaultRootSpanBuilder`] with the custom ones you specify when calling it (e.g. `client_id` in our example).  \n\nWe need to use a macro because `tracing` requires all the properties attached to a span to be declared upfront, when the span is created.  \nYou cannot add new ones afterwards. This makes it extremely fast, but it pushes us to reach for macros when we need some level of\ncomposition.\n\n[`root_span!`] exposes more or less the same knob you can find on `tracing`'s `span!` macro. You can, for example, customise\nthe span level:\n\n```rust\nuse actix_web::body::MessageBody;\nuse actix_web::dev::{ServiceResponse, ServiceRequest};\nuse actix_web::Error;\nuse tracing_actix_web::{TracingLogger, DefaultRootSpanBuilder, RootSpanBuilder, Level};\nuse tracing::Span;\n\npub struct CustomLevelRootSpanBuilder;\n\nimpl RootSpanBuilder for CustomLevelRootSpanBuilder {\n    fn on_request_start(request: \u0026ServiceRequest) -\u003e Span {\n        let level = if request.path() == \"/health_check\" {\n            Level::DEBUG\n        } else {\n            Level::INFO\n        };\n        tracing_actix_web::root_span!(level = level, request)\n    }\n\n    fn on_request_end\u003cB: MessageBody\u003e(span: Span, outcome: \u0026Result\u003cServiceResponse\u003cB\u003e, Error\u003e) {\n        DefaultRootSpanBuilder::on_request_end(span, outcome);\n    }\n}\n\nlet custom_middleware = TracingLogger::\u003cCustomLevelRootSpanBuilder\u003e::new();\n```\n\n## The [`RootSpan`] extractor\n\nIt often happens that not all information about a task is known upfront, encoded in the incoming request.  \nYou can use the [`RootSpan`] extractor to grab the root span in your handlers and attach more information\nto your root span as it becomes available:\n\n```rust\nuse actix_web::body::MessageBody;\nuse actix_web::dev::{ServiceResponse, ServiceRequest};\nuse actix_web::{Error, HttpResponse};\nuse tracing_actix_web::{RootSpan, DefaultRootSpanBuilder, RootSpanBuilder};\nuse tracing::Span;\nuse actix_web::get;\nuse tracing_actix_web::RequestId;\nuse uuid::Uuid;\n\n#[get(\"/\")]\nasync fn handler(root_span: RootSpan) -\u003e HttpResponse {\n    let application_id: \u0026str = todo!(\"Some domain logic\");\n    // Record the property value against the root span\n    root_span.record(\"application_id\", \u0026application_id);\n\n    // [...]\n    # todo!()\n}\n\npub struct DomainRootSpanBuilder;\n\nimpl RootSpanBuilder for DomainRootSpanBuilder {\n    fn on_request_start(request: \u0026ServiceRequest) -\u003e Span {\n        let client_id: \u0026str = todo!(\"Somehow extract it from the authorization header\");\n        // All fields you want to capture must be declared upfront.\n        // If you don't know the value (yet), use tracing's `Empty`\n        tracing_actix_web::root_span!(\n            request,\n            client_id, application_id = tracing::field::Empty\n        )\n    }\n\n    fn on_request_end\u003cB: MessageBody\u003e(span: Span, response: \u0026Result\u003cServiceResponse\u003cB\u003e, Error\u003e) {\n        DefaultRootSpanBuilder::on_request_end(span, response);\n    }\n}\n```\n\n# Unique identifiers\n\n## Request Id\n\n`tracing-actix-web` generates a unique identifier for each incoming request, the **request id**.  \n\nYou can extract the request id using the [`RequestId`] extractor:\n\n```rust\nuse actix_web::get;\nuse tracing_actix_web::RequestId;\nuse uuid::Uuid;\n\n#[get(\"/\")]\nasync fn index(request_id: RequestId) -\u003e String {\n  format!(\"{}\", request_id)\n}\n```\n\nThe request id is meant to identify all operations related to a particular request **within the boundary of your API**.  \nIf you need to **trace** a request across multiple services (e.g. in a microservice architecture), you want to look at the `trace_id` field - see the next section on OpenTelemetry for more details.\n\n\nOptionally, using the `uuid_v7` feature flag will allow [`RequestId`] to use UUID v7 instead of the currently used UUID v4.\n\n## Trace Id\n\nTo fulfill a request you often have to perform additional I/O operations - e.g. calls to other REST or gRPC APIs, database queries, etc.  \n**Distributed tracing** is the standard approach to **trace** a single request across the entirety of your stack.\n\n`tracing-actix-web` provides support for distributed tracing by supporting the [OpenTelemetry standard](https://opentelemetry.io/).  \n`tracing-actix-web` follows [OpenTelemetry's semantic convention](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#spancontext)\nfor field names.  \nFurthermore, it provides an `opentelemetry_0_17` feature flag to automatically performs trace propagation: it tries to extract the OpenTelemetry context out of the headers of incoming requests and, when it finds one, it sets it as the remote context for the current root span. The context is then propagated to your downstream dependencies if your HTTP or gRPC clients are OpenTelemetry-aware - e.g. using [`reqwest-middleware` and `reqwest-tracing`](https://github.com/TrueLayer/reqwest-middleware) if you are using `reqwest` as your HTTP client.  \nYou can then find all logs for the same request across all the services it touched by looking for the `trace_id`, automatically logged by `tracing-actix-web`.\n\nIf you add [`tracing-opentelemetry::OpenTelemetryLayer`](https://docs.rs/tracing-opentelemetry/0.17.0/tracing_opentelemetry/struct.OpenTelemetryLayer.html)\nin your `tracing::Subscriber` you will be able to export the root span (and all its children) as OpenTelemetry spans.\n\nCheck out the [relevant example in the GitHub repository](https://github.com/LukeMathWalker/tracing-actix-web/tree/main/examples/opentelemetry) for reference.\n\n# License\n\nLicensed under either of \u003ca href=\"LICENSE-APACHE\"\u003eApache License, Version 2.0\u003c/a\u003e or \u003ca href=\"LICENSE-MIT\"\u003eMIT license\u003c/a\u003e at your option.\n\nUnless you explicitly state otherwise, any contribution intentionally submitted for inclusion in `tracing-actix-web` by you, as defined in the Apache-2.0 license, shall be\ndual licensed as above, without any additional terms or conditions.\n\n[`TracingLogger`]: https://docs.rs/tracing-actix-web/4.0.0-beta.1/tracing_actix_web/struct.TracingLogger.html\n[`RequestId`]: https://docs.rs/tracing-actix-web/4.0.0-beta.1/tracing_actix_web/struct.RequestId.html\n[`RootSpan`]: https://docs.rs/tracing-actix-web/4.0.0-beta.1/tracing_actix_web/struct.RootSpan.html\n[`RootSpanBuilder`]: https://docs.rs/tracing-actix-web/4.0.0-beta.1/tracing_actix_web/trait.RootSpanBuilder.html\n[`DefaultRootSpanBuilder`]: https://docs.rs/tracing-actix-web/4.0.0-beta.1/tracing_actix_web/struct.DefaultRootSpanBuilder.html\n[`DefaultRootSpanBuilder::default`]: https://docs.rs/tracing-actix-web/4.0.0-beta.1/tracing_actix_web/struct.DefaultRootSpanBuilder.html#method.default\n[`tracing`]: https://docs.rs/tracing\n[`tracing::Span`]: https://docs.rs/tracing/latest/tracing/struct.Span.html\n[`root_span!`]: https://docs.rs/tracing-actix-web/4.0.0-beta.1/tracing_actix_web/macro.root_span.html\n[root span]: https://docs.rs/tracing-actix-web/4.0.0-beta.1/tracing_actix_web/struct.RootSpan.html\n[`actix-web`]: https://docs.rs/actix-web/4.0.0-beta.6/actix_web/index.html\n[`uuid`]: https://docs.rs/uuid\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukemathwalker%2Ftracing-actix-web","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flukemathwalker%2Ftracing-actix-web","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukemathwalker%2Ftracing-actix-web/lists"}