-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Document how to create a client with tracing #870
Comments
Ok I got this going if anyone is interested - reproducible example at https://github.com/hardbyte/rust-telemetry-example TL;DR version is use In build.rs: let mut generator = Generator::new(
GenerationSettings::new()
.with_interface(InterfaceStyle::Builder)
// Progenitor has an issue where
// an inner type MUST be set to use with_pre_hook_async
.with_inner_type(quote! { crate::ClientState })
.with_pre_hook_async(quote! {
|_, request: &mut reqwest::Request| {
// Synchronously modify the request here (e.g., add headers)
// to propagate OpenTelemetry context
crate::inject_opentelemetry_context_into_request(request);
// Return immediately since we aren't using async functionality
Box::pin(async { Ok::<_, Box<dyn std::error::Error>>(()) })
}
}),
); Injection of trace context all carried out by the otel module: use reqwest::header::{HeaderName, HeaderValue};
use reqwest::Request;
use std::str::FromStr;
use tracing::Span;
/// Injects the given OpenTelemetry Context into a reqwest::Request headers to allow propagation downstream.
pub fn inject_opentelemetry_context_into_request(request: &mut Request) {
opentelemetry::global::get_text_map_propagator(|injector| {
use tracing_opentelemetry::OpenTelemetrySpanExt;
let context = Span::current().context();
injector.inject_context(&context, &mut RequestCarrier::new(request))
});
}
// "traceparent" => https://www.w3.org/TR/trace-context/#trace-context-http-headers-format
/// Injector used via opentelemetry propagator to tell the extractor how to insert the "traceparent" header value
/// This will allow the propagator to inject opentelemetry context into a standard data structure. Will basically
/// insert a "traceparent" string value "{version}-{trace_id}-{span_id}-{trace-flags}" of the spans context into the headers.
/// Listeners can then re-hydrate the context to add additional spans to the same trace.
struct RequestCarrier<'a> {
request: &'a mut Request,
}
impl<'a> RequestCarrier<'a> {
pub fn new(request: &'a mut Request) -> Self {
RequestCarrier { request }
}
fn set_inner(&mut self, key: &str, value: String) {
let header_name = HeaderName::from_str(key).expect("Must be header name");
let header_value = HeaderValue::from_str(&value).expect("Must be a header value");
self.request.headers_mut().insert(header_name, header_value);
}
}
impl<'a> opentelemetry::propagation::Injector for RequestCarrier<'a> {
fn set(&mut self, key: &str, value: String) {
self.set_inner(key, value)
}
} In the /// State maintained by a [`Client`].
/// Currently empty but required to use the with_pre_hook_async functionality
/// with progenitor as of our pinned version https://github.com/oxidecomputer/progenitor/blob/4a3dfec3926f1f9db78eb6dc90087a1e2a1f9e45/progenitor-impl/src/method.rs#L1144-L1151
#[derive(Clone, Debug)]
pub struct ClientState {}
impl Default for ClientState {
fn default() -> Self {
ClientState {}
}
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Given the decision not to support reqwest middleware it would be beneficial to show how to build a client with tracing (or any other customization) using the pre and post hooks.
Taking tracing as an example, the reqwest-tracing middleware wraps a new
tracing
Span around each outgoing request including various OpenTelemetry defined fields and propagates trace context to the server via request headers.The text was updated successfully, but these errors were encountered: