It's an example Salvo example interated with OpenTelemetry.
The first version is from https://github.com/salvo-rs/salvo/tree/main/examples/otel-jaeger
It's an example Salvo example interated with OpenTelemetry.
The first version is from https://github.com/salvo-rs/salvo/tree/main/examples/otel-jaeger
| [package] | |
| name = "otel_test" | |
| version = "0.1.0" | |
| edition = "2024" | |
| [[bin]] | |
| name = "example-otel-client" | |
| path = "src/client.rs" | |
| [[bin]] | |
| name = "example-otel-server1" | |
| path = "src/server1.rs" | |
| [[bin]] | |
| name = "example-otel-server2" | |
| path = "src/server2.rs" | |
| [dependencies] | |
| salvo = { version = "0.80", features = ["affix-state", "otel"] } | |
| tokio = { version = "1", features = ["macros"] } | |
| tracing = "0.1" | |
| tracing-subscriber = "0.3" | |
| opentelemetry_sdk = "0.30" | |
| opentelemetry-otlp = { version = "0.30", features = ["grpc-tonic"] } | |
| opentelemetry = { version = "0.30", features = [] } | |
| reqwest = { version = "0.12", default-features = false } | |
| opentelemetry-http = { version = "0.30" } |
| use std::error::Error; | |
| use std::str::FromStr; | |
| use opentelemetry::{ | |
| Context, KeyValue, global, | |
| trace::{FutureExt, TraceContextExt, Tracer as _}, | |
| }; | |
| use opentelemetry_http::HeaderInjector; | |
| use opentelemetry_otlp::WithExportConfig; | |
| use opentelemetry_sdk::propagation::TraceContextPropagator; | |
| use opentelemetry_sdk::Resource; | |
| use opentelemetry_sdk::trace::SdkTracerProvider; | |
| use reqwest::{Client, Method, Url}; | |
| fn init_tracer_provider() -> SdkTracerProvider { | |
| global::set_text_map_propagator(TraceContextPropagator::new()); | |
| let exporter = opentelemetry_otlp::SpanExporter::builder() | |
| .with_tonic() | |
| .with_endpoint("grpc://127.0.0.1:4317") | |
| .build() | |
| .expect("failed to create exporter"); | |
| SdkTracerProvider::builder() | |
| .with_resource(Resource::builder().with_service_name("client").build()) | |
| .with_batch_exporter(exporter) | |
| .build() | |
| } | |
| #[tokio::main] | |
| async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> { | |
| let provider = init_tracer_provider(); | |
| global::set_tracer_provider(provider.clone()); | |
| let client = Client::new(); | |
| let span = global::tracer("example-opentelemetry/client").start("request/server1"); | |
| let cx = Context::current_with_span(span); | |
| let req = { | |
| let mut req = reqwest::Request::new( | |
| Method::GET, | |
| Url::from_str("http://localhost:5800/api1").unwrap(), | |
| ); | |
| global::get_text_map_propagator(|propagator| { | |
| propagator.inject_context(&cx, &mut HeaderInjector(req.headers_mut())); | |
| println!("{:?}", req.headers_mut()); | |
| }); | |
| *req.body_mut() = Some("client\n".into()); | |
| req | |
| }; | |
| async move { | |
| let cx = Context::current(); | |
| let span = cx.span(); | |
| span.add_event("Send request to server1".to_string(), vec![]); | |
| let resp = client.execute(req).await.unwrap(); | |
| span.add_event( | |
| "Got response from server1!".to_string(), | |
| vec![KeyValue::new("status", resp.status().to_string())], | |
| ); | |
| println!("{}", resp.text().await.unwrap()); | |
| } | |
| .with_context(cx) | |
| .await; | |
| provider.shutdown()?; | |
| Ok(()) | |
| } |
| use std::str::FromStr; | |
| use std::sync::Arc; | |
| use opentelemetry::trace::{FutureExt, TraceContextExt, Tracer as _, TracerProvider}; | |
| use opentelemetry::{KeyValue, global}; | |
| use opentelemetry_http::HeaderInjector; | |
| use opentelemetry_sdk::Resource; | |
| use opentelemetry_sdk::propagation::TraceContextPropagator; | |
| use opentelemetry_sdk::trace::{SdkTracerProvider}; | |
| use reqwest::{Client, Method, Url}; | |
| use salvo::otel::{Metrics, Tracing}; | |
| use opentelemetry_otlp::WithExportConfig; | |
| use salvo::prelude::*; | |
| fn init_tracer_provider() -> SdkTracerProvider { | |
| global::set_text_map_propagator(TraceContextPropagator::new()); | |
| let exporter = opentelemetry_otlp::SpanExporter::builder() | |
| .with_tonic() | |
| .with_endpoint("grpc://127.0.0.1:4317") | |
| .build() | |
| .expect("failed to create exporter"); | |
| SdkTracerProvider::builder() | |
| .with_batch_exporter(exporter) | |
| .with_resource(Resource::builder().with_service_name("server1").build()) | |
| .build() | |
| } | |
| #[handler] | |
| async fn index(req: &mut Request, depot: &mut Depot) -> String { | |
| // let tracer = depot.obtain::<Arc<Tracer>>().unwrap(); | |
| // let span = tracer | |
| // .span_builder("request/server2") | |
| // .with_kind(SpanKind::Client) | |
| // .start(&**tracer); | |
| // let cx = opentelemetry::Context::current_with_span(span); | |
| let client = Client::new(); | |
| let cx = opentelemetry::Context::current(); | |
| let body = std::str::from_utf8(req.payload().await.unwrap()).unwrap(); | |
| let req = { | |
| let mut req = reqwest::Request::new( | |
| Method::GET, | |
| Url::from_str("http://localhost:5801/api2").unwrap(), | |
| ); | |
| global::get_text_map_propagator(|propagator| { | |
| propagator.inject_context(&cx, &mut HeaderInjector(req.headers_mut())) | |
| }); | |
| *req.body_mut() = Some(format!("{body} server1\n").into()); | |
| req | |
| }; | |
| let fut = async move { | |
| let cx = opentelemetry::Context::current(); | |
| let span = cx.span(); | |
| span.add_event("Send request to server2".to_string(), vec![]); | |
| let resp = client.execute(req).await.unwrap(); | |
| span.add_event( | |
| "Got response from server2!".to_string(), | |
| vec![KeyValue::new("status", resp.status().to_string())], | |
| ); | |
| resp | |
| } | |
| .with_context(cx); | |
| fut.await.text().await.unwrap() | |
| } | |
| #[tokio::main] | |
| async fn main() { | |
| tracing_subscriber::fmt().init(); | |
| let tracer = init_tracer_provider().tracer("app"); | |
| let router = Router::new() | |
| .hoop(affix_state::inject(Arc::new(tracer.clone()))) | |
| .hoop(Metrics::new()) | |
| .hoop(Tracing::new(tracer)) | |
| .push(Router::with_path("api1").get(index)); | |
| let acceptor = TcpListener::new("0.0.0.0:5800").bind().await; | |
| Server::new(acceptor).serve(router).await; | |
| } |
| use opentelemetry::global; | |
| use opentelemetry::trace::TracerProvider; | |
| use opentelemetry_sdk::trace::SdkTracerProvider; | |
| use opentelemetry_sdk::{Resource, propagation::TraceContextPropagator}; | |
| use salvo::otel::{Metrics, Tracing}; | |
| use opentelemetry_otlp::WithExportConfig; | |
| use salvo::prelude::*; | |
| fn init_tracer_provider() -> SdkTracerProvider { | |
| global::set_text_map_propagator(TraceContextPropagator::new()); | |
| let exporter = opentelemetry_otlp::SpanExporter::builder() | |
| .with_tonic() | |
| .with_endpoint("grpc://127.0.0.1:4317") | |
| .build() | |
| .expect("failed to create exporter"); | |
| SdkTracerProvider::builder() | |
| .with_resource(Resource::builder().with_service_name("server2").build()) | |
| .with_batch_exporter(exporter) | |
| .build() | |
| } | |
| #[handler] | |
| async fn index(req: &mut Request) -> String { | |
| format!( | |
| "Body: {}", | |
| std::str::from_utf8(req.payload().await.unwrap()).unwrap() | |
| ) | |
| } | |
| #[tokio::main] | |
| async fn main() { | |
| tracing_subscriber::fmt().init(); | |
| let tracer = init_tracer_provider().tracer("app"); | |
| let router = Router::new() | |
| .hoop(Metrics::new()) | |
| .hoop(Tracing::new(tracer)) | |
| .push(Router::with_path("api2").get(index)); | |
| let acceptor = TcpListener::new("0.0.0.0:5801").bind().await; | |
| Server::new(acceptor).serve(router).await; | |
| } |