|
| 1 | +use axum::{ |
| 2 | + body::Bytes, |
| 3 | + extract::MatchedPath, |
| 4 | + http::{HeaderMap, Request}, |
| 5 | + response::{Html, Response}, |
| 6 | + routing::get, |
| 7 | + Router, |
| 8 | +}; |
| 9 | +use opentelemetry::trace::TracerProvider as _; |
| 10 | +use std::time::Duration; |
| 11 | +use tower_http::{classify::ServerErrorsFailureClass, trace::TraceLayer}; |
| 12 | +use tracing::{info_span, Span}; |
| 13 | +use tracing_subscriber::layer::SubscriberExt; |
| 14 | +#[tokio::main] |
| 15 | +async fn main() { |
| 16 | + let trace_provider = opentelemetry_sdk::trace::Builder::default() |
| 17 | + .with_simple_exporter(opentelemetry_stdout::SpanExporter::default()) |
| 18 | + .build(); |
| 19 | + let tracer = trace_provider.tracer("Axum Example"); |
| 20 | + let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); |
| 21 | + tracing_subscriber::registry().with(telemetry); |
| 22 | + |
| 23 | + // build our application with a route |
| 24 | + let app = Router::new() |
| 25 | + .route("/", get(handler)) |
| 26 | + // `TraceLayer` is provided by tower-http so you have to add that as a dependency. |
| 27 | + // It provides good defaults but is also very customizable. |
| 28 | + // |
| 29 | + // See https://docs.rs/tower-http/0.1.1/tower_http/trace/index.html for more details. |
| 30 | + // |
| 31 | + // If you want to customize the behavior using closures here is how. |
| 32 | + .layer( |
| 33 | + TraceLayer::new_for_http() |
| 34 | + .make_span_with(|request: &Request<_>| { |
| 35 | + // Log the matched route's path (with placeholders not filled in). |
| 36 | + // Use request.uri() or OriginalUri if you want the real path. |
| 37 | + let matched_path = request |
| 38 | + .extensions() |
| 39 | + .get::<MatchedPath>() |
| 40 | + .map(MatchedPath::as_str); |
| 41 | + |
| 42 | + info_span!( |
| 43 | + "http_request", |
| 44 | + method = ?request.method(), |
| 45 | + matched_path, |
| 46 | + some_other_field = tracing::field::Empty, |
| 47 | + ) |
| 48 | + }) |
| 49 | + .on_request(|_request: &Request<_>, _span: &Span| { |
| 50 | + // You can use `_span.record("some_other_field", value)` in one of these |
| 51 | + // closures to attach a value to the initially empty field in the info_span |
| 52 | + // created above. |
| 53 | + }) |
| 54 | + .on_response(|_response: &Response, _latency: Duration, _span: &Span| { |
| 55 | + // ... |
| 56 | + }) |
| 57 | + .on_body_chunk(|_chunk: &Bytes, _latency: Duration, _span: &Span| { |
| 58 | + // ... |
| 59 | + }) |
| 60 | + .on_eos( |
| 61 | + |_trailers: Option<&HeaderMap>, _stream_duration: Duration, _span: &Span| { |
| 62 | + // ... |
| 63 | + }, |
| 64 | + ) |
| 65 | + .on_failure( |
| 66 | + |_error: ServerErrorsFailureClass, _latency: Duration, _span: &Span| { |
| 67 | + // ... |
| 68 | + }, |
| 69 | + ), |
| 70 | + ); |
| 71 | + |
| 72 | + // Run it |
| 73 | + axum::Server::bind(&"127.0.0.1:3000".parse().unwrap()) |
| 74 | + .serve(app.into_make_service()) |
| 75 | + .await |
| 76 | + .unwrap(); |
| 77 | +} |
| 78 | + |
| 79 | +async fn handler() -> Html<&'static str> { |
| 80 | + Html("<h1>Hello, World!</h1>") |
| 81 | +} |
0 commit comments