mirror of
https://github.com/actix/actix-extras.git
synced 2025-02-24 03:13:03 +01:00
* Add http.route. * Align all fields with OpenTelemetry's semantic conventions. * Add span kind. * Emit event for errors. Add OTEL status code. * Create otel.status_code field as empty. * Fix errors. * Add (feature-gated) support for OpenTelemetry span propagation. * Capture the trace id as an attribute on the span. * Change message. * Log the newly-generated trace id if there is no parent context. * Define a root_span macro as a stepping stone to allow crate users to add their own fields to the root span. * Add comments. * mut is no longer necessary. * Allow users to customise generation of the root span. Split recording fields on span end from emission of log record. Make log record on error optional via feature flag. * Provide constructor + default implementation. * Explode into multiple modules. Fix various paths/private imports in root_span. * Rename module to root_span_macro. * Add a new extractor to retrieve the root span. * Document crate. * Docs! * Add section on OTEL. * Mention actix-web-opentelemetry. * Add OpenTelemetry example. * Improve readme. * Add custom root span example. Co-authored-by: LukeMathWalker <contact@palmieri.com>
94 lines
3.4 KiB
Rust
94 lines
3.4 KiB
Rust
use actix_web::dev::{ServiceRequest, ServiceResponse};
|
|
use actix_web::{web, App, Error, HttpServer};
|
|
use opentelemetry::{
|
|
global, runtime::TokioCurrentThread, sdk::propagation::TraceContextPropagator,
|
|
};
|
|
use std::io;
|
|
use tracing::Span;
|
|
use tracing_actix_web::{DefaultRootSpanBuilder, RootSpan, RootSpanBuilder, TracingLogger};
|
|
use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer};
|
|
use tracing_subscriber::layer::SubscriberExt;
|
|
use tracing_subscriber::{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>(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(())
|
|
}
|
|
|
|
/// 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() {
|
|
let app_name = "tracing-actix-web-demo";
|
|
|
|
global::set_text_map_propagator(TraceContextPropagator::new());
|
|
let tracer = opentelemetry_jaeger::new_pipeline()
|
|
.with_service_name(app_name)
|
|
.install_batch(TokioCurrentThread)
|
|
.expect("Failed to install OpenTelemetry tracer.");
|
|
|
|
let env_filter = EnvFilter::try_from_default_env().unwrap_or(EnvFilter::new("info"));
|
|
let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
|
|
let formatting_layer = BunyanFormattingLayer::new(app_name.into(), std::io::stdout);
|
|
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.")
|
|
}
|