### Initialize Sentry and register tracing layer Source: https://docs.sentry.io/platforms/rust/guides/tracing Initializes the Sentry client and registers the Sentry tracing layer. The client must be initialized before the async runtime is started; macros like #[tokio::main] are not supported. The example captures a root span and child span, and adds a breadcrumb and error event. ```rust use tracing_subscriber::prelude::*; fn main() { // Initialize Sentry let _guard = sentry::init( sentry::ClientOptions::new() .dsn("https://@o.ingest.sentry.io/") .maybe_release(sentry::release_name!()) # ___PRODUCT_OPTION_START___ performance // Capture all traces and spans. Set to a lower value in production .traces_sample_rate(1.0) # ___PRODUCT_OPTION_END___ performance ); // Register the Sentry tracing layer tracing_subscriber::registry() .with(tracing_subscriber::fmt::layer()) .with(sentry::integrations::tracing::layer()) .init(); tokio::runtime::Builder::new_multi_thread() .enable_all() .build()? .block_on(async { // Futures should to be bound to a Hub // Learn more at https://docs.rs/sentry-core/latest/sentry_core/#parallelism-concurrency-and-async fail().bind_hub(sentry::Hub::current()).await; }); } # ___PRODUCT_OPTION_START___ performance #[tracing::instrument] // Captures a root span (transaction) around this function execution # ___PRODUCT_OPTION_END___ performance async fn fail() { tracing::debug!("Doing work"); // Adds a breadrcumb # ___PRODUCT_OPTION_START___ performance let _span = tracing::info_span("Child span").entered(); // Captures a child span # ___PRODUCT_OPTION_END___ performance tracing::error!("Everything is on fire!"); } ``` -------------------------------- ### Create a transaction and span with sentry::start_transaction Source: https://docs.sentry.io/platforms/rust/guides/actix-web/tracing/instrumentation/custom-instrumentation Shows how to start a transaction with a name and operation, add a child span, and finish both. Only finished spans are sent with the transaction; finishing the transaction sends it to Sentry. ```rust // Transaction can be started by providing the name and the operation let tx_ctx = sentry::TransactionContext::new( "transaction name", "transaction operation", ); let transaction = sentry::start_transaction(tx_ctx); // Transactions can have child spans, and those spans can have child spans as well. let span = transaction.start_child("span operation", "span description"); // ... // Perform your operations // ... span.finish(); // Remember that only finished spans will be sent with the transaction transaction.finish(); // Finishing the transaction will send it to Sentry ``` -------------------------------- ### Configure Sentry with axum and tower middleware in Rust Source: https://docs.sentry.io/platforms/rust/guides/axum Initializes the Sentry client before the async runtime, sets up an axum router with Sentry layers for error correlation and transaction tracing, and runs a server that panics for testing. Macros like #[tokio::main] are not supported; the client must be initialized before the runtime starts. ```rust use axum::{body::Body, http::Request, routing::get, Router}; use sentry::integrations::tower::{NewSentryLayer, SentryHttpLayer}; use std::io; use tower::ServiceBuilder; async fn failing() -> () { panic!("Everything is on fire!") } fn main() -> io::Result<()> { let _guard = sentry::init( sentry::ClientOptions::new() .dsn("https://@o.ingest.sentry.io/") .maybe_release(sentry::release_name!()) # ___PRODUCT_OPTION_START___ performance // Capture all traces and spans. Set to a lower value in production .traces_sample_rate(1.0) # ___PRODUCT_OPTION_END___ performance // Capture user IPs and potentially sensitive headers when using HTTP server integrations // see https://docs.sentry.io/platforms/rust/data-management/data-collected for more info .send_default_pii(true), ); let app = Router::new().route("/", get(failing)).layer( ServiceBuilder::new() .layer(NewSentryLayer::>::new_from_top()) // Bind a new Hub per request, to ensure correct error <> request correlation # ___PRODUCT_OPTION_START___ performance .layer(SentryHttpLayer::new().enable_transaction()), // Start a transaction (Sentry root span) for each request // If you're binding the layers directly on the `Router`, bind them in the opposite order, otherwise you might run into a memory leak # ___PRODUCT_OPTION_END___ performance ); tokio::runtime::Builder::new_multi_thread() .enable_all() .build()? .block_on(async { let listener = tokio::net::TcpListener::bind("127.0.0.1:3001") .await .unwrap(); axum::serve(listener, app.into_make_service()) .await .unwrap(); }); Ok(()) } ``` -------------------------------- ### Set error sample rate with sample_rate builder Source: https://docs.sentry.io/platforms/rust/configuration/sampling Call the `sample_rate` builder on `ClientOptions` with a number between 0 and 1 to set a static rate for error events. The example sets the rate to 1.0 (100% of errors sent). The default is 1, meaning all errors are sent. Changing the rate requires re-deployment. ```rust let _guard = sentry::init(sentry::ClientOptions::new().sample_rate(1.0)); ``` -------------------------------- ### Add data attributes to transactions and spans at creation Source: https://docs.sentry.io/platforms/rust/guides/actix-web/tracing/instrumentation/custom-instrumentation Set data attributes when starting a transaction or span. This example creates a transaction and a child span, then sets data attributes on each using set_data. ```rust use serde_json::json; // Create a transaction and assign data attributes... let tx_ctx = sentry::TransactionContext::new("Example Transaction", "http.server"); let transaction = sentry::start_transaction(tx_ctx); transaction.set_data("attribute_1", "hello".into()); transaction.set_data("attribute_2", 42.into()); // ... or create a span and assign data attributes let span = transaction.start_child("http.client", "Example span"); span.set_data("is_ok", true.into()); span.set_data("other_data", json!({ "an": "object" })); ``` -------------------------------- ### Initialize Sentry with ClientOptions builder and release_name macro Source: https://docs.sentry.io/platforms/rust/guides/axum/configuration/options Shows how to initialize the Sentry SDK with `sentry::init()` using a `ClientOptions` value built with builder methods. It sets the DSN, uses the `release_name!` macro to automatically generate the release, and configures options like `max_breadcrumbs`, `send_default_pii`, and `debug`. Note the comment that `debug(true)` should only be used during development. ```rust let _guard = sentry::init( sentry::ClientOptions::new() .dsn("https://@o.ingest.sentry.io/") .maybe_release(sentry::release_name!()) .max_breadcrumbs(50) // Capture user IPs and potentially sensitive headers when using HTTP server integrations // see https://docs.sentry.io/platforms/rust/data-management/data-collected for more info .send_default_pii(true) .debug(true), // <- this should only be used during development ); ``` -------------------------------- ### Initialize Sentry SDK with release and session tracking Source: https://docs.sentry.io/platforms/rust/configuration/releases Activate the `release-health` feature flag (enabled by default) and initialize the SDK with `sentry::ClientOptions`. Use `maybe_release` with the `sentry::release_name!()` macro to infer the release from cargo metadata, or set it manually with `.release("my-project-name@2.3.12")`. Enable automatic session tracking with `auto_session_tracking(true)` and set the session mode to `sentry::SessionMode::Application` for user-attended programs or `sentry::SessionMode::Request` for servers. ```rust use sentry; let _guard = sentry::init( sentry::ClientOptions::new() .dsn("https://@o.ingest.sentry.io/") // The `sentry::release_name` macro automatically infers the release from the cargo metadata, and is the preferred way to set the release .maybe_release(sentry::release_name!()) // OR, manually: // .release("my-project-name@2.3.12") // Enables automatic session tracking, according to the configured `session_mode`. // If the value of this option is set `false`, sessions need to be manually managed using `sentry::start_session` and `sentry::stop_session`. .auto_session_tracking(true) // Sets the automatic session tracking mode. // Possible values are `sentry::SessionMode::Application` and `sentry::SessionMode::Request`. // `Application` should be used for user-attended programs, which typically have a single long running session that span the application's lifetime. // `Request` should be used for servers, where each incoming request shall be considered as its own session. .session_mode(sentry::SessionMode::Application), ); ``` -------------------------------- ### Add data attributes to an existing span Source: https://docs.sentry.io/platforms/rust/guides/actix-web/tracing/instrumentation/custom-instrumentation Retrieve the current span from the scope and set data attributes on it. This example uses Hub::current().configure_scope to get the span and then sets a data attribute. ```rust let span = Hub::current().configure_scope(|scope| scope.get_span()); if let Some(span) = span { span.set_data("hello", "world".into()); } ``` -------------------------------- ### Set up Sentry tracing layer with #[tracing::instrument] Source: https://docs.sentry.io/platforms/rust/guides/actix-web/tracing/instrumentation/automatic-instrumentation Sets up the Sentry tracing layer with tracing_subscriber and initializes the Sentry SDK with a 100% traces sample rate and default PII enabled. Functions annotated with #[tracing::instrument] automatically create spans that are tracked as transactions or child spans. ```rust fn main() { tracing_subscriber::Registry::default() .with(sentry::integrations::tracing::layer()) .init(); let _guard = sentry::init( sentry::ClientOptions::new() .maybe_release(sentry::release_name!()) .traces_sample_rate(1.0) // Capture user IPs and potentially sensitive headers when using HTTP server integrations // see https://docs.sentry.io/platforms/rust/data-management/data-collected for more info .send_default_pii(true), ); main_span1(); } #[tracing::instrument] fn main_span1() { thread::sleep(Duration::from_millis(50)); main_span2() } #[tracing::instrument] fn main_span2() { thread::sleep(Duration::from_millis(200)); } ``` -------------------------------- ### Build release binary and extract debug info (Linux) Source: https://docs.sentry.io/platforms/rust/guides/axum/source-context Build the release binary, extract debug information into a separate file, and strip it from the binary. ```bash cargo build --release objcopy --only-keep-debug target/release/app{,.d} objcopy --strip-debug --strip-unneeded target/release/app objcopy --add-gnu-debuglink target/release/app{.d,} ``` -------------------------------- ### Hash sensitive data before sending as a tag (JavaScript example) Source: https://docs.sentry.io/platforms/rust/data-management/sensitive-data Instead of sending confidential information in plaintext, hash it and set it as a tag. This example is from JavaScript as the Rust SDK does not support this functionality directly. ```javascript Sentry.setTag("birthday", checksumOrHash("08/12/1990")); ``` -------------------------------- ### Send internal user identifier instead of email (JavaScript example) Source: https://docs.sentry.io/platforms/rust/data-management/sensitive-data If emails are considered confidential, send an internal identifier or username instead. This example is from JavaScript as the Rust SDK does not support this functionality directly. ```javascript Sentry.setUser({ id: user.id }); // or Sentry.setUser({ username: user.username }); ``` -------------------------------- ### Verify Sentry setup by causing a panic in Rust Source: https://docs.sentry.io/platforms/rust Use this minimal Rust program to verify that Sentry captures a panic. It initializes the SDK with a DSN, sets the release name, and enables sending default PII (user IPs and potentially sensitive headers) for HTTP server integrations. Running it triggers a panic that Sentry will capture. ```rust fn main() { let _guard = sentry::init( sentry::ClientOptions::new() .dsn("https://@o.ingest.sentry.io/") .maybe_release(sentry::release_name!()) // Capture user IPs and potentially sensitive headers when using HTTP server integrations // see https://docs.sentry.io/platforms/rust/data-management/data-collected for more info .send_default_pii(true), ); // Sentry will capture this panic!("Everything is on fire!"); } ``` -------------------------------- ### Retrieve and set the current span with Scope::get_span and Scope::set_span Source: https://docs.sentry.io/platforms/rust/guides/actix-web/tracing/instrumentation/custom-instrumentation Use `Scope::get_span` to retrieve the currently running span or transaction, returning `None` if none exists. If a parent span exists, start a child span; otherwise, start a new transaction. Then set the resulting span as the current span with `Scope::set_span`. ```rust // Retrieve the currently running span let parent_span = sentry::configure_scope(|scope| scope.get_span()); let span: sentry::TransactionOrSpan = match &parent_span { Some(parent) => parent.start_child("subtask", "description").into(), None => { let ctx = sentry::TransactionContext::new("task", "op"); sentry::start_transaction(ctx).into() } }; // Set the currently running span sentry::configure_scope(|scope| scope.set_span(Some(span))); ``` -------------------------------- ### Capture a standard error in Rust Source: https://docs.sentry.io/platforms/rust/guides/actix-web/usage Captures any std::error::Error type using sentry::capture_error. The example matches on a Result, captures the error, and returns the error. ```rust let result = match function_returns_error() { Ok(result) => result, Err(err) => { sentry::capture_error(&err); return Err(err); } }; ``` -------------------------------- ### Propagate trace headers with reqwest Source: https://docs.sentry.io/platforms/rust/guides/actix-web/tracing/trace-propagation/custom-instrumentation Use `span.iter_headers()` to get trace headers and attach them to an outgoing HTTP request built with the `reqwest` crate. This is recommended when communication happens over HTTP. ```rust let client = reqwest::Client::new(); let mut request = client.get("downstream-url"); if let Some(span) = sentry::configure_scope(|scope| scope.get_span()) { for (k, v) in span.iter_headers() { request = request.header(k, v); } } let response = request.send().await?; ``` -------------------------------- ### Configure Sentry client and Actix Web middleware Source: https://docs.sentry.io/platforms/rust/guides/actix-web Initializes the Sentry client with a DSN, enables tracing and PII capture, and wraps the Actix Web app with Sentry middleware. The middleware must be the last wrap call to ensure correct request association. Macros like #[tokio::main] are not supported; the client must be initialized before the async runtime starts. ```rust use std::io; use actix_web::{get, App, Error, HttpRequest, HttpServer}; #[get("/")] async fn failing(_req: HttpRequest) -> Result { Err(io::Error::new(io::ErrorKind::Other, "An error happens here").into()) } fn main() -> io::Result<()> { let _guard = sentry::init( sentry::ClientOptions::new() .dsn("https://@o.ingest.sentry.io/") .maybe_release(sentry::release_name!()) // Capture all traces and spans. Set to a lower value in production .traces_sample_rate(1.0) // Capture user IPs and potentially sensitive headers when using HTTP server integrations // see https://docs.sentry.io/platforms/rust/data-management/data-collected for more info .send_default_pii(true) // Capture all HTTP request bodies, regardless of size .max_request_body_size(sentry::MaxRequestBodySize::Always), ); actix_web::rt::System::new().block_on(async { HttpServer::new(|| { App::new() .wrap( sentry::integrations::actix::Sentry::builder() .capture_server_errors(true) // Capture server errors .start_transaction(true) // Start a transaction (Sentry root span) for each request .finish(), ) .service(failing) }) .bind("127.0.0.1:3001")? .run() .await })?; Ok(()) } ``` -------------------------------- ### Wrong async main with #[tokio::main] Source: https://docs.sentry.io/platforms/rust Incorrect pattern: using #[tokio::main] starts the async runtime before Sentry is initialized, so spawned threads won't inherit the Hub. ```rust // WRONG #[tokio::main] async fn main() { let _guard = sentry::init( sentry::ClientOptions::new() .dsn("https://@o.ingest.sentry.io/") .maybe_release(sentry::release_name!()) // Capture user IPs and potentially sensitive headers when using HTTP server integrations // see https://docs.sentry.io/platforms/rust/data-management/data-collected for more info .send_default_pii(true), ); // implementation of main } ``` -------------------------------- ### Linux: Enable debug info and extract with objcopy Source: https://docs.sentry.io/platforms/rust/guides/actix-web/source-context Enable full debug information for release builds on Linux, then build the release binary and use objcopy to extract debug information, strip it from the binary, and add a GNU debug link. ```toml [profile.release] debug = true ``` ```bash cargo build --release objcopy --only-keep-debug target/release/app{,.d} objcopy --strip-debug --strip-unneeded target/release/app objcopy --add-gnu-debuglink target/release/app{.d,} ``` -------------------------------- ### Define a tag with sentry::configure_scope Source: https://docs.sentry.io/platforms/rust/enriching-events/tags Use sentry::configure_scope to set a tag on the isolation scope, binding it to all future events within that scope. The example sets the tag 'page.locale' to 'de-at'. ```rust sentry::configure_scope(|scope| { scope.set_tag("page.locale", "de-at"); }); ```