actix_web_opentelemetry/lib.rs
1//! [OpenTelemetry] integration for [Actix Web].
2//!
3//! This crate allows you to easily instrument client and server requests.
4//!
5//! * Server requests can be traced by using the [`RequestTracing`] middleware.
6//!
7//! The `awc` feature allows you to instrument client requests made by the [awc] crate.
8//!
9//! * Client requests can be traced by using the [`ClientExt::trace_request`] method.
10//!
11//! The `metrics` feature allows you to expose request metrics to [Prometheus].
12//!
13//! * Metrics can be tracked using the [`RequestMetrics`] middleware.
14//!
15//! [OpenTelemetry]: https://opentelemetry.io
16//! [Actix Web]: https://actix.rs
17//! [awc]: https://docs.rs/awc
18//! [Prometheus]: https://prometheus.io
19//!
20//! ### Client Request Examples:
21//!
22//! Note: this requires the `awc` feature to be enabled.
23//!
24//! ```no_run
25//! # #[cfg(feature="awc")]
26//! # {
27//! use awc::{Client, error::SendRequestError};
28//! use actix_web_opentelemetry::ClientExt;
29//!
30//! async fn execute_request(client: &Client) -> Result<(), SendRequestError> {
31//! let res = client
32//! .get("http://localhost:8080")
33//! // Add `trace_request` before `send` to any awc request to add instrumentation
34//! .trace_request()
35//! .send()
36//! .await?;
37//!
38//! println!("Response: {:?}", res);
39//! Ok(())
40//! }
41//! # }
42//! ```
43//!
44//! ### Server middleware examples:
45//!
46//! Tracing and metrics middleware can be used together or independently.
47//!
48//! Tracing server example:
49//!
50//! ```no_run
51//! use actix_web::{web, App, HttpServer};
52//! use actix_web_opentelemetry::RequestTracing;
53//! use opentelemetry::global;
54//! use opentelemetry_sdk::trace::SdkTracerProvider;
55//!
56//! async fn index() -> &'static str {
57//! "Hello world!"
58//! }
59//!
60//! #[actix_web::main]
61//! async fn main() -> std::io::Result<()> {
62//! // Install an OpenTelemetry trace pipeline.
63//! // Swap for https://docs.rs/opentelemetry-jaeger or other compatible
64//! // exporter to send trace information to your collector.
65//! let exporter = opentelemetry_stdout::SpanExporter::default();
66//!
67//! // Configure your tracer provider with your exporter(s)
68//! let provider = SdkTracerProvider::builder()
69//! .with_simple_exporter(exporter)
70//! .build();
71//! global::set_tracer_provider(provider);
72//!
73//! // add the request tracing middleware to create spans for each request
74//! HttpServer::new(|| {
75//! App::new()
76//! .wrap(RequestTracing::new())
77//! .service(web::resource("/").to(index))
78//! })
79//! .bind("127.0.0.1:8080")?
80//! .run()
81//! .await
82//! }
83//! ```
84//!
85//! Request metrics middleware (requires the `metrics` feature):
86//!
87//! ```no_run
88//! use actix_web::{dev, http, web, App, HttpRequest, HttpServer};
89//! # #[cfg(feature = "metrics-prometheus")]
90//! use actix_web_opentelemetry::{PrometheusMetricsHandler, RequestMetrics, RequestTracing};
91//! use opentelemetry::global;
92//! use opentelemetry_sdk::metrics::SdkMeterProvider;
93//!
94//! # #[cfg(feature = "metrics-prometheus")]
95//! #[actix_web::main]
96//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
97//! // Configure prometheus or your preferred metrics service
98//! let registry = prometheus::Registry::new();
99//! let exporter = opentelemetry_prometheus::exporter()
100//! .with_registry(registry.clone())
101//! .build()?;
102//!
103//! // set up your meter provider with your exporter(s)
104//! let provider = SdkMeterProvider::builder()
105//! .with_reader(exporter)
106//! .build();
107//! global::set_meter_provider(provider);
108//!
109//! // Run actix server, metrics are now available at http://localhost:8080/metrics
110//! HttpServer::new(move || {
111//! App::new()
112//! .wrap(RequestTracing::new())
113//! .wrap(RequestMetrics::default())
114//! .route("/metrics", web::get().to(PrometheusMetricsHandler::new(registry.clone())))
115//! })
116//! .bind("localhost:8080")?
117//! .run()
118//! .await;
119//!
120//! Ok(())
121//! }
122//! # #[cfg(not(feature = "metrics-prometheus"))]
123//! # fn main() {}
124//! ```
125//!
126//! ### Exporter configuration
127//!
128//! [`actix-web`] uses [`tokio`] as the underlying executor, so exporters should be
129//! configured to be non-blocking:
130//!
131//! ```toml
132//! [dependencies]
133//! ## if exporting to jaeger, use the `tokio` feature.
134//! opentelemetry-jaeger = { version = "..", features = ["rt-tokio-current-thread"] }
135//!
136//! ## if exporting to zipkin, use the `tokio` based `reqwest-client` feature.
137//! opentelemetry-zipkin = { version = "..", features = ["reqwest-client"], default-features = false }
138//!
139//! ## ... ensure the same same for any other exporters
140//! ```
141//!
142//! [`actix-web`]: https://crates.io/crates/actix-web
143//! [`tokio`]: https://crates.io/crates/tokio
144#![deny(missing_docs, unreachable_pub, missing_debug_implementations)]
145#![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::broken_intra_doc_links))]
146
147#[cfg(feature = "awc")]
148mod client;
149mod middleware;
150mod util;
151
152#[cfg(feature = "awc")]
153#[cfg_attr(docsrs, doc(cfg(feature = "awc")))]
154pub use client::{ClientExt, InstrumentedClientRequest};
155
156#[cfg(feature = "metrics-prometheus")]
157#[cfg_attr(docsrs, doc(cfg(feature = "metrics-prometheus")))]
158pub use middleware::metrics::prometheus::PrometheusMetricsHandler;
159#[cfg(feature = "metrics")]
160#[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
161pub use middleware::metrics::{RequestMetrics, RequestMetricsBuilder, RequestMetricsMiddleware};
162#[cfg(feature = "metrics")]
163#[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
164pub use util::metrics_attributes_from_request;
165
166pub use {
167 middleware::route_formatter::RouteFormatter,
168 middleware::trace::{RequestTracing, RequestTracingMiddleware},
169};