Everything that fails is an event.

Application events with typed listeners — and every failure the framework swallowed into a response, a retry or a log line, reported as one of 27 typed events.

What you get

  • events.listen(|e: Arc<PostPublished>| …): the listener’s parameter type is the subscription.
  • Failures from the HTTP kernel, queue workers, drivers, listeners, the scheduler, webhooks, caches and more, each with its own fields.
  • A catch-all ExceptionReported { failure: Failure } for error trackers.
  • Everything is reported by default, 404s included; filter with dont_report(ErrorKind) or ignore(FailureKind).
  • Serialisable, so a failure can be forwarded over Kafka or a queue.
  • TestApp::reported_of::<F>() asserts on them in tests.

In the docs

src/bootstrap.rs
use rainier_framework::report::{HttpRequestFailed, JobAttemptFailed};

Rainier::new(".").with_events(|events| {
    events.listen(|failed: Arc<HttpRequestFailed>| async move {
        if failed.exception.is_server_error() {
            tracing::error!(route = ?failed.route, "{} {}: {}", failed.method, failed.path, failed.exception);
        }
        Ok(())
    });

    events.listen(|failed: Arc<JobAttemptFailed>| async move {
        if failed.final_attempt {
            page_on_call(&failed.job, &failed.exception).await?;
        }
        Ok(())
    });
})
src/app/providers/reporting.rs
use rainier_framework::{ExceptionReported, Failure};

// Every failure the framework swallowed into a response, a retry or a log
// line arrives here once, as a typed value — 27 kinds, each with its fields.
events.listen(|reported: Arc<ExceptionReported>| async move {
    match &reported.failure {
        Failure::HttpRequest(failed) if failed.exception.is_client_error() => {}
        Failure::JobAttempt(failed) if !failed.final_attempt => {}
        Failure::QueueMessageDiscarded(lost) => alert_lost_message(&lost.queue).await?,
        // `Failure` is #[non_exhaustive]: the catalogue grows, so the rest
        // goes somewhere on purpose rather than nowhere by accident.
        other => tracker::capture(other.kind().as_str(), other.exception(), other.meta()),
    }
    Ok(())
});

Up close

Select an image to see it full size.

The same typed failure the event bus carries, as Lookout stored it: kind, status, message and report metadata, serialisable so it can be forwarded anywhere.
A per-process rate limiter and an in-process scheduler lock are reported as Configuration failures at boot, not just logged, so they surface next to request errors.
Publishing dispatches PostPublished; its listener queues a job, and every step is a span under the event, so you can see what one event set off.
All features

Type to search 2.0.0 docs.