Reputation: 21542
For Actors that can be expressed fairly concisely, it's frustrating to have to add in blocks ({...}
) just so I can add a log command. I would like to log my internal state before the message is handled and then after the message is handled - is this possible?
def receive = {
// I want to log here instead and remove the non-critical logs from below
// e.g. log.debug(s"Received $message")
// log.debug(s"Internal state is $subscriptions")
case RegisterChannel(name, owner) => {
getChannel(name) match {
case Some(deadChannel: DeadChannel) => {
subscriptions += (RealChannel(name, Server(owner)) -> subscriptions(deadChannel))
subscriptions -= deadChannel
context.watch(owner)
log.debug(s"Replaced $deadChannel with a real channel $channels")
}
case Some(realChannel: RealChannel) =>
log.error(s"Refusing to use RegisterChannel($name, $owner) due to $realChannel")
case None => {
subscriptions += (RealChannel(name, Server(owner)) -> Vector())
context.watch(owner)
log.debug(s"Registered a new channel $channels")
}
}
}
case Terminated(dead) => {
getRole(dead) match {
case Some(client: Client) => // Remove subscriptions
log.debug(s"Received Client Terminated($dead) $client")
subscriptionsFor(client).foreach { subscription =>
subscriptions += (subscription._1 -> subscription._2.filterNot(c => c == client))
}
case Some(server: Server) => { // Remove any channels
log.debug(s"Received Server Terminated($dead) $server")
channelsBy(server).foreach { realChannel =>
subscriptions += (DeadChannel(realChannel.name) -> subscriptions(realChannel))
subscriptions -= realChannel
}
}
case None =>
log.debug(s"Received Terminated($dead) but no channel is registered")
}
}
// I want to log here as well, to see what effect the message had
// e.g. log.debug(s"Finished $message")
// log.debug(s"Internal state is now $subscriptions")
}
I'm not sure if this is an Akka-specific or Scala pattern-matching specific question, so I tagged both
EDIT: After trying @aepurniet 's answer, I have no idea how to solve the compiler error. receive needs to return PartialFunction[Any,Unit]
, but when match is not the only item in the => {...}
it seems to be returning Any=>AnyRef
// Compiler error because msg=>{...} is not proper type
def receive = msg => {
log.info(s"some log")
msg match {
case RegisterChannel(name, owner) => {
getChannel(name) match {
<snip>
Upvotes: 3
Views: 200
Reputation: 5974
There is akka.event.LoggingReceive that you can use like this:
def receive = LoggingReceive {
case ...
}
Then you set akka.actor.debug.receive
to on
and this will log (to DEBUG) all message that were received and whether they were handled or not.
See Tracing Actor Invocations section in the official documentation of Akka.
For the additional state logging, you can do something similar to LoggingReceive
def withStateLogging(handler: Receive): Receive = {
case msg if handler.isDefinedAt(msg) ⇒
log.debug("before actual receive")
log.debug(s"received: $msg")
log.debug(s"state: $state")
handler(msg)
log.debug("after actual receive")
log.debug(s"finished: $msg")
log.debug(s"state: $state")
}
def receive = withStateLogging {
case ...
}
Upvotes: 3
Reputation: 3058
Compiler complains because Actor#receive return type is Receive which is actually defined as
type Receive = PartialFunction[Any, Unit]
Here is nice example how your problem can be solved with stackable traits: how to add logging function in sending and receiving action in akka
It is a little tricky and overrides default behavior of PartialFunction.
Upvotes: 1
Reputation: 1727
received = { case ... }
is actually shorthand for received = msg => msg match { case ... }
. you can rewrite that receive = msg => { log.info(..); msg match { case ... } }
you may have to additionally specify types.
Upvotes: 5