tracing-actix 0.4.0

Allow tracing actor futures in actix
Documentation

Actix ActorFuture instrumentation for use with [tracing]

Example

use actix::prelude::*;
use actix::fut::{ready, ActorFuture};
use tracing::{span, event, Level};
use tracing_actix::ActorInstrument;
#
# // The response type returned by the actor future
# type OriginalActorResponse = ();
# // The error type returned by the actor future
# type MessageError = ();
# // This is the needed result for the DeferredWork message
# // It's a result that combine both Response and Error from the future response.
# type DeferredWorkResult = Result<OriginalActorResponse, MessageError>;
#
# struct ActorState {}
#
# impl ActorState {
#    fn update_from(&mut self, _result: ()) {}
# }
#
# struct OtherActor {}
#
# impl Actor for OtherActor {
#    type Context = Context<Self>;
# }
#
# impl Handler<OtherMessage> for OtherActor {
#    type Result = ();
#
#    fn handle(&mut self, _msg: OtherMessage, _ctx: &mut Context<Self>) -> Self::Result {
#    }
# }
#
# struct OriginalActor{
#     other_actor: Addr<OtherActor>,
#     inner_state: ActorState
# }
#
# impl Actor for OriginalActor{
#     type Context = Context<Self>;
# }
#
# #[derive(Message)]
# #[rtype(result = "Result<(), MessageError>")]
# struct DeferredWork{}
#
# #[derive(Message)]
# #[rtype(result = "()")]
# struct OtherMessage{}

/// This example is modified from the actix::fut module and intended to show how
/// the `ActorInstrument` trait may be used to integrate tracing `Span`s within
/// asynchronous message handlers.
impl Handler<DeferredWork> for OriginalActor {
    type Result = ResponseActFuture<Self, Result<OriginalActorResponse, MessageError>>;

    fn handle(
        &mut self,
        _msg: DeferredWork,
        _ctx: &mut Context<Self>,
    ) -> Self::Result {
        // this creates a `Future` representing the `.send` and subsequent `Result` from
        // `other_actor`
        let span = span!(Level::INFO, "deferred work context");
        // Addr<A>::send returns `actix::prelude::Request`, which implements Unpin, so we can wrap
        // into_actor within an ActorInstrument.
        Box::pin(
            self.other_actor
                .send(OtherMessage {})
                .into_actor(self)
                .actor_instrument(span)
                .map(|result, actor, _ctx| {
                    // Actor's state updated here
                    match result {
                        Ok(v) => {
                            event!(Level::INFO, "I'm within deferred work context");
                            actor.inner_state.update_from(v);
                            Ok(())
                        }
                        // Failed to send message to other_actor
                        Err(e) => {
                            event!(Level::ERROR, "Error from deferred work: {:?}", e);
                            Err(())
                        }
                    }
                }),
        )
    }
}
#
# #[derive(Message)]
# #[rtype(result = "Pong")]
# struct Ping;
#
# struct Pong;

/// In this example, there isn't an `actix::prelude::Request` in our `ActorFuture`.
/// Since `ActorInstrument` needs to wrap `ActorFuture + Unpin`, we can't use
/// `async {}.into_actor(self)` because `async {}` doesn't implement `Unpin`.
impl Handler<Ping> for OriginalActor {
    type Result = ResponseActFuture<Self, Pong>;

    fn handle(
        &mut self,
        _msg: Ping,
        _ctx: &mut Context<Self>,
    ) -> Self::Result {
        // `async {}` doesn't implement Unpin, so it can't be used.
        // `actix::fut::Ready` ActorFutures work fine though.
        let span = span!(Level::INFO, "ping");
        Box::pin(
            ready::<Pong>(Pong {})
                .actor_instrument(span)
                .map(|pong, _this, _ctx| {
                    // the pong event occurs in the ping span, even though this is async.
                    event!(Level::INFO, "pong");
                    pong
                }),
        )
    }
}