-
-
Notifications
You must be signed in to change notification settings - Fork 153
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
Provide a minimal example for propagating request IDs into the distributed tracing system (w/ Axum) #656
Comments
I would recommend using Otherwise, the SDK is currently only using the |
Thanks for the quick response, @Swatinem! I've since switched the initial SentryLayer tower layer to use new_from_top, thanks for that tip. However, I do not see the Is it intended that clients generate the Thanks for your time! |
Apologies for double-posting, but is this still the case? #604 (comment) I am currently initializing sentry ( Thanks again for your effort, it's very appreciated! |
Yes, you should switch that around. Sentry automatically inherits a per-thread Hub from the Hub initialized on the main thread. That won’t work if you start the thread pool before initializing the main Hub.
As you edited your comment, I believe you have since added the
As its pretty much impossible to have automatic hooks / monkey patching in Rust on the language level, yes this needs to be done manually. We use this snippet in combination with if let Some(span) = sentry::configure_scope(|scope| scope.get_span()) {
for (k, v) in span.iter_headers() {
request = request.header(k, v);
}
} |
Thanks for confirming! I can report back once I've made these changes.
This was an edit for clarity, but I did have a sentry_tracing::layer() configured beforehand. It's currently configured before the Axum router and tower layers are constructed.
Where should one configure the Sentry scope with the span details within the context of a larger [Axum] app? Within each route handler/endpoint? I have written a [hopefully] minimal, complete example of my application with some of the corrections you've mentioned (excluding configuring the scope). Could you point out where in the example the scope should be configured? Summary: // My TODO app's domain type
#[derive(Serialize, Deserialize, Clone)]
struct Todo {
id: u32,
title: String,
completed: bool,
}
// My TODO app's state for the Axum server
#[derive(Clone)]
struct AppState {
todos: Arc<Mutex<Vec<Todo>>>,
}
// An axum HTTP endpoint for querying stored TODOs
async fn get_todos(State(state): State<Arc<AppState>>) -> impl IntoResponse {
let todos = state.todos.lock().await;
Response::builder()
.status(StatusCode::OK)
.body(Body::from(serde_json::to_string(&todos).unwrap()))
.unwrap()
}
fn router(state: Arc<AppState>) -> Router {
Router::new()
.route("/", get(get_todos))
.layer(
// use svc builder to ensure correct layer ordering
ServiceBuilder::new()
.layer(sentry_tower::SentryLayer::new_from_top())
.layer(sentry_tower::SentryHttpLayer::with_transaction()),
)
.with_state(state)
}
fn main() -> Result<(), AwsLambdaHandlerError> {
// init sentry before starting any async runtime
let _guard = sentry::init("DSN".to_string(), sentry::ClientOptions::default());
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
// app state init is an async task
let state = Arc::new(AppState {
todos: Arc::new(Mutex::new(Vec::new())),
});
// init tracing subscriber
tracing::set_global_default(
Registry::default()
// other layers
.with(sentry_tracing::layer())
);
// aws lambda handler func builds new router with reused state for each request
let handler_func = move |event: Request| async move {
// sentry tower layers are initialized here
let router = router(state.clone());
let resp = router.into_service().ready().await?.call(req).await?;
Ok(resp)
};
lambda_http::run(service_fn(handler_func)).await
})
} Hopefully this is not unnecessarily long, I tried to remove anything irrelevant to the process. Thank you for your time! |
Ohhhhh, I think I completely misunderstood this from the very beginning 🙈 I was assuming that you wanted to propagate the trace headers to outgoing http requests you are doing internally. The snippet I posted was assuming Now I see that you want to attach the trace headers for the response. This should be doable in the SDK, but we haven’t done that so far. I would have to check with the rest of the team how to properly handle these cases. Usually, the "client" (Browser, or other service doing the request) creates the trace and propagates the trace id. Its not common to return it in the response. |
I've made the necessary changes so that my async application to handle this.
This should hopefully eliminate any issues with the Hub initializing outside the main thread.
Is it more commonplace for the frontend client to start a Sentry transaction and generate an ID for a particular user session, forward that ID value in the API requests to the backend, and have the backend attach that ID to any of its responses? I suppose that might explain why none of my exceptions generated by the backend are attached to any transaction -- because any Sentry transaction that my frontend creates has no way to associate by ID with any of the backend exceptions. |
Yes, the idea is that the "user initiated" action (on the frontend) starts a transaction/span, which is then propagated downward to any services that are called. I checked with the team who have confirmed that the usecase of returning a trace-id within a response is valid, but very uncommon, and none of the other SDKs have builtin support for that. |
Hello there! Is it possible for any maintainer here to help me configure distributed tracing using Axum, Tracing, and Sentry?
I am following the guide on custom instrumentation for distributed tracing here posted here. However, this guide does not show how to inject an existing request ID from an incoming HTTP request into the system for distributed tracing.
I am generating a random request ID (
x-request-id
) and propagating the header using the Tower middleware. The following two layers are added to handle this for every request made to my Axum router.As a part of my system, I also initialize a
tracing
Subscriber
with the standardsentry_tracing::layer()
(not shown, before the code below). After this is initialized in a tracingRegistry
, thesentry_tower::{SentryHttpLayer, SentryLayer}
layers are added to my Axum router (withhttp
/axum-matched-path
features enabled).Here's a snippet from my codebase (there's many LOC so it would take more time to create an MVCE):
In my Sentry dashboard, I see that new exceptions and events contain the header data from each request, including
x-request-id
. However, thetransaction
field for each event is(empty string)
, indicating that thewith_transaction()
layer variant does not work as intended (?). Additionally, each event has no custom trace ID that I can see, so it does not seem to be instrumented correctly either.I have been at this for several days and I would greatly appreciate any help or guidance you could provide.
Thanks,
Sean
The text was updated successfully, but these errors were encountered: