+act #15163 allows to suppress certain messages from being dead-letter-ed

Conflicts:
	akka-actor/src/main/scala/akka/actor/ActorRef.scala
This commit is contained in:
Konrad 'ktoso' Malawski 2014-11-27 13:12:48 +01:00
parent 3cf00cecb1
commit 73cd1d7375
6 changed files with 181 additions and 2 deletions

View file

@ -0,0 +1,80 @@
/**
* Copyright (C) 2014 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor
import akka.event.Logging
import akka.testkit.AkkaSpec
import akka.testkit.ImplicitSender
import akka.testkit.TestActors
import akka.testkit.TestProbe
import scala.concurrent.duration._
object DeadLetterSupressionSpec {
case object NormalMsg
case object SuppressedMsg extends DeadLetterSuppression
}
class DeadLetterSupressionSpec extends AkkaSpec with ImplicitSender {
import DeadLetterSupressionSpec._
val deadActor = system.actorOf(TestActors.echoActorProps)
watch(deadActor)
deadActor ! PoisonPill
expectTerminated(deadActor)
s"must suppress message from default dead-letters logging (sent to dead: ${Logging.simpleName(deadActor)})" in {
val deadListener = TestProbe()
system.eventStream.subscribe(deadListener.ref, classOf[DeadLetter])
val suppressedListener = TestProbe()
system.eventStream.subscribe(suppressedListener.ref, classOf[SuppressedDeadLetter])
val allListener = TestProbe()
system.eventStream.subscribe(allListener.ref, classOf[AllDeadLetters])
deadActor ! SuppressedMsg
deadActor ! NormalMsg
deadListener.expectMsg(DeadLetter(NormalMsg, testActor, deadActor))
deadListener.expectNoMsg(200.millis)
suppressedListener.expectMsg(SuppressedDeadLetter(SuppressedMsg, testActor, system.deadLetters))
suppressedListener.expectNoMsg(200.millis)
allListener.expectMsg(SuppressedDeadLetter(SuppressedMsg, testActor, system.deadLetters))
allListener.expectMsg(DeadLetter(NormalMsg, testActor, deadActor))
allListener.expectNoMsg(200.millis)
}
s"must suppress message from default dead-letters logging (sent to dead: ${Logging.simpleName(system.deadLetters)})" in {
val deadListener = TestProbe()
system.eventStream.subscribe(deadListener.ref, classOf[DeadLetter])
val suppressedListener = TestProbe()
system.eventStream.subscribe(suppressedListener.ref, classOf[SuppressedDeadLetter])
val allListener = TestProbe()
system.eventStream.subscribe(allListener.ref, classOf[AllDeadLetters])
system.deadLetters ! SuppressedMsg
system.deadLetters ! NormalMsg
deadListener.expectMsg(200.millis, DeadLetter(NormalMsg, testActor, system.deadLetters))
suppressedListener.expectMsg(200.millis, SuppressedDeadLetter(SuppressedMsg, testActor, system.deadLetters))
allListener.expectMsg(200.millis, SuppressedDeadLetter(SuppressedMsg, testActor, system.deadLetters))
allListener.expectMsg(200.millis, DeadLetter(NormalMsg, testActor, system.deadLetters))
Thread.sleep(200)
deadListener.expectNoMsg(Duration.Zero)
suppressedListener.expectNoMsg(Duration.Zero)
allListener.expectNoMsg(Duration.Zero)
}
}

View file

@ -459,12 +459,37 @@ private[akka] trait MinimalActorRef extends InternalActorRef with LocalRef {
protected def writeReplace(): AnyRef = SerializedActorRef(this)
}
/** Subscribe to this class to be notified about all DeadLetters (also the supressed ones). */
sealed trait AllDeadLetters {
def message: Any
def sender: ActorRef
def recipient: ActorRef
}
/**
* When a message is sent to an Actor that is terminated before receiving the message, it will be sent as a DeadLetter
* to the ActorSystem's EventStream
*/
@SerialVersionUID(1L)
final case class DeadLetter(message: Any, sender: ActorRef, recipient: ActorRef) {
final case class DeadLetter(message: Any, sender: ActorRef, recipient: ActorRef) extends AllDeadLetters {
require(sender ne null, "DeadLetter sender may not be null")
require(recipient ne null, "DeadLetter recipient may not be null")
}
/**
* Use with caution: Messages extending this trait will not be logged by the default dead-letters listener.
* Instead they will be wrapped as [[SuppressedDeadLetter]] and may be subscribed for explicitly.
*/
trait DeadLetterSuppression
/**
* Similar to [[DeadLetter]] with the slight twist of NOT being logged by the default dead letters listener.
* Messages which end up being suppressed dead letters are internal messages for which ending up as dead-letter is both expected and harmless.
*
* It is possible to subscribe to suppressed dead letters on the ActorSystem's EventStream explicitly.
*/
@SerialVersionUID(1L)
final case class SuppressedDeadLetter(message: DeadLetterSuppression, sender: ActorRef, recipient: ActorRef) extends AllDeadLetters {
require(sender ne null, "DeadLetter sender may not be null")
require(recipient ne null, "DeadLetter recipient may not be null")
}
@ -523,6 +548,9 @@ private[akka] class EmptyLocalActorRef(override val provider: ActorRefProvider,
eventStream.publish(DeadLetter(sel.msg, if (sender eq Actor.noSender) provider.deadLetters else sender, this))
}
true
case m: DeadLetterSuppression
eventStream.publish(SuppressedDeadLetter(m, if (sender eq Actor.noSender) provider.deadLetters else sender, this))
true
case _ false
}
}

