+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:
parent
3cf00cecb1
commit
73cd1d7375
6 changed files with 181 additions and 2 deletions
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -459,12 +459,37 @@ private[akka] trait MinimalActorRef extends InternalActorRef with LocalRef {
|
||||||
protected def writeReplace(): AnyRef = SerializedActorRef(this)
|
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
|
* 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
|
* to the ActorSystem's EventStream
|
||||||
*/
|
*/
|
||||||
@SerialVersionUID(1L)
|
@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(sender ne null, "DeadLetter sender may not be null")
|
||||||
require(recipient ne null, "DeadLetter recipient 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))
|
eventStream.publish(DeadLetter(sel.msg, if (sender eq Actor.noSender) provider.deadLetters else sender, this))
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
case m: DeadLetterSuppression ⇒
|
||||||
|
eventStream.publish(SuppressedDeadLetter(m, if (sender eq Actor.noSender) provider.deadLetters else sender, this))
|
||||||
|
true
|
||||||
case _ ⇒ false
|
case _ ⇒ false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@
|
||||||
package docs.event;
|
package docs.event;
|
||||||
|
|
||||||
//#imports
|
//#imports
|
||||||
|
import akka.actor.ActorRef;
|
||||||
|
import akka.actor.ActorSystem;
|
||||||
|
import akka.actor.AllDeadLetters;
|
||||||
|
import akka.actor.SuppressedDeadLetter;
|
||||||
import akka.event.Logging;
|
import akka.event.Logging;
|
||||||
import akka.event.LoggingAdapter;
|
import akka.event.LoggingAdapter;
|
||||||
|
|
||||||
|
|
@ -65,6 +69,29 @@ public class LoggingDocTest {
|
||||||
JavaTestKit.shutdownActorSystem(system);
|
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
|
@Test
|
||||||
public void demonstrateMultipleArgs() {
|
public void demonstrateMultipleArgs() {
|
||||||
final ActorSystem system = ActorSystem.create("multiArg");
|
final ActorSystem system = ActorSystem.create("multiArg");
|
||||||
|
|
|
||||||
|
|
@ -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
|
wrapper holds the original sender, receiver and message of the envelope which
|
||||||
was redirected.
|
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
|
Other Uses
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
*/
|
*/
|
||||||
package docs.event
|
package docs.event
|
||||||
|
|
||||||
|
import akka.actor.AllDeadLetters
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.AkkaSpec
|
||||||
import akka.actor.Actor
|
import akka.actor.Actor
|
||||||
import akka.actor.Props
|
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 {
|
"demonstrate logging more arguments" in {
|
||||||
//#array
|
//#array
|
||||||
val args = Array("The", "brown", "fox", "jumps", 42)
|
val args = Array("The", "brown", "fox", "jumps", 42)
|
||||||
|
|
|
||||||
|
|
@ -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
|
wrapper holds the original sender, receiver and message of the envelope which
|
||||||
was redirected.
|
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
|
Other Uses
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue