|
| 1 | +<p align="center"> |
| 2 | + <a href="https://sentry.io" target="_blank" align="center"> |
| 3 | + <img src="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" width="280"> |
| 4 | + </a> |
| 5 | +</p> |
| 6 | + |
| 7 | +# Sentry Rust SDK: sentry-tower |
| 8 | + |
| 9 | +Adds support for automatic hub binding for each request received by the Tower server (or client, |
| 10 | +though usefulness is limited in this case). |
| 11 | + |
| 12 | +This allows breadcrumbs collected during the request handling to land in a specific hub, and |
| 13 | +avoid having them mixed across requests should a new hub be bound at each request. |
| 14 | + |
| 15 | +## Examples |
| 16 | + |
| 17 | +```rust |
| 18 | +use sentry_tower::NewSentryLayer; |
| 19 | + |
| 20 | +// Compose a Tower service where each request gets its own Sentry hub |
| 21 | +let service = ServiceBuilder::new() |
| 22 | + .layer(NewSentryLayer::<Request>::new_from_top()) |
| 23 | + .timeout(Duration::from_secs(30)) |
| 24 | + .service(tower::service_fn(|req: Request| format!("hello {}", req))); |
| 25 | +``` |
| 26 | + |
| 27 | +More customization can be achieved through the `new` function, such as passing a [`Hub`] |
| 28 | +directly. |
| 29 | + |
| 30 | +```rust |
| 31 | +use sentry::Hub; |
| 32 | +use sentry_tower::SentryLayer; |
| 33 | + |
| 34 | +// Create a hub dedicated to web requests |
| 35 | +let hub = Arc::new(Hub::with(|hub| Hub::new_from_top(hub))); |
| 36 | + |
| 37 | +// Compose a Tower service |
| 38 | +let service = ServiceBuilder::new() |
| 39 | + .layer(SentryLayer::<_, _, Request>::new(hub)) |
| 40 | + .timeout(Duration::from_secs(30)) |
| 41 | + .service(tower::service_fn(|req: Request| format!("hello {}", req))); |
| 42 | +``` |
| 43 | + |
| 44 | +The layer can also accept a closure to return a hub depending on the incoming request. |
| 45 | + |
| 46 | +```rust |
| 47 | +use sentry::Hub; |
| 48 | +use sentry_tower::SentryLayer; |
| 49 | + |
| 50 | +// Compose a Tower service |
| 51 | +let hello = Arc::new(Hub::with(|hub| Hub::new_from_top(hub))); |
| 52 | +let other = Arc::new(Hub::with(|hub| Hub::new_from_top(hub))); |
| 53 | + |
| 54 | +let service = ServiceBuilder::new() |
| 55 | + .layer(SentryLayer::new(|req: &Request| match req.as_str() { |
| 56 | + "hello" => hello.clone(), |
| 57 | + _ => other.clone(), |
| 58 | + })) |
| 59 | + .timeout(Duration::from_secs(30)) |
| 60 | + .service(tower::service_fn(|req: Request| format!("{} world", req))); |
| 61 | +``` |
| 62 | + |
| 63 | +When using Tonic, the layer can be used directly by the Tonic stack: |
| 64 | + |
| 65 | +```rust |
| 66 | +use sentry_tower::NewSentryLayer; |
| 67 | +use hello_world::{*, greeter_server::*}; |
| 68 | + |
| 69 | +struct GreeterService; |
| 70 | + |
| 71 | +#[tonic::async_trait] |
| 72 | +impl Greeter for GreeterService { |
| 73 | + async fn say_hello(&self, req: Request<HelloRequest>) -> Result<Response<HelloReply>, Status> { |
| 74 | + let HelloRequest { name } = req.into_inner(); |
| 75 | + if name == "world" { |
| 76 | + capture_anyhow(&anyhow!("Trying to greet a planet")); |
| 77 | + return Err(Status::invalid_argument("Cannot greet a planet")); |
| 78 | + } |
| 79 | + Ok(Response::new(HelloReply { message: format!("Hello {}", name) })) |
| 80 | + } |
| 81 | +} |
| 82 | + |
| 83 | +Server::builder() |
| 84 | + .layer(NewSentryLayer::new_from_top()) |
| 85 | + .add_service(GreeterServer::new(GreeterService)) |
| 86 | + .serve("127.0.0.1:50051".parse().unwrap()) |
| 87 | + .await?; |
| 88 | +``` |
| 89 | + |
| 90 | +## Resources |
| 91 | + |
| 92 | +License: Apache-2.0 |
| 93 | + |
| 94 | +- [Discord](https://discord.gg/ez5KZN7) server for project discussions. |
| 95 | +- Follow [@getsentry](https://twitter.com/getsentry) on Twitter for updates |
0 commit comments