Skip to content

Instantly share code, notes, and snippets.

@timzaak
Created July 17, 2025 02:48
Show Gist options
  • Select an option

  • Save timzaak/8ca90c14a5647b0e4d0f849e169ab7c6 to your computer and use it in GitHub Desktop.

Select an option

Save timzaak/8ca90c14a5647b0e4d0f849e169ab7c6 to your computer and use it in GitHub Desktop.
Salvo + Reqwest + OpenTelemetry Example
[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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment