mirror of
https://github.com/actix/actix-extras.git
synced 2025-07-01 12:15:08 +02:00
Merge remote-tracking branch 'tracing/actix-extras' into tracing-actix-web
This commit is contained in:
17
tracing-actix-web/examples/custom-root-span/Cargo.toml
Normal file
17
tracing-actix-web/examples/custom-root-span/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "custom-root-span"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
actix-web = "4"
|
||||
opentelemetry = "0.25"
|
||||
opentelemetry-otlp = "0.25"
|
||||
opentelemetry_sdk = { version = "0.25", features = ["rt-tokio-current-thread"] }
|
||||
opentelemetry-semantic-conventions = "0.25"
|
||||
tracing-opentelemetry = { version = "0.26" }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["registry", "env-filter"] }
|
||||
tracing-bunyan-formatter = "0.3"
|
||||
tracing-actix-web = { path = "../..", features = ["opentelemetry_0_25"] }
|
38
tracing-actix-web/examples/custom-root-span/README.md
Normal file
38
tracing-actix-web/examples/custom-root-span/README.md
Normal file
@ -0,0 +1,38 @@
|
||||
# Custom root span
|
||||
|
||||
## Running
|
||||
|
||||
You can launch this example with
|
||||
|
||||
```bash
|
||||
cargo run
|
||||
```
|
||||
|
||||
An `actix-web` application will be listening on port `8080`.
|
||||
You can fire requests to it with:
|
||||
|
||||
```bash
|
||||
curl -v http://localhost:8080/hello
|
||||
```
|
||||
```text
|
||||
Hello world!
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
curl -v http://localhost:8080/hello/my-name
|
||||
```
|
||||
```text
|
||||
Hello my-name!
|
||||
```
|
||||
|
||||
## Visualising traces
|
||||
|
||||
Spans will be also printed to the console in JSON format, as structured log records.
|
||||
|
||||
You can look at the exported spans in your browser by visiting [http://localhost:16686](http://localhost:16686) if you launch a Jaeger instance:
|
||||
|
||||
```bash
|
||||
docker run -d -p6831:6831/udp -p6832:6832/udp -p16686:16686 jaegertracing/all-in-one:latest
|
||||
```
|
116
tracing-actix-web/examples/custom-root-span/src/main.rs
Normal file
116
tracing-actix-web/examples/custom-root-span/src/main.rs
Normal file
@ -0,0 +1,116 @@
|
||||
use actix_web::body::MessageBody;
|
||||
use actix_web::dev::{ServiceRequest, ServiceResponse};
|
||||
use actix_web::{web, App, Error, HttpServer};
|
||||
use opentelemetry::trace::TracerProvider;
|
||||
use opentelemetry::{global, KeyValue};
|
||||
use opentelemetry_otlp::WithExportConfig;
|
||||
use opentelemetry_sdk::{
|
||||
propagation::TraceContextPropagator, runtime::TokioCurrentThread, trace::Config, Resource,
|
||||
};
|
||||
use opentelemetry_semantic_conventions::resource;
|
||||
use std::io;
|
||||
use std::sync::LazyLock;
|
||||
use tracing::Span;
|
||||
use tracing_actix_web::{DefaultRootSpanBuilder, RootSpan, RootSpanBuilder, TracingLogger};
|
||||
use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer};
|
||||
use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry};
|
||||
|
||||
/// We will define a custom root span builder to capture additional fields, specific
|
||||
/// to our application, on top of the ones provided by `DefaultRootSpanBuilder` out of the box.
|
||||
pub struct CustomRootSpanBuilder;
|
||||
|
||||
impl RootSpanBuilder for CustomRootSpanBuilder {
|
||||
fn on_request_start(request: &ServiceRequest) -> Span {
|
||||
// Not sure why you'd be keen to capture this, but it's an example and we try to keep it simple
|
||||
let n_headers = request.headers().len();
|
||||
// We set `cloud_provider` to a constant value.
|
||||
//
|
||||
// `name` is not known at this point - we delegate the responsibility to populate it
|
||||
// to the `personal_hello` handler. We MUST declare the field though, otherwise
|
||||
// `span.record("caller_name", XXX)` will just be silently ignored by `tracing`.
|
||||
tracing_actix_web::root_span!(
|
||||
request,
|
||||
n_headers,
|
||||
cloud_provider = "localhost",
|
||||
caller_name = tracing::field::Empty
|
||||
)
|
||||
}
|
||||
|
||||
fn on_request_end<B: MessageBody>(span: Span, outcome: &Result<ServiceResponse<B>, Error>) {
|
||||
// Capture the standard fields when the request finishes.
|
||||
DefaultRootSpanBuilder::on_request_end(span, outcome);
|
||||
}
|
||||
}
|
||||
|
||||
async fn hello() -> &'static str {
|
||||
"Hello world!"
|
||||
}
|
||||
|
||||
async fn personal_hello(root_span: RootSpan, name: web::Path<String>) -> String {
|
||||
// Add more context to the root span of the request.
|
||||
root_span.record("caller_name", &name.as_str());
|
||||
format!("Hello {}!", name)
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
init_telemetry();
|
||||
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.wrap(TracingLogger::<CustomRootSpanBuilder>::new())
|
||||
.service(web::resource("/hello").to(hello))
|
||||
.service(web::resource("/hello/{name}").to(personal_hello))
|
||||
})
|
||||
.bind("127.0.0.1:8080")?
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
// Ensure all spans have been shipped to Jaeger.
|
||||
opentelemetry::global::shutdown_tracer_provider();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const APP_NAME: &str = "tracing-actix-web-demo";
|
||||
|
||||
static RESOURCE: LazyLock<Resource> =
|
||||
LazyLock::new(|| Resource::new(vec![KeyValue::new(resource::SERVICE_NAME, APP_NAME)]));
|
||||
|
||||
/// Init a `tracing` subscriber that prints spans to stdout as well as
|
||||
/// ships them to Jaeger.
|
||||
///
|
||||
/// Check the `opentelemetry` example for more details.
|
||||
fn init_telemetry() {
|
||||
// Start a new otlp trace pipeline.
|
||||
// Spans are exported in batch - recommended setup for a production application.
|
||||
global::set_text_map_propagator(TraceContextPropagator::new());
|
||||
let tracer = opentelemetry_otlp::new_pipeline()
|
||||
.tracing()
|
||||
.with_exporter(
|
||||
opentelemetry_otlp::new_exporter()
|
||||
.tonic()
|
||||
.with_endpoint("http://localhost:4317"),
|
||||
)
|
||||
.with_trace_config(Config::default().with_resource(RESOURCE.clone()))
|
||||
.install_batch(TokioCurrentThread)
|
||||
.expect("Failed to install OpenTelemetry tracer.")
|
||||
.tracer_builder(APP_NAME)
|
||||
.build();
|
||||
|
||||
// Filter based on level - trace, debug, info, warn, error
|
||||
// Tunable via `RUST_LOG` env variable
|
||||
let env_filter = EnvFilter::try_from_default_env().unwrap_or(EnvFilter::new("info"));
|
||||
// Create a `tracing` layer using the otlp tracer
|
||||
let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
|
||||
// Create a `tracing` layer to emit spans as structured logs to stdout
|
||||
let formatting_layer = BunyanFormattingLayer::new(APP_NAME.into(), std::io::stdout);
|
||||
// Combined them all together in a `tracing` subscriber
|
||||
let subscriber = Registry::default()
|
||||
.with(env_filter)
|
||||
.with(telemetry)
|
||||
.with(JsonStorageLayer)
|
||||
.with(formatting_layer);
|
||||
tracing::subscriber::set_global_default(subscriber)
|
||||
.expect("Failed to install `tracing` subscriber.")
|
||||
}
|
17
tracing-actix-web/examples/opentelemetry/Cargo.toml
Normal file
17
tracing-actix-web/examples/opentelemetry/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "otel"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
actix-web = "4"
|
||||
opentelemetry = "0.25"
|
||||
opentelemetry_sdk = { version = "0.25", features = ["rt-tokio-current-thread"] }
|
||||
opentelemetry-otlp = "0.25"
|
||||
opentelemetry-semantic-conventions = "0.25"
|
||||
tracing-opentelemetry = "0.26"
|
||||
tracing-subscriber = { version = "0.3", features = ["registry", "env-filter"] }
|
||||
tracing-bunyan-formatter = "0.3"
|
||||
tracing-actix-web = { path = "../..", features = ["opentelemetry_0_25"] }
|
||||
tracing = "0.1.40"
|
33
tracing-actix-web/examples/opentelemetry/README.md
Normal file
33
tracing-actix-web/examples/opentelemetry/README.md
Normal file
@ -0,0 +1,33 @@
|
||||
# OpenTelemetry integration
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To execute this example you need a running Jaeger instance.
|
||||
You can launch one using Docker:
|
||||
|
||||
```bash
|
||||
docker run -d -p6831:6831/udp -p6832:6832/udp -p16686:16686 jaegertracing/all-in-one:latest
|
||||
```
|
||||
|
||||
## Running
|
||||
|
||||
You can launch this example with
|
||||
|
||||
```bash
|
||||
cargo run
|
||||
```
|
||||
|
||||
An `actix-web` application will be listening on port `8080`.
|
||||
You can fire requests to it with:
|
||||
|
||||
```bash
|
||||
curl -v http://localhost:8080/hello
|
||||
```
|
||||
```text
|
||||
Hello world!
|
||||
```
|
||||
|
||||
## Traces
|
||||
|
||||
You can look at the exported traces in your browser by visiting [http://localhost:16686](http://localhost:16686).
|
||||
Spans will be also printed to the console in JSON format, as structured log records.
|
75
tracing-actix-web/examples/opentelemetry/src/main.rs
Normal file
75
tracing-actix-web/examples/opentelemetry/src/main.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use actix_web::{web, App, HttpServer};
|
||||
use opentelemetry::trace::TracerProvider;
|
||||
use opentelemetry::{global, KeyValue};
|
||||
use opentelemetry_otlp::WithExportConfig;
|
||||
use opentelemetry_sdk::{
|
||||
propagation::TraceContextPropagator, runtime::TokioCurrentThread, trace::Config, Resource,
|
||||
};
|
||||
use opentelemetry_semantic_conventions::resource;
|
||||
use std::io;
|
||||
use std::sync::LazyLock;
|
||||
use tracing_actix_web::TracingLogger;
|
||||
use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer};
|
||||
use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry};
|
||||
|
||||
const APP_NAME: &str = "tracing-actix-web-demo";
|
||||
|
||||
static RESOURCE: LazyLock<Resource> =
|
||||
LazyLock::new(|| Resource::new(vec![KeyValue::new(resource::SERVICE_NAME, APP_NAME)]));
|
||||
|
||||
async fn hello() -> &'static str {
|
||||
"Hello world!"
|
||||
}
|
||||
|
||||
fn init_telemetry() {
|
||||
// Start a new otlp trace pipeline.
|
||||
// Spans are exported in batch - recommended setup for a production application.
|
||||
global::set_text_map_propagator(TraceContextPropagator::new());
|
||||
let tracer = opentelemetry_otlp::new_pipeline()
|
||||
.tracing()
|
||||
.with_exporter(
|
||||
opentelemetry_otlp::new_exporter()
|
||||
.tonic()
|
||||
.with_endpoint("http://localhost:4317"),
|
||||
)
|
||||
.with_trace_config(Config::default().with_resource(RESOURCE.clone()))
|
||||
.install_batch(TokioCurrentThread)
|
||||
.expect("Failed to install OpenTelemetry tracer.")
|
||||
.tracer_builder(APP_NAME)
|
||||
.build();
|
||||
|
||||
// Filter based on level - trace, debug, info, warn, error
|
||||
// Tunable via `RUST_LOG` env variable
|
||||
let env_filter = EnvFilter::try_from_default_env().unwrap_or(EnvFilter::new("info"));
|
||||
// Create a `tracing` layer using the otlp tracer
|
||||
let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
|
||||
// Create a `tracing` layer to emit spans as structured logs to stdout
|
||||
let formatting_layer = BunyanFormattingLayer::new(APP_NAME.into(), std::io::stdout);
|
||||
// Combined them all together in a `tracing` subscriber
|
||||
let subscriber = Registry::default()
|
||||
.with(env_filter)
|
||||
.with(telemetry)
|
||||
.with(JsonStorageLayer)
|
||||
.with(formatting_layer);
|
||||
tracing::subscriber::set_global_default(subscriber)
|
||||
.expect("Failed to install `tracing` subscriber.")
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
init_telemetry();
|
||||
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.wrap(TracingLogger::default())
|
||||
.service(web::resource("/hello").to(hello))
|
||||
})
|
||||
.bind("127.0.0.1:8080")?
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
// Ensure all spans have been shipped to Jaeger.
|
||||
opentelemetry::global::shutdown_tracer_provider();
|
||||
|
||||
Ok(())
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "request-id-response-header"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
actix-web = "4"
|
||||
tracing-actix-web = { path = "../.." }
|
@ -0,0 +1,25 @@
|
||||
# Request ID in Response Header
|
||||
|
||||
This example shows how to set the `RequestId` as a response header.
|
||||
|
||||
## Running
|
||||
|
||||
You can launch this example with
|
||||
|
||||
```bash
|
||||
cargo run
|
||||
```
|
||||
|
||||
An `actix-web` application will be listening on port `8080`.
|
||||
You can fire requests to it with:
|
||||
|
||||
```bash
|
||||
curl -v http://localhost:8080/hello
|
||||
```
|
||||
```text
|
||||
...
|
||||
< HTTP/1.1 200 OK
|
||||
< content-length: 12
|
||||
< x-request-id: 1d5c5448-44d2-4051-ab59-985868875f94
|
||||
...
|
||||
```
|
@ -0,0 +1,41 @@
|
||||
use actix_web::{
|
||||
dev::Service,
|
||||
http::header::{HeaderName, HeaderValue},
|
||||
web, App, HttpMessage, HttpServer,
|
||||
};
|
||||
use std::io;
|
||||
use tracing_actix_web::{RequestId, TracingLogger};
|
||||
|
||||
async fn hello() -> &'static str {
|
||||
"Hello world!"
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
// set the request id in the `x-request-id` response header
|
||||
.wrap_fn(|req, srv| {
|
||||
let request_id = req.extensions().get::<RequestId>().copied();
|
||||
let res = srv.call(req);
|
||||
async move {
|
||||
let mut res = res.await?;
|
||||
if let Some(request_id) = request_id {
|
||||
res.headers_mut().insert(
|
||||
HeaderName::from_static("x-request-id"),
|
||||
// this unwrap never fails, since UUIDs are valid ASCII strings
|
||||
HeaderValue::from_str(&request_id.to_string()).unwrap(),
|
||||
);
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
})
|
||||
.wrap(TracingLogger::default())
|
||||
.service(web::resource("/hello").to(hello))
|
||||
})
|
||||
.bind("127.0.0.1:8080")?
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
Reference in New Issue
Block a user