Getting Started with Rust Error Tracking
At the intersection of safety and performance, you’ll find Rust — Stack Overflow’s “most-loved programming language” for three years running. Rust provides the control needed to ensure optimal performance while offering relatively high abstractions at zero-cost.
Earlier this year, we introduced Sentry’s Rust SDK, which makes it easy for developers to:
Catch panics.
Easily log failures.
Catch errors from error-chain.
Log messages.
Record breadcrumbs.
Get useful context data, like OS and device information.
The Rust SDK provides you with as much error information as possible, as long as you ship your debug information in the executable. Below, you'll find some helpful tips for getting the most out of the SDK.
Getting Started
Add sentry
to your Cargo.toml
, and activate debug information in release builds:
[dependencies]
sentry = "0.12"
[profile.release]
debug = true
Simple so far, right? Now, capture failures and panics with this:
extern crate sentry;
use sentry::integrations::panic::register_panic_handler;
use sentry::integrations::failure::capture_error;
fn main() {
let _sentry = sentry::init("YOUR_DSN_HERE");
register_panic_handler();
if let Err(err) = your_potentially_failing_function() {
println!("error: {}", err);
capture_error(&err);
}
}
Integrations
While the Rust SDK works out of the box, integrations work to expand its functionality.
Failures
Failure errors and Fail
objects can be logged with the failure integration. This works really well if you use the failure::Error
type or if you have failure::Fail
objects that use the failure context internally to gain a backtrace.
use sentry::integrations::failure::capture_error;
let result = match function_that_might_fail() {
Ok(result) => result,
Err(err) => {
capture_error(&err);
return Err(err);
}
};
If you’d like to capture fails, but not errors, use capture_fail
.
Panics
A panic handler can be installed that will automatically dispatch all errors to Sentry that are caused by a panic. Panics are forwarded to the previously registered panic hook.
use sentry::integrations::panic::register_panic_handler;
register_panic_handler();
Logging
You can add support for automatic breadcrumb capturing from logs with env_logger
. Call this crate’s init function instead of the one from env_logger
, and pass None
as logger:
sentry::integrations::env_logger::init(None, Default::default());
This parses the default RUST_LOG
environment variable and configures both env_logger
and this crate appropriately. Want to create your own logger? No problem. Forward it accordingly:
let mut log_builder = pretty_env_logger::formatted_builder().unwrap();
log_builder.parse("info,foo=debug");
sentry::integrations::env_logger::init(Some(log_builder.build()), Default::default());
You can also add support for automatic breadcrumb capturing from logs
. The log
crate is supported in two ways:
Events can be captured as breadcrumbs for later.
Error events can be logged as events to Sentry.
By default, anything above Info
is recorded as breadcrumb, and anything above Error
is captured as error event.
Due to how log systems in Rust work, this currently requires you to slightly change your log setup. This is an example with the pretty_env_logger
crate:
let mut log_builder = pretty_env_logger::formatted_builder().unwrap();
log_builder.parse("info"); // or env::var("RUST_LOG")
let logger = log_builder.build();
let options = sentry::integrations::log::LoggerOptions {
global_filter: Some(logger.filter()),
..Default::default()
};
sentry::integrations::log::init(Some(Box::new(logger)), options);
For loggers based on env_logger
(like pretty_env_logger
), you can also use the env_logger
integration. Save yourself some time here — this integration will be much easier for you to use.
Advanced Usage
Want to receive more information about your app? The SDK provides an easy way to capture additional context information which will then attached to all captured events. This can be useful to identify authenticated users, record breadcrumbs while users are interacting with your software, or simply capture the name and version of a runtime that you’re interested in.
If some of this information is only valid during a certain operation, you can even temporarily push a Scope
and configure it. Once your operation has completed, drop the scope, and you’re left with the state before pushing the scope:
use sentry::Hub;
Hub::get_current().with_scope(
|scope| {
// configure the scope first
scope.set_transaction(Some("my_operation"));
scope.set_tag("operation", "my");
},
|| {
// now run the operation
my_operation();
}
)
But how can this work with asynchronous code, such as futures
, where multiple operations might overlap in time? No need to worry, we’ve got you covered here! Simply create a new Hub
to capture exceptions. It automatically inherits the ambient scope but can be configured separately for asynchronous tasks. And since it’s both Send
and Sync
, you can easily move it between threads.
use sentry::Hub;
use sentry::integrations::failure::event_from_fail;
let hub = Arc::new(Hub::new_from_top(Hub::current()));
// optionally configure it next
hub.configure_scope(|scope| {
scope.set_transaction(Some("my_operation"));
scope.set_tag("operation", "my");
});
// spawn a future and use the hub
let future = first_operation_async()
.and_then(second_operation_async)
.map_err(move |fail| {
hub.capture_event(event_from_fail(fail));
fail
});
tokio::spawn(future);
Looking for a bit more help? Our API docs are a great place to start. And if you have questions or feedback, post in our GitHub or issue tracker, or shout out to our support engineers. They’re here to help. And also to code. But mostly to help.