Runtime

Events

In-process event bus and observed events.

Events

nidus-events provides an in-process typed event bus for lightweight application events.

let bus = EventBus::<UserCreated>::new();
let subscriber = bus.subscribe();

bus.publish(UserCreated { user_id: 42 });

let events = subscriber.drain();

Subscribers receive events published after they subscribe. Dropping a subscriber handle removes it from future delivery; the bus prunes dropped handles during publish and subscriber counting.

If a subscriber list or queue mutex is poisoned by a panic, the bus logs a warning and recovers the inner state so later subscribers and publishes can continue.

Observed Events

ObservedEventBus wraps an existing EventBus and records publication context while preserving normal subscriber delivery.

#[derive(Clone)]
struct EventMetrics;

impl EventObserver<UserCreated> for EventMetrics {
    fn on_event_published(&self, context: &ObservedEventContext) {
        tracing::info!(
            event.name = context.event_name(),
            event.operation_id = context.operation_id()
        );
    }
}

let bus = EventBus::<UserCreated>::new();
let observed = bus.clone()
    .observed(EventMetrics)
    .context("request_id", "req-123");
observed.publish_named("user.created", UserCreated { user_id: 42 });

Use context attributes to propagate request IDs, tenant IDs, or job run IDs into event publication metrics and spans.

For the recommended production path, pass Observability::event_observer():

let observability = Observability::production("users-api").prometheus();
let observed = observability.observed_event_bus::<UserCreated>();
observed.publish_named("user.created", UserCreated { user_id: 42 });

Only publications through ObservedEventBus are instrumented. Plain EventBus::publish keeps its existing behavior and emits no metrics.

When observation needs slower export work, use a channel-backed observer and process contexts away from the publish call:

let (observer, receiver) = event_observer_channel();
let observed = ObservedEventBus::new(bus.clone(), observer);

observed.publish_named("user.created", UserCreated { user_id: 42 });

let context = receiver.recv()?;

The publish path only sends the ObservedEventContext; a dropped receiver does not fail event publication.