View file

@ -4,6 +4,10 @@
package docs.event;
//#imports
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.AllDeadLetters;
import akka.actor.SuppressedDeadLetter;
import akka.event.Logging;
import akka.event.LoggingAdapter;
@ -64,7 +68,30 @@ public class LoggingDocTest {
//#deadletters
JavaTestKit.shutdownActorSystem(system);
}
@Test
public void subscribeToSuppressedDeadLetters() {
final ActorSystem system = ActorSystem.create("SuppressedDeadLetters");
final ActorRef actor = system.actorOf(Props.create(DeadLetterActor.class));
//#suppressed-deadletters
system.eventStream().subscribe(actor, SuppressedDeadLetter.class);
//#suppressed-deadletters
JavaTestKit.shutdownActorSystem(system);
}
@Test
public void subscribeToAllDeadLetters() {
final ActorSystem system = ActorSystem.create("AllDeadLetters");
final ActorRef actor = system.actorOf(Props.create(DeadLetterActor.class));
//#all-deadletters
system.eventStream().subscribe(actor, AllDeadLetters.class);
//#all-deadletters
JavaTestKit.shutdownActorSystem(system);
}
@Test
public void demonstrateMultipleArgs() {
final ActorSystem system = ActorSystem.create("multiArg");

View file

@ -187,6 +187,19 @@ which by default will publish the messages wrapped in :class:`DeadLetter`. This
wrapper holds the original sender, receiver and message of the envelope which
was redirected.
Some internal messages (marked with the :class:`DeadLetterSuppression` trait) will not end up as
dead letters like normal messages. These are by design safe and expected to sometimes arrive at a terminated actor
and since they are nothing to worry about, they are suppressed from the default dead letters logging mechanism.
However, in case you find yourself in need of debugging these kinds of low level suppressed dead letters,
it's still possible to subscribe to them explicitly:
.. includecode:: code/docs/event/LoggingDocTest.java#suppressed-deadletters
or all dead letters (including the suppressed ones):
.. includecode:: code/docs/event/LoggingDocTest.java#all-deadletters
Other Uses
----------

View file

@ -3,6 +3,7 @@
*/
package docs.event
import akka.actor.AllDeadLetters
import akka.testkit.AkkaSpec
import akka.actor.Actor
import akka.actor.Props
@ -149,6 +150,23 @@ class LoggingDocSpec extends AkkaSpec {
}
}
"allow registration to suppressed dead letters" in {
new AnyRef {
import akka.actor.Props
val listener = system.actorOf(Props[MyActor])
//#suppressed-deadletters
import akka.actor.SuppressedDeadLetter
system.eventStream.subscribe(listener, classOf[SuppressedDeadLetter])
//#suppressed-deadletters
//#all-deadletters
import akka.actor.AllDeadLetters
system.eventStream.subscribe(listener, classOf[AllDeadLetters])
//#all-deadletters
}
}
"demonstrate logging more arguments" in {
//#array
val args = Array("The", "brown", "fox", "jumps", 42)

View file

@ -182,6 +182,19 @@ which by default will publish the messages wrapped in :class:`DeadLetter`. This
wrapper holds the original sender, receiver and message of the envelope which
was redirected.
Some internal messages (marked with the :class:`DeadLetterSuppression` trait) will not end up as
dead letters like normal messages. These are by design safe and expected to sometimes arrive at a terminated actor
and since they are nothing to worry about, they are suppressed from the default dead letters logging mechanism.
However, in case you find yourself in need of debugging these kinds of low level suppressed dead letters,
it's still possible to subscribe to them explicitly:
.. includecode:: code/docs/event/LoggingDocSpec.scala#suppressed-deadletters
or all dead letters (including the suppressed ones):
.. includecode:: code/docs/event/LoggingDocSpec.scala#all-deadletters
Other Uses
----------