diff --git a/akka-actor-testkit-typed/src/main/resources/reference.conf b/akka-actor-testkit-typed/src/main/resources/reference.conf index c0e2ca1742..c25fdacfc5 100644 --- a/akka-actor-testkit-typed/src/main/resources/reference.conf +++ b/akka-actor-testkit-typed/src/main/resources/reference.conf @@ -6,26 +6,32 @@ # Make your edits/overrides in your application.conf. akka.actor.testkit.typed { - # factor by which to scale timeouts during tests, e.g. to account for shared - # build system load + # Factor by which to scale timeouts during tests, e.g. to account for shared + # build system load. timefactor = 1.0 - # duration to wait in expectMsg and friends outside of within() block - # by default, will be dilated by the timefactor. + # Duration to wait in expectMsg and friends outside of within() block + # by default. + # Dilated by the timefactor. single-expect-default = 3s - # duration to wait in expectNoMessage by default, - # will be dilated by the timefactor. + # Duration to wait in expectNoMessage by default. + # Dilated by the timefactor. expect-no-message-default = 100ms - # The timeout that is added as an implicit by DefaultTimeout trait, will be dilated by the timefactor. + # The timeout that is used as an implicit Timeout. + # Dilated by the timefactor. default-timeout = 5s - # Default timeout for shutting down the actor system (used when no explicit timeout specified), - # will be dilated by the timefactor. + # Default timeout for shutting down the actor system (used when no explicit timeout specified). + # Dilated by the timefactor. system-shutdown-default=10s # Throw an exception on shutdown if the timeout is hit, if false an error is printed to stdout instead. throw-on-shutdown-timeout=true + # Duration to wait for all required logging events in LoggingEventFilter.intercept. + # Dilated by the timefactor. + filter-leeway = 3s + } diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/CapturedLogEvent.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/CapturedLogEvent.scala index 10fa9396d2..6d75daffcc 100644 --- a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/CapturedLogEvent.scala +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/CapturedLogEvent.scala @@ -6,69 +6,63 @@ package akka.actor.testkit.typed import java.util.Optional -import akka.actor.typed.LogMarker import akka.annotation.InternalApi -import akka.event.Logging.LogLevel import akka.util.OptionVal - -import akka.util.ccompat.JavaConverters._ import scala.compat.java8.OptionConverters._ +import org.slf4j.Marker +import org.slf4j.event.Level + /** * Representation of a Log Event issued by a [[akka.actor.typed.Behavior]] + * when testing with [[akka.actor.testkit.typed.scaladsl.BehaviorTestKit`]] + * or [[akka.actor.testkit.typed.javadsl.BehaviorTestKit`]]. */ -final case class CapturedLogEvent( - logLevel: LogLevel, - message: String, - cause: Option[Throwable], - marker: Option[LogMarker], - mdc: Map[String, Any]) { +final case class CapturedLogEvent(level: Level, message: String, cause: Option[Throwable], marker: Option[Marker]) { /** * Constructor for Java API */ def this( - logLevel: LogLevel, + level: Level, message: String, errorCause: Optional[Throwable], - marker: Optional[LogMarker], + marker: Optional[Marker], mdc: java.util.Map[String, Any]) { - this(logLevel, message, errorCause.asScala, marker.asScala, mdc.asScala.toMap) + this(level, message, errorCause.asScala, marker.asScala) } /** * Constructor for Java API */ - def this(logLevel: LogLevel, message: String) { - this(logLevel, message, Option.empty, Option.empty, Map.empty[String, Any]) + def this(level: Level, message: String) { + this(level, message, Option.empty, Option.empty) } /** * Constructor for Java API */ - def this(logLevel: LogLevel, message: String, errorCause: Throwable) { - this(logLevel, message, Some(errorCause), Option.empty[LogMarker], Map.empty[String, Any]) + def this(level: Level, message: String, errorCause: Throwable) { + this(level, message, Some(errorCause), Option.empty[Marker]) } /** * Constructor for Java API */ - def this(logLevel: LogLevel, message: String, marker: LogMarker) { - this(logLevel, message, Option.empty[Throwable], Some(marker), Map.empty[String, Any]) + def this(level: Level, message: String, marker: Marker) { + this(level, message, Option.empty[Throwable], Some(marker)) } /** * Constructor for Java API */ - def this(logLevel: LogLevel, message: String, errorCause: Throwable, marker: LogMarker) { - this(logLevel, message, Some(errorCause), Some(marker), Map.empty[String, Any]) + def this(level: Level, message: String, errorCause: Throwable, marker: Marker) { + this(level, message, Some(errorCause), Some(marker)) } - def getMdc: java.util.Map[String, Any] = mdc.asJava - def getErrorCause: Optional[Throwable] = cause.asJava - def getLogMarker: Optional[LogMarker] = marker.asJava + def getMarker: Optional[Marker] = marker.asJava } object CapturedLogEvent { @@ -81,8 +75,8 @@ object CapturedLogEvent { case _ => None } - def apply(logLevel: LogLevel, message: String): CapturedLogEvent = { - CapturedLogEvent(logLevel, message, None, None, Map.empty[String, Any]) + def apply(level: Level, message: String): CapturedLogEvent = { + CapturedLogEvent(level, message, None, None) } /** @@ -91,11 +85,10 @@ object CapturedLogEvent { */ @InternalApi private[akka] def apply( - logLevel: LogLevel, + level: Level, message: String, errorCause: OptionVal[Throwable], - logMarker: OptionVal[LogMarker], - mdc: Map[String, Any]): CapturedLogEvent = { - new CapturedLogEvent(logLevel, message, toOption(errorCause), toOption(logMarker), mdc) + logMarker: OptionVal[Marker]): CapturedLogEvent = { + new CapturedLogEvent(level, message, toOption(errorCause), toOption(logMarker)) } } diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/LoggingEvent.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/LoggingEvent.scala new file mode 100644 index 0000000000..5cdbfb9082 --- /dev/null +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/LoggingEvent.scala @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2019 Lightbend Inc. + */ + +package akka.actor.testkit.typed + +import scala.compat.java8.OptionConverters._ +import akka.util.ccompat.JavaConverters._ + +import java.util.Optional + +import org.slf4j.Marker +import org.slf4j.event.Level + +object LoggingEvent { + + /** + * Scala API + */ + def apply(level: Level, loggerName: String, threadName: String, message: String, timeStamp: Long): LoggingEvent = + new LoggingEvent(level, loggerName, threadName, message, timeStamp, None, None, Map.empty) + + /** + * Java API + */ + def create(level: Level, loggerName: String, threadName: String, message: String, timeStamp: Long): LoggingEvent = + apply(level, loggerName, threadName, message, timeStamp) + + /** + * Java API + */ + def create( + level: Level, + loggerName: String, + threadName: String, + message: String, + timeStamp: Long, + marker: Optional[Marker], + throwable: Optional[Throwable], + mdc: java.util.Map[String, String]) = + apply(level, loggerName, threadName, message, timeStamp, marker.asScala, throwable.asScala, mdc.asScala.toMap) +} + +/** + * Representation of logging event when testing with [[akka.actor.testkit.typed.scaladsl.LoggingEventFilter]] + * or [[akka.actor.testkit.typed.javadsl.LoggingEventFilter]]. + */ +final case class LoggingEvent( + level: Level, + loggerName: String, + threadName: String, + message: String, + timeStamp: Long, + marker: Option[Marker], + throwable: Option[Throwable], + mdc: Map[String, String]) { + + /** + * Java API + */ + def getMarker: Optional[Marker] = + marker.asJava + + /** + * Java API + */ + def getThrowable: Optional[Throwable] = + throwable.asJava + + /** + * Java API + */ + def getMdc: java.util.Map[String, String] = { + import akka.util.ccompat.JavaConverters._ + mdc.asJava + } + +} diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/TestKitSettings.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/TestKitSettings.scala index a9f9a427ae..a2d103dc7d 100644 --- a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/TestKitSettings.scala +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/TestKitSettings.scala @@ -5,11 +5,13 @@ package akka.actor.testkit.typed import com.typesafe.config.Config - import scala.concurrent.duration.{ Duration, FiniteDuration } + import akka.util.JavaDurationConverters._ import akka.util.Timeout import akka.actor.typed.ActorSystem +import akka.actor.typed.Extension +import akka.actor.typed.ExtensionId object TestKitSettings { @@ -17,7 +19,7 @@ object TestKitSettings { * Reads configuration settings from `akka.actor.testkit.typed` section. */ def apply(system: ActorSystem[_]): TestKitSettings = - apply(system.settings.config.getConfig("akka.actor.testkit.typed")) + Ext(system).settings /** * Reads configuration settings from given `Config` that @@ -38,30 +40,42 @@ object TestKitSettings { */ def create(config: Config): TestKitSettings = new TestKitSettings(config) + + private object Ext extends ExtensionId[Ext] { + override def createExtension(system: ActorSystem[_]): Ext = new Ext(system) + def get(system: ActorSystem[_]): Ext = apply(system) + } + + private class Ext(system: ActorSystem[_]) extends Extension { + val settings: TestKitSettings = TestKitSettings(system.settings.config.getConfig("akka.actor.testkit.typed")) + } } final class TestKitSettings(val config: Config) { import akka.util.Helpers._ - val TestTimeFactor = config + val TestTimeFactor: Double = config .getDouble("timefactor") .requiring(tf => !tf.isInfinite && tf > 0, "timefactor must be positive finite double") - /** dilated with `TestTimeFactor` */ + /** Dilated with `TestTimeFactor`. */ val SingleExpectDefaultTimeout: FiniteDuration = dilated(config.getMillisDuration("single-expect-default")) - /** dilated with `TestTimeFactor` */ + /** Dilated with `TestTimeFactor`. */ val ExpectNoMessageDefaultTimeout: FiniteDuration = dilated(config.getMillisDuration("expect-no-message-default")) - /** dilated with `TestTimeFactor` */ + /** Dilated with `TestTimeFactor`. */ val DefaultTimeout: Timeout = Timeout(dilated(config.getMillisDuration("default-timeout"))) - /** dilated with `TestTimeFactor` */ + /** Dilated with `TestTimeFactor`. */ val DefaultActorSystemShutdownTimeout: FiniteDuration = dilated(config.getMillisDuration("system-shutdown-default")) val ThrowOnShutdownTimeout: Boolean = config.getBoolean("throw-on-shutdown-timeout") + /** Dilated with `TestTimeFactor`. */ + val FilterLeeway: FiniteDuration = dilated(config.getMillisDuration("filter-leeway")) + /** * Scala API: Scale the `duration` with the configured `TestTimeFactor` */ diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/ActorSystemStub.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/ActorSystemStub.scala index f5a3d90739..ce67a6da03 100644 --- a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/ActorSystemStub.scala +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/ActorSystemStub.scala @@ -14,7 +14,6 @@ import akka.actor.typed.DispatcherSelector import akka.actor.typed.Dispatchers import akka.actor.typed.Extension import akka.actor.typed.ExtensionId -import akka.actor.typed.Logger import akka.actor.typed.Props import akka.actor.typed.Scheduler import akka.actor.typed.Settings @@ -22,12 +21,15 @@ import akka.annotation.InternalApi import akka.{ actor => classic } import akka.Done import com.typesafe.config.ConfigFactory - import scala.compat.java8.FutureConverters import scala.concurrent._ + import akka.actor.ActorRefProvider +import akka.actor.ReflectiveDynamicAccess import akka.actor.typed.internal.InternalRecipientRef import com.github.ghik.silencer.silent +import org.slf4j.Logger +import org.slf4j.LoggerFactory /** * INTERNAL API @@ -41,7 +43,14 @@ import com.github.ghik.silencer.silent override val path: classic.ActorPath = classic.RootActorPath(classic.Address("akka", name)) / "user" - override val settings: Settings = new Settings(getClass.getClassLoader, ConfigFactory.empty, name) + override val settings: Settings = { + val classLoader = getClass.getClassLoader + val dynamicAccess = new ReflectiveDynamicAccess(classLoader) + val config = + classic.ActorSystem.Settings.amendSlf4jConfig(ConfigFactory.defaultReference(classLoader), dynamicAccess) + val untypedSettings = new classic.ActorSystem.Settings(classLoader, config, name) + new Settings(untypedSettings) + } override def tell(message: Nothing): Unit = throw new UnsupportedOperationException("must not send message to ActorSystemStub") @@ -103,5 +112,5 @@ import com.github.ghik.silencer.silent override def hasExtension(ext: ExtensionId[_ <: Extension]): Boolean = throw new UnsupportedOperationException("ActorSystemStub cannot register extensions") - override def log: Logger = new StubbedLogger + override def log: Logger = LoggerFactory.getLogger(getClass) } diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/CapturingAppender.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/CapturingAppender.scala new file mode 100644 index 0000000000..be3ac0c39f --- /dev/null +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/CapturingAppender.scala @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2019 Lightbend Inc. + */ + +package akka.actor.testkit.typed.internal + +import akka.annotation.InternalApi +import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.core.AppenderBase + +/** + * INTERNAL API + */ +@InternalApi private[akka] object CapturingAppender { + import LogbackUtil._ + + private val CapturingAppenderName = "CapturingAppender" + + def get(loggerName: String): CapturingAppender = { + val logbackLogger = getLogbackLogger(loggerName) + logbackLogger.getAppender(CapturingAppenderName) match { + case null => + throw new IllegalStateException( + s"$CapturingAppenderName not defined for [${loggerNameOrRoot(loggerName)}] in logback-test.xml") + case appender: CapturingAppender => appender + case other => + throw new IllegalStateException(s"Unexpected $CapturingAppender: $other") + } + } + +} + +/** + * INTERNAL API + * + * Logging from tests can be silenced by this appender. When there is a test failure + * the captured logging events are flushed to the appenders defined for the + * akka.actor.testkit.typed.internal.CapturingAppenderDelegate logger. + * + * The flushing on test failure is handled by [[akka.actor.testkit.typed.scaladsl.LogCapturing]] + * for ScalaTest and [[akka.actor.testkit.typed.javadsl.LogCapturing]] for JUnit. + * + * Use configuration like the following the logback-test.xml: + * + * {{{ + * + * + * + * + * + * + * + * + * + * }}} + */ +@InternalApi private[akka] class CapturingAppender extends AppenderBase[ILoggingEvent] { + import LogbackUtil._ + + private var buffer: Vector[ILoggingEvent] = Vector.empty + + // invocations are synchronized via doAppend in AppenderBase + override def append(event: ILoggingEvent): Unit = { + event.prepareForDeferredProcessing() + buffer :+= event + } + + /** + * Flush buffered logging events to the output appenders + * Also clears the buffer.. + */ + def flush(): Unit = synchronized { + import akka.util.ccompat.JavaConverters._ + val logbackLogger = getLogbackLogger(classOf[CapturingAppender].getName + "Delegate") + val appenders = logbackLogger.iteratorForAppenders().asScala.filterNot(_ == this).toList + for (event <- buffer; appender <- appenders) { + appender.doAppend(event) + } + clear() + } + + /** + * Discards the buffered logging events without output. + */ + def clear(): Unit = synchronized { + buffer = Vector.empty + } + +} diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/LogbackUtil.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/LogbackUtil.scala new file mode 100644 index 0000000000..6f28238210 --- /dev/null +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/LogbackUtil.scala @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 Lightbend Inc. + */ + +package akka.actor.testkit.typed.internal + +import akka.annotation.InternalApi +import org.slf4j.LoggerFactory +import org.slf4j.event.Level + +/** + * INTERNAL API + */ +@InternalApi private[akka] object LogbackUtil { + def loggerNameOrRoot(loggerName: String): String = + if (loggerName == "") org.slf4j.Logger.ROOT_LOGGER_NAME else loggerName + + def getLogbackLogger(loggerName: String): ch.qos.logback.classic.Logger = { + LoggerFactory.getLogger(loggerNameOrRoot(loggerName)) match { + case logger: ch.qos.logback.classic.Logger => logger + case null => + throw new IllegalArgumentException(s"Couldn't find logger for [$loggerName].") + case other => + throw new IllegalArgumentException( + s"Requires Logback logger for [$loggerName], it was a [${other.getClass.getName}]") + } + } + + def convertLevel(level: ch.qos.logback.classic.Level): Level = { + level.levelInt match { + case ch.qos.logback.classic.Level.TRACE_INT => Level.TRACE + case ch.qos.logback.classic.Level.DEBUG_INT => Level.DEBUG + case ch.qos.logback.classic.Level.INFO_INT => Level.INFO + case ch.qos.logback.classic.Level.WARN_INT => Level.WARN + case ch.qos.logback.classic.Level.ERROR_INT => Level.ERROR + case _ => + throw new IllegalArgumentException("Level " + level.levelStr + ", " + level.levelInt + " is unknown.") + } + } +} diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/LoggingEventFilterImpl.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/LoggingEventFilterImpl.scala new file mode 100644 index 0000000000..4c2c96b9d2 --- /dev/null +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/LoggingEventFilterImpl.scala @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2019 Lightbend Inc. + */ + +package akka.actor.testkit.typed.internal + +import java.util.function.Supplier + +import scala.concurrent.duration.Duration +import scala.reflect.ClassTag +import scala.util.matching.Regex + +import akka.actor.testkit.typed.LoggingEvent +import akka.actor.testkit.typed.TestKitSettings +import akka.actor.testkit.typed.javadsl +import akka.actor.testkit.typed.scaladsl +import akka.actor.typed.ActorSystem +import akka.annotation.InternalApi +import akka.testkit.TestKit +import org.slf4j.event.Level + +/** + * INTERNAL API + */ +@InternalApi private[akka] object LoggingEventFilterImpl { + def empty: LoggingEventFilterImpl = new LoggingEventFilterImpl(1, None, None, None, None, None, None, Map.empty, None) +} + +/** + * INTERNAL API + */ +@InternalApi private[akka] final case class LoggingEventFilterImpl( + occurrences: Int, + logLevel: Option[Level], + loggerName: Option[String], + source: Option[String], + messageContains: Option[String], + messageRegex: Option[Regex], + cause: Option[Class[_ <: Throwable]], + mdc: Map[String, String], + custom: Option[Function[LoggingEvent, Boolean]]) + extends javadsl.LoggingEventFilter + with scaladsl.LoggingEventFilter { + + @volatile // JMM does not guarantee visibility for non-final fields + private var todo = occurrences + + def matches(event: LoggingEvent): Boolean = { + logLevel.forall(_ == event.level) && + source.forall(_ == sourceOrEmpty(event)) && + messageContains.forall(messageOrEmpty(event).contains) && + messageRegex.forall(_.findFirstIn(messageOrEmpty(event)).isDefined) && + cause.forall(c => event.throwable.isDefined && c.isInstance(event.throwable.get)) && + mdc.forall { case (key, value) => event.mdc.contains(key) && event.mdc(key) == value } && + custom.forall(f => f(event)) + + // loggerName is handled when installing the filter, in `intercept` + } + + private def messageOrEmpty(event: LoggingEvent): String = + if (event.message == null) "" else event.message + + private def sourceOrEmpty(event: LoggingEvent): String = + event.mdc.getOrElse("akkaSource", "") + + def apply(event: LoggingEvent): Boolean = { + if (matches(event)) { + if (todo != Int.MaxValue) todo -= 1 + true + } else false + } + + private def awaitDone(max: Duration): Boolean = { + if (todo != Int.MaxValue && todo > 0) TestKit.awaitCond(todo <= 0, max, noThrow = true) + todo == Int.MaxValue || todo == 0 + } + + override def intercept[T](code: => T)(implicit system: ActorSystem[_]): T = { + val effectiveLoggerName = loggerName.getOrElse("") + checkLogback(system) + TestAppender.setupTestAppender(effectiveLoggerName) + TestAppender.addFilter(effectiveLoggerName, this) + val leeway = TestKitSettings(system).FilterLeeway + try { + val result = code + if (!awaitDone(leeway)) + if (todo > 0) + throw new AssertionError(s"Timeout ($leeway) waiting for $todo messages on $this.") + else + throw new AssertionError(s"Received ${-todo} excess messages on $this.") + result + } finally { + todo = occurrences + TestAppender.removeFilter(effectiveLoggerName, this) + } + } + + private def checkLogback(system: ActorSystem[_]): Unit = { + if (!system.dynamicAccess.classIsOnClasspath("ch.qos.logback.classic.spi.ILoggingEvent")) { + throw new IllegalStateException("LoggingEventFilter requires logback-classic dependency in classpath.") + } + } + + override def withOccurrences(newOccurrences: Int): LoggingEventFilterImpl = + copy(occurrences = newOccurrences) + + override def withLogLevel(newLogLevel: Level): LoggingEventFilterImpl = + copy(logLevel = Option(newLogLevel)) + + def withLoggerName(newLoggerName: String): LoggingEventFilterImpl = + copy(loggerName = Some(newLoggerName)) + + override def withSource(newSource: String): LoggingEventFilterImpl = + copy(source = Option(newSource)) + + override def withMessageContains(newMessageContains: String): LoggingEventFilterImpl = + copy(messageContains = Option(newMessageContains)) + + def withMessageRegex(newMessageRegex: String): LoggingEventFilterImpl = + copy(messageRegex = Option(new Regex(newMessageRegex))) + + override def withCause[A <: Throwable: ClassTag]: LoggingEventFilterImpl = { + val causeClass = implicitly[ClassTag[A]].runtimeClass.asInstanceOf[Class[Throwable]] + copy(cause = Option(causeClass)) + } + + override def withMdc(newMdc: Map[String, String]): LoggingEventFilterImpl = + copy(mdc = newMdc) + + override def withMdc(newMdc: java.util.Map[String, String]): javadsl.LoggingEventFilter = { + import akka.util.ccompat.JavaConverters._ + withMdc(newMdc.asScala.toMap) + } + + override def withCustom(newCustom: Function[LoggingEvent, Boolean]): LoggingEventFilterImpl = + copy(custom = Option(newCustom)) + + override def withCause(newCause: Class[_ <: Throwable]): javadsl.LoggingEventFilter = + copy(cause = Option(newCause)) + + override def intercept[T](system: ActorSystem[_], code: Supplier[T]): T = + intercept(code.get())(system) + +} diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/StubbedActorContext.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/StubbedActorContext.scala index 0e9d946100..7316e013df 100644 --- a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/StubbedActorContext.scala +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/StubbedActorContext.scala @@ -6,13 +6,11 @@ package akka.actor.testkit.typed.internal import akka.actor.typed._ import akka.actor.typed.internal._ -import akka.actor.typed.internal.adapter.AbstractLogger import akka.actor.testkit.typed.CapturedLogEvent import akka.actor.testkit.typed.scaladsl.TestInbox import akka.actor.{ ActorPath, InvalidMessageException } import akka.annotation.InternalApi -import akka.event.Logging -import akka.util.{ Helpers, OptionVal } +import akka.util.Helpers import akka.{ actor => classic } import java.util.concurrent.ThreadLocalRandom.{ current => rnd } @@ -21,6 +19,9 @@ import scala.concurrent.ExecutionContextExecutor import scala.concurrent.duration.FiniteDuration import akka.actor.ActorRefProvider +import org.slf4j.Logger +import org.slf4j.helpers.MessageFormatter +import org.slf4j.helpers.SubstituteLoggerFactory /** * INTERNAL API @@ -54,108 +55,6 @@ private[akka] final class FunctionRef[-T](override val path: ActorPath, send: (T def isTerminated: Boolean = false } -/** - * INTERNAL API - * - * Captures log events for test inspection - */ -@InternalApi private[akka] final class StubbedLogger extends AbstractLogger { - - private var logBuffer: List[CapturedLogEvent] = Nil - - override def isErrorEnabled: Boolean = true - override def isWarningEnabled: Boolean = true - override def isInfoEnabled: Boolean = true - override def isDebugEnabled: Boolean = true - - override def isErrorEnabled(marker: LogMarker): Boolean = true - override def isWarningEnabled(marker: LogMarker): Boolean = true - override def isInfoEnabled(marker: LogMarker): Boolean = true - override def isDebugEnabled(marker: LogMarker): Boolean = true - - override private[akka] def notifyError( - message: String, - cause: OptionVal[Throwable], - marker: OptionVal[LogMarker]): Unit = - logBuffer = CapturedLogEvent(Logging.ErrorLevel, message, cause, marker, mdc) :: logBuffer - override private[akka] def notifyWarning( - message: String, - cause: OptionVal[Throwable], - marker: OptionVal[LogMarker]): Unit = - logBuffer = CapturedLogEvent(Logging.WarningLevel, message, OptionVal.None, marker, mdc) :: logBuffer - - override private[akka] def notifyInfo(message: String, marker: OptionVal[LogMarker]): Unit = - logBuffer = CapturedLogEvent(Logging.InfoLevel, message, OptionVal.None, marker, mdc) :: logBuffer - - override private[akka] def notifyDebug(message: String, marker: OptionVal[LogMarker]): Unit = - logBuffer = CapturedLogEvent(Logging.DebugLevel, message, OptionVal.None, marker, mdc) :: logBuffer - - def logEntries: List[CapturedLogEvent] = logBuffer.reverse - def clearLog(): Unit = logBuffer = Nil - - override def withMdc(mdc: Map[String, Any]): Logger = { - // we need to decorate to get log entries ending up the same logBuffer - val withMdc = new StubbedLoggerWithMdc(this) - withMdc.mdc = mdc - withMdc - } - - // we don't care about log class and source here as we only track message, level and marker - def withLoggerClass(clazz: Class[_]): Logger = this - def withLogSource(logSource: String): Logger = this -} - -@InternalApi private[akka] final class StubbedLoggerWithMdc(actual: StubbedLogger) extends AbstractLogger { - override def isErrorEnabled: Boolean = actual.isErrorEnabled - override def isWarningEnabled: Boolean = actual.isWarningEnabled - override def isInfoEnabled: Boolean = actual.isInfoEnabled - override def isDebugEnabled: Boolean = actual.isDebugEnabled - override def withMdc(mdc: Map[String, Any]): Logger = actual.withMdc(mdc) - - override def isErrorEnabled(marker: LogMarker): Boolean = actual.isErrorEnabled(marker) - override def isWarningEnabled(marker: LogMarker): Boolean = actual.isWarningEnabled(marker) - override def isInfoEnabled(marker: LogMarker): Boolean = actual.isInfoEnabled(marker) - override def isDebugEnabled(marker: LogMarker): Boolean = actual.isDebugEnabled(marker) - - override private[akka] def notifyError( - message: String, - cause: OptionVal[Throwable], - marker: OptionVal[LogMarker]): Unit = { - val original = actual.mdc - actual.mdc = mdc - actual.notifyError(message, cause, marker) - actual.mdc = original - } - - override private[akka] def notifyWarning( - message: String, - cause: OptionVal[Throwable], - marker: OptionVal[LogMarker]): Unit = { - val original = actual.mdc - actual.mdc = mdc - actual.notifyWarning(message, cause, marker) - actual.mdc = original - } - - override private[akka] def notifyInfo(message: String, marker: OptionVal[LogMarker]): Unit = { - val original = actual.mdc - actual.mdc = mdc - actual.notifyInfo(message, marker) - actual.mdc = original - } - - override private[akka] def notifyDebug(message: String, marker: OptionVal[LogMarker]): Unit = { - val original = actual.mdc - actual.mdc = mdc - actual.notifyDebug(message, marker) - actual.mdc = original - } - - // we don't care about log class and source here as we only track message, level and marker - def withLoggerClass(clazz: Class[_]): Logger = this - def withLogSource(logSource: String): Logger = this -} - /** * INTERNAL API * @@ -179,7 +78,8 @@ private[akka] final class FunctionRef[-T](override val path: ActorPath, send: (T override val system = new ActorSystemStub("StubbedActorContext") private var _children = TreeMap.empty[String, BehaviorTestKitImpl[_]] private val childName = Iterator.from(0).map(Helpers.base64(_)) - private val loggingAdapter = new StubbedLogger + private val substituteLoggerFactory = new SubstituteLoggerFactory + private val logger: Logger = substituteLoggerFactory.getLogger("StubbedLogger") private var unhandled: List[T] = Nil private[akka] def classicActorContext = @@ -285,20 +185,36 @@ private[akka] final class FunctionRef[-T](override val path: ActorPath, send: (T override def toString: String = s"Inbox($self)" - override def log: Logger = loggingAdapter + override def log: Logger = logger - override def setLoggerClass(clazz: Class[_]): Unit = () // nop as we dont track logger class + override def setLoggerName(name: String): Unit = () // nop as we don't track logger + + override def setLoggerName(clazz: Class[_]): Unit = () // nop as we don't track logger /** * The log entries logged through context.log.{debug, info, warn, error} are captured and can be inspected through * this method. */ - def logEntries: List[CapturedLogEvent] = loggingAdapter.logEntries + def logEntries: List[CapturedLogEvent] = { + import akka.util.ccompat.JavaConverters._ + substituteLoggerFactory.getEventQueue + .iterator() + .asScala + .map { evt => + CapturedLogEvent( + level = evt.getLevel, + message = MessageFormatter.arrayFormat(evt.getMessage, evt.getArgumentArray).getMessage, + cause = Option(evt.getThrowable), + marker = Option(evt.getMarker)) + } + .toList + } /** * Clear the log entries. */ - def clearLog(): Unit = loggingAdapter.clearLog() + def clearLog(): Unit = + substituteLoggerFactory.getEventQueue.clear() override private[akka] def onUnhandled(msg: T): Unit = unhandled = msg :: unhandled diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/TestAppender.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/TestAppender.scala new file mode 100644 index 0000000000..b589c9224f --- /dev/null +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/TestAppender.scala @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2019 Lightbend Inc. + */ + +package akka.actor.testkit.typed.internal + +import akka.actor.testkit.typed.LoggingEvent +import akka.annotation.InternalApi +import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.classic.spi.ThrowableProxy +import ch.qos.logback.core.AppenderBase + +/** + * INTERNAL API + * + * The `TestAppender` emits the logging events to the registered [[LoggingEventFilterImpl]], which + * are added and removed to the appender dynamically from tests. + * + * `TestAppender` is currently requiring Logback as SLF4J implementation. + * Similar can probably be implemented with other backends, such as Log4j2. + */ +@InternalApi private[akka] object TestAppender { + import LogbackUtil._ + + private val TestAppenderName = "AkkaTestAppender" + + def setupTestAppender(loggerName: String): Unit = { + val logbackLogger = getLogbackLogger(loggerName) + logbackLogger.getAppender(TestAppenderName) match { + case null => + val testAppender = new TestAppender + testAppender.setName(TestAppenderName) + testAppender.setContext(logbackLogger.getLoggerContext) + testAppender.start() + logbackLogger.addAppender(testAppender) + case _: TestAppender => + // ok, already setup + case other => + throw new IllegalStateException(s"Unexpected $TestAppenderName already added: $other") + } + } + + def addFilter(loggerName: String, filter: LoggingEventFilterImpl): Unit = + getTestAppender(loggerName).addTestFilter(filter) + + def removeFilter(loggerName: String, filter: LoggingEventFilterImpl): Unit = + getTestAppender(loggerName).removeTestFilter(filter) + + private def getTestAppender(loggerName: String): TestAppender = { + val logger = getLogbackLogger(loggerName) + logger.getAppender(TestAppenderName) match { + case testAppender: TestAppender => testAppender + case null => + throw new IllegalStateException(s"No $TestAppenderName was setup for logger [${logger.getName}]") + case other => + throw new IllegalStateException( + s"Unexpected $TestAppenderName already added for logger [${logger.getName}]: $other") + } + } +} + +/** + * INTERNAL API + */ +@InternalApi private[akka] class TestAppender extends AppenderBase[ILoggingEvent] { + import LogbackUtil._ + + private var filters: List[LoggingEventFilterImpl] = Nil + + // invocations are synchronized via doAppend in AppenderBase + override def append(event: ILoggingEvent): Unit = { + import akka.util.ccompat.JavaConverters._ + + val throwable = event.getThrowableProxy match { + case p: ThrowableProxy => + Option(p.getThrowable) + case _ => None + } + + val loggingEvent = LoggingEvent( + level = convertLevel(event.getLevel), + message = event.getFormattedMessage, + loggerName = event.getLoggerName, + threadName = event.getThreadName, + timeStamp = event.getTimeStamp, + marker = Option(event.getMarker), + throwable = throwable, + mdc = event.getMDCPropertyMap.asScala.toMap) + + filter(loggingEvent) + } + + private def filter(event: LoggingEvent): Boolean = { + filters.exists(f => + try { + f.apply(event) + } catch { + case _: Exception => false + }) + } + + def addTestFilter(filter: LoggingEventFilterImpl): Unit = synchronized { + filters ::= filter + } + + def removeTestFilter(filter: LoggingEventFilterImpl): Unit = synchronized { + @scala.annotation.tailrec + def removeFirst( + list: List[LoggingEventFilterImpl], + zipped: List[LoggingEventFilterImpl] = Nil): List[LoggingEventFilterImpl] = + list match { + case head :: tail if head == filter => tail.reverse_:::(zipped) + case head :: tail => removeFirst(tail, head :: zipped) + case Nil => filters // filter not found, just return original list + } + filters = removeFirst(filters) + } + +} diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/javadsl/LogCapturing.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/javadsl/LogCapturing.scala new file mode 100644 index 0000000000..c65ddbe562 --- /dev/null +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/javadsl/LogCapturing.scala @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 Lightbend Inc. + */ + +package akka.actor.testkit.typed.javadsl + +import scala.util.control.NonFatal + +import akka.actor.testkit.typed.internal.CapturingAppender +import org.junit.rules.TestRule +import org.junit.runner.Description +import org.junit.runners.model.Statement +import org.slf4j.LoggerFactory + +/** + * JUnit `TestRule` to make log lines appear only when the test failed. + * + * Use this in test by adding a public field annotated with `@TestRule`: + * {{{ + * @Rule public final LogCapturing logCapturing = new LogCapturing(); + * }}} + * + * Requires Logback and configuration like the following the logback-test.xml: + * + * {{{ + * + * + * + * + * + * + * + * + * + * }}} + */ +final class LogCapturing extends TestRule { + // eager access of CapturingAppender to fail fast if misconfigured + private val capturingAppender = CapturingAppender.get("") + + private val myLogger = LoggerFactory.getLogger(classOf[LogCapturing]) + + override def apply(base: Statement, description: Description): Statement = { + new Statement { + override def evaluate(): Unit = { + try { + myLogger.info(s"Logging started for test [${description.getClassName}: ${description.getMethodName}]") + base.evaluate() + myLogger.info( + s"Logging finished for test [${description.getClassName}: ${description.getMethodName}] that was successful") + } catch { + case NonFatal(e) => + println( + s"--> [${Console.BLUE}${description.getClassName}: ${description.getMethodName}${Console.RESET}] " + + s"Start of log messages of test that failed with ${e.getMessage}") + capturingAppender.flush() + println( + s"<-- [${Console.BLUE}${description.getClassName}: ${description.getMethodName}${Console.RESET}] " + + s"End of log messages of test that failed with ${e.getMessage}") + throw e + } finally { + capturingAppender.clear() + } + } + } + } +} diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/javadsl/LoggingEventFilter.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/javadsl/LoggingEventFilter.scala new file mode 100644 index 0000000000..462bb1807f --- /dev/null +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/javadsl/LoggingEventFilter.scala @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2019 Lightbend Inc. + */ + +package akka.actor.testkit.typed.javadsl + +import java.util.function.Supplier + +import akka.actor.testkit.typed.LoggingEvent +import akka.actor.testkit.typed.internal.LoggingEventFilterImpl +import akka.actor.typed.ActorSystem +import akka.annotation.DoNotInherit +import org.slf4j.event.Level + +/** + * Facilities for selectively filtering out expected events from logging + * to verify that they were logged. + * + * Requires Logback. + * + * See the static factory methods as starting point for creating `LoggingEventFilter`. + * + * Not for user extension. + */ +@DoNotInherit abstract class LoggingEventFilter { + + /** + * Number of events the filter is supposed to match. By default 1. + */ + def withOccurrences(newOccurrences: Int): LoggingEventFilter + + /** + * Matching events with the given log level. + */ + def withLogLevel(newLogLevel: Level): LoggingEventFilter + + /** + * Matching events with the given logger name or sub-names in the same way + * as configuration loggers are configured in logback.xml. + * By default the root logger is used. + */ + def withLoggerName(newLoggerName: String): LoggingEventFilter + + /** + * Matching events that have "akkaSource" MDC value equal to the given value. + * "akkaSource" is typically the actor path. + */ + def withSource(newSource: String): LoggingEventFilter + + /** + * Matching events with a message that contains the given value. + */ + def withMessageContains(newMessageContains: String): LoggingEventFilter + + /** + * Matching events with a message that matches the given regular expression. + */ + def withMessageRegex(newMessageRegex: String): LoggingEventFilter + + /** + * Matching events with an included `throwable` that is a class or subclass of the given + * `Throwable` class. + */ + def withCause(newCause: Class[_ <: Throwable]): LoggingEventFilter + + /** + * Matching events with MDC containing all entries of the given `Map`. + * The event MDC may have more entries than the given `Map`. + */ + def withMdc(newMdc: java.util.Map[String, String]): LoggingEventFilter + + /** + * Matching events for which the supplied function returns `true`. + */ + def withCustom(newCustom: Function[LoggingEvent, Boolean]): LoggingEventFilter + // this is a Scala Function, ^ but that can be used with lambda from Java + + /** + * @return `true` if the event matches the conditions of the filter. + */ + def matches(event: LoggingEvent): Boolean + + /** + * Apply this filter while executing the given code block. + * Assert that this filter has matched within the configured `akka.actor.testkit.typed.filter-leeway` + * as often as requested by its `occurrences` parameter specifies. + * + * Care is taken to remove the filter when the block is finished or aborted. + */ + def intercept[T](system: ActorSystem[_], code: Supplier[T]): T + +} + +/** + * Facilities for selectively matching expected events from logging. + * + * Requires Logback. + */ +object LoggingEventFilter { + + /** + * An empty filter that doesn't match any events. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def empty: LoggingEventFilter = LoggingEventFilterImpl.empty + + /** + * Create a filter for events with a log message + * that contains the given `messageIncludes`. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def messageContains(str: String): LoggingEventFilter = + empty.withMessageContains(str) + + /** + * Create a filter for TRACE level events with a log message + * that contains the given `messageIncludes`. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def trace(messageIncludes: String): LoggingEventFilter = + messageContains(messageIncludes).withLogLevel(Level.TRACE) + + /** + * Create a filter for DEBUG level events with a log message + * that contains the given `messageIncludes`. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def debug(messageIncludes: String): LoggingEventFilter = + messageContains(messageIncludes).withLogLevel(Level.DEBUG) + + /** + * Create a filter for INFO level events with a log message + * that contains the given `messageIncludes`. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def info(messageIncludes: String): LoggingEventFilter = + messageContains(messageIncludes).withLogLevel(Level.INFO) + + /** + * Create a filter for WARN level events with a log message + * that contains the given `messageIncludes`. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def warn(messageIncludes: String): LoggingEventFilter = + messageContains(messageIncludes).withLogLevel(Level.WARN) + + /** + * Create a filter for WARN level events with a an included + * `throwable` that is a class or subclass of the given + * * `Throwable` class. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def warn(causeClass: Class[Throwable]): LoggingEventFilter = + empty.withLogLevel(Level.WARN).withCause(causeClass) + + /** + * Create a filter for ERROR level events with a log message + * that contains the given `messageIncludes`. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def error(messageIncludes: String): LoggingEventFilter = + messageContains(messageIncludes).withLogLevel(Level.ERROR) + + /** + * Create a filter for WARN level events with a an included + * `throwable` that is a class or subclass of the given + * * `Throwable` class. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def error(causeClass: Class[_ <: Throwable]): LoggingEventFilter = + empty.withLogLevel(Level.ERROR).withCause(causeClass) + + /** + * Create a custom event filter. The filter will match those events for + * which for which the supplied function returns `true`. + */ + def custom(test: Function[LoggingEvent, Boolean]): LoggingEventFilter = + empty.withCustom(test) // this is a Scala Function, but that can be used with lambda from Java + + /** + * Filter for the logging of dead letters. + */ + def deadLetters(): LoggingEventFilter = + empty.withLogLevel(Level.INFO).withMessageRegex(".*was not delivered.*dead letters encountered.*") +} diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/scaladsl/LogCapturing.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/scaladsl/LogCapturing.scala new file mode 100644 index 0000000000..47a0a965a3 --- /dev/null +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/scaladsl/LogCapturing.scala @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2009-2019 Lightbend Inc. + */ + +package akka.actor.testkit.typed.scaladsl + +import scala.util.control.NonFatal + +import akka.actor.testkit.typed.internal.CapturingAppender +import org.scalatest.BeforeAndAfterAll +import org.scalatest.Outcome +import org.scalatest.TestSuite +import org.slf4j.LoggerFactory + +/** + * Mixin this trait to a ScalaTest test to make log lines appear only when the test failed. + * + * Requires Logback and configuration like the following the logback-test.xml: + * + * {{{ + * + * + * + * + * + * + * + * + * + * }}} + */ +trait LogCapturing extends BeforeAndAfterAll { self: TestSuite => + + // eager access of CapturingAppender to fail fast if misconfigured + private val capturingAppender = CapturingAppender.get("") + + private val myLogger = LoggerFactory.getLogger(classOf[LogCapturing]) + + override protected def afterAll(): Unit = { + try { + super.afterAll() + } catch { + case NonFatal(e) => + myLogger.error("Exception from afterAll", e) + capturingAppender.flush() + } finally { + capturingAppender.clear() + } + } + + abstract override def withFixture(test: NoArgTest): Outcome = { + myLogger.info(s"Logging started for test [${self.getClass.getName}: ${test.name}]") + val res = test() + myLogger.info(s"Logging finished for test [${self.getClass.getName}: ${test.name}] that [$res]") + + if (!(res.isSucceeded || res.isPending)) { + println( + s"--> [${Console.BLUE}${self.getClass.getName}: ${test.name}${Console.RESET}] Start of log messages of test that [$res]") + capturingAppender.flush() + println( + s"<-- [${Console.BLUE}${self.getClass.getName}: ${test.name}${Console.RESET}] End of log messages of test that [$res]") + } + + res + } +} diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/scaladsl/LoggingEventFilter.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/scaladsl/LoggingEventFilter.scala new file mode 100644 index 0000000000..7eadc02da8 --- /dev/null +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/scaladsl/LoggingEventFilter.scala @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2019 Lightbend Inc. + */ + +package akka.actor.testkit.typed.scaladsl + +import scala.reflect.ClassTag + +import akka.actor.testkit.typed.LoggingEvent +import akka.actor.testkit.typed.internal.LoggingEventFilterImpl +import akka.actor.typed.ActorSystem +import akka.annotation.DoNotInherit +import org.slf4j.event.Level + +/** + * Facilities for selectively filtering out expected events from logging + * to verify that they were logged. + * + * Requires Logback. + * + * See the companion object for convenient factory methods. + * + * Not for user extension. + */ +@DoNotInherit trait LoggingEventFilter { + + /** + * Number of events the filter is supposed to match. By default 1. + */ + def withOccurrences(newOccurrences: Int): LoggingEventFilter + + /** + * Matching events with the given log level. + */ + def withLogLevel(newLogLevel: Level): LoggingEventFilter + + /** + * Matching events with the given logger name or sub-names in the same way + * as configuration loggers are configured in logback.xml. + * By default the root logger is used. + */ + def withLoggerName(newLoggerName: String): LoggingEventFilter + + /** + * Matching events that have "akkaSource" MDC value equal to the given value. + * "akkaSource" is typically the actor path. + */ + def withSource(newSource: String): LoggingEventFilter + + /** + * Matching events with a message that contains the given value. + */ + def withMessageContains(newMessageContains: String): LoggingEventFilter + + /** + * Matching events with a message that matches the given regular expression. + */ + def withMessageRegex(newMessageRegex: String): LoggingEventFilter + + /** + * Matching events with an included `throwable` that is a class or subclass of the given + * `Throwable` `ClassTag`. + */ + def withCause[A <: Throwable: ClassTag]: LoggingEventFilter + + /** + * Matching events with MDC containing all entries of the given `Map`. + * The event MDC may have more entries than the given `Map`. + */ + def withMdc(newMdc: Map[String, String]): LoggingEventFilter + + /** + * Matching events for which the supplied function returns`true`. + */ + def withCustom(newCustom: Function[LoggingEvent, Boolean]): LoggingEventFilter + + /** + * @return `true` if the event matches the conditions of the filter. + */ + def matches(event: LoggingEvent): Boolean + + /** + * Apply this filter while executing the given code block. + * Assert that this filter has matched as often as requested by its + * `occurrences` parameter specifies. + * + * Care is taken to remove the filter when the block is finished or aborted. + */ + def intercept[T](code: => T)(implicit system: ActorSystem[_]): T + +} + +/** + * Facilities for selectively matching expected events from logging. + * + * Requires Logback. + */ +object LoggingEventFilter { + + /** + * An empty filter that doesn't match any events. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def empty: LoggingEventFilter = LoggingEventFilterImpl.empty + + /** + * Create a filter for events with a log message + * that contains the given `messageIncludes`. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def messageContains(str: String): LoggingEventFilter = + empty.withMessageContains(str) + + /** + * Create a filter for TRACE level events with a log message + * that contains the given `messageIncludes`. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def trace(messageIncludes: String): LoggingEventFilter = + messageContains(messageIncludes).withLogLevel(Level.TRACE) + + /** + * Create a filter for DEBUG level events with a log message + * that contains the given `messageIncludes`. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def debug(messageIncludes: String): LoggingEventFilter = + messageContains(messageIncludes).withLogLevel(Level.DEBUG) + + /** + * Create a filter for INFO level events with a log message + * that contains the given `messageIncludes`. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def info(messageIncludes: String): LoggingEventFilter = + messageContains(messageIncludes).withLogLevel(Level.INFO) + + /** + * Create a filter for WARN level events with a log message + * that contains the given `messageIncludes`. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def warn(messageIncludes: String): LoggingEventFilter = + messageContains(messageIncludes).withLogLevel(Level.WARN) + + /** + * Create a filter for WARN level events with a an included + * `throwable` that is a class or subclass of the given + * `Throwable` `ClassTag`. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def warn[A <: Throwable: ClassTag]: LoggingEventFilter = + empty.withLogLevel(Level.WARN).withCause[A] + + /** + * Create a filter for ERROR level events with a log message + * that contains the given `messageIncludes`. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def error(messageIncludes: String): LoggingEventFilter = + messageContains(messageIncludes).withLogLevel(Level.ERROR) + + /** + * Create a filter for WARN level events with a an included + * `throwable` that is a class or subclass of the given + * `Throwable` `ClassTag`. + * + * More conditions can be added to the returned [LoggingEventFilter]. + */ + def error[A <: Throwable: ClassTag]: LoggingEventFilter = + empty.withLogLevel(Level.ERROR).withCause[A] + + /** + * Create a custom event filter. The filter will match those events for + * which the supplied function returns `true`. + */ + def custom(test: Function[LoggingEvent, Boolean]): LoggingEventFilter = + empty.withCustom(test) + + /** + * Filter for the logging of dead letters. + */ + def deadLetters(): LoggingEventFilter = + empty.withLogLevel(Level.INFO).withMessageRegex(".*was not delivered.*dead letters encountered.*") +} diff --git a/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/ActorTestKitTest.java b/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/ActorTestKitTest.java index 14aa41ef6e..0550e0c628 100644 --- a/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/ActorTestKitTest.java +++ b/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/ActorTestKitTest.java @@ -7,6 +7,7 @@ package akka.actor.testkit.typed.javadsl; import akka.Done; import akka.actor.typed.javadsl.Behaviors; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -21,6 +22,8 @@ public class ActorTestKitTest extends JUnitSuite { @ClassRule public static TestKitJunitResource testKit = new TestKitJunitResource(); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + @Test public void systemNameShouldComeFromTestClassViaJunitResource() { assertEquals("ActorTestKitTest", testKit.system().name()); diff --git a/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/BehaviorTestKitTest.java b/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/BehaviorTestKitTest.java index f3a9f6659a..578d968895 100644 --- a/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/BehaviorTestKitTest.java +++ b/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/BehaviorTestKitTest.java @@ -11,10 +11,10 @@ import akka.actor.typed.ActorRef; import akka.actor.typed.Behavior; import akka.actor.typed.Props; import akka.actor.typed.javadsl.Behaviors; -import akka.event.Logging; import org.junit.Ignore; import org.junit.Test; import org.scalatest.junit.JUnitSuite; +import org.slf4j.event.Level; import java.util.Arrays; import java.util.Collections; @@ -250,7 +250,7 @@ public class BehaviorTestKitTest extends JUnitSuite { test.run(new Log(what)); final List allLogEntries = test.getAllLogEntries(); assertEquals(1, allLogEntries.size()); - assertEquals(new CapturedLogEvent(Logging.InfoLevel(), what), allLogEntries.get(0)); + assertEquals(new CapturedLogEvent(Level.INFO, what), allLogEntries.get(0)); } @Test diff --git a/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/LoggingEventFilterTest.java b/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/LoggingEventFilterTest.java new file mode 100644 index 0000000000..144b64aa56 --- /dev/null +++ b/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/LoggingEventFilterTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2018-2019 Lightbend Inc. + */ + +package akka.actor.testkit.typed.javadsl; + +import akka.actor.testkit.typed.LoggingEvent; +import akka.actor.testkit.typed.TestException; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.scalatest.junit.JUnitSuite; +import org.slf4j.event.Level; + +import java.util.Collections; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class LoggingEventFilterTest extends JUnitSuite { + + @ClassRule public static TestKitJunitResource testKit = new TestKitJunitResource(); + + @Rule public final LogCapturing logCapturing = new LogCapturing(); + + private LoggingEvent errorNoCause() { + return LoggingEvent.create( + Level.ERROR, + getClass().getName(), + Thread.currentThread().getName(), + "this is an error", + System.currentTimeMillis(), + Optional.empty(), + Optional.empty(), + Collections.emptyMap()); + } + + private LoggingEvent errorWithCause(Throwable cause) { + return LoggingEvent.create( + Level.ERROR, + getClass().getName(), + Thread.currentThread().getName(), + "this is an error", + System.currentTimeMillis(), + Optional.empty(), + Optional.of(cause), + Collections.emptyMap()); + } + + @Test + public void filterErrorsWithMatchingMessage() { + assertTrue( + LoggingEventFilter.error("an error").matches(errorWithCause(new TestException("exc")))); + assertTrue(LoggingEventFilter.error("an error").matches(errorNoCause())); + assertFalse(LoggingEventFilter.error("another error").matches(errorNoCause())); + } + + @Test + public void filterErrorsWithMatchingCause() { + assertTrue( + LoggingEventFilter.error(TestException.class) + .matches(errorWithCause(new TestException("exc")))); + assertFalse( + LoggingEventFilter.error(TestException.class) + .matches(errorWithCause(new RuntimeException("exc")))); + assertTrue( + LoggingEventFilter.error("an error") + .withCause(TestException.class) + .matches(errorWithCause(new TestException("exc")))); + assertFalse( + LoggingEventFilter.error("another error") + .withCause(TestException.class) + .matches(errorWithCause(new TestException("exc")))); + } + + @Test + public void filterErrorsWithMatchingCustomFunction() { + assertTrue(LoggingEventFilter.custom(event -> true).matches(errorNoCause())); + assertFalse( + LoggingEventFilter.custom(event -> event.getMdc().containsKey("aKey")) + .matches(errorNoCause())); + } +} diff --git a/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/TestProbeTest.java b/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/TestProbeTest.java index f1e3edf0b3..0bda428b48 100644 --- a/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/TestProbeTest.java +++ b/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/TestProbeTest.java @@ -11,6 +11,7 @@ import java.util.List; import akka.actor.testkit.typed.scaladsl.TestProbeSpec; import akka.actor.testkit.typed.scaladsl.TestProbeSpec.*; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -20,6 +21,8 @@ public class TestProbeTest extends JUnitSuite { @ClassRule public static TestKitJunitResource testKit = new TestKitJunitResource(); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + @Test public void testReceiveMessage() { TestProbe probe = TestProbe.create(testKit.system()); diff --git a/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/JunitIntegrationExampleTest.java b/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/JunitIntegrationExampleTest.java index ce7c380c07..707ee7ddde 100644 --- a/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/JunitIntegrationExampleTest.java +++ b/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/JunitIntegrationExampleTest.java @@ -9,16 +9,20 @@ import static jdocs.akka.actor.testkit.typed.javadsl.AsyncTestingExampleTest.Pon import static jdocs.akka.actor.testkit.typed.javadsl.AsyncTestingExampleTest.echoActor; // #junit-integration +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import akka.actor.typed.ActorRef; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; public class JunitIntegrationExampleTest { @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + @Test public void testSomething() { ActorRef pinger = testKit.spawn(echoActor(), "ping"); diff --git a/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/ManualTimerExampleTest.java b/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/ManualTimerExampleTest.java index 9313065591..33eae704ca 100644 --- a/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/ManualTimerExampleTest.java +++ b/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/ManualTimerExampleTest.java @@ -6,10 +6,12 @@ package jdocs.akka.actor.testkit.typed.javadsl; // #manual-scheduling-simple +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.typed.Behavior; import akka.actor.testkit.typed.javadsl.ManualTime; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import org.junit.ClassRule; +import org.junit.Rule; import org.scalatest.junit.JUnitSuite; import java.time.Duration; @@ -24,6 +26,8 @@ public class ManualTimerExampleTest extends JUnitSuite { @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(ManualTime.config()); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + private final ManualTime manualTime = ManualTime.get(testKit.system()); static final class Tick {} diff --git a/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/SyncTestingExampleTest.java b/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/SyncTestingExampleTest.java index 08f08f0967..7ea37ff1d9 100644 --- a/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/SyncTestingExampleTest.java +++ b/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/SyncTestingExampleTest.java @@ -11,10 +11,10 @@ import akka.actor.testkit.typed.javadsl.Effects; import akka.actor.testkit.typed.javadsl.TestInbox; import akka.actor.typed.*; import akka.actor.typed.javadsl.*; -import akka.event.Logging; import java.util.HashMap; import java.util.List; import java.util.Optional; +import org.slf4j.event.Level; // #imports import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -172,7 +172,7 @@ public class SyncTestingExampleTest extends JUnitSuite { assertEquals(1, allLogEntries.size()); CapturedLogEvent expectedLogEvent = new CapturedLogEvent( - Logging.InfoLevel(), + Level.INFO, "Saying hello to Inboxer", Optional.empty(), Optional.empty(), diff --git a/akka-actor-testkit-typed/src/test/resources/logback-test.xml b/akka-actor-testkit-typed/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..e90cc999d2 --- /dev/null +++ b/akka-actor-testkit-typed/src/test/resources/logback-test.xml @@ -0,0 +1,35 @@ + + + + + + + + DEBUG + DENY + + + %date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n + + + + + + + + + + + + + + + diff --git a/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/ActorTestKitSpec.scala b/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/ActorTestKitSpec.scala index 78fd001a4b..4db9c9affc 100644 --- a/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/ActorTestKitSpec.scala +++ b/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/ActorTestKitSpec.scala @@ -14,7 +14,7 @@ import org.scalatest.Matchers import org.scalatest.WordSpec import org.scalatest.WordSpecLike -class ActorTestKitSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class ActorTestKitSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { "the Scala testkit" should { @@ -91,7 +91,7 @@ class ActorTestKitSpec extends ScalaTestWithActorTestKit with WordSpecLike { } // derivative classes should also work fine (esp the naming part -abstract class MyBaseSpec extends ScalaTestWithActorTestKit with Matchers with WordSpecLike +abstract class MyBaseSpec extends ScalaTestWithActorTestKit with Matchers with WordSpecLike with LogCapturing class MyConcreteDerivateSpec extends MyBaseSpec { "A derivative test" should { @@ -116,7 +116,7 @@ class MyConcreteDerivateSpec extends MyBaseSpec { } -class CompositionSpec extends WordSpec with Matchers with BeforeAndAfterAll { +class CompositionSpec extends WordSpec with Matchers with BeforeAndAfterAll with LogCapturing { val testKit = ActorTestKit() override def afterAll(): Unit = { diff --git a/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/BehaviorTestKitSpec.scala b/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/BehaviorTestKitSpec.scala index b11c988d93..d7b94f7474 100644 --- a/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/BehaviorTestKitSpec.scala +++ b/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/BehaviorTestKitSpec.scala @@ -11,11 +11,11 @@ import akka.actor.testkit.typed.{ CapturedLogEvent, Effect } import akka.actor.testkit.typed.Effect._ import akka.actor.testkit.typed.scaladsl.BehaviorTestKitSpec.{ Child, Father } import akka.actor.testkit.typed.scaladsl.BehaviorTestKitSpec.Father._ -import akka.event.Logging import org.scalatest.{ Matchers, WordSpec } - import scala.reflect.ClassTag +import org.slf4j.event.Level + object BehaviorTestKitSpec { object Father { @@ -121,7 +121,7 @@ object BehaviorTestKitSpec { } -class BehaviorTestKitSpec extends WordSpec with Matchers { +class BehaviorTestKitSpec extends WordSpec with Matchers with LogCapturing { private val props = Props.empty.withDispatcherFromConfig("cat") @@ -183,14 +183,14 @@ class BehaviorTestKitSpec extends WordSpec with Matchers { val what = "Hello!" val testkit = BehaviorTestKit[Father.Command](Father.init) testkit.run(Log(what)) - testkit.logEntries() shouldBe Seq(CapturedLogEvent(Logging.InfoLevel, what)) + testkit.logEntries() shouldBe Seq(CapturedLogEvent(Level.INFO, what)) } "allow clearing log messages issued by behavior" in { - val what = "Hello!" + val what = "Hi!" val testkit = BehaviorTestKit[Father.Command](Father.init) testkit.run(Log(what)) - testkit.logEntries() shouldBe Seq(CapturedLogEvent(Logging.InfoLevel, what)) + testkit.logEntries() shouldBe Seq(CapturedLogEvent(Level.INFO, what)) testkit.clearLog() testkit.logEntries() shouldBe Seq.empty } diff --git a/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/LoggingEventFilterSpec.scala b/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/LoggingEventFilterSpec.scala new file mode 100644 index 0000000000..0660335368 --- /dev/null +++ b/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/LoggingEventFilterSpec.scala @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2009-2019 Lightbend Inc. + */ + +package akka.actor.testkit.typed.scaladsl + +import akka.actor.testkit.typed.LoggingEvent +import org.scalatest.WordSpecLike +import org.slf4j.event.Level + +class LoggingEventFilterSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { + + private class AnError extends Exception + private def errorNoCause = + LoggingEvent( + level = Level.ERROR, + loggerName = getClass.getName, + message = "this is an error", + threadName = Thread.currentThread().getName, + timeStamp = System.currentTimeMillis()) + private def errorWithCause(cause: Throwable) = + LoggingEvent( + level = Level.ERROR, + loggerName = getClass.getName, + message = "this is an error", + threadName = Thread.currentThread().getName, + timeStamp = System.currentTimeMillis(), + marker = None, + throwable = Option(cause), + mdc = Map.empty) + private def warningNoCause = + LoggingEvent( + level = Level.WARN, + loggerName = getClass.getName, + message = "this is a warning", + threadName = Thread.currentThread().getName, + timeStamp = System.currentTimeMillis()) + private def warningWithCause(cause: Throwable) = + LoggingEvent( + level = Level.WARN, + loggerName = getClass.getName, + message = "this is a warning", + threadName = Thread.currentThread().getName, + timeStamp = System.currentTimeMillis(), + marker = None, + throwable = Option(cause), + mdc = Map.empty) + private def warningWithSource(source: String) = + LoggingEvent( + level = Level.WARN, + loggerName = getClass.getName, + message = "this is a warning", + threadName = Thread.currentThread().getName, + timeStamp = System.currentTimeMillis(), + marker = None, + throwable = None, + mdc = Map("akkaSource" -> source)) + + "The LoggingEventFilter.error" must { + "filter errors without cause" in { + val filter = LoggingEventFilter.empty.withLogLevel(Level.ERROR) + filter.matches(errorNoCause) should ===(true) + } + + "filter errors with cause" in { + val filter = LoggingEventFilter.empty.withLogLevel(Level.ERROR) + filter.matches(errorWithCause(new AnError)) should ===(true) + } + + "filter error with matching message" in { + LoggingEventFilter.error("an error").matches(errorWithCause(new AnError)) should ===(true) + LoggingEventFilter.error("an error").matches(errorNoCause) should ===(true) + LoggingEventFilter.error("another error").matches(errorNoCause) should ===(false) + } + + "filter with matching MDC" in { + LoggingEventFilter.empty.withMdc(Map("a" -> "A")).matches(errorNoCause.copy(mdc = Map("a" -> "A"))) should ===( + true) + LoggingEventFilter.empty + .withMdc(Map("a" -> "A", "b" -> "B")) + .matches(errorNoCause.copy(mdc = Map("a" -> "A", "b" -> "B"))) should ===(true) + LoggingEventFilter.empty + .withMdc(Map("a" -> "A")) + .matches(errorNoCause.copy(mdc = Map("a" -> "A", "b" -> "B"))) should ===(true) + LoggingEventFilter.empty + .withMdc(Map("a" -> "A", "b" -> "B")) + .matches(errorNoCause.copy(mdc = Map("a" -> "A"))) should ===(false) + LoggingEventFilter.empty.withMdc(Map("a" -> "A", "b" -> "B")).matches(errorNoCause) should ===(false) + } + } + + "The LoggingEventFilter with cause" must { + "not filter errors without cause" in { + val filter = LoggingEventFilter.error[AnError] + filter.matches(errorNoCause) should ===(false) + } + + "not filter errors with an unrelated cause" in { + object AnotherError extends Exception + val filter = LoggingEventFilter.error[AnError] + filter.matches(errorWithCause(AnotherError)) should ===(false) + } + + "filter errors with a matching cause" in { + val filter = LoggingEventFilter.error[AnError] + filter.matches(errorWithCause(new AnError)) should ===(true) + } + "filter errors with a matching cause and message" in { + val filter = LoggingEventFilter.error("this is an error").withCause[AnError] + filter.matches(errorWithCause(new AnError)) should ===(true) + } + } + + "The LoggingEventFilter.warn" must { + "filter warnings without cause" in { + val filter = LoggingEventFilter.empty.withLogLevel(Level.WARN) + filter.matches(warningNoCause) should ===(true) + } + "filter warning with cause" in { + val filter = LoggingEventFilter.empty.withLogLevel(Level.WARN) + filter.matches(warningWithCause(new AnError)) should ===(true) + } + "filter warning with matching message" in { + LoggingEventFilter.warn("this is a warning").matches(warningWithCause(new AnError)) should ===(true) + LoggingEventFilter.warn("this is another warning").matches(warningWithCause(new AnError)) should ===(false) + } + "filter warning with matching source" in { + val source = "akka://Sys/user/foo" + LoggingEventFilter.empty + .withLogLevel(Level.WARN) + .withSource(source) + .matches(warningWithSource(source)) should ===(true) + LoggingEventFilter.empty + .withLogLevel(Level.WARN) + .withSource("akka://Sys/user/bar") + .matches(warningWithSource(source)) should ===(false) + } + + } + +} diff --git a/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/TestAppenderSpec.scala b/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/TestAppenderSpec.scala new file mode 100644 index 0000000000..a0c704a5a3 --- /dev/null +++ b/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/TestAppenderSpec.scala @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2009-2019 Lightbend Inc. + */ + +package akka.actor.testkit.typed.scaladsl + +import java.util.concurrent.atomic.AtomicInteger + +import akka.actor.testkit.typed.TestException +import org.scalatest.WordSpecLike +import org.slf4j.LoggerFactory + +class TestAppenderSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { + + class AnotherLoggerClass + + private val log = LoggerFactory.getLogger(getClass) + + "TestAppender and LoggingEventFilter" must { + "filter errors without cause" in { + LoggingEventFilter.error("an error").withOccurrences(2).intercept { + log.error("an error") + log.error("an error") + } + } + + "filter errors with cause" in { + LoggingEventFilter.error("err").withCause[TestException].intercept { + log.error("err", TestException("an error")) + } + } + + "filter warnings" in { + LoggingEventFilter.warn("a warning").withOccurrences(2).intercept { + log.error("an error") + log.warn("a warning") + log.error("an error") + log.warn("a warning") + } + } + + "only filter events for given logger name" in { + val count = new AtomicInteger + LoggingEventFilter + .custom({ + case logEvent => + count.incrementAndGet() + logEvent.message == "Hello from right logger" && logEvent.loggerName == classOf[AnotherLoggerClass].getName + }) + .withOccurrences(2) + .withLoggerName(classOf[AnotherLoggerClass].getName) + .intercept { + LoggerFactory.getLogger(classOf[AnotherLoggerClass]).info("Hello from right logger") + log.info("Hello wrong logger") + LoggerFactory.getLogger(classOf[AnotherLoggerClass]).info("Hello from right logger") + } + count.get should ===(2) + } + + "find unexpected events withOccurences(0)" in { + LoggingEventFilter.warn("a warning").withOccurrences(0).intercept { + log.error("an error") + log.warn("another warning") + } + + intercept[AssertionError] { + LoggingEventFilter.warn("a warning").withOccurrences(0).intercept { + log.error("an error") + log.warn("a warning") + log.warn("another warning") + } + }.getMessage should include("Received 1 excess messages") + + intercept[AssertionError] { + LoggingEventFilter.warn("a warning").withOccurrences(0).intercept { + log.warn("a warning") + log.warn("a warning") + } + }.getMessage should include("Received 2 excess messages") + } + + } + +} diff --git a/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/TestProbeSpec.scala b/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/TestProbeSpec.scala index b41041b20f..1909feae3e 100644 --- a/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/TestProbeSpec.scala +++ b/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/TestProbeSpec.scala @@ -10,7 +10,7 @@ import com.typesafe.config.ConfigFactory import scala.concurrent.duration._ import org.scalatest.WordSpecLike -class TestProbeSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class TestProbeSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { import TestProbeSpec._ @@ -181,7 +181,10 @@ object TestProbeSpec { for (n <- 1 to expected) yield EventT(n) } -class TestProbeTimeoutSpec extends ScalaTestWithActorTestKit(TestProbeSpec.timeoutConfig) with WordSpecLike { +class TestProbeTimeoutSpec + extends ScalaTestWithActorTestKit(TestProbeSpec.timeoutConfig) + with WordSpecLike + with LogCapturing { import TestProbeSpec._ diff --git a/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/AsyncTestingExampleSpec.scala b/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/AsyncTestingExampleSpec.scala index 29d759bd8f..f9347ed7a6 100644 --- a/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/AsyncTestingExampleSpec.scala +++ b/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/AsyncTestingExampleSpec.scala @@ -4,6 +4,7 @@ package docs.akka.actor.testkit.typed.scaladsl +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.Scheduler //#test-header import akka.actor.testkit.typed.scaladsl.ActorTestKit @@ -58,7 +59,7 @@ object AsyncTestingExampleSpec { } //#test-header -class AsyncTestingExampleSpec extends WordSpec with BeforeAndAfterAll with Matchers { +class AsyncTestingExampleSpec extends WordSpec with BeforeAndAfterAll with Matchers with LogCapturing { val testKit = ActorTestKit() //#test-header diff --git a/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/ManualTimerExampleSpec.scala b/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/ManualTimerExampleSpec.scala index 67f39c59f6..adc78fdfc1 100644 --- a/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/ManualTimerExampleSpec.scala +++ b/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/ManualTimerExampleSpec.scala @@ -6,13 +6,15 @@ package docs.akka.actor.testkit.typed.scaladsl //#manual-scheduling-simple import scala.concurrent.duration._ + import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.ManualTime import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.scaladsl.Behaviors import org.scalatest.WordSpecLike -class ManualTimerExampleSpec extends ScalaTestWithActorTestKit(ManualTime.config) with WordSpecLike { +class ManualTimerExampleSpec extends ScalaTestWithActorTestKit(ManualTime.config) with WordSpecLike with LogCapturing { val manualTime: ManualTime = ManualTime() diff --git a/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/SyncTestingExampleSpec.scala b/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/SyncTestingExampleSpec.scala index 0aff7f0bb3..e006523cef 100644 --- a/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/SyncTestingExampleSpec.scala +++ b/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/SyncTestingExampleSpec.scala @@ -11,7 +11,7 @@ import akka.actor.testkit.typed.scaladsl.BehaviorTestKit import akka.actor.testkit.typed.scaladsl.TestInbox import akka.actor.typed._ import akka.actor.typed.scaladsl._ -import akka.event.Logging +import org.slf4j.event.Level //#imports import org.scalatest.Matchers import org.scalatest.WordSpec @@ -115,8 +115,7 @@ class SyncTestingExampleSpec extends WordSpec with Matchers { val testKit = BehaviorTestKit(myBehavior) val inbox = TestInbox[String]("Inboxer") testKit.run(LogAndSayHello(inbox.ref)) - - testKit.logEntries() shouldBe Seq(CapturedLogEvent(Logging.InfoLevel, "Saying hello to Inboxer")) + testKit.logEntries() shouldBe Seq(CapturedLogEvent(Level.INFO, "Saying hello to Inboxer")) //#test-check-logging } } diff --git a/akka-actor-tests/src/test/scala/akka/config/ConfigSpec.scala b/akka-actor-tests/src/test/scala/akka/config/ConfigSpec.scala index 34446cf044..b56c08b8e8 100644 --- a/akka-actor-tests/src/test/scala/akka/config/ConfigSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/config/ConfigSpec.scala @@ -12,9 +12,10 @@ import akka.event.Logging.DefaultLogger import akka.testkit.AkkaSpec import com.typesafe.config.ConfigFactory import org.scalatest.Assertions - import scala.concurrent.duration._ +import akka.actor.ExtendedActorSystem + class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference(ActorSystem.findClassLoader())) with Assertions { "The default configuration file (i.e. reference.conf)" must { @@ -164,4 +165,24 @@ class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference(ActorSystem.fin } } } + + "SLF4J Settings" must { + "not be amended for default reference in akka-actor" in { + val dynamicAccess = system.asInstanceOf[ExtendedActorSystem].dynamicAccess + val config = ActorSystem.Settings.amendSlf4jConfig(ConfigFactory.defaultReference(), dynamicAccess) + config.getStringList("akka.loggers").size() should ===(1) + config.getStringList("akka.loggers").get(0) should ===(classOf[DefaultLogger].getName) + config.getString("akka.logging-filter") should ===(classOf[DefaultLoggingFilter].getName) + } + + "not be amended when akka-slf4j is not in classpath" in { + val dynamicAccess = system.asInstanceOf[ExtendedActorSystem].dynamicAccess + val config = ActorSystem.Settings.amendSlf4jConfig( + ConfigFactory.parseString("akka.use-slf4j = on").withFallback(ConfigFactory.defaultReference()), + dynamicAccess) + config.getStringList("akka.loggers").size() should ===(1) + config.getStringList("akka.loggers").get(0) should ===(classOf[DefaultLogger].getName) + config.getString("akka.logging-filter") should ===(classOf[DefaultLoggingFilter].getName) + } + } } diff --git a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ActorContextAskTest.java b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ActorContextAskTest.java index e50f92f3f3..0cf99e3a99 100644 --- a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ActorContextAskTest.java +++ b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ActorContextAskTest.java @@ -4,6 +4,7 @@ package akka.actor.typed.javadsl; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.typed.ActorRef; import akka.actor.typed.Behavior; import akka.testkit.AkkaSpec; @@ -11,6 +12,7 @@ import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import akka.util.Timeout; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -22,6 +24,8 @@ public class ActorContextAskTest extends JUnitSuite { @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(AkkaSpec.testConf()); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + static class Ping { final ActorRef respondTo; diff --git a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ActorContextPipeToSelfTest.java b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ActorContextPipeToSelfTest.java index 6242d84bfc..67793117cc 100644 --- a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ActorContextPipeToSelfTest.java +++ b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ActorContextPipeToSelfTest.java @@ -4,12 +4,14 @@ package akka.actor.typed.javadsl; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import akka.actor.typed.Behavior; import akka.actor.typed.Props; import com.typesafe.config.ConfigFactory; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -28,6 +30,8 @@ public final class ActorContextPipeToSelfTest extends JUnitSuite { "pipe-to-self-spec-dispatcher.executor = thread-pool-executor\n" + "pipe-to-self-spec-dispatcher.type = PinnedDispatcher\n")); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + static final class Msg { final String response; final String selfName; diff --git a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ActorLoggingTest.java b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ActorLoggingTest.java index 3f6671be1a..7eef5e7acc 100644 --- a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ActorLoggingTest.java +++ b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ActorLoggingTest.java @@ -4,6 +4,7 @@ package akka.actor.typed.javadsl; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.typed.ActorRef; import akka.actor.typed.Behavior; @@ -12,6 +13,7 @@ import akka.japi.pf.PFBuilder; import akka.testkit.CustomEventFilter; import com.typesafe.config.ConfigFactory; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; import scala.concurrent.duration.FiniteDuration; @@ -44,6 +46,8 @@ public class ActorLoggingTest extends JUnitSuite { } } + @Rule public final LogCapturing logCapturing = new LogCapturing(); + @Test public void loggingProvidesClassWhereLogWasCalled() { CustomEventFilter eventFilter = @@ -72,9 +76,9 @@ public class ActorLoggingTest extends JUnitSuite { Behaviors.setup( context -> Behaviors.withMdc( - null, + Protocol.class, (message) -> { - Map mdc = new HashMap<>(); + Map mdc = new HashMap<>(); mdc.put("txId", message.getTransactionId()); return mdc; }, diff --git a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/AdapterTest.java b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/AdapterTest.java index b87ccdc7b3..945ab96bcc 100644 --- a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/AdapterTest.java +++ b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/AdapterTest.java @@ -4,7 +4,9 @@ package akka.actor.typed.javadsl; +import akka.actor.testkit.typed.javadsl.LogCapturing; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -240,6 +242,8 @@ public class AdapterTest extends JUnitSuite { public static AkkaJUnitActorSystemResource actorSystemResource = new AkkaJUnitActorSystemResource("ActorSelectionTest", AkkaSpec.testConf()); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + private final ActorSystem system = actorSystemResource.getSystem(); @Test diff --git a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/BehaviorBuilderTest.java b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/BehaviorBuilderTest.java index 6d0f6828fa..288664df70 100644 --- a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/BehaviorBuilderTest.java +++ b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/BehaviorBuilderTest.java @@ -4,9 +4,11 @@ package akka.actor.typed.javadsl; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -25,6 +27,8 @@ public class BehaviorBuilderTest extends JUnitSuite { @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + interface Message {} static final class One implements Message { diff --git a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/InterceptTest.java b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/InterceptTest.java index 5031871439..dfcb075622 100644 --- a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/InterceptTest.java +++ b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/InterceptTest.java @@ -4,11 +4,13 @@ package akka.actor.typed.javadsl; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import akka.actor.typed.*; import akka.testkit.AkkaSpec; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -17,6 +19,8 @@ public class InterceptTest extends JUnitSuite { @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(AkkaSpec.testConf()); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + @Test public void interceptMessage() { final TestProbe interceptProbe = testKit.createTestProbe(); diff --git a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ReceiveBuilderTest.java b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ReceiveBuilderTest.java index 71c302870d..21dc6385a4 100644 --- a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ReceiveBuilderTest.java +++ b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ReceiveBuilderTest.java @@ -4,10 +4,12 @@ package akka.actor.typed.javadsl; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import akka.actor.typed.ActorRef; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -21,6 +23,8 @@ public class ReceiveBuilderTest extends JUnitSuite { @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + @Test public void testMutableCounter() { Behavior mutable = diff --git a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/WatchTest.java b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/WatchTest.java index 8c284ad836..b89067e3b2 100644 --- a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/WatchTest.java +++ b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/WatchTest.java @@ -8,8 +8,10 @@ import java.util.concurrent.CompletionStage; import java.util.concurrent.TimeUnit; import akka.Done; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import org.junit.ClassRule; +import org.junit.Rule; import org.scalatest.junit.JUnitSuite; import org.junit.Test; @@ -24,6 +26,8 @@ public class WatchTest extends JUnitSuite { @ClassRule public static TestKitJunitResource testKit = new TestKitJunitResource(); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + interface Message {} static final class RunTest implements Message { diff --git a/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/BubblingSampleTest.java b/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/BubblingSampleTest.java index daf4efc255..61d4c871ba 100644 --- a/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/BubblingSampleTest.java +++ b/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/BubblingSampleTest.java @@ -4,10 +4,12 @@ package jdocs.akka.typed; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import akka.actor.typed.ActorRef; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -22,6 +24,8 @@ public class BubblingSampleTest extends JUnitSuite { public static final TestKitJunitResource testKit = new TestKitJunitResource("akka.loglevel = off"); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + @Test public void testBubblingSample() throws Exception { ActorRef boss = testKit.spawn(Boss.create(), "upper-management"); diff --git a/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/InteractionPatternsTest.java b/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/InteractionPatternsTest.java index f784cf5d37..e2f84b74e1 100644 --- a/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/InteractionPatternsTest.java +++ b/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/InteractionPatternsTest.java @@ -4,6 +4,7 @@ package jdocs.akka.typed; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.typed.ActorRef; import akka.actor.typed.ActorSystem; @@ -11,6 +12,7 @@ import akka.actor.typed.Behavior; import akka.actor.typed.javadsl.*; import akka.actor.testkit.typed.javadsl.TestProbe; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -647,6 +649,8 @@ public class InteractionPatternsTest extends JUnitSuite { @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + @Test public void fireAndForgetSample() throws Exception { // #fire-and-forget-doit diff --git a/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/MailboxDocTest.java b/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/MailboxDocTest.java index 6d37e5ddcb..cfd661be5d 100644 --- a/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/MailboxDocTest.java +++ b/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/MailboxDocTest.java @@ -5,6 +5,7 @@ package jdocs.akka.typed; import akka.Done; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import akka.actor.typed.ActorRef; @@ -13,6 +14,7 @@ import akka.actor.typed.MailboxSelector; import akka.actor.typed.javadsl.Behaviors; import com.typesafe.config.ConfigFactory; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -22,6 +24,8 @@ public class MailboxDocTest extends JUnitSuite { public static final TestKitJunitResource testKit = new TestKitJunitResource(ConfigFactory.load("mailbox-config-sample.conf")); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + @Test public void startSomeActorsWithDifferentMailboxes() { TestProbe testProbe = testKit.createTestProbe(); diff --git a/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/StashDocTest.java b/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/StashDocTest.java index ec1669340b..465db02db5 100644 --- a/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/StashDocTest.java +++ b/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/StashDocTest.java @@ -5,10 +5,12 @@ package jdocs.akka.typed; import akka.Done; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import akka.actor.typed.ActorRef; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -22,6 +24,8 @@ public class StashDocTest extends JUnitSuite { @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + @Test public void stashingExample() throws Exception { final DB db = diff --git a/akka-actor-typed-tests/src/test/resources/logback-test.xml b/akka-actor-typed-tests/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..9e4d05a8dd --- /dev/null +++ b/akka-actor-typed-tests/src/test/resources/logback-test.xml @@ -0,0 +1,35 @@ + + + + + + + + DEBUG + DENY + + + %date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n + + + + + + + + + + + + + + + diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/ActorContextSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/ActorContextSpec.scala index 4b2b93083c..d7489ecc81 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/ActorContextSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/ActorContextSpec.scala @@ -7,12 +7,15 @@ package akka.actor.typed import akka.actor.InvalidMessageException import akka.actor.typed.scaladsl.Behaviors import akka.actor.testkit.typed.scaladsl.TestProbe - import scala.concurrent.duration._ import scala.reflect.ClassTag + +import akka.actor.UnhandledMessage import akka.actor.testkit.typed.TestException +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit -import akka.testkit.EventFilter +import akka.actor.testkit.typed.scaladsl.LogCapturing +import akka.actor.typed.eventstream.EventStream import org.scalatest.WordSpecLike object ActorSpecMessages { @@ -63,13 +66,7 @@ object ActorSpecMessages { } -abstract class ActorContextSpec extends ScalaTestWithActorTestKit(""" - akka.loggers = [akka.testkit.TestEventListener] - """) with WordSpecLike { - - // FIXME #24348: eventfilter support in typed testkit - import scaladsl.adapter._ - implicit val classicSystem = system.toClassic +abstract class ActorContextSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { import ActorSpecMessages._ @@ -89,6 +86,8 @@ abstract class ActorContextSpec extends ScalaTestWithActorTestKit(""" } "canonicalize behaviors" in { + val unhandledProbe = createTestProbe[UnhandledMessage]() + system.eventStream ! EventStream.Subscribe(unhandledProbe.ref) val probe = TestProbe[Event]() lazy val behavior: Behavior[Command] = Behaviors @@ -112,11 +111,11 @@ abstract class ActorContextSpec extends ScalaTestWithActorTestKit(""" val actor = spawn(behavior) actor ! Ping probe.expectMessage(Pong) - // unhandled gives warning from EventFilter - EventFilter.warning(occurrences = 1).intercept { - actor ! Miss - probe.expectMessage(Missed) - } + + actor ! Miss + probe.expectMessage(Missed) + unhandledProbe.receiveMessage() + actor ! Renew(probe.ref) probe.expectMessage(Renewed) actor ! Ping @@ -140,7 +139,7 @@ abstract class ActorContextSpec extends ScalaTestWithActorTestKit(""" val behavior = Behaviors.supervise(internal).onFailure(SupervisorStrategy.restart) val actor = spawn(behavior) - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { actor ! Fail } probe.expectMessage(ReceivedSignal(PreRestart)) @@ -204,7 +203,7 @@ abstract class ActorContextSpec extends ScalaTestWithActorTestKit(""" val parentRef = spawn(parent) val childRef = probe.expectMessageType[ChildMade].ref - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { childRef ! Fail } probe.expectMessage(GotChildSignal(PreRestart)) @@ -261,7 +260,7 @@ abstract class ActorContextSpec extends ScalaTestWithActorTestKit(""" val actor = spawn(behavior) actor ! Ping probe.expectMessage(1) - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { actor ! Fail } actor ! Ping @@ -287,7 +286,7 @@ abstract class ActorContextSpec extends ScalaTestWithActorTestKit(""" val actor = spawn(behavior) actor ! Ping probe.expectMessage(1) - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { actor ! Fail } actor ! Ping @@ -329,7 +328,7 @@ abstract class ActorContextSpec extends ScalaTestWithActorTestKit(""" probe.expectMessage(Pong) watcher ! Ping probe.expectMessage(Pong) - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { actorToWatch ! Fail } probe.expectMessage(ReceivedSignal(PostStop)) @@ -514,7 +513,7 @@ abstract class ActorContextSpec extends ScalaTestWithActorTestKit(""" val childRef = probe.expectMessageType[ChildMade].ref actor ! Inert probe.expectMessage(InertEvent) - EventFilter[DeathPactException](occurrences = 1).intercept { + LoggingEventFilter.error[DeathPactException].intercept { childRef ! Stop probe.expectMessage(GotChildSignal(PostStop)) probe.expectMessage(ReceivedSignal(PostStop)) diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/AskSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/AskSpec.scala index 7cb0512cbf..06a01fd447 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/AskSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/AskSpec.scala @@ -8,36 +8,32 @@ import akka.actor.typed.internal.adapter.ActorSystemAdapter import akka.actor.typed.scaladsl.AskPattern._ import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.Behaviors._ -import akka.testkit.EventFilter import akka.actor.testkit.typed.scaladsl.TestProbe import akka.util.Timeout - import scala.concurrent.duration._ import scala.concurrent.{ ExecutionContext, TimeoutException } import scala.util.Success + import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import org.scalatest.WordSpecLike - import scala.concurrent.Future +import akka.actor.DeadLetter +import akka.actor.UnhandledMessage +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter +import akka.actor.testkit.typed.scaladsl.LogCapturing +import akka.actor.typed.eventstream.EventStream + object AskSpec { sealed trait Msg final case class Foo(s: String, replyTo: ActorRef[String]) extends Msg final case class Stop(replyTo: ActorRef[Unit]) extends Msg } -class AskSpec extends ScalaTestWithActorTestKit(""" - akka.loglevel=warning - akka.loggers = [ akka.testkit.TestEventListener ] - """) with WordSpecLike { +class AskSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { - // FIXME #24348: eventfilter support in typed testkit import AskSpec._ - // FIXME #24348: eventfilter support in typed testkit - import scaladsl.adapter._ - implicit val classicSystem = system.toClassic - implicit def executor: ExecutionContext = system.executionContext @@ -58,10 +54,7 @@ class AskSpec extends ScalaTestWithActorTestKit(""" val probe = createTestProbe() probe.expectTerminated(ref, probe.remainingOrDefault) - val answer: Future[String] = - EventFilter.warning(pattern = ".*received dead letter.*", occurrences = 1).intercept { - ref.ask(Foo("bar", _)) - } + val answer: Future[String] = ref.ask(Foo("bar", _)) val result = answer.failed.futureValue result shouldBe a[TimeoutException] result.getMessage should include("had already been terminated.") @@ -80,17 +73,19 @@ class AskSpec extends ScalaTestWithActorTestKit(""" } "fail the future if the actor doesn't reply in time" in { + val unhandledProbe = createTestProbe[UnhandledMessage]() + system.eventStream ! EventStream.Subscribe(unhandledProbe.ref) + val actor = spawn(Behaviors.empty[Foo]) implicit val timeout: Timeout = 10.millis - EventFilter.warning(pattern = ".*unhandled message.*", occurrences = 1).intercept { - val answer: Future[String] = actor.ask(Foo("bar", _)) - val result = answer.failed.futureValue - result shouldBe a[TimeoutException] - result.getMessage should startWith("Ask timed out on") - } + + val answer: Future[String] = actor.ask(Foo("bar", _)) + unhandledProbe.receiveMessage() + val result = answer.failed.futureValue + result shouldBe a[TimeoutException] + result.getMessage should startWith("Ask timed out on") } - /** See issue #19947 (MatchError with adapted ActorRef) */ "fail the future if the actor doesn't exist" in { val noSuchActor: ActorRef[Msg] = system match { case adaptedSys: ActorSystemAdapter[_] => @@ -100,13 +95,19 @@ class AskSpec extends ScalaTestWithActorTestKit(""" fail("this test must only run in an adapted actor system") } - val answer: Future[String] = - EventFilter.warning(pattern = ".*received dead letter.*", occurrences = 1).intercept { - noSuchActor.ask(Foo("bar", _)) - } + val deadLetterProbe = createTestProbe[DeadLetter]() + system.eventStream ! EventStream.Subscribe(deadLetterProbe.ref) + + val answer: Future[String] = noSuchActor.ask(Foo("bar", _)) val result = answer.failed.futureValue result shouldBe a[TimeoutException] result.getMessage should include("had already been terminated") + + val deadLetter = deadLetterProbe.receiveMessage() + deadLetter.message match { + case Foo(s, _) => s should ===("bar") + case _ => fail(s"unexpected DeadLetter: $deadLetter") + } } "transform a replied akka.actor.Status.Failure to a failed future" in { @@ -127,6 +128,7 @@ class AskSpec extends ScalaTestWithActorTestKit(""" val legacyActor = classicSystem.actorOf(akka.actor.Props(new LegacyActor)) import scaladsl.AskPattern._ + import akka.actor.typed.scaladsl.adapter._ implicit val timeout: Timeout = 3.seconds implicit val scheduler = classicSystem.toTyped.scheduler val typedLegacy: ActorRef[AnyRef] = legacyActor @@ -172,10 +174,11 @@ class AskSpec extends ScalaTestWithActorTestKit(""" ref ! "start-ask" val Question(replyRef2) = probe.expectMessageType[Question] - EventFilter[RuntimeException](message = "Exception thrown out of adapter. Stopping myself.", occurrences = 1) + LoggingEventFilter + .error("Exception thrown out of adapter. Stopping myself.") .intercept { replyRef2 ! 42L - }(system.toClassic) + }(system) probe.expectTerminated(ref, probe.remainingOrDefault) } diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/BehaviorSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/BehaviorSpec.scala index c93018c000..e0b775cbdd 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/BehaviorSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/BehaviorSpec.scala @@ -12,6 +12,7 @@ import java.util.function.{ Function => F1 } import akka.Done import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.testkit.typed.scaladsl.{ BehaviorTestKit, TestInbox } import org.scalactic.TypeCheckedTripleEquals import org.scalatest.Matchers @@ -68,7 +69,7 @@ object BehaviorSpec { override def next = StateA } - trait Common extends WordSpecLike with Matchers with TypeCheckedTripleEquals { + trait Common extends WordSpecLike with Matchers with TypeCheckedTripleEquals with LogCapturing { type Aux >: Null <: AnyRef def behavior(monitor: ActorRef[Event]): (Behavior[Command], Aux) @silent("never used") diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/DeferredSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/DeferredSpec.scala index 61fffbecf6..346ade880d 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/DeferredSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/DeferredSpec.scala @@ -4,13 +4,13 @@ package akka.actor.typed -import akka.testkit.EventFilter import akka.actor.typed.scaladsl.Behaviors import akka.actor.testkit.typed.TestKitSettings import akka.actor.testkit.typed.scaladsl._ - import scala.util.control.NoStackTrace + import akka.actor.ActorInitializationException +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter import org.scalatest.{ Matchers, WordSpec, WordSpecLike } object DeferredSpec { @@ -30,17 +30,11 @@ object DeferredSpec { }) } -class DeferredSpec extends ScalaTestWithActorTestKit(""" - akka.loggers = [akka.testkit.TestEventListener] - """) with WordSpecLike { +class DeferredSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { import DeferredSpec._ implicit val testSettings = TestKitSettings(system) - // FIXME #24348: eventfilter support in typed testkit - import scaladsl.adapter._ - implicit val classicSystem = system.toClassic - "Deferred behavior" must { "must create underlying" in { val probe = TestProbe[Event]("evt") @@ -68,7 +62,7 @@ class DeferredSpec extends ScalaTestWithActorTestKit(""" Behaviors.stopped } } - EventFilter[ActorInitializationException](occurrences = 1).intercept { + LoggingEventFilter.error[ActorInitializationException].intercept { spawn(behv) probe.expectMessage(Started) probe.expectMessage(Pong) @@ -144,7 +138,7 @@ class DeferredSpec extends ScalaTestWithActorTestKit(""" Behaviors.same } } - EventFilter[ActorInitializationException](occurrences = 1).intercept { + LoggingEventFilter.error[ActorInitializationException].intercept { val ref = spawn(behv) probe.expectTerminated(ref, probe.remainingOrDefault) } @@ -152,7 +146,7 @@ class DeferredSpec extends ScalaTestWithActorTestKit(""" } } -class DeferredStubbedSpec extends WordSpec with Matchers { +class DeferredStubbedSpec extends WordSpec with Matchers with LogCapturing { import DeferredSpec._ diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/ExtensionsSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/ExtensionsSpec.scala index 32512aca5a..ecb15a3818 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/ExtensionsSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/ExtensionsSpec.scala @@ -12,6 +12,7 @@ import scala.concurrent.Future import akka.actor.BootstrapSetup import akka.actor.setup.ActorSystemSetup import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.receptionist.Receptionist import akka.actor.typed.receptionist.ServiceKey import akka.actor.typed.scaladsl.Behaviors @@ -72,7 +73,7 @@ akka.actor.typed { """).resolve() } -class ExtensionsSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class ExtensionsSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { "The extensions subsystem" must { "return the same instance for the same id" in diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/InterceptSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/InterceptSpec.scala index 521a571770..03c9089206 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/InterceptSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/InterceptSpec.scala @@ -6,14 +6,15 @@ package akka.actor.typed import java.util.concurrent.atomic.AtomicBoolean -import akka.testkit.EventFilter import akka.actor.testkit.typed.scaladsl.TestProbe import akka.actor.typed.scaladsl.Behaviors import org.scalatest.WordSpecLike import scala.concurrent.duration._ import akka.actor.ActorInitializationException +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.internal.PoisonPill import akka.actor.typed.internal.PoisonPillInterceptor @@ -74,16 +75,10 @@ object InterceptSpec { } } -class InterceptSpec extends ScalaTestWithActorTestKit(""" - akka.loggers = [akka.testkit.TestEventListener] - """) with WordSpecLike { +class InterceptSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { import BehaviorInterceptor._ import InterceptSpec._ - // FIXME #24348: eventfilter support in typed testkit - import scaladsl.adapter._ - implicit val classicSystem = system.toClassic - private def snitchingInterceptor(probe: ActorRef[String]) = new BehaviorInterceptor[String, String] { override def aroundReceive( context: TypedActorContext[String], @@ -287,7 +282,7 @@ class InterceptSpec extends ScalaTestWithActorTestKit(""" val probe = TestProbe[String]() val interceptor = snitchingInterceptor(probe.ref) - EventFilter[ActorInitializationException](occurrences = 1).intercept { + LoggingEventFilter.error[ActorInitializationException].intercept { val ref = spawn(Behaviors.intercept(() => interceptor)(Behaviors.setup[String] { _ => Behaviors.same[String] })) diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/LogMessagesSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/LogMessagesSpec.scala index b80cd695d8..1bb981c72c 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/LogMessagesSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/LogMessagesSpec.scala @@ -5,17 +5,17 @@ package akka.actor.typed import akka.actor +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.adapter._ -import akka.event.Logging -import akka.testkit.EventFilter import org.scalatest.WordSpecLike +import org.slf4j.event.Level class LogMessagesSpec extends ScalaTestWithActorTestKit(""" akka.loglevel = DEBUG # test verifies debug - akka.loggers = ["akka.testkit.TestEventListener"] - """) with WordSpecLike { + """) with WordSpecLike with LogCapturing { implicit val classic: actor.ActorSystem = system.toClassic @@ -26,26 +26,26 @@ class LogMessagesSpec extends ScalaTestWithActorTestKit(""" val ref: ActorRef[String] = spawn(behavior) - EventFilter.debug("received message Hello", source = ref.path.toString, occurrences = 1).intercept { + LoggingEventFilter.debug(s"actor [${ref.path.toString}] received message: Hello").intercept { ref ! "Hello" } - EventFilter.debug("received signal PostStop", source = ref.path.toString, occurrences = 1).intercept { + LoggingEventFilter.debug(s"actor [${ref.path}] received signal: PostStop").intercept { testKit.stop(ref) } } "log messages with provided log level" in { - val opts = LogOptions().withLevel(Logging.InfoLevel) + val opts = LogOptions().withLevel(Level.INFO) val behavior: Behavior[String] = Behaviors.logMessages(opts, Behaviors.ignore) val ref: ActorRef[String] = spawn(behavior) - EventFilter.info("received message Hello", source = ref.path.toString, occurrences = 1).intercept { + LoggingEventFilter.info(s"actor [${ref.path}] received message: Hello").intercept { ref ! "Hello" } - EventFilter.info("received signal PostStop", source = ref.path.toString, occurrences = 1).intercept { + LoggingEventFilter.info(s"actor [${ref.path}] received signal: PostStop").intercept { testKit.stop(ref) } } @@ -57,11 +57,11 @@ class LogMessagesSpec extends ScalaTestWithActorTestKit(""" val ref: ActorRef[String] = spawn(behavior) - EventFilter.debug("received message Hello", source = "LogMessagesSpec", occurrences = 1).intercept { + LoggingEventFilter.debug(s"actor [${ref.path}] received message: Hello").intercept { ref ! "Hello" } - EventFilter.debug("received signal PostStop", source = "LogMessagesSpec", occurrences = 1).intercept { + LoggingEventFilter.debug(s"actor [${ref.path}] received signal: PostStop").intercept { testKit.stop(ref) } } @@ -72,46 +72,65 @@ class LogMessagesSpec extends ScalaTestWithActorTestKit(""" val ref: ActorRef[String] = spawn(behavior) - EventFilter.debug("received message Hello", source = ref.path.toString, occurrences = 0).intercept { + LoggingEventFilter.debug(s"actor [${ref.path}] received message: Hello").withOccurrences(0).intercept { ref ! "Hello" } - EventFilter.debug("received signal PostStop", source = ref.path.toString, occurrences = 0).intercept { + LoggingEventFilter.debug(s"actor [${ref.path}] received signal: PostStop").withOccurrences(0).intercept { testKit.stop(ref) } } "log messages with decorated MDC values" in { - val behavior = Behaviors.withMdc[String](Map("mdc" -> true))(Behaviors.logMessages(Behaviors.ignore)) + val opts = LogOptions().withLevel(Level.DEBUG) + val mdc = Map("mdc" -> "true") + val behavior = Behaviors.withMdc[String](mdc)(Behaviors.logMessages(opts, Behaviors.ignore)) val ref = spawn(behavior) - EventFilter - .custom( - { - case logEvent if logEvent.level == Logging.DebugLevel => - logEvent.message should ===("received message Hello") - logEvent.mdc should ===(Map("mdc" -> true)) - true - case _ => - false - }, - occurrences = 1) - .intercept { - ref ! "Hello" - } + LoggingEventFilter.debug(s"actor [${ref.path}] received message: Hello").withMdc(mdc).intercept { + ref ! "Hello" + } - EventFilter.debug("received signal PostStop", source = ref.path.toString, occurrences = 1).intercept { + LoggingEventFilter.debug(s"actor [${ref.path}] received signal: PostStop").withMdc(mdc).intercept { testKit.stop(ref) } } + "log messages with different decorated MDC values in different actors" in { + val opts = LogOptions().withLevel(Level.DEBUG) + val mdc1 = Map("mdc" -> "true") + val behavior1 = Behaviors.withMdc[String](mdc1)(Behaviors.logMessages(opts, Behaviors.ignore)) + val mdc2 = Map("mdc" -> "false") + val behavior2 = Behaviors.withMdc[String](mdc2)(Behaviors.logMessages(opts, Behaviors.ignore)) + + val ref2 = spawn(behavior2) + + LoggingEventFilter.debug(s"actor [${ref2.path}] received message: Hello").withMdc(mdc2).intercept { + ref2 ! "Hello" + } + + val ref1 = spawn(behavior1) + + LoggingEventFilter.debug(s"actor [${ref1.path}] received message: Hello").withMdc(mdc1).intercept { + ref1 ! "Hello" + } + + LoggingEventFilter.debug(s"actor [${ref2.path}] received signal: PostStop").withMdc(mdc2).intercept { + testKit.stop(ref2) + } + + LoggingEventFilter.debug(s"actor [${ref1.path}] received signal: PostStop").withMdc(mdc1).intercept { + testKit.stop(ref1) + } + } + "log messages of different type" in { val behavior: Behavior[String] = Behaviors.logMessages(Behaviors.ignore[String]) val ref = spawn(behavior) - EventFilter.debug("received message 13", source = ref.path.toString, occurrences = 1).intercept { + LoggingEventFilter.debug(s"actor [${ref.path}] received message: 13").intercept { ref.unsafeUpcast[Any] ! 13 } } diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/MailboxSelectorSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/MailboxSelectorSpec.scala index a3c28b014d..bd0886af10 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/MailboxSelectorSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/MailboxSelectorSpec.scala @@ -4,8 +4,13 @@ package akka.actor.typed +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit + import akka.actor.ActorCell +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.internal.adapter.ActorContextAdapter import akka.actor.typed.scaladsl.AskPattern._ import akka.actor.typed.scaladsl.Behaviors @@ -13,24 +18,14 @@ import akka.dispatch.BoundedMessageQueueSemantics import akka.dispatch.BoundedNodeMessageQueue import akka.dispatch.MessageQueue import akka.dispatch.UnboundedMessageQueueSemantics -import akka.testkit.EventFilter -import akka.testkit.TestLatch import org.scalatest.WordSpecLike -import scala.concurrent.Await -import scala.concurrent.duration._ - class MailboxSelectorSpec extends ScalaTestWithActorTestKit(""" specific-mailbox { mailbox-type = "akka.dispatch.NonBlockingBoundedMailbox" mailbox-capacity = 4 } - akka.loggers = [ akka.testkit.TestEventListener ] - """) with WordSpecLike { - - // FIXME #24348: eventfilter support in typed testkit - import scaladsl.adapter._ - implicit val classicSystem = system.toClassic + """) with WordSpecLike with LogCapturing { case class WhatsYourMailbox(replyTo: ActorRef[MessageQueue]) private def behavior: Behavior[WhatsYourMailbox] = @@ -65,24 +60,24 @@ class MailboxSelectorSpec extends ScalaTestWithActorTestKit(""" } "set capacity on a bounded mailbox" in { - val latch = TestLatch(1) + val latch = new CountDownLatch(1) val actor = spawn(Behaviors.receiveMessage[String] { case "one" => // block here so we can fill mailbox up - Await.ready(latch, 10.seconds) + latch.await(10, TimeUnit.SECONDS) Behaviors.same case _ => Behaviors.same }, MailboxSelector.bounded(2)) actor ! "one" // actor will block here actor ! "two" - EventFilter.warning(start = "received dead letter:", occurrences = 1).intercept { + LoggingEventFilter.deadLetters().intercept { // one or both of these doesn't fit in mailbox // depending on race with how fast actor consumes actor ! "three" actor ! "four" } - latch.open() + latch.countDown() } "select an arbitrary mailbox from config" in { diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/MonitorSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/MonitorSpec.scala index be0678b667..69a65f2b6a 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/MonitorSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/MonitorSpec.scala @@ -6,10 +6,11 @@ package akka.actor.typed import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.scaladsl.Behaviors import org.scalatest.WordSpecLike -class MonitorSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class MonitorSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { "The monitor behavior" should { diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/OrElseSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/OrElseSpec.scala index 15539144a5..cb7a95f4e5 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/OrElseSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/OrElseSpec.scala @@ -183,7 +183,7 @@ object OrElseSpec { } -class OrElseSpec extends WordSpec with Matchers { +class OrElseSpec extends WordSpec with Matchers with LogCapturing { import OrElseSpec._ diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/PropsSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/PropsSpec.scala index 7946e20a67..6ca9e3b81d 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/PropsSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/PropsSpec.scala @@ -4,10 +4,11 @@ package akka.actor.typed +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.Matchers import org.scalatest.WordSpec -class PropsSpec extends WordSpec with Matchers { +class PropsSpec extends WordSpec with Matchers with LogCapturing { val dispatcherFirst = Props.empty.withDispatcherFromConfig("pool").withDispatcherDefault diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/SpawnProtocolSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/SpawnProtocolSpec.scala index 92e53d1ebc..1133ed03d7 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/SpawnProtocolSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/SpawnProtocolSpec.scala @@ -26,7 +26,7 @@ object SpawnProtocolSpec { } } -class SpawnProtocolSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class SpawnProtocolSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { import SpawnProtocolSpec._ implicit val testSettings = TestKitSettings(system) @@ -96,7 +96,7 @@ class SpawnProtocolSpec extends ScalaTestWithActorTestKit with WordSpecLike { } } -class StubbedSpawnProtocolSpec extends WordSpec with Matchers { +class StubbedSpawnProtocolSpec extends WordSpec with Matchers with LogCapturing { import SpawnProtocolSpec._ diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/SupervisionSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/SupervisionSpec.scala index f2a9db5836..03ee41154f 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/SupervisionSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/SupervisionSpec.scala @@ -7,22 +7,26 @@ package akka.actor.typed import java.io.IOException import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit -import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger } +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger + +import scala.concurrent.duration._ +import scala.util.control.NoStackTrace import akka.actor.ActorInitializationException -import akka.actor.typed.scaladsl.{ AbstractBehavior, Behaviors } -import akka.actor.typed.scaladsl.Behaviors._ -import akka.testkit.EventFilter -import akka.actor.testkit.typed.scaladsl._ -import akka.actor.testkit.typed._ -import org.scalatest.{ Matchers, WordSpec, WordSpecLike } - -import scala.util.control.NoStackTrace -import scala.concurrent.duration._ - import akka.actor.Dropped +import akka.actor.testkit.typed._ +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter +import akka.actor.testkit.typed.scaladsl._ import akka.actor.typed.SupervisorStrategy.Resume -import akka.event.Logging +import akka.actor.typed.eventstream.EventStream +import akka.actor.typed.scaladsl.Behaviors._ +import akka.actor.typed.scaladsl.AbstractBehavior +import akka.actor.typed.scaladsl.Behaviors +import org.scalatest.Matchers +import org.scalatest.WordSpec +import org.scalatest.WordSpecLike +import org.slf4j.event.Level object SupervisionSpec { @@ -88,7 +92,7 @@ object SupervisionSpec { } } -class StubbedSupervisionSpec extends WordSpec with Matchers { +class StubbedSupervisionSpec extends WordSpec with Matchers with LogCapturing { import SupervisionSpec._ @@ -248,21 +252,15 @@ class StubbedSupervisionSpec extends WordSpec with Matchers { } class SupervisionSpec extends ScalaTestWithActorTestKit(""" - akka.loggers = [akka.testkit.TestEventListener] akka.log-dead-letters = off - """) with WordSpecLike { + """) with WordSpecLike with LogCapturing { - import SupervisionSpec._ import BehaviorInterceptor._ + import SupervisionSpec._ private val nameCounter = Iterator.from(0) private def nextName(prefix: String = "a"): String = s"$prefix-${nameCounter.next()}" - // FIXME #24348: eventfilter support in typed testkit - import akka.actor.typed.scaladsl.adapter._ - - implicit val classicSystem = system.toClassic - class FailingConstructorTestSetup(failCount: Int) { val failCounter = new AtomicInteger(0) class FailingConstructor(monitor: ActorRef[Event]) extends AbstractBehavior[Command] { @@ -315,7 +313,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" val probe = TestProbe[Event]("evt") val behv = targetBehavior(probe.ref) val ref = spawn(behv) - EventFilter[Exc3](occurrences = 1).intercept { + LoggingEventFilter.error[Exc3].intercept { ref ! Throw(new Exc3) probe.expectMessage(ReceivedSignal(PostStop)) probe.expectTerminated(ref) @@ -325,7 +323,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" val probe = TestProbe[Event]("evt") val behv = Behaviors.supervise(targetBehavior(probe.ref)).onFailure[Throwable](SupervisorStrategy.stop) val ref = spawn(behv) - EventFilter[Exc3](occurrences = 1).intercept { + LoggingEventFilter.error[Exc3].intercept { ref ! Throw(new Exc3) probe.expectMessage(ReceivedSignal(PostStop)) probe.expectTerminated(ref) @@ -339,7 +337,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" targetBehavior(probe.ref) }) val behv = Behaviors.supervise(failedSetup).onFailure[Throwable](SupervisorStrategy.stop) - EventFilter[Exc3](occurrences = 1).intercept { + LoggingEventFilter.error[Exc3].intercept { spawn(behv) } } @@ -352,12 +350,12 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" val ref = spawn(behv) - EventFilter[IOException](occurrences = 1).intercept { + LoggingEventFilter.error[IOException].intercept { ref ! Throw(new IOException()) probe.expectMessage(ReceivedSignal(PreRestart)) } - EventFilter[IllegalArgumentException](occurrences = 1).intercept { + LoggingEventFilter.error[IllegalArgumentException].intercept { ref ! Throw(new IllegalArgumentException("cat")) probe.expectMessage(ReceivedSignal(PostStop)) } @@ -373,7 +371,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" val ref = spawn(behv) - EventFilter[Exception](occurrences = 1).intercept { + LoggingEventFilter.error[Exception].intercept { ref ! Throw(new IOException()) probe.expectMessage(ReceivedSignal(PreRestart)) } @@ -381,7 +379,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" ref ! Ping(1) probe.expectMessage(Pong(1)) - EventFilter[IllegalArgumentException](occurrences = 1).intercept { + LoggingEventFilter.error[IllegalArgumentException].intercept { ref ! Throw(new IllegalArgumentException("cat")) probe.expectMessage(ReceivedSignal(PreRestart)) } @@ -399,7 +397,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" val ref = spawn(behv) - EventFilter[Exception](occurrences = 1).intercept { + LoggingEventFilter.error[Exception].intercept { ref ! Throw(new IOException()) probe.expectMessage(ReceivedSignal(PreRestart)) } @@ -407,7 +405,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" ref ! Ping(1) probe.expectMessage(Pong(1)) - EventFilter[IllegalArgumentException](occurrences = 1).intercept { + LoggingEventFilter.error[IllegalArgumentException].intercept { ref ! Throw(new IllegalArgumentException("cat")) probe.expectMessage(ReceivedSignal(PreRestart)) } @@ -421,7 +419,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" val probe = TestProbe[Event]("evt") val behv = targetBehavior(probe.ref) val ref = spawn(behv) - EventFilter[Exc3](occurrences = 1).intercept { + LoggingEventFilter.error[Exc3].intercept { ref ! Throw(new Exc3) probe.expectMessage(ReceivedSignal(PostStop)) } @@ -431,7 +429,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" val probe = TestProbe[Event]("evt") val behv = Behaviors.supervise(targetBehavior(probe.ref)).onFailure[Exc1](SupervisorStrategy.restart) val ref = spawn(behv) - EventFilter[Exc3](occurrences = 1).intercept { + LoggingEventFilter.error[Exc3].intercept { ref ! Throw(new Exc3) probe.expectMessage(ReceivedSignal(PostStop)) } @@ -445,7 +443,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" ref ! GetState probe.expectMessage(State(1, Map.empty)) - EventFilter[Exc2](occurrences = 1).intercept { + LoggingEventFilter.error[Exc2].intercept { ref ! Throw(new Exc2) probe.expectMessage(ReceivedSignal(PreRestart)) } @@ -464,7 +462,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" ref ! GetState probe.expectMessage(State(1, Map.empty)) - EventFilter[Exc2](occurrences = 3).intercept { + LoggingEventFilter.error[Exc2].withOccurrences(3).intercept { ref ! Throw(new Exc2) probe.expectMessage(ReceivedSignal(PreRestart)) ref ! Throw(new Exc2) @@ -472,10 +470,8 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" ref ! Throw(new Exc2) probe.expectMessage(ReceivedSignal(PostStop)) } - EventFilter.warning(start = "received dead letter", occurrences = 1).intercept { - ref ! GetState - probe.expectNoMessage() - } + + probe.expectTerminated(ref) } "reset fixed limit after timeout" in { @@ -489,7 +485,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" ref ! GetState probe.expectMessage(State(1, Map.empty)) - EventFilter[Exc2](occurrences = 3).intercept { + LoggingEventFilter.error[Exc2].withOccurrences(3).intercept { ref ! Throw(new Exc2) probe.expectMessage(ReceivedSignal(PreRestart)) ref ! Throw(new Exc2) @@ -529,7 +525,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" ref ! GetState parentProbe.expectMessageType[State].children.keySet should ===(Set(child1Name, child2Name)) - EventFilter[Exc1](occurrences = 1).intercept { + LoggingEventFilter.error[Exc1].intercept { ref ! Throw(new Exc1) parentProbe.expectMessage(ReceivedSignal(PreRestart)) ref ! GetState @@ -567,7 +563,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" ref ! GetState parentProbe.expectMessageType[State].children.keySet should contain(childName) - EventFilter[Exc1](occurrences = 1).intercept { + LoggingEventFilter.error[Exc1].intercept { ref ! Throw(new Exc1) parentProbe.expectMessage(ReceivedSignal(PreRestart)) ref ! GetState @@ -598,7 +594,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" val child2Name = nextName() - EventFilter[Exc1](occurrences = 1).intercept { + LoggingEventFilter.error[Exc1].intercept { ref ! Throw(new Exc1) parentProbe.expectMessage(ReceivedSignal(PreRestart)) ref ! GetState @@ -607,7 +603,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" ref ! Throw(new Exc1) } - EventFilter[Exc1](occurrences = 1).intercept { + LoggingEventFilter.error[Exc1].intercept { slowStop.countDown() childProbe.expectMessage(ReceivedSignal(PostStop)) // child1 parentProbe.expectMessageType[State].children.keySet should ===(Set.empty) @@ -652,7 +648,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" } .onFailure[RuntimeException](strategy) - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { val ref = spawn(behv) slowStop1.countDown() child1Probe.expectMessage(ReceivedSignal(PostStop)) @@ -703,12 +699,12 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" throwFromSetup.set(true) - EventFilter[Exc1](occurrences = 1).intercept { + LoggingEventFilter.error[Exc1].intercept { ref ! Throw(new Exc1) parentProbe.expectMessage(ReceivedSignal(PreRestart)) } - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { slowStop1.countDown() child1Probe.expectMessage(ReceivedSignal(PostStop)) child1Probe.expectMessage(ReceivedSignal(PostStop)) @@ -729,7 +725,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" ref ! GetState probe.expectMessage(State(1, Map.empty)) - EventFilter[Exc2](occurrences = 1).intercept { + LoggingEventFilter.error[Exc2].intercept { ref ! Throw(new Exc2) ref ! GetState probe.expectMessage(State(1, Map.empty)) @@ -747,7 +743,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" probe.expectMessage(State(1, Map.empty)) // resume - EventFilter[Exc2](occurrences = 1).intercept { + LoggingEventFilter.error[Exc2].intercept { ref ! Throw(new Exc2) probe.expectNoMessage() ref ! GetState @@ -755,7 +751,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" } // restart - EventFilter[Exc3](occurrences = 1).intercept { + LoggingEventFilter.error[Exc3].intercept { ref ! Throw(new Exc3) probe.expectMessage(ReceivedSignal(PreRestart)) ref ! GetState @@ -763,13 +759,16 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" } // stop - EventFilter[Exc1](occurrences = 1).intercept { + LoggingEventFilter.error[Exc1].intercept { ref ! Throw(new Exc1) probe.expectMessage(ReceivedSignal(PostStop)) } } "publish dropped messages while backing off and stash is full" in { + import akka.actor.typed.scaladsl.adapter._ + val droppedMessagesProbe = TestProbe[Dropped]() + system.eventStream ! EventStream.Subscribe(droppedMessagesProbe.ref) val probe = TestProbe[Event]("evt") val startedProbe = TestProbe[Event]("started") val minBackoff = 1.seconds @@ -781,27 +780,27 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" }) .onFailure[Exception](strategy) - val droppedMessagesProbe = TestProbe[Dropped]() - system.toClassic.eventStream.subscribe(droppedMessagesProbe.ref.toClassic, classOf[Dropped]) val ref = spawn(behv) - EventFilter[Exc1](occurrences = 1).intercept { + LoggingEventFilter.error[Exc1].intercept { startedProbe.expectMessage(Started) ref ! Throw(new Exc1) probe.expectMessage(ReceivedSignal(PreRestart)) } - EventFilter.warning(start = "dropped message", occurrences = 2).intercept { - ref ! Ping(1) - ref ! Ping(2) - ref ! Ping(3) - ref ! Ping(4) - probe.expectMessage(Pong(1)) - probe.expectMessage(Pong(2)) - droppedMessagesProbe.expectMessage(Dropped(Ping(3), "Stash is full in [RestartSupervisor]", ref.toClassic)) - droppedMessagesProbe.expectMessage(Dropped(Ping(4), "Stash is full in [RestartSupervisor]", ref.toClassic)) - } + + ref ! Ping(1) + ref ! Ping(2) + ref ! Ping(3) + ref ! Ping(4) + probe.expectMessage(Pong(1)) + probe.expectMessage(Pong(2)) + droppedMessagesProbe.expectMessage(Dropped(Ping(3), "Stash is full in [RestartSupervisor]", ref.toClassic)) + droppedMessagesProbe.expectMessage(Dropped(Ping(4), "Stash is full in [RestartSupervisor]", ref.toClassic)) } "restart after exponential backoff" in { + import akka.actor.typed.scaladsl.adapter._ + val droppedMessagesProbe = TestProbe[Dropped]() + system.eventStream ! EventStream.Subscribe(droppedMessagesProbe.ref) val probe = TestProbe[Event]("evt") val startedProbe = TestProbe[Event]("started") val minBackoff = 1.seconds @@ -817,14 +816,13 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" .onFailure[Exception](strategy) val ref = spawn(behv) - EventFilter[Exc1](occurrences = 1).intercept { + LoggingEventFilter.error[Exc1].intercept { startedProbe.expectMessage(Started) ref ! IncrementState ref ! Throw(new Exc1) probe.expectMessage(ReceivedSignal(PreRestart)) - EventFilter.warning(start = "dropped message", occurrences = 1).intercept { - ref ! Ping(1) // dropped due to backoff, no stashing - } + ref ! Ping(1) // dropped due to backoff, no stashing + droppedMessagesProbe.expectMessage(Dropped(Ping(1), "Stash is full in [RestartSupervisor]", ref.toClassic)) } startedProbe.expectNoMessage(minBackoff - 100.millis) @@ -834,13 +832,12 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" probe.expectMessage(State(0, Map.empty)) // one more time - EventFilter[Exc1](occurrences = 1).intercept { + LoggingEventFilter.error[Exc1].intercept { ref ! IncrementState ref ! Throw(new Exc1) probe.expectMessage(ReceivedSignal(PreRestart)) - EventFilter.warning(start = "dropped message", occurrences = 1).intercept { - ref ! Ping(2) // dropped due to backoff, no stashing - } + ref ! Ping(2) // dropped due to backoff, no stashing + droppedMessagesProbe.expectMessage(Dropped(Ping(2), "Stash is full in [RestartSupervisor]", ref.toClassic)) } startedProbe.expectNoMessage((minBackoff * 2) - 100.millis) @@ -873,8 +870,8 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" .onFailure[Exception](strategy) val ref = spawn(behv) - EventFilter[Exc1](occurrences = 1).intercept { - EventFilter[TestException](occurrences = 2).intercept { + LoggingEventFilter.error[Exc1].intercept { + LoggingEventFilter.error[TestException].withOccurrences(2).intercept { startedProbe.expectMessage(Started) ref ! Throw(new Exc1) probe.expectTerminated(ref, 3.seconds) @@ -883,6 +880,9 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" } "reset exponential backoff count after reset timeout" in { + import akka.actor.typed.scaladsl.adapter._ + val droppedMessagesProbe = TestProbe[Dropped]() + system.eventStream ! EventStream.Subscribe(droppedMessagesProbe.ref) val probe = TestProbe[Event]("evt") val minBackoff = 1.seconds val strategy = SupervisorStrategy @@ -892,13 +892,12 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" val behv = supervise(targetBehavior(probe.ref)).onFailure[Exc1](strategy) val ref = spawn(behv) - EventFilter[Exc1](occurrences = 1).intercept { + LoggingEventFilter.error[Exc1].intercept { ref ! IncrementState ref ! Throw(new Exc1) probe.expectMessage(ReceivedSignal(PreRestart)) - EventFilter.warning(start = "dropped message", occurrences = 1).intercept { - ref ! Ping(1) // dropped due to backoff, no stash - } + ref ! Ping(1) // dropped due to backoff, no stash + droppedMessagesProbe.expectMessage(Dropped(Ping(1), "Stash is full in [RestartSupervisor]", ref.toClassic)) } probe.expectNoMessage(minBackoff + 100.millis.dilated) @@ -906,14 +905,13 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" probe.expectMessage(State(0, Map.empty)) // one more time after the reset timeout - EventFilter[Exc1](occurrences = 1).intercept { + LoggingEventFilter.error[Exc1].intercept { probe.expectNoMessage(strategy.resetBackoffAfter + 100.millis.dilated) ref ! IncrementState ref ! Throw(new Exc1) probe.expectMessage(ReceivedSignal(PreRestart)) - EventFilter.warning(start = "dropped message", occurrences = 1).intercept { - ref ! Ping(2) // dropped due to backoff - } + ref ! Ping(2) // dropped due to backoff + droppedMessagesProbe.expectMessage(Dropped(Ping(2), "Stash is full in [RestartSupervisor]", ref.toClassic)) } // backoff was reset, so restarted after the minBackoff @@ -938,7 +936,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" failCount = 1, strategy = SupervisorStrategy.restart) { - EventFilter[ActorInitializationException](occurrences = 1).intercept { + LoggingEventFilter.error[ActorInitializationException].intercept { spawn(behv) } } @@ -946,7 +944,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" "fail to restart when deferred factory throws unhandled" in new FailingUnhandledTestSetup( strategy = SupervisorStrategy.restart) { - EventFilter[ActorInitializationException](occurrences = 1).intercept { + LoggingEventFilter.error[ActorInitializationException].intercept { spawn(behv) } } @@ -954,8 +952,8 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" "fail to resume when deferred factory throws" in new FailingDeferredTestSetup( failCount = 1, strategy = SupervisorStrategy.resume) { - EventFilter[TestException](occurrences = 1).intercept { - EventFilter[ActorInitializationException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { + LoggingEventFilter.error[ActorInitializationException].intercept { spawn(behv) } } @@ -965,7 +963,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" failCount = 1, strategy = SupervisorStrategy.restartWithBackoff(minBackoff = 100.millis.dilated, maxBackoff = 1.second, 0)) { - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { spawn(behv) probe.expectMessage(StartFailed) @@ -978,7 +976,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" "fail instead of restart with exponential backoff when deferred factory throws unhandled" in new FailingUnhandledTestSetup( strategy = SupervisorStrategy.restartWithBackoff(minBackoff = 100.millis.dilated, maxBackoff = 1.second, 0)) { - EventFilter[ActorInitializationException](occurrences = 1).intercept { + LoggingEventFilter.error[ActorInitializationException].intercept { spawn(behv) probe.expectMessage(StartFailed) } @@ -988,7 +986,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" failCount = 1, strategy = SupervisorStrategy.restart.withLimit(3, 1.second)) { - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { spawn(behv) probe.expectMessage(StartFailed) @@ -1000,8 +998,8 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" failCount = 20, strategy = SupervisorStrategy.restart.withLimit(2, 1.second)) { - EventFilter[ActorInitializationException](occurrences = 1).intercept { - EventFilter[TestException](occurrences = 2).intercept { + LoggingEventFilter.error[ActorInitializationException].intercept { + LoggingEventFilter.error[TestException].withOccurrences(2).intercept { spawn(behv) // first one from initial setup @@ -1017,7 +1015,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" "fail instead of restart with limit when deferred factory throws unhandled" in new FailingUnhandledTestSetup( strategy = SupervisorStrategy.restart.withLimit(3, 1.second)) { - EventFilter[ActorInitializationException](occurrences = 1).intercept { + LoggingEventFilter.error[ActorInitializationException].intercept { spawn(behv) probe.expectMessage(StartFailed) } @@ -1028,7 +1026,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" val behv = supervise(setup[Command](_ => new FailingConstructor(probe.ref))) .onFailure[Exception](SupervisorStrategy.restart) - EventFilter[ActorInitializationException](occurrences = 1).intercept { + LoggingEventFilter.error[ActorInitializationException].intercept { spawn(behv) probe.expectMessage(Started) // first one before failure } @@ -1073,7 +1071,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" actor ! "ping" probe.expectMessage("pong") - EventFilter[RuntimeException](occurrences = 1).intercept { + LoggingEventFilter.error[RuntimeException].intercept { // Should be supervised as resume actor ! "boom" } @@ -1121,7 +1119,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" actor ! "ping" probe.expectMessage("pong") - EventFilter[RuntimeException](occurrences = 1).intercept { + LoggingEventFilter.error[RuntimeException].intercept { // Should be supervised as resume actor ! "boom" } @@ -1165,7 +1163,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" probe.expectMessage("started 1") ref ! "ping" probe.expectMessage("pong 1") - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { ref ! "boom" probe.expectMessage("crashing 1") ref ! "ping" @@ -1175,10 +1173,12 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" probe.expectMessage("pong 2") // from "ping" that was stashed ref ! "ping" probe.expectMessage("pong 2") - EventFilter[TestException](occurrences = 1).intercept { - ref ! "boom" // now we should have replaced supervision with the resuming one - probe.expectMessage("crashing 2") - } + LoggingEventFilter + .error[TestException] + .intercept { + ref ! "boom" // now we should have replaced supervision with the resuming one + probe.expectMessage("crashing 2") + }(system) ref ! "ping" probe.expectMessage("pong 2") } @@ -1208,7 +1208,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" }) .onFailure[DeathPactException](SupervisorStrategy.restart)) - EventFilter[DeathPactException](occurrences = 1).intercept { + LoggingEventFilter.error[DeathPactException].intercept { actor ! "boom" val child = probe.expectMessageType[ActorRef[_]] probe.expectTerminated(child, 3.seconds) @@ -1221,9 +1221,9 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" val probe = TestProbe[Event]("evt") val behv = Behaviors .supervise(targetBehavior(probe.ref)) - .onFailure[Exc1](SupervisorStrategy.restart.withLoggingEnabled(true).withLogLevel(Logging.InfoLevel)) + .onFailure[Exc1](SupervisorStrategy.restart.withLoggingEnabled(true).withLogLevel(Level.INFO)) val ref = spawn(behv) - EventFilter.info(pattern = "exc-1", source = ref.path.toString, occurrences = 1).intercept { + LoggingEventFilter.info("exc-1").intercept { ref ! Throw(new Exc1) probe.expectMessage(ReceivedSignal(PreRestart)) } @@ -1233,9 +1233,9 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" val probe = TestProbe[Event]("evt") val behv = Behaviors .supervise(targetBehavior(probe.ref)) - .onFailure[Exc1](SupervisorStrategy.restart.withLoggingEnabled(true).withLogLevel(Logging.DebugLevel)) + .onFailure[Exc1](SupervisorStrategy.restart.withLoggingEnabled(true).withLogLevel(Level.DEBUG)) val ref = spawn(behv) - EventFilter.info(pattern = "exc-1", source = ref.path.toString, occurrences = 0).intercept { + LoggingEventFilter.info("exc-1").withSource(ref.path.toString).withOccurrences(0).intercept { ref ! Throw(new Exc1) probe.expectMessage(ReceivedSignal(PreRestart)) } @@ -1263,7 +1263,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" ref ! Ping(1) probe.expectMessage(Pong(1)) - EventFilter[Exc1](occurrences = 1).intercept { + LoggingEventFilter.error[Exc1].intercept { ref.unsafeUpcast ! "boom" probe.expectMessage(ReceivedSignal(PreRestart)) } @@ -1311,7 +1311,7 @@ class SupervisionSpec extends ScalaTestWithActorTestKit(""" }) .onFailure[TestException](strategy)) - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { actor ! "boom" } createTestProbe().expectTerminated(actor, 3.second) diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/TerminatedSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/TerminatedSpec.scala index d5370e6e1a..e954407f76 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/TerminatedSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/TerminatedSpec.scala @@ -5,9 +5,10 @@ package akka.actor.typed import akka.actor.testkit.typed.scaladsl.TestInbox +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.{ Matchers, WordSpec } -class TerminatedSpec extends WordSpec with Matchers { +class TerminatedSpec extends WordSpec with Matchers with LogCapturing { "Child Failed" must { "should be pattern matchable" in { diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/TimerSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/TimerSpec.scala index cc2bc4b4c3..221b8be3d7 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/TimerSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/TimerSpec.scala @@ -13,19 +13,15 @@ import scala.util.control.NoStackTrace import akka.actor.DeadLetter import akka.actor.testkit.typed.TestException +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter import akka.actor.testkit.typed.scaladsl._ +import akka.actor.typed.eventstream.EventStream import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.TimerScheduler -import akka.testkit.{ EventFilter, TimingTest } +import akka.testkit.TimingTest import org.scalatest.WordSpecLike -class TimerSpec extends ScalaTestWithActorTestKit(""" - akka.loggers = [ akka.testkit.TestEventListener ] - """) with WordSpecLike { - - // FIXME #24348: eventfilter support in typed testkit - import scaladsl.adapter._ - implicit val classicSystem = system.toClassic +class TimerSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { sealed trait Command case class Tick(n: Int) extends Command @@ -179,7 +175,7 @@ class TimerSpec extends ScalaTestWithActorTestKit(""" probe.expectMessage(Tock(1)) val latch = new CountDownLatch(1) - EventFilter[Exc](occurrences = 1).intercept { + LoggingEventFilter.error[Exc].intercept { // next Tock(1) is enqueued in mailbox, but should be discarded by new incarnation ref ! SlowThenThrow(latch, new Exc) @@ -209,7 +205,7 @@ class TimerSpec extends ScalaTestWithActorTestKit(""" probe.expectMessage(Tock(2)) - EventFilter[Exc](occurrences = 1).intercept { + LoggingEventFilter.error[Exc].intercept { val latch = new CountDownLatch(1) // next Tock(2) is enqueued in mailbox, but should be discarded by new incarnation ref ! SlowThenThrow(latch, new Exc) @@ -230,7 +226,7 @@ class TimerSpec extends ScalaTestWithActorTestKit(""" target(probe.ref, timer, 1) } val ref = spawn(behv) - EventFilter[Exc](occurrences = 1).intercept { + LoggingEventFilter.error[Exc].intercept { ref ! Throw(new Exc) probe.expectMessage(GotPostStop(false)) } @@ -300,7 +296,7 @@ class TimerSpec extends ScalaTestWithActorTestKit(""" } "not leak timers when PostStop is used" in { - val probe = TestProbe[Any]() + val probe = TestProbe[DeadLetter]() val ref = spawn(Behaviors.withTimers[String] { timers => Behaviors.setup { _ => timers.startTimerWithFixedDelay("test", "test", 250.millis) @@ -309,11 +305,11 @@ class TimerSpec extends ScalaTestWithActorTestKit(""" } } }) - EventFilter.info("stopping").intercept { + LoggingEventFilter.info("stopping").intercept { ref ! "stop" } probe.expectTerminated(ref) - system.toClassic.eventStream.subscribe(probe.ref.toClassic, classOf[DeadLetter]) + system.eventStream ! EventStream.Subscribe(probe.ref) probe.expectNoMessage(1.second) } } @@ -348,7 +344,7 @@ class TimerSpec extends ScalaTestWithActorTestKit(""" Behaviors.unhandled } - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { val ref = spawn(Behaviors.supervise(behv).onFailure[TestException](SupervisorStrategy.restart)) ref ! Tick(-1) probe.expectMessage(Tock(-1)) @@ -383,7 +379,7 @@ class TimerSpec extends ScalaTestWithActorTestKit(""" Behaviors.unhandled } - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { val ref = spawn(Behaviors.supervise(behv).onFailure[TestException](SupervisorStrategy.restart)) ref ! Tick(-1) probe.expectMessage(Tock(-1)) diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/TransformMessagesSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/TransformMessagesSpec.scala index b48fda98b4..8e6eed4e5a 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/TransformMessagesSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/TransformMessagesSpec.scala @@ -11,10 +11,12 @@ import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.adapter._ -import akka.testkit.EventFilter import org.scalatest.WordSpecLike import scala.concurrent.duration._ +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter +import akka.actor.testkit.typed.scaladsl.LogCapturing + object TransformMessagesSpec { // this is the sample from the Scaladoc @@ -31,9 +33,7 @@ object TransformMessagesSpec { } } -class TransformMessagesSpec extends ScalaTestWithActorTestKit(""" - akka.loggers = [akka.testkit.TestEventListener] - """) with WordSpecLike { +class TransformMessagesSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { implicit val classicSystem = system.toClassic @@ -62,14 +62,11 @@ class TransformMessagesSpec extends ScalaTestWithActorTestKit(""" val probe = TestProbe[String]() val ref = spawn(intToString(probe.ref)) - // TestEventListener logs unhandled as warnings, silence that - EventFilter.warning(occurrences = 1).intercept { - ref ! 42 - ref ! 13 - ref ! 43 - probe.expectMessage("42") - probe.expectMessage("43") - } + ref ! 42 + ref ! 13 + ref ! 43 + probe.expectMessage("42") + probe.expectMessage("43") } "not build up when the same transformMessages is used many times (initially)" in { @@ -137,7 +134,7 @@ class TransformMessagesSpec extends ScalaTestWithActorTestKit(""" case s => s.toLowerCase } - EventFilter[ActorInitializationException](occurrences = 1).intercept { + LoggingEventFilter.error[ActorInitializationException].intercept { val ref = spawn(transform(transform(Behaviors.receiveMessage[String] { _ => Behaviors.same }))) diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/WatchSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/WatchSpec.scala index ef26168619..e866d00ac6 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/WatchSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/WatchSpec.scala @@ -8,20 +8,17 @@ import akka.Done import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.AbstractBehavior import akka.actor.typed.scaladsl.adapter._ -import akka.testkit.EventFilter import akka.actor.testkit.typed.scaladsl.TestProbe - import scala.concurrent._ import scala.concurrent.duration._ + import akka.actor.testkit.typed.TestException +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit -import com.typesafe.config.ConfigFactory +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.WordSpecLike object WatchSpec { - val config = ConfigFactory.parseString(""" - akka.loggers = ["akka.testkit.TestEventListener"] - """.stripMargin) case object Stop @@ -44,7 +41,7 @@ object WatchSpec { case class StartWatchingWith(watchee: ActorRef[Stop.type], message: CustomTerminationMessage) extends Message } -class WatchSpec extends ScalaTestWithActorTestKit(WatchSpec.config) with WordSpecLike { +class WatchSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { implicit def classicSystem = system.toClassic @@ -110,7 +107,7 @@ class WatchSpec extends ScalaTestWithActorTestKit(WatchSpec.config) with WordSpe }, "supervised-child-parent") - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { parent ! "boom" } probe.expectMessageType[ChildHasFailed].t.cause shouldEqual ex @@ -145,7 +142,7 @@ class WatchSpec extends ScalaTestWithActorTestKit(WatchSpec.config) with WordSpe } val parent = spawn(behavior, "parent") - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { parent ! "boom" } probe.expectMessageType[ChildHasFailed].t.cause shouldEqual ex @@ -184,8 +181,8 @@ class WatchSpec extends ScalaTestWithActorTestKit(WatchSpec.config) with WordSpe }, "grosso-bosso") - EventFilter[TestException](occurrences = 1).intercept { - EventFilter[DeathPactException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { + LoggingEventFilter.error[DeathPactException].intercept { grossoBosso ! "boom" } } @@ -325,7 +322,9 @@ class WatchSpec extends ScalaTestWithActorTestKit(WatchSpec.config) with WordSpe "fail when watch is used after watchWith on same subject" in new ErrorTestSetup { watcher ! StartWatchingWith(terminator, CustomTerminationMessage) - EventFilter[IllegalStateException](pattern = ".*termination message was not overwritten.*", occurrences = 1) + LoggingEventFilter + .error[IllegalStateException] + .withMessageContains("termination message was not overwritten") .intercept { watcher ! StartWatching(terminator) } @@ -336,7 +335,9 @@ class WatchSpec extends ScalaTestWithActorTestKit(WatchSpec.config) with WordSpe "fail when watchWitch is used after watchWith with different termination message" in new ErrorTestSetup { watcher ! StartWatchingWith(terminator, CustomTerminationMessage) - EventFilter[IllegalStateException](pattern = ".*termination message was not overwritten.*", occurrences = 1) + LoggingEventFilter + .error[IllegalStateException] + .withMessageContains("termination message was not overwritten") .intercept { watcher ! StartWatchingWith(terminator, CustomTerminationMessage2) } @@ -346,7 +347,9 @@ class WatchSpec extends ScalaTestWithActorTestKit(WatchSpec.config) with WordSpe "fail when watchWith is used after watch on same subject" in new ErrorTestSetup { watcher ! StartWatching(terminator) - EventFilter[IllegalStateException](pattern = ".*termination message was not overwritten.*", occurrences = 1) + LoggingEventFilter + .error[IllegalStateException] + .withMessageContains("termination message was not overwritten") .intercept { watcher ! StartWatchingWith(terminator, CustomTerminationMessage) } diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/coexistence/TypedSupervisingClassicSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/coexistence/TypedSupervisingClassicSpec.scala index 7f82a9d1fa..e0b6321221 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/coexistence/TypedSupervisingClassicSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/coexistence/TypedSupervisingClassicSpec.scala @@ -5,6 +5,7 @@ package akka.actor.typed.coexistence import akka.actor.Actor import akka.actor.testkit.typed.TestException +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.testkit.typed.scaladsl.{ ScalaTestWithActorTestKit, TestProbe } import akka.actor.typed.ActorRef import akka.actor.typed.scaladsl.Behaviors @@ -42,7 +43,7 @@ object TypedSupervisingClassicSpec { class TypedSupervisingClassicSpec extends ScalaTestWithActorTestKit(""" akka.loglevel = INFO - """.stripMargin) with WordSpecLike { + """.stripMargin) with WordSpecLike with LogCapturing { import TypedSupervisingClassicSpec._ "Typed supervising classic" should { diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/eventstream/EventStreamSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/eventstream/EventStreamSpec.scala index 0065ad389f..f404a3b028 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/eventstream/EventStreamSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/eventstream/EventStreamSpec.scala @@ -6,10 +6,11 @@ package akka.actor.typed.eventstream import scala.concurrent.duration._ +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.testkit.typed.scaladsl.{ ScalaTestWithActorTestKit, TestProbe } import org.scalatest.WordSpecLike -class EventStreamSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class EventStreamSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { import EventStreamSpec._ import EventStream._ diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/ActorRefSerializationSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/ActorRefSerializationSpec.scala index 27f79fed56..8f3596c359 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/ActorRefSerializationSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/ActorRefSerializationSpec.scala @@ -9,6 +9,7 @@ import akka.actor.typed.scaladsl.adapter._ import akka.actor.typed.ActorRef import akka.serialization.{ JavaSerializer, SerializationExtension } import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import com.typesafe.config.ConfigFactory import org.scalatest.WordSpecLike @@ -27,7 +28,10 @@ object ActorRefSerializationSpec { case class MessageWrappingActorRef(s: String, ref: ActorRef[Unit]) extends java.io.Serializable } -class ActorRefSerializationSpec extends ScalaTestWithActorTestKit(ActorRefSerializationSpec.config) with WordSpecLike { +class ActorRefSerializationSpec + extends ScalaTestWithActorTestKit(ActorRefSerializationSpec.config) + with WordSpecLike + with LogCapturing { val serialization = SerializationExtension(system.toClassic) diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/ActorSystemSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/ActorSystemSpec.scala index 2515b420e5..d40045c4d6 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/ActorSystemSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/ActorSystemSpec.scala @@ -14,13 +14,20 @@ import akka.Done import akka.actor.CoordinatedShutdown import akka.actor.InvalidMessageException import akka.actor.testkit.typed.scaladsl.TestInbox +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.adapter._ import org.scalatest._ import org.scalatest.concurrent.Eventually import org.scalatest.concurrent.ScalaFutures -class ActorSystemSpec extends WordSpec with Matchers with BeforeAndAfterAll with ScalaFutures with Eventually { +class ActorSystemSpec + extends WordSpec + with Matchers + with BeforeAndAfterAll + with ScalaFutures + with Eventually + with LogCapturing { override implicit val patienceConfig = PatienceConfig(1.second) def system[T](behavior: Behavior[T], name: String) = ActorSystem(behavior, name) diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/receptionist/LocalReceptionistSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/receptionist/LocalReceptionistSpec.scala index b644c31bc1..16d9c24d7d 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/receptionist/LocalReceptionistSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/receptionist/LocalReceptionistSpec.scala @@ -5,10 +5,12 @@ package akka.actor.typed.internal.receptionist import scala.concurrent.Future + import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.BehaviorTestKit import akka.actor.testkit.typed.scaladsl.TestInbox import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed._ import akka.actor.typed.receptionist.Receptionist import akka.actor.typed.receptionist.Receptionist._ @@ -36,7 +38,7 @@ object LocalReceptionistSpec { } -class LocalReceptionistSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class LocalReceptionistSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { import LocalReceptionistSpec._ abstract class TestSetup { @@ -128,7 +130,7 @@ class LocalReceptionistSpec extends ScalaTestWithActorTestKit with WordSpecLike } } -class LocalReceptionistBehaviorSpec extends WordSpec with Matchers { +class LocalReceptionistBehaviorSpec extends WordSpec with Matchers with LogCapturing { import LocalReceptionistSpec._ def assertEmpty(inboxes: TestInbox[_]*): Unit = { diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/receptionist/ServiceKeySerializationSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/receptionist/ServiceKeySerializationSpec.scala index 9736978dc7..5fd7908902 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/receptionist/ServiceKeySerializationSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/receptionist/ServiceKeySerializationSpec.scala @@ -9,11 +9,13 @@ import akka.actor.typed.receptionist.ServiceKey import akka.actor.typed.scaladsl.adapter._ import akka.serialization.SerializationExtension import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.WordSpecLike class ServiceKeySerializationSpec extends ScalaTestWithActorTestKit(ActorRefSerializationSpec.config) - with WordSpecLike { + with WordSpecLike + with LogCapturing { val serialization = SerializationExtension(system.toClassic) diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/routing/RoutingLogicSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/routing/RoutingLogicSpec.scala index fd4867cc32..cc5d7e9de7 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/routing/RoutingLogicSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/internal/routing/RoutingLogicSpec.scala @@ -5,10 +5,11 @@ package akka.actor.typed.internal.routing import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.Matchers import org.scalatest.WordSpecLike -class RoutingLogicSpec extends ScalaTestWithActorTestKit with WordSpecLike with Matchers { +class RoutingLogicSpec extends ScalaTestWithActorTestKit with WordSpecLike with Matchers with LogCapturing { "The round robin routing logic" must { diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/ActorContextAskSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/ActorContextAskSpec.scala index 26d66ed8fd..7978b56014 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/ActorContextAskSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/ActorContextAskSpec.scala @@ -4,22 +4,21 @@ package akka.actor.typed.scaladsl -import akka.actor.typed.scaladsl.adapter._ import akka.actor.typed.{ ActorRef, PostStop, Props } -import akka.testkit.EventFilter import akka.actor.testkit.typed.scaladsl.TestProbe import com.typesafe.config.ConfigFactory - import scala.concurrent.TimeoutException import scala.concurrent.duration._ import scala.reflect.ClassTag import scala.util.{ Failure, Success } + +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.WordSpecLike object ActorContextAskSpec { val config = ConfigFactory.parseString(""" - akka.loggers = ["akka.testkit.TestEventListener"] ping-pong-dispatcher { executor = thread-pool-executor type = PinnedDispatcher @@ -31,9 +30,10 @@ object ActorContextAskSpec { """) } -class ActorContextAskSpec extends ScalaTestWithActorTestKit(ActorContextAskSpec.config) with WordSpecLike { - - implicit val classic = system.toClassic // FIXME #24348: eventfilter support in testkit +class ActorContextAskSpec + extends ScalaTestWithActorTestKit(ActorContextAskSpec.config) + with WordSpecLike + with LogCapturing { "The Scala DSL ActorContext" must { @@ -104,7 +104,7 @@ class ActorContextAskSpec extends ScalaTestWithActorTestKit(ActorContextAskSpec. } } - EventFilter[NotImplementedError](occurrences = 1, start = "Pong").intercept { + LoggingEventFilter.error[NotImplementedError].withMessageContains("Pong").intercept { spawn(snitch) } @@ -126,11 +126,7 @@ class ActorContextAskSpec extends ScalaTestWithActorTestKit(ActorContextAskSpec. } } - EventFilter.warning(occurrences = 1, message = "received dead letter: boo").intercept { - EventFilter.info(occurrences = 1, start = "Message [java.lang.String]").intercept { - spawn(snitch) - } - } + spawn(snitch) val exc = probe.expectMessageType[TimeoutException] exc.getMessage should include("had already been terminated") diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/ActorContextPipeToSelfSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/ActorContextPipeToSelfSpec.scala index 76809c4d5c..495e26bb0f 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/ActorContextPipeToSelfSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/ActorContextPipeToSelfSpec.scala @@ -8,6 +8,7 @@ import scala.concurrent.Future import scala.util.control.NoStackTrace import scala.util.{ Failure, Success } +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.testkit.typed.scaladsl.{ ScalaTestWithActorTestKit, TestProbe } import akka.actor.typed.Props import com.typesafe.config.ConfigFactory @@ -24,7 +25,8 @@ object ActorContextPipeToSelfSpec { final class ActorContextPipeToSelfSpec extends ScalaTestWithActorTestKit(ActorContextPipeToSelfSpec.config) - with WordSpecLike { + with WordSpecLike + with LogCapturing { "The Scala DSL ActorContext pipeToSelf" must { "handle success" in { responseFrom(Future.successful("hi")) should ===("ok: hi") } diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/ActorLoggingSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/ActorLoggingSpec.scala index ae78c663fd..833f662bed 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/ActorLoggingSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/ActorLoggingSpec.scala @@ -5,15 +5,24 @@ package akka.actor.typed.scaladsl import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.atomic.AtomicReference -import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.TestException +import akka.actor.testkit.typed.scaladsl.ActorTestKit +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter +import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing +import akka.actor.typed.Behavior import akka.actor.typed.scaladsl.adapter._ -import akka.actor.typed.{ Behavior, LogMarker } -import akka.event.Logging -import akka.event.Logging.{ LogEvent, LogEventWithCause, LogEventWithMarker } -import akka.testkit.EventFilter +import akka.event.DefaultLoggingFilter +import akka.event.Logging.DefaultLogger +import akka.event.slf4j.Slf4jLogger +import akka.event.slf4j.Slf4jLoggingFilter +import com.typesafe.config.ConfigFactory import org.scalatest.WordSpecLike +import org.slf4j.LoggerFactory +import org.slf4j.MDC +import org.slf4j.helpers.BasicMarkerFactory class SomeClass @@ -38,133 +47,139 @@ class BehaviorWhereTheLoggerIsUsed(context: ActorContext[String]) extends Abstra class ActorLoggingSpec extends ScalaTestWithActorTestKit(""" akka.loglevel = DEBUG # test verifies debug - akka.loggers = ["akka.testkit.TestEventListener"] - """) with WordSpecLike { + """) with WordSpecLike with LogCapturing { - val marker = LogMarker("marker") - val cause = new TestException("böö") + val marker = new BasicMarkerFactory().getMarker("marker") + val cause = TestException("böö") implicit val classic = system.toClassic + class AnotherLoggerClass + "Logging in an actor" must { "be conveniently available from the context" in { - val actor = - EventFilter.info("Started", source = "akka://ActorLoggingSpec/user/the-actor", occurrences = 1).intercept { - spawn( - Behaviors.setup[String] { context => - context.log.info("Started") - Behaviors.receive { (context, message) => - context.log.info("got message {}", message) - Behaviors.same - } - }, - "the-actor") + val behavior: Behavior[String] = Behaviors.setup[String] { context => + context.log.info("Started") + + Behaviors.receive { (context, message) => + context.log.info("got message {}", message) + Behaviors.same + } + } + + val actor = LoggingEventFilter.info("Started").intercept(spawn(behavior, "the-actor")) + + LoggingEventFilter.info("got message Hello").intercept(actor ! "Hello") + + } + + "log with custom Logger class" in { + val behavior: Behavior[String] = Behaviors.setup[String] { context => + context.setLoggerName(classOf[AnotherLoggerClass]) + context.log.info("Started") + + Behaviors.receive { (context, message) => + context.log.info("got message {}", message) + Behaviors.same + } + } + + val actor = + LoggingEventFilter.info("Started").withLoggerName(classOf[AnotherLoggerClass].getName).intercept { + spawn(behavior, "the-other-actor") } - EventFilter - .info("got message Hello", source = "akka://ActorLoggingSpec/user/the-actor", occurrences = 1) + // verify that it's logged with `AnotherLoggerClass` + // verify that it's only capturing log events for that logger and not any other logger when interceptLogger + // is used + val count = new AtomicInteger + LoggingEventFilter + .custom { logEvent => + count.incrementAndGet() + logEvent.message == "got message Hello" && logEvent.loggerName == classOf[AnotherLoggerClass].getName + } + .withLoggerName(classOf[AnotherLoggerClass].getName) + .withOccurrences(2) .intercept { actor ! "Hello" + LoggerFactory.getLogger(classOf[ActorLoggingSpec]).debug("Hello from other logger") + actor ! "Hello" } + count.get should ===(2) + } "contain the class name where the first log was called" in { - val eventFilter = EventFilter.custom({ - case l: LogEvent if l.logClass == classOf[ActorLoggingSpec] => true - case l: LogEvent => - println(l.logClass) + val eventFilter = LoggingEventFilter.custom({ + case event if event.loggerName == classOf[ActorLoggingSpec].getName => + true + case event => + println(event.loggerName) false - }, occurrences = 1) + }) - eventFilter.intercept { - spawn(Behaviors.setup[String] { context => - context.log.info("Started") + eventFilter.intercept(spawn(Behaviors.setup[String] { context => + context.log.info("Started") + + Behaviors.receive { (context, message) => + context.log.info("got message {}", message) + Behaviors.same + } + }, "the-actor-with-class")) - Behaviors.receive { (context, message) => - context.log.info("got message {}", message) - Behaviors.same - } - }, "the-actor-with-class") - } } "contain the object class name where the first log was called" in { - val eventFilter = EventFilter.custom({ - case l: LogEvent if l.logClass == WhereTheBehaviorIsDefined.getClass => true - case l: LogEvent => - println(l.logClass) + val eventFilter = LoggingEventFilter.custom({ + case event if event.loggerName == WhereTheBehaviorIsDefined.getClass.getName => true + case other => + println(other.loggerName) false - }, occurrences = 1) + }) - eventFilter.intercept { - spawn(WhereTheBehaviorIsDefined.behavior, "the-actor-with-object") - } + eventFilter.intercept(spawn(WhereTheBehaviorIsDefined.behavior, "the-actor-with-object")) } "contain the abstract behavior class name where the first log was called" in { - val eventFilter = EventFilter.custom({ - case l: LogEvent if l.logClass == classOf[BehaviorWhereTheLoggerIsUsed] => true - case l: LogEvent => - println(l.logClass) + val eventFilter = LoggingEventFilter.custom({ + case event if event.loggerName == classOf[BehaviorWhereTheLoggerIsUsed].getName => true + case other => + println(other.loggerName) false - }, occurrences = 1) + }) eventFilter.intercept { - spawn(BehaviorWhereTheLoggerIsUsed.behavior, "the-actor-with-behavior") - } - } - - "allow for adapting log source and class" in { - val eventFilter = EventFilter.custom({ - case l: LogEvent => - l.logClass == classOf[SomeClass] && - l.logSource == "who-knows-where-it-came-from" && - l.mdc == Map("mdc" -> true) // mdc should be kept - }, occurrences = 1) - - eventFilter.intercept { - spawn( - Behaviors.setup[String] { context => - val log = context.log - .withMdc(Map("mdc" -> true)) - .withLoggerClass(classOf[SomeClass]) - .withLogSource("who-knows-where-it-came-from") - log.info("Started") - - Behaviors.empty - }, - "the-actor-with-custom-class") + spawn(Behaviors.setup[String](context => new BehaviorWhereTheLoggerIsUsed(context)), "the-actor-with-behavior") } } "pass markers to the log" in { - EventFilter - .custom({ - case event: LogEventWithMarker if event.marker.name == marker.name => true - }, occurrences = 9) + LoggingEventFilter + .custom { event => + event.marker.map(_.getName) == Option(marker.getName) + } + .withOccurrences(5) .intercept(spawn(Behaviors.setup[Any] { context => context.log.debug(marker, "whatever") context.log.info(marker, "whatever") - context.log.warning(marker, "whatever") + context.log.warn(marker, "whatever") context.log.error(marker, "whatever") - context.log.error(marker, cause, "whatever") - Logging.AllLogLevels.foreach(level => { - context.log.log(level, marker, "whatever") - }) + context.log.error(marker, "whatever", cause) Behaviors.stopped })) } - "pass cause with warning" in { - EventFilter - .custom({ - case event: LogEventWithCause if event.cause == cause => true - }, occurrences = 2) + "pass cause with warn" in { + LoggingEventFilter + .custom { event => + event.throwable == Option(cause) + } + .withOccurrences(2) .intercept(spawn(Behaviors.setup[Any] { context => - context.log.warning(cause, "whatever") - context.log.warning(marker, cause, "whatever") + context.log.warn("whatever", cause) + context.log.warn(marker, "whatever", cause) Behaviors.stopped })) } @@ -173,111 +188,108 @@ class ActorLoggingSpec extends ScalaTestWithActorTestKit(""" // Not the best test but at least it exercises every log overload ;) - EventFilter - .custom({ - case _ => true // any is fine, we're just after the right count of statements reaching the listener - }, occurrences = 120) - .intercept { - spawn(Behaviors.setup[String] { context => - context.log.debug("message") - context.log.debug("{}", "arg1") - context.log.debug("{} {}", "arg1", "arg2") - context.log.debug("{} {} {}", "arg1", "arg2", "arg3") - context.log.debug("{} {} {} {}", "arg1", "arg2", "arg3", "arg4") - context.log.debug("{} {} {} {} {}", Array("arg1", "arg2", "arg3", "arg4", "arg5")) - context.log.debug(marker, "message") - context.log.debug(marker, "{}", "arg1") - context.log.debug(marker, "{} {}", "arg1", "arg2") - context.log.debug(marker, "{} {} {}", "arg1", "arg2", "arg3") - context.log.debug(marker, "{} {} {} {}", "arg1", "arg2", "arg3", "arg4") - context.log.debug(marker, "{} {} {} {} {}", Array("arg1", "arg2", "arg3", "arg4", "arg5")) - - context.log.info("message") - context.log.info("{}", "arg1") - context.log.info("{} {}", "arg1", "arg2") - context.log.info("{} {} {}", "arg1", "arg2", "arg3") - context.log.info("{} {} {} {}", "arg1", "arg2", "arg3", "arg4") - context.log.info("{} {} {} {} {}", Array("arg1", "arg2", "arg3", "arg4", "arg5")) - context.log.info(marker, "message") - context.log.info(marker, "{}", "arg1") - context.log.info(marker, "{} {}", "arg1", "arg2") - context.log.info(marker, "{} {} {}", "arg1", "arg2", "arg3") - context.log.info(marker, "{} {} {} {}", "arg1", "arg2", "arg3", "arg4") - context.log.info(marker, "{} {} {} {} {}", Array("arg1", "arg2", "arg3", "arg4", "arg5")) - - context.log.warning("message") - context.log.warning("{}", "arg1") - context.log.warning("{} {}", "arg1", "arg2") - context.log.warning("{} {} {}", "arg1", "arg2", "arg3") - context.log.warning("{} {} {} {}", "arg1", "arg2", "arg3", "arg4") - context.log.warning("{} {} {} {} {}", Array("arg1", "arg2", "arg3", "arg4", "arg5")) - context.log.warning(marker, "message") - context.log.warning(marker, "{}", "arg1") - context.log.warning(marker, "{} {}", "arg1", "arg2") - context.log.warning(marker, "{} {} {}", "arg1", "arg2", "arg3") - context.log.warning(marker, "{} {} {} {}", "arg1", "arg2", "arg3", "arg4") - context.log.warning(marker, "{} {} {} {} {}", Array("arg1", "arg2", "arg3", "arg4", "arg5")) - - context.log.warning(cause, "message") - context.log.warning(cause, "{}", "arg1") - context.log.warning(cause, "{} {}", "arg1", "arg2") - context.log.warning(cause, "{} {} {}", "arg1", "arg2", "arg3") - context.log.warning(cause, "{} {} {} {}", "arg1", "arg2", "arg3", "arg4") - context.log.warning(cause, "{} {} {} {} {}", Array("arg1", "arg2", "arg3", "arg4", "arg5")) - context.log.warning(marker, cause, "message") - context.log.warning(marker, cause, "{}", "arg1") - context.log.warning(marker, cause, "{} {}", "arg1", "arg2") - context.log.warning(marker, cause, "{} {} {}", "arg1", "arg2", "arg3") - context.log.warning(marker, cause, "{} {} {} {}", "arg1", "arg2", "arg3", "arg4") - context.log.warning(marker, cause, "{} {} {} {} {}", Array("arg1", "arg2", "arg3", "arg4", "arg5")) - - context.log.error("message") - context.log.error("{}", "arg1") - context.log.error("{} {}", "arg1", "arg2") - context.log.error("{} {} {}", "arg1", "arg2", "arg3") - context.log.error("{} {} {} {}", "arg1", "arg2", "arg3", "arg4") - context.log.error("{} {} {} {} {}", Array("arg1", "arg2", "arg3", "arg4", "arg5")) - context.log.error(marker, "message") - context.log.error(marker, "{}", "arg1") - context.log.error(marker, "{} {}", "arg1", "arg2") - context.log.error(marker, "{} {} {}", "arg1", "arg2", "arg3") - context.log.error(marker, "{} {} {} {}", "arg1", "arg2", "arg3", "arg4") - context.log.error(marker, "{} {} {} {} {}", Array("arg1", "arg2", "arg3", "arg4", "arg5")) - - context.log.error(cause, "message") - context.log.error(cause, "{}", "arg1") - context.log.error(cause, "{} {}", "arg1", "arg2") - context.log.error(cause, "{} {} {}", "arg1", "arg2", "arg3") - context.log.error(cause, "{} {} {} {}", "arg1", "arg2", "arg3", "arg4") - context.log.error(cause, "{} {} {} {} {}", Array("arg1", "arg2", "arg3", "arg4", "arg5")) - context.log.error(marker, cause, "message") - context.log.error(marker, cause, "{}", "arg1") - context.log.error(marker, cause, "{} {}", "arg1", "arg2") - context.log.error(marker, cause, "{} {} {}", "arg1", "arg2", "arg3") - context.log.error(marker, cause, "{} {} {} {}", "arg1", "arg2", "arg3", "arg4") - context.log.error(marker, cause, "{} {} {} {} {}", Array("arg1", "arg2", "arg3", "arg4", "arg5")) - - Logging.AllLogLevels.foreach(level => { - context.log.log(level, "message") - context.log.log(level, "{}", "arg1") - context.log.log(level, "{} {}", "arg1", "arg2") - context.log.log(level, "{} {} {}", "arg1", "arg2", "arg3") - context.log.log(level, "{} {} {} {}", "arg1", "arg2", "arg3", "arg4") - context.log.log(level, "{} {} {} {} {}", Array("arg1", "arg2", "arg3", "arg4", "arg5")) - - context.log.log(level, marker, "message") - context.log.log(level, marker, "{}", "arg1") - context.log.log(level, marker, "{} {}", "arg1", "arg2") - context.log.log(level, marker, "{} {} {}", "arg1", "arg2", "arg3") - context.log.log(level, marker, "{} {} {} {}", "arg1", "arg2", "arg3", "arg4") - context.log.log(level, marker, "{} {} {} {} {}", Array("arg1", "arg2", "arg3", "arg4", "arg5")) - }) - - Behaviors.stopped - }) + LoggingEventFilter + .custom { _ => + true // any is fine, we're just after the right count of statements reaching the listener } + .withOccurrences(36) + .intercept({ + spawn(Behaviors.setup[String] { + context => + context.log.debug("message") + context.log.debug("{}", "arg1") + // using `: Any` to avoid "ambiguous reference to overloaded definition", see also LoggerOpsSpec + context.log.debug("{} {}", "arg1", "arg2": Any) + context.log.debug("{} {} {}", "arg1", "arg2", "arg3") + context.log.debug(marker, "message") + context.log.debug(marker, "{}", "arg1") + context.log.debug(marker, "{} {}", "arg1", "arg2": Any) + context.log.debug(marker, "{} {} {}", "arg1", "arg2", "arg3") + + context.log.info("message") + context.log.info("{}", "arg1") + context.log.info("{} {}", "arg1", "arg2": Any) + context.log.info("{} {} {}", "arg1", "arg2", "arg3") + context.log.info(marker, "message") + context.log.info(marker, "{}", "arg1") + context.log.info(marker, "{} {}", "arg1", "arg2": Any) + context.log.info(marker, "{} {} {}", "arg1", "arg2", "arg3") + + context.log.warn("message") + context.log.warn("{}", "arg1") + context.log.warn("{} {}", "arg1", "arg2": Any) + context.log.warn("{} {} {}", "arg1", "arg2", "arg3") + context.log.warn(marker, "message") + context.log.warn(marker, "{}", "arg1") + context.log.warn(marker, "{} {}", "arg1", "arg2": Any) + context.log.warn(marker, "{} {} {}", "arg1", "arg2", "arg3") + context.log.warn("message", cause) + + context.log.error("message") + context.log.error("{}", "arg1") + context.log.error("{} {}", "arg1", "arg2": Any) + context.log.error("{} {} {}", "arg1", "arg2", "arg3") + // using to avoid vararg problem for primitive type, see also LoggerOpsSpec + context.log.error("{} {} {}", "arg1", "arg2", 3.asInstanceOf[AnyRef]) + context.log.error(marker, "message") + context.log.error(marker, "{}", "arg1") + context.log.error(marker, "{} {}", "arg1", "arg2": Any) + context.log.error(marker, "{} {} {}", "arg1", "arg2", "arg3") + context.log.error(marker, "{} {} {}", "arg1", "arg2", 3.asInstanceOf[AnyRef]) + context.log.error("message", cause) + + Behaviors.stopped + }) + }) } + "use Slf4jLogger from akka-slf4j automatically" in { + LoggingEventFilter.info("via Slf4jLogger").intercept { + // this will log via classic eventStream + system.toClassic.log.info("via Slf4jLogger") + } + } + + } + + "SLF4J Settings" must { + import akka.actor.typed.scaladsl.adapter._ + import akka.actor.ExtendedActorSystem + import akka.actor.{ ActorSystem => ClassicActorSystem } + + "by default be amended to use Slf4jLogger" in { + system.settings.config.getStringList("akka.loggers").size() should ===(1) + system.settings.config.getStringList("akka.loggers").get(0) should ===(classOf[Slf4jLogger].getName) + system.settings.config.getString("akka.logging-filter") should ===(classOf[Slf4jLoggingFilter].getName) + + system.toClassic.settings.Loggers should ===(List(classOf[Slf4jLogger].getName)) + system.toClassic.settings.LoggingFilter should ===(classOf[Slf4jLoggingFilter].getName) + } + + "by default be amended to use Slf4jLogger when starting classic ActorSystem" in { + val classicSys = akka.actor.ActorSystem(system.name) + try { + classicSys.settings.config.getStringList("akka.loggers").size() should ===(1) + classicSys.settings.config.getStringList("akka.loggers").get(0) should ===(classOf[Slf4jLogger].getName) + classicSys.settings.config.getString("akka.logging-filter") should ===(classOf[Slf4jLoggingFilter].getName) + + classicSys.settings.Loggers should ===(List(classOf[Slf4jLogger].getName)) + classicSys.settings.LoggingFilter should ===(classOf[Slf4jLoggingFilter].getName) + + } finally { + ActorTestKit.shutdown(classicSys.toTyped) + } + } + + "not be amended when use-slf4j=off" in { + val dynamicAccess = system.toClassic.asInstanceOf[ExtendedActorSystem].dynamicAccess + val config = ClassicActorSystem.Settings.amendSlf4jConfig( + ConfigFactory.parseString("akka.use-slf4j = off").withFallback(ConfigFactory.defaultReference()), + dynamicAccess) + config.getStringList("akka.loggers").size() should ===(1) + config.getStringList("akka.loggers").get(0) should ===(classOf[DefaultLogger].getName) + config.getString("akka.logging-filter") should ===(classOf[DefaultLoggingFilter].getName) + } } trait Protocol { @@ -289,12 +301,12 @@ class ActorLoggingSpec extends ScalaTestWithActorTestKit(""" "provide the MDC values in the log" in { val behaviors = Behaviors.withMdc[Protocol]( - Map("static" -> 1), + Map("static" -> "1"), // FIXME why u no infer the type here Scala?? (message: Protocol) => if (message.transactionId == 1) - Map("txId" -> message.transactionId, "first" -> true) - else Map("txId" -> message.transactionId)) { + Map("txId" -> message.transactionId.toString, "first" -> "true") + else Map("txId" -> message.transactionId.toString)) { Behaviors.setup { context => context.log.info("Starting") Behaviors.receiveMessage { _ => @@ -304,47 +316,28 @@ class ActorLoggingSpec extends ScalaTestWithActorTestKit(""" } } - // mdc on defer is empty (thread and timestamp MDC is added by logger backend) - val ref = EventFilter - .custom( - { - case logEvent if logEvent.level == Logging.InfoLevel => - logEvent.message should ===("Starting") - logEvent.mdc shouldBe empty - true - case other => system.log.error(s"Unexpected log event: {}", other); false - }, - occurrences = 1) + // mdc on defer is empty + val ref = LoggingEventFilter + .info("Starting") + // not counting for example "akkaSource", but it shouldn't have any other entries + .withCustom(logEvent => logEvent.mdc.keysIterator.forall(_.startsWith("akka"))) .intercept { spawn(behaviors) } // mdc on message - EventFilter - .custom( - { - case logEvent if logEvent.level == Logging.InfoLevel => - logEvent.message should ===("Got message!") - logEvent.mdc should ===(Map("static" -> 1, "txId" -> 1L, "first" -> true)) - true - case other => system.log.error(s"Unexpected log event: {}", other); false - }, - occurrences = 1) + LoggingEventFilter + .info("Got message!") + .withMdc(Map("static" -> "1", "txId" -> "1", "first" -> "true")) .intercept { ref ! Message(1, "first") } // mdc does not leak between messages - EventFilter - .custom( - { - case logEvent if logEvent.level == Logging.InfoLevel => - logEvent.message should ===("Got message!") - logEvent.mdc should ===(Map("static" -> 1, "txId" -> 2L)) - true - case other => system.log.error(s"Unexpected log event: {}", other); false - }, - occurrences = 1) + LoggingEventFilter + .info("Got message!") + .withMdc(Map("static" -> "1", "txId" -> "2")) + .withCustom(event => !event.mdc.contains("first")) .intercept { ref ! Message(2, "second") } @@ -353,8 +346,8 @@ class ActorLoggingSpec extends ScalaTestWithActorTestKit(""" "use the outermost initial mdc" in { // when we declare it, we expect the outermost to win val behavior = - Behaviors.withMdc[String](Map("outermost" -> true)) { - Behaviors.withMdc(Map("innermost" -> true)) { + Behaviors.withMdc[String](Map("outermost" -> "true")) { + Behaviors.withMdc(Map("innermost" -> "true")) { Behaviors.receive { (context, message) => context.log.info(message) Behaviors.same @@ -363,16 +356,10 @@ class ActorLoggingSpec extends ScalaTestWithActorTestKit(""" } val ref = spawn(behavior) - EventFilter - .custom( - { - case logEvent if logEvent.level == Logging.InfoLevel => - logEvent.message should ===("message") - logEvent.mdc should ===(Map("outermost" -> true)) - true - case other => system.log.error(s"Unexpected log event: {}", other); false - }, - occurrences = 1) + LoggingEventFilter + .info("message") + .withMdc(Map("outermost" -> "true")) + .withCustom(event => !event.mdc.contains("innermost")) .intercept { ref ! "message" } @@ -390,33 +377,16 @@ class ActorLoggingSpec extends ScalaTestWithActorTestKit(""" } } - val ref = spawn(Behaviors.withMdc(Map("hasMdc" -> true))(behavior)) - EventFilter - .custom( - { - case logEvent if logEvent.level == Logging.InfoLevel => - logEvent.message should ===("message") - logEvent.mdc should ===(Map("hasMdc" -> true)) - true - case other => system.log.error(s"Unexpected log event: {}", other); false - }, - occurrences = 1) - .intercept { - ref ! "message" - } + val ref = spawn(Behaviors.withMdc(Map("hasMdc" -> "true"))(behavior)) + LoggingEventFilter.info("message").withMdc(Map("hasMdc" -> "true")).intercept { + ref ! "message" + } ref ! "new-behavior" - EventFilter - .custom( - { - case logEvent if logEvent.level == Logging.InfoLevel => - logEvent.message should ===("message") - logEvent.mdc should ===(Map("hasMdc" -> true)) // original mdc should stay - true - case other => system.log.error(s"Unexpected log event: {}", other); false - }, - occurrences = 1) + LoggingEventFilter + .info("message") + .withMdc(Map("hasMdc" -> "true")) // original mdc should stay .intercept { ref ! "message" } @@ -427,7 +397,7 @@ class ActorLoggingSpec extends ScalaTestWithActorTestKit(""" // when it changes while running, we expect the latest one to apply val id = new AtomicInteger(0) def behavior: Behavior[String] = - Behaviors.withMdc(Map("mdc-version" -> id.incrementAndGet())) { + Behaviors.withMdc(Map("mdc-version" -> id.incrementAndGet().toString)) { Behaviors.receive { (context, message) => message match { case "new-mdc" => @@ -440,30 +410,13 @@ class ActorLoggingSpec extends ScalaTestWithActorTestKit(""" } val ref = spawn(behavior) - EventFilter - .custom( - { - case logEvent if logEvent.level == Logging.InfoLevel => - logEvent.message should ===("message") - logEvent.mdc should ===(Map("mdc-version" -> 1)) - true - case other => system.log.error(s"Unexpected log event: {}", other); false - }, - occurrences = 1) - .intercept { - ref ! "message" - } + LoggingEventFilter.info("message").withMdc(Map("mdc-version" -> "1")).intercept { + ref ! "message" + } ref ! "new-mdc" - EventFilter - .custom( - { - case logEvent if logEvent.level == Logging.InfoLevel => - logEvent.message should ===("message") - logEvent.mdc should ===(Map("mdc-version" -> 2)) // mdc should have been replaced - true - case other => system.log.error(s"Unexpected log event: {}", other); false - }, - occurrences = 1) + LoggingEventFilter + .info("message") + .withMdc(Map("mdc-version" -> "2")) // mdc should have been replaced .intercept { ref ! "message" } @@ -473,40 +426,55 @@ class ActorLoggingSpec extends ScalaTestWithActorTestKit(""" "provide a withMdc decorator" in { val behavior = Behaviors.withMdc[Protocol](Map("mdc" -> "outer"))(Behaviors.setup { context => Behaviors.receiveMessage { _ => - context.log.withMdc(Map("mdc" -> "inner")).info("Got message log.withMDC!") - // after log.withMdc so we know it didn't change the outer mdc - context.log.info("Got message behavior.withMdc!") + context.log.info("first") + org.slf4j.MDC.put("mdc", "inner-" + org.slf4j.MDC.get("mdc")) + context.log.info("second") Behaviors.same } }) // mdc on message val ref = spawn(behavior) - EventFilter - .custom( - { - case logEvent if logEvent.level == Logging.InfoLevel => - logEvent.message should ===("Got message behavior.withMdc!") - logEvent.mdc should ===(Map("mdc" -> "outer")) - true - case other => system.log.error(s"Unexpected log event: {}", other); false - }, - occurrences = 1) - .intercept { - EventFilter - .custom( - { - case logEvent if logEvent.level == Logging.InfoLevel => - logEvent.message should ===("Got message log.withMDC!") - logEvent.mdc should ===(Map("mdc" -> "inner")) - true - case other => system.log.error(s"Unexpected log event: {}", other); false - }, - occurrences = 1) - .intercept { - ref ! Message(1, "first") - } + LoggingEventFilter.info("first").withMdc(Map("mdc" -> "outer")).intercept { + LoggingEventFilter.info("second").withMdc(Map("mdc" -> "inner-outer")).intercept { + ref ! Message(1, "first") } + } + } + + "always include some MDC values in the log" in { + // need AtomicReference because LoggingFilter defined before actor is created and ActorTestKit names are dynamic + val actorPathStr = new AtomicReference[String] + val behavior = + Behaviors.setup[Message] { context => + actorPathStr.set(context.self.path.toString) + context.log.info("Starting") + Behaviors.receiveMessage { _ => + if (MDC.get("logSource") != null) + throw new IllegalStateException("MDC wasn't cleared. logSource has value before context.log is accessed.") + context.log.info("Got message!") + Behaviors.same + } + } + + // log from setup + // can't use LoggingEventFilter.withMdc here because the actorPathStr isn't know yet + val ref = + LoggingEventFilter.info("Starting").withCustom(event => event.mdc("akkaSource") == actorPathStr.get).intercept { + spawn(behavior) + } + + // on message + LoggingEventFilter + .info("Got message!") + .withMdc(Map("akkaSource" -> actorPathStr.get)) + .withOccurrences(10) + .intercept { + (1 to 10).foreach { n => + ref ! Message(n, s"msg-$n") + } + } + } } diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/DispatcherSelectorSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/DispatcherSelectorSpec.scala index fe2124e7eb..efd965951c 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/DispatcherSelectorSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/DispatcherSelectorSpec.scala @@ -9,6 +9,7 @@ import akka.actor.setup.ActorSystemSetup import akka.actor.testkit.typed.scaladsl.ActorTestKit import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.ActorRef import akka.actor.typed.ActorSystem import akka.actor.typed.Behavior @@ -39,7 +40,10 @@ object DispatcherSelectorSpec { } -class DispatcherSelectorSpec extends ScalaTestWithActorTestKit(DispatcherSelectorSpec.config) with WordSpecLike { +class DispatcherSelectorSpec + extends ScalaTestWithActorTestKit(DispatcherSelectorSpec.config) + with WordSpecLike + with LogCapturing { import DispatcherSelectorSpec.PingPong import DispatcherSelectorSpec.PingPong._ diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/GracefulStopSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/GracefulStopSpec.scala index 8376401d4f..0740bbda58 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/GracefulStopSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/GracefulStopSpec.scala @@ -9,9 +9,10 @@ import akka.Done import akka.NotUsed import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.WordSpecLike -final class GracefulStopSpec extends ScalaTestWithActorTestKit with WordSpecLike { +final class GracefulStopSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { "Graceful stop" must { diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/LoggerOpsSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/LoggerOpsSpec.scala new file mode 100644 index 0000000000..e9aa74b4df --- /dev/null +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/LoggerOpsSpec.scala @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2009-2019 Lightbend Inc. + */ + +package akka.actor.typed.scaladsl + +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter +import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing +import org.scalatest.WordSpecLike +import org.slf4j.LoggerFactory + +object LoggerOpsSpec { + case class Value1(i: Int) + case class Value2(i: Int) + case class Value3(i: Int) +} + +class LoggerOpsSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { + import LoggerOpsSpec._ + + val log = LoggerFactory.getLogger(getClass) + + "LoggerOps" must { + + "provide extension method for 2 arguments" in { + LoggingEventFilter.info("[template a b]").intercept { + log.info2("[template {} {}]", "a", "b") + } + LoggingEventFilter.info("[template a 2]").intercept { + log.info2("[template {} {}]", "a", 2) + } + LoggingEventFilter.info("[template 1 2]").intercept { + log.info2("[template {} {}]", 1, 2) + } + LoggingEventFilter.info("[template 1 b]").intercept { + log.info2("[template {} {}]", 1, "b") + } + LoggingEventFilter.info("[template a Value2(2)]").intercept { + log.info2("[template {} {}]", "a", Value2(2)) + } + LoggingEventFilter.info("[template Value1(1) Value1(1)]").intercept { + log.info2("[template {} {}]", Value1(1), Value1(1)) + } + LoggingEventFilter.info("[template Value1(1) Value2(2)]").intercept { + log.info2("[template {} {}]", Value1(1), Value2(2)) + } + } + + "provide extension method for vararg arguments" in { + LoggingEventFilter.info("[template a b c]").intercept { + log.infoN("[template {} {} {}]", "a", "b", "c") + } + LoggingEventFilter.info("[template a b 3]").intercept { + log.infoN("[template {} {} {}]", "a", "b", 3) + } + LoggingEventFilter.info("[template a 2 c]").intercept { + log.infoN("[template {} {} {}]", "a", 2, "c") + } + LoggingEventFilter.info("[template 1 2 3]").intercept { + log.infoN("[template {} {} {}]", 1, 2, 3) + } + LoggingEventFilter.info("[template 1 b c]").intercept { + log.infoN("[template {} {} {}]", 1, "b", "c") + } + LoggingEventFilter.info("[template a Value2(2) Value3(3)]").intercept { + log.infoN("[template {} {} {}]", "a", Value2(2), Value3(3)) + } + LoggingEventFilter.info("[template Value1(1) Value1(1) Value1(1)]").intercept { + log.infoN("[template {} {} {}]", Value1(1), Value1(1), Value1(1)) + } + LoggingEventFilter.info("[template Value1(1) Value2(2) Value3(3)]").intercept { + log.infoN("[template {} {} {}]", Value1(1), Value2(2), Value3(3)) + } + } + } +} diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/MessageAdapterSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/MessageAdapterSpec.scala index ae90d30315..4e2f8839df 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/MessageAdapterSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/MessageAdapterSpec.scala @@ -4,21 +4,22 @@ package akka.actor.typed.scaladsl +import akka.actor.UnhandledMessage import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.TestException -import akka.actor.typed.scaladsl.adapter._ +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter import akka.actor.typed.ActorRef import akka.actor.typed.Behavior import akka.actor.typed.PostStop import akka.actor.typed.Props -import akka.testkit.EventFilter import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing +import akka.actor.typed.eventstream.EventStream import com.typesafe.config.ConfigFactory import org.scalatest.WordSpecLike object MessageAdapterSpec { val config = ConfigFactory.parseString(""" - akka.loggers = ["akka.testkit.TestEventListener"] akka.log-dead-letters = off ping-pong-dispatcher { executor = thread-pool-executor @@ -31,9 +32,10 @@ object MessageAdapterSpec { """) } -class MessageAdapterSpec extends ScalaTestWithActorTestKit(MessageAdapterSpec.config) with WordSpecLike { - - implicit val classic = system.toClassic // FIXME #24348: eventfilter support in testkit +class MessageAdapterSpec + extends ScalaTestWithActorTestKit(MessageAdapterSpec.config) + with WordSpecLike + with LogCapturing { "Message adapters" must { @@ -139,6 +141,8 @@ class MessageAdapterSpec extends ScalaTestWithActorTestKit(MessageAdapterSpec.co Behaviors.same }) + val unhandledProbe = createTestProbe[UnhandledMessage]() + system.eventStream ! EventStream.Subscribe(unhandledProbe.ref) val probe = TestProbe[Wrapped]() val snitch = Behaviors.setup[Wrapped] { context => @@ -155,9 +159,8 @@ class MessageAdapterSpec extends ScalaTestWithActorTestKit(MessageAdapterSpec.co } } - EventFilter.warning(start = "unhandled message", occurrences = 1).intercept { - spawn(snitch) - } + spawn(snitch) + unhandledProbe.receiveMessage() probe.expectMessage(Wrapped("1", Pong1("hello-1"))) // hello-2 discarded because it was wrong type @@ -199,11 +202,7 @@ class MessageAdapterSpec extends ScalaTestWithActorTestKit(MessageAdapterSpec.co } } - EventFilter.warning(pattern = ".*received dead letter.*", occurrences = 1).intercept { - EventFilter[TestException](occurrences = 1).intercept { - spawn(snitch) - } - } + spawn(snitch) probe.expectMessage(Wrapped(1, Pong("hello"))) probe.expectMessage(Wrapped(2, Pong("hello"))) @@ -250,11 +249,9 @@ class MessageAdapterSpec extends ScalaTestWithActorTestKit(MessageAdapterSpec.co behv(count = 1) } - EventFilter.warning(pattern = ".*received dead letter.*", occurrences = 2).intercept { - // Not expecting "Exception thrown out of adapter. Stopping myself" - EventFilter[TestException](message = "boom", occurrences = 1).intercept { - spawn(snitch) - } + // Not expecting "Exception thrown out of adapter. Stopping myself" + LoggingEventFilter.error[TestException].withMessageContains("boom").intercept { + spawn(snitch) } probe.expectMessage(1) diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/OnSignalSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/OnSignalSpec.scala index 9dcb791a45..57982f50b6 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/OnSignalSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/OnSignalSpec.scala @@ -8,9 +8,10 @@ package scaladsl import akka.Done import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.WordSpecLike -final class OnSignalSpec extends ScalaTestWithActorTestKit with WordSpecLike { +final class OnSignalSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { "An Actor.OnSignal behavior" must { "must correctly install the signal handler" in { diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/ReceivePartialSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/ReceivePartialSpec.scala index e2e3a9946c..bc541c4c2a 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/ReceivePartialSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/ReceivePartialSpec.scala @@ -7,9 +7,10 @@ package scaladsl import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.WordSpecLike -class ReceivePartialSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class ReceivePartialSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { implicit val ec = system.executionContext diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/RoutersSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/RoutersSpec.scala index 96cb451bd3..fd7cdb69d8 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/RoutersSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/RoutersSpec.scala @@ -6,23 +6,24 @@ package akka.actor.typed.scaladsl import java.util.concurrent.atomic.AtomicInteger import akka.actor.Dropped +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.ActorRef import akka.actor.typed.Behavior +import akka.actor.typed.eventstream.EventStream import akka.actor.typed.internal.routing.GroupRouterImpl import akka.actor.typed.internal.routing.RoutingLogics import akka.actor.typed.receptionist.Receptionist import akka.actor.typed.receptionist.ServiceKey import akka.actor.typed.scaladsl.adapter._ -import akka.testkit.EventFilter import org.scalatest.Matchers import org.scalatest.WordSpecLike class RoutersSpec extends ScalaTestWithActorTestKit(""" - akka.loggers = ["akka.testkit.TestEventListener"] akka.loglevel=debug - """) with WordSpecLike with Matchers { + """) with WordSpecLike with Matchers with LogCapturing { // needed for the event filter implicit val classicSystem = system.toClassic @@ -84,7 +85,7 @@ class RoutersSpec extends ScalaTestWithActorTestKit(""" Behaviors.same })) - EventFilter.debug(start = "Pool child stopped", occurrences = 2).intercept { + LoggingEventFilter.debug("Pool child stopped").withOccurrences(2).intercept { pool ! "stop" pool ! "stop" } @@ -109,7 +110,7 @@ class RoutersSpec extends ScalaTestWithActorTestKit(""" Behaviors.stopped })) - EventFilter.info(start = "Last pool child stopped, stopping pool", occurrences = 1).intercept { + LoggingEventFilter.info("Last pool child stopped, stopping pool").intercept { (0 to 3).foreach { _ => pool ! "stop" } @@ -152,18 +153,12 @@ class RoutersSpec extends ScalaTestWithActorTestKit(""" val serviceKey = ServiceKey[String]("group-routing-2") val group = spawn(Routers.group(serviceKey), "group-router-2") val probe = TestProbe[Dropped]() - system.toClassic.eventStream.subscribe(probe.ref.toClassic, classOf[Dropped]) + system.eventStream ! EventStream.Subscribe(probe.ref) (0 to 3).foreach { n => val msg = s"message-$n" -// EventFilter.info(start = "Message [java.lang.String] ... was not delivered.", occurrences = 1).intercept { */ - EventFilter.warning(start = "dropped message", occurrences = 1).intercept { - EventFilter.info(pattern = ".*was dropped. No routees in group router", occurrences = 1).intercept { - group ! msg - probe.expectMessageType[Dropped] - } - } - /* } */ + group ! msg + probe.expectMessageType[Dropped] } testKit.stop(group) diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/StashBufferSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/StashBufferSpec.scala index f1af111dc1..2e43415ca9 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/StashBufferSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/StashBufferSpec.scala @@ -7,9 +7,10 @@ package akka.actor.typed.scaladsl import akka.actor.typed.Behavior import akka.actor.testkit.typed.internal.StubbedActorContext import akka.actor.testkit.typed.scaladsl.TestInbox +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.{ Matchers, WordSpec } -class StashBufferSpec extends WordSpec with Matchers { +class StashBufferSpec extends WordSpec with Matchers with LogCapturing { val context = new StubbedActorContext[String]( "StashBufferSpec", diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/StashSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/StashSpec.scala index 155b83b040..b695895f91 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/StashSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/StashSpec.scala @@ -10,10 +10,14 @@ import java.util.concurrent.TimeUnit import akka.actor.DeadLetter import scala.concurrent.duration._ + +import akka.actor.UnhandledMessage import akka.actor.testkit.typed.TestException +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe -import akka.testkit.EventFilter +import akka.actor.testkit.typed.scaladsl.LogCapturing +import akka.actor.typed.eventstream.EventStream import org.scalatest.WordSpecLike object AbstractStashSpec { @@ -197,7 +201,7 @@ class MutableStashSpec extends AbstractStashSpec { } } -abstract class AbstractStashSpec extends ScalaTestWithActorTestKit with WordSpecLike { +abstract class AbstractStashSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { import AbstractStashSpec._ def testQualifier: String @@ -252,15 +256,7 @@ abstract class AbstractStashSpec extends ScalaTestWithActorTestKit with WordSpec } -class UnstashingSpec extends ScalaTestWithActorTestKit(""" - akka.loggers = ["akka.testkit.TestEventListener"] - """) with WordSpecLike { - - // needed for EventFilter - private implicit val classicSys: akka.actor.ActorSystem = { - import akka.actor.typed.scaladsl.adapter._ - system.toClassic - } +class UnstashingSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { private def slowStoppingChild(latch: CountDownLatch): Behavior[String] = Behaviors.receiveSignal { @@ -422,13 +418,16 @@ class UnstashingSpec extends ScalaTestWithActorTestKit(""" ref ! "stash" ref ! "stash-fail" ref ! "stash" - EventFilter[TestException](start = "unstash-fail", occurrences = 1).intercept { - ref ! "unstash" - probe.expectMessage("unstashing-0") - probe.expectMessage("unstashing-1") - probe.expectMessage("stash-fail-2") - probe.expectMessage("post-stop-2") - } + LoggingEventFilter + .error[TestException] + .withMessageContains("unstash-fail") + .intercept { + ref ! "unstash" + probe.expectMessage("unstashing-0") + probe.expectMessage("unstashing-1") + probe.expectMessage("stash-fail-2") + probe.expectMessage("post-stop-2") + }(system) } "signal PostStop to the latest unstashed behavior on failure" in { @@ -449,7 +448,9 @@ class UnstashingSpec extends ScalaTestWithActorTestKit(""" ref ! "stash" ref ! "stash-fail" ref ! "stash" - EventFilter[TestException](start = "Supervisor RestartSupervisor saw failure: unstash-fail", occurrences = 1) + LoggingEventFilter + .error[TestException] + .withMessageContains("Supervisor RestartSupervisor saw failure: unstash-fail") .intercept { ref ! "unstash" // when childLatch is defined this be stashed in the internal stash of the RestartSupervisor @@ -466,7 +467,7 @@ class UnstashingSpec extends ScalaTestWithActorTestKit(""" ref ! "get-stash-size" probe.expectMessage("stash-size-0") - } + }(system) } "signal PreRestart to the latest unstashed behavior on failure with restart supervision" in { @@ -530,7 +531,9 @@ class UnstashingSpec extends ScalaTestWithActorTestKit(""" ref ! "stash" ref ! "stash-fail" ref ! "stash" - EventFilter[TestException](start = "Supervisor ResumeSupervisor saw failure: unstash-fail", occurrences = 1) + LoggingEventFilter + .error[TestException] + .withMessageContains("Supervisor ResumeSupervisor saw failure: unstash-fail") .intercept { ref ! "unstash" ref ! "get-current" @@ -541,7 +544,7 @@ class UnstashingSpec extends ScalaTestWithActorTestKit(""" probe.expectMessage("current-2") ref ! "get-stash-size" probe.expectMessage("stash-size-5") - } + }(system) ref ! "unstash" ref ! "get-current" @@ -588,6 +591,9 @@ class UnstashingSpec extends ScalaTestWithActorTestKit(""" "deal with unhandled the same way as normal unhandled" in { val probe = TestProbe[String]() + val unhandledProbe = createTestProbe[UnhandledMessage]() + system.eventStream ! EventStream.Subscribe(unhandledProbe.ref) + val ref = spawn(Behaviors.withStash[String](10) { stash => stash.stash("unhandled") stash.stash("handled") @@ -609,11 +615,12 @@ class UnstashingSpec extends ScalaTestWithActorTestKit(""" } }) - EventFilter.warning(start = "unhandled message from", occurrences = 2).intercept { - ref ! "unstash" - } + ref ! "unstash" + + unhandledProbe.receiveMessage() probe.expectMessage("handled 1") probe.expectMessage("handled 2") + unhandledProbe.receiveMessage() probe.expectMessage("handled 3") ref ! "handled" @@ -637,18 +644,17 @@ class UnstashingSpec extends ScalaTestWithActorTestKit(""" "deal with stop" in { val probe = TestProbe[Any] - import akka.actor.typed.scaladsl.adapter._ - classicSys.eventStream.subscribe(probe.ref.toClassic, classOf[DeadLetter]) + system.eventStream ! EventStream.Subscribe(probe.ref.narrow[DeadLetter]) + val ref = spawn(Behaviors.withStash[String](10) { stash => stash.stash("one") stash.stash("two") Behaviors.receiveMessage { case "unstash" => - stash.unstashAll(Behaviors.receiveMessage { - case unstashed => - probe.ref ! unstashed - Behaviors.stopped + stash.unstashAll(Behaviors.receiveMessage { unstashed => + probe.ref ! unstashed + Behaviors.stopped }) case _ => Behaviors.same diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/StopSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/StopSpec.scala index 3ea21b2399..cfdec3b7dd 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/StopSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/StopSpec.scala @@ -7,13 +7,14 @@ package akka.actor.typed.scaladsl import akka.Done import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed import akka.actor.typed.Behavior import akka.actor.typed.BehaviorInterceptor import akka.actor.typed.PostStop import org.scalatest.WordSpecLike -class StopSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class StopSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { import BehaviorInterceptor._ "Stopping an actor" should { diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/adapter/AdapterSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/adapter/AdapterSpec.scala index 71378c5489..fa7dbde6ab 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/adapter/AdapterSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/adapter/AdapterSpec.scala @@ -16,6 +16,7 @@ import akka.actor.typed.Terminated import akka.testkit._ import akka.Done import akka.NotUsed +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter import akka.{ actor => classic } object AdapterSpec { @@ -161,9 +162,7 @@ object AdapterSpec { } -class AdapterSpec extends AkkaSpec(""" - akka.loggers = [akka.testkit.TestEventListener] - """) { +class AdapterSpec extends AkkaSpec { import AdapterSpec._ "ActorSystem adaption" must { @@ -278,10 +277,12 @@ class AdapterSpec extends AkkaSpec(""" val typedRef = system.spawnAnonymous(typed1(ignore, probe.ref)) // only stop supervisorStrategy - EventFilter[AdapterSpec.ThrowIt3.type](occurrences = 1).intercept { - typedRef ! "supervise-restart" - probe.expectMsg("ok") - } + LoggingEventFilter + .error[AdapterSpec.ThrowIt3.type] + .intercept { + typedRef ! "supervise-restart" + probe.expectMsg("ok") + }(system.toTyped) } "stop typed child from classic parent" in { @@ -302,10 +303,12 @@ class AdapterSpec extends AkkaSpec(""" "log exception if not by handled typed supervisor" in { val throwMsg = "sad panda" - EventFilter.warning(pattern = ".*sad panda.*").intercept { - system.spawnAnonymous(unhappyTyped(throwMsg)) - Thread.sleep(1000) - } + LoggingEventFilter + .error("sad panda") + .intercept { + system.spawnAnonymous(unhappyTyped(throwMsg)) + Thread.sleep(1000) + }(system.toTyped) } } } diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/adapter/GuardianStartupSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/adapter/GuardianStartupSpec.scala index 2eeaf099d3..634e9857d5 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/adapter/GuardianStartupSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/scaladsl/adapter/GuardianStartupSpec.scala @@ -9,13 +9,14 @@ import java.util.concurrent.TimeUnit import akka.actor.ActorSystemImpl import akka.actor.testkit.typed.scaladsl.ActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.ActorSystem import akka.actor.typed.scaladsl.Behaviors import org.scalatest.Matchers import org.scalatest.WordSpec import org.scalatest.concurrent.ScalaFutures -class GuardianStartupSpec extends WordSpec with Matchers with ScalaFutures { +class GuardianStartupSpec extends WordSpec with Matchers with ScalaFutures with LogCapturing { "The user guardian" must { diff --git a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/DispatchersDocSpec.scala b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/DispatchersDocSpec.scala index 5c2c343fdb..72456cba4c 100644 --- a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/DispatchersDocSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/DispatchersDocSpec.scala @@ -14,9 +14,10 @@ import DispatchersDocSpec._ import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import com.typesafe.config.ConfigFactory import org.scalatest.WordSpecLike - import scala.concurrent.Future +import akka.actor.testkit.typed.scaladsl.LogCapturing + object DispatchersDocSpec { val config = ConfigFactory.parseString(""" @@ -57,7 +58,10 @@ object DispatchersDocSpec { } -class DispatchersDocSpec extends ScalaTestWithActorTestKit(DispatchersDocSpec.config) with WordSpecLike { +class DispatchersDocSpec + extends ScalaTestWithActorTestKit(DispatchersDocSpec.config) + with WordSpecLike + with LogCapturing { "Actor Dispatchers" should { "support default and blocking dispatcher" in { diff --git a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/FSMDocSpec.scala b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/FSMDocSpec.scala index 675f917933..b08f53b64f 100644 --- a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/FSMDocSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/FSMDocSpec.scala @@ -7,10 +7,11 @@ package docs.akka.typed import akka.actor.testkit.typed.scaladsl.TestProbe import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.{ ActorRef, Behavior } - import scala.collection.immutable import scala.concurrent.duration._ + import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.WordSpecLike object FSMDocSpec { @@ -67,7 +68,7 @@ object FSMDocSpec { //#test-code } -class FSMDocSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class FSMDocSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { import FSMDocSpec._ diff --git a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/GracefulStopDocSpec.scala b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/GracefulStopDocSpec.scala index e3148c84c4..834eb5e734 100644 --- a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/GracefulStopDocSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/GracefulStopDocSpec.scala @@ -5,10 +5,14 @@ package docs.akka.typed //#imports +import akka.actor.testkit.typed.scaladsl.LogCapturing +import akka.actor.typed.Behavior import akka.actor.typed.scaladsl.Behaviors -import akka.actor.typed.{ ActorSystem, Behavior, Logger, PostStop } +import akka.actor.typed.{ ActorSystem, PostStop } //#imports + +import org.slf4j.Logger import scala.concurrent.duration._ import scala.concurrent.Await import org.scalatest.WordSpecLike @@ -70,7 +74,7 @@ object GracefulStopDocSpec { } -class GracefulStopDocSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class GracefulStopDocSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { import GracefulStopDocSpec._ diff --git a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/InteractionPatternsSpec.scala b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/InteractionPatternsSpec.scala index a83ba580bd..4bbf3af4db 100644 --- a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/InteractionPatternsSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/InteractionPatternsSpec.scala @@ -6,17 +6,23 @@ package docs.akka.typed import java.net.URI -import akka.NotUsed -import akka.actor.typed.{ ActorRef, ActorSystem, Behavior } -import akka.actor.typed.scaladsl.{ Behaviors, TimerScheduler } - import scala.concurrent.Future import scala.concurrent.duration._ -import scala.util.{ Failure, Success } +import scala.util.Failure +import scala.util.Success + +import akka.NotUsed import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing +import akka.actor.typed.ActorRef +import akka.actor.typed.ActorSystem +import akka.actor.typed.Behavior +import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.LoggerOps +import akka.actor.typed.scaladsl.TimerScheduler import org.scalatest.WordSpecLike -class InteractionPatternsSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class InteractionPatternsSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { "The interaction patterns docs" must { @@ -120,10 +126,10 @@ class InteractionPatternsSpec extends ScalaTestWithActorTestKit with WordSpecLik context.log.info("Started {}", taskId) Behaviors.same case Backend.JobProgress(taskId, progress) => - context.log.info("Progress {}: {}", taskId, progress) + context.log.info2("Progress {}: {}", taskId, progress) Behaviors.same case Backend.JobCompleted(taskId, result) => - context.log.info("Completed {}: {}", taskId, result) + context.log.info2("Completed {}: {}", taskId, result) inProgress(taskId) ! result active(inProgress - taskId, count) } diff --git a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/IntroSpec.scala b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/IntroSpec.scala index e2ca7e923c..0c227c0dd5 100644 --- a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/IntroSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/IntroSpec.scala @@ -6,7 +6,9 @@ package docs.akka.typed //#fiddle_code //#imports +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.LoggerOps import akka.actor.typed.{ ActorRef, ActorSystem, Behavior } //#imports //#fiddle_code @@ -54,7 +56,7 @@ object IntroSpec { Behaviors.receive { (context, message) => val n = greetingCounter + 1 //#fiddle_code - context.log.info("Greeting {} for {}", n, message.whom) + context.log.info2("Greeting {} for {}", n, message.whom) //#fiddle_code //#hello-world-bot println(s"Greeting $n for ${message.whom}") @@ -190,7 +192,7 @@ object IntroSpec { handle ! PostMessage("Hello World!") Behaviors.same case MessagePosted(screenName, message) => - context.log.info("message has been posted by '{}': {}", screenName, message) + context.log.info2("message has been posted by '{}': {}", screenName, message) Behaviors.stopped } } @@ -221,7 +223,7 @@ object IntroSpec { } -class IntroSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class IntroSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { import IntroSpec._ diff --git a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/MailboxDocSpec.scala b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/MailboxDocSpec.scala index 7b87fc80e5..0fcdebf586 100644 --- a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/MailboxDocSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/MailboxDocSpec.scala @@ -6,6 +6,7 @@ package docs.akka.typed import akka.Done import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.Behavior import akka.actor.typed.MailboxSelector import akka.actor.typed.scaladsl.Behaviors @@ -14,7 +15,8 @@ import org.scalatest.WordSpecLike class MailboxDocSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("mailbox-config-sample.conf")) - with WordSpecLike { + with WordSpecLike + with LogCapturing { "Specifying mailbox through props" must { "work" in { diff --git a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/OOIntroSpec.scala b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/OOIntroSpec.scala index f8cc733b07..9976cba581 100644 --- a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/OOIntroSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/OOIntroSpec.scala @@ -6,8 +6,9 @@ package docs.akka.typed //#imports import akka.Done +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.{ ActorRef, ActorSystem, Behavior } -import akka.actor.typed.scaladsl.{ AbstractBehavior, ActorContext, Behaviors } +import akka.actor.typed.scaladsl.{ AbstractBehavior, ActorContext, Behaviors, LoggerOps } //#imports import akka.NotUsed @@ -104,7 +105,7 @@ object OOIntroSpec { handle ! PostMessage("Hello World!") Behaviors.same case MessagePosted(screenName, message) => - context.log.info("message has been posted by '{}': {}", screenName, message) + context.log.info2("message has been posted by '{}': {}", screenName, message) Behaviors.stopped } } @@ -135,7 +136,7 @@ object OOIntroSpec { } -class OOIntroSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class OOIntroSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { import OOIntroSpec._ diff --git a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/RouterSpec.scala b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/RouterSpec.scala index 082e4e6e74..6f9a87a0dd 100644 --- a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/RouterSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/RouterSpec.scala @@ -6,6 +6,7 @@ package docs.akka.typed // #pool import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.Behavior import akka.actor.typed.SupervisorStrategy import akka.actor.typed.receptionist.Receptionist @@ -39,7 +40,7 @@ object RouterSpec { val serviceKey = ServiceKey[Worker.Command]("log-worker") } -class RouterSpec extends ScalaTestWithActorTestKit("akka.loglevel=warning") with WordSpecLike { +class RouterSpec extends ScalaTestWithActorTestKit("akka.loglevel=warning") with WordSpecLike with LogCapturing { import RouterSpec._ "The routing sample" must { diff --git a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/SpawnProtocolDocSpec.scala b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/SpawnProtocolDocSpec.scala index bdd989196a..4364f2ebd1 100644 --- a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/SpawnProtocolDocSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/SpawnProtocolDocSpec.scala @@ -7,8 +7,10 @@ package docs.akka.typed import scala.concurrent.ExecutionContext import scala.concurrent.Future import scala.concurrent.duration._ + import akka.actor.testkit.typed.scaladsl.ActorTestKit import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import docs.akka.typed.IntroSpec.HelloWorld import org.scalatest.WordSpecLike import com.github.ghik.silencer.silent @@ -17,6 +19,7 @@ import com.github.ghik.silencer.silent import akka.actor.typed.Behavior import akka.actor.typed.SpawnProtocol import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.LoggerOps //#imports1 @@ -46,7 +49,7 @@ object SpawnProtocolDocSpec { //#main } -class SpawnProtocolDocSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class SpawnProtocolDocSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { import SpawnProtocolDocSpec._ @@ -67,7 +70,7 @@ class SpawnProtocolDocSpec extends ScalaTestWithActorTestKit with WordSpecLike { system.ask(SpawnProtocol.Spawn(behavior = HelloWorld(), name = "greeter", props = Props.empty, _)) val greetedBehavior = Behaviors.receive[HelloWorld.Greeted] { (context, message) => - context.log.info("Greeting for {} from {}", message.whom, message.from) + context.log.info2("Greeting for {} from {}", message.whom, message.from) Behaviors.stopped } diff --git a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StashDocSpec.scala b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StashDocSpec.scala index 7ef8ee752d..390ab66ac9 100644 --- a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StashDocSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StashDocSpec.scala @@ -5,7 +5,7 @@ package docs.akka.typed import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit - +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.WordSpecLike object StashDocSpec { @@ -100,7 +100,7 @@ object StashDocSpec { // #stashing } -class StashDocSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class StashDocSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { import StashDocSpec.DB import StashDocSpec.DataAccess import scala.concurrent.Future diff --git a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StyleGuideDocExamples.scala b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StyleGuideDocExamples.scala index f5aeeab3df..9fca66bb1e 100644 --- a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StyleGuideDocExamples.scala +++ b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StyleGuideDocExamples.scala @@ -9,6 +9,7 @@ import scala.concurrent.Future import akka.actor.typed.ActorSystem import akka.actor.typed.ActorRef +import akka.actor.typed.scaladsl.LoggerOps import akka.actor.typed.scaladsl.TimerScheduler import scala.concurrent.duration.FiniteDuration @@ -120,13 +121,13 @@ object StyleGuideDocExamples { context.log.debug( "[{}] Starting repeated increments with interval [{}], current count is [{}]", name, - interval, - n) + interval.toString, + n.toString) timers.startTimerWithFixedDelay("repeat", Increment, interval) Behaviors.same case Increment => val newValue = n + 1 - context.log.debug("[{}] Incremented counter to [{}]", name, newValue) + context.log.debug2("[{}] Incremented counter to [{}]", name, newValue) counter(name, timers, newValue) case GetValue(replyTo) => replyTo ! Value(n) @@ -160,7 +161,7 @@ object StyleGuideDocExamples { private def counter(setup: Setup, n: Int): Behavior[Command] = Behaviors.receiveMessage { case IncrementRepeatedly(interval) => - setup.context.log.debug( + setup.context.log.debugN( "[{}] Starting repeated increments with interval [{}], current count is [{}]", setup.name, interval, @@ -169,7 +170,7 @@ object StyleGuideDocExamples { Behaviors.same case Increment => val newValue = n + 1 - setup.context.log.debug("[{}] Incremented counter to [{}]", setup.name, newValue) + setup.context.log.debug2("[{}] Incremented counter to [{}]", setup.name, newValue) counter(setup, newValue) case GetValue(replyTo) => replyTo ! Value(n) @@ -207,7 +208,7 @@ object StyleGuideDocExamples { private def counter(n: Int): Behavior[Command] = Behaviors.receiveMessage { case IncrementRepeatedly(interval) => - context.log.debug( + context.log.debugN( "[{}] Starting repeated increments with interval [{}], current count is [{}]", name, interval, @@ -216,7 +217,7 @@ object StyleGuideDocExamples { Behaviors.same case Increment => val newValue = n + 1 - context.log.debug("[{}] Incremented counter to [{}]", name, newValue) + context.log.debug2("[{}] Incremented counter to [{}]", name, newValue) counter(newValue) case GetValue(replyTo) => replyTo ! Value(n) @@ -243,7 +244,7 @@ object StyleGuideDocExamples { def counter(n: Int): Behavior[Command] = Behaviors.receiveMessage { case IncrementRepeatedly(interval) => - context.log.debug( + context.log.debugN( "[{}] Starting repeated increments with interval [{}], current count is [{}]", name, interval, @@ -252,7 +253,7 @@ object StyleGuideDocExamples { Behaviors.same case Increment => val newValue = n + 1 - context.log.debug("[{}] Incremented counter to [{}]", name, newValue) + context.log.debug2("[{}] Incremented counter to [{}]", name, newValue) counter(newValue) case GetValue(replyTo) => replyTo ! Value(n) @@ -353,11 +354,11 @@ object StyleGuideDocExamples { Behaviors.receiveMessage { case Increment => val newValue = n + 1 - context.log.debug("[{}] Incremented counter to [{}]", name, newValue) + context.log.debug2("[{}] Incremented counter to [{}]", name, newValue) counter(newValue) case Tick => val newValue = n + 1 - context.log.debug("[{}] Incremented counter by background tick to [{}]", name, newValue) + context.log.debug2("[{}] Incremented counter by background tick to [{}]", name, newValue) counter(newValue) case GetValue(replyTo) => replyTo ! Value(n) @@ -404,11 +405,11 @@ object StyleGuideDocExamples { Behaviors.receiveMessage { case Increment => val newValue = n + 1 - context.log.debug("[{}] Incremented counter to [{}]", name, newValue) + context.log.debug2("[{}] Incremented counter to [{}]", name, newValue) counter(newValue) case Tick => val newValue = n + 1 - context.log.debug("[{}] Incremented counter by background tick to [{}]", name, newValue) + context.log.debug2("[{}] Incremented counter by background tick to [{}]", name, newValue) counter(newValue) case GetValue(replyTo) => replyTo ! Value(n) diff --git a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/coexistence/ClassicWatchingTypedSpec.scala b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/coexistence/ClassicWatchingTypedSpec.scala index 55051293cf..b1547c36f8 100644 --- a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/coexistence/ClassicWatchingTypedSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/coexistence/ClassicWatchingTypedSpec.scala @@ -5,6 +5,7 @@ package docs.akka.typed.coexistence import akka.actor.ActorLogging +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed._ import akka.actor.typed.scaladsl.Behaviors import akka.testkit.TestKit @@ -71,7 +72,7 @@ object ClassicWatchingTypedSpec { //#typed } -class ClassicWatchingTypedSpec extends WordSpec { +class ClassicWatchingTypedSpec extends WordSpec with LogCapturing { import ClassicWatchingTypedSpec._ diff --git a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/coexistence/TypedWatchingClassicSpec.scala b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/coexistence/TypedWatchingClassicSpec.scala index 4765819414..ce727407bc 100644 --- a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/coexistence/TypedWatchingClassicSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/coexistence/TypedWatchingClassicSpec.scala @@ -4,6 +4,7 @@ package docs.akka.typed.coexistence +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed._ import akka.actor.typed.scaladsl.Behaviors import akka.testkit.TestKit @@ -67,7 +68,7 @@ object TypedWatchingClassicSpec { //#classic } -class TypedWatchingClassicSpec extends WordSpec { +class TypedWatchingClassicSpec extends WordSpec with LogCapturing { import TypedWatchingClassicSpec._ diff --git a/akka-actor-typed/src/main/resources/reference.conf b/akka-actor-typed/src/main/resources/reference.conf index ba101309c0..a9de17de7f 100644 --- a/akka-actor-typed/src/main/resources/reference.conf +++ b/akka-actor-typed/src/main/resources/reference.conf @@ -45,3 +45,17 @@ akka.actor { "akka.actor.typed.internal.receptionist.DefaultServiceKey" = service-key } } + +# When using Akka Typed (having akka-actor-typed in classpath) the +# akka.event.slf4j.Slf4jLogger is enabled instead of the DefaultLogger +# even though it has not been explicitly defined in `akka.loggers` +# configuration. +# +# Slf4jLogger will be used for all Akka classic logging via eventStream, +# including logging from Akka internals. The Slf4jLogger is then using +# an ordinary org.slf4j.Logger to emit the log events. +# +# The Slf4jLoggingFilter is also enabled automatically. +# +# This behavior can be disabled by setting this property to `off`. +akka.use-slf4j = on diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/ActorSystem.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/ActorSystem.scala index 6f90acaf22..ec98313ab2 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/ActorSystem.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/ActorSystem.scala @@ -17,7 +17,7 @@ import akka.annotation.DoNotInherit import akka.util.Helpers.Requiring import akka.{ Done, actor => classic } import com.typesafe.config.{ Config, ConfigFactory } - +import org.slf4j.Logger import scala.concurrent.{ ExecutionContextExecutor, Future } /** @@ -50,7 +50,7 @@ abstract class ActorSystem[-T] extends ActorRef[T] with Extensions with ClassicA def logConfiguration(): Unit /** - * A [[akka.actor.typed.Logger]] that can be used to emit log messages + * A [[org.slf4j.Logger]] that can be used to emit log messages * without specifying a more detailed source. Typically it is desirable to * use the dedicated `Logger` available from each Actor’s [[TypedActorContext]] * as that ties the log entries to the actor. @@ -271,12 +271,6 @@ object ActorSystem { * This class is immutable. */ final class Settings(val config: Config, val classicSettings: classic.ActorSystem.Settings, val name: String) { - def this(classLoader: ClassLoader, config: Config, name: String) = - this({ - val cfg = config.withFallback(ConfigFactory.defaultReference(classLoader)) - cfg.checkValid(ConfigFactory.defaultReference(classLoader), "akka") - cfg - }, new classic.ActorSystem.Settings(classLoader, config, name), name) def this(settings: classic.ActorSystem.Settings) = this(settings.config, settings, settings.name) diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/Logger.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/Logger.scala index 0f194939d3..c8a2763ae4 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/Logger.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/Logger.scala @@ -7,8 +7,8 @@ package akka.actor.typed import java.util.Optional import akka.annotation.{ DoNotInherit, InternalApi } -import akka.event.Logging -import akka.event.Logging._ +import org.slf4j.Logger +import org.slf4j.event.Level /** * A log marker is an additional metadata tag supported by some logging backends to identify "special" log events. @@ -59,15 +59,15 @@ abstract sealed class LogOptions { /** * The [[akka.event.Logging.LogLevel]] to use when logging messages. */ - def withLevel(level: LogLevel): LogOptions + def withLevel(level: Level): LogOptions /** - * A [[akka.actor.typed.Logger]] to use when logging messages. + * A [[org.slf4j.Logger]] to use when logging messages. */ def withLogger(logger: Logger): LogOptions def enabled: Boolean - def level: LogLevel + def level: Level def logger: Option[Logger] /** Java API */ @@ -83,7 +83,7 @@ object LogOptions { * INTERNAL API */ @InternalApi - private[akka] final case class LogOptionsImpl(enabled: Boolean, level: LogLevel, logger: Option[Logger]) + private[akka] final case class LogOptionsImpl(enabled: Boolean, level: Level, logger: Option[Logger]) extends LogOptions { /** @@ -95,10 +95,10 @@ object LogOptions { /** * The [[akka.event.Logging.LogLevel]] to use when logging messages. */ - override def withLevel(level: LogLevel): LogOptions = this.copy(level = level) + override def withLevel(level: Level): LogOptions = this.copy(level = level) /** - * A [[akka.actor.typed.Logger]] to use when logging messages. + * A [[org.slf4j.Logger]] to use when logging messages. */ override def withLogger(logger: Logger): LogOptions = this.copy(logger = Option(logger)) @@ -109,745 +109,10 @@ object LogOptions { /** * Scala API: Create a new log options with defaults. */ - def apply(): LogOptions = LogOptionsImpl(enabled = true, Logging.DebugLevel, None) + def apply(): LogOptions = LogOptionsImpl(enabled = true, Level.DEBUG, None) /** * Java API: Create a new log options. */ def create(): LogOptions = apply() } - -/** - * Logging API provided inside of actors through the actor context. - * - * All log-level methods support simple interpolation templates with up to four - * arguments placed by using {} within the template (first string - * argument): - * - * {{{ - * ctx.log.error(exception, "Exception while processing {} in state {}", msg, state) - * }}} - * - * More than four arguments can be defined by using an `Array` with the method with - * one argument parameter. - * - * *Rationale for an Akka-specific logging API:* - * Provided rather than a specific logging library logging API to not enforce a specific logging library on users but - * still providing a convenient, performant, asynchronous and testable logging solution. Additionally it allows unified - * logging for both user implemented actors and built in Akka actors where the actual logging backend can be selected - * by the user. This logging facade is also used by Akka internally, without having to depend on specific logging frameworks. - * - * The [[Logger]] of an actor is tied to the actor path and should not be shared with other threads outside of the actor. - * - * Not for user extension - */ -@DoNotInherit -abstract class Logger private[akka] () { - - /** - * Whether error logging is enabled on the actor system level, may not represent the setting all the way to the - * logger implementation, but when it does it allows avoiding unnecessary resource usage for log entries that - * will not actually end up in any logger output. - */ - def isErrorEnabled: Boolean - - /** - * Whether error logging with this marker is enabled on the actor system level, may not represent the setting all - * the way to the logger implementation, but when it does it allows avoiding unnecessary resource usage for log - * entries that will not actually end up in any logger output. - */ - def isErrorEnabled(marker: LogMarker): Boolean - - /** - * Whether warning logging is enabled on the actor system level, may not represent the setting all the way to the - * logger implementation, but when it does it allows avoiding unnecessary resource usage for log entries that - * will not actually end up in any logger output. - */ - def isWarningEnabled: Boolean - - /** - * Whether warning logging with this marker is enabled on the actor system level, may not represent the setting all - * the way to the logger implementation, but when it does it allows avoiding unnecessary resource usage for log - * entries that will not actually end up in any logger output. - */ - def isWarningEnabled(marker: LogMarker): Boolean - - /** - * Whether info logging is enabled on the actor system level, may not represent the setting all the way to the - * logger implementation, but when it does it allows avoiding unnecessary resource usage for log entries that - * will not actually end up in any logger output. - */ - def isInfoEnabled: Boolean - - /** - * Whether info logging with this marker is enabled on the actor system level, may not represent the setting all - * the way to the logger implementation, but when it does it allows avoiding unnecessary resource usage for log - * entries that will not actually end up in any logger output. - */ - def isInfoEnabled(marker: LogMarker): Boolean - - /** - * Whether debug logging is enabled on the actor system level, may not represent the setting all the way to the - * logger implementation, but when it does it allows avoiding unnecessary resource usage for log entries that - * will not actually end up in any logger output. - */ - def isDebugEnabled: Boolean - - /** - * Whether debug logging with this marker is enabled on the actor system level, may not represent the setting all - * the way to the logger implementation, but when it does it allows avoiding unnecessary resource usage for log - * entries that will not actually end up in any logger output. - */ - def isDebugEnabled(marker: LogMarker): Boolean - - /** - * Whether a log level is enabled on the actor system level, may not represent the setting all the way to the - * logger implementation, but when it does it allows avoiding unnecessary resource usage for log entries that - * will not actually end up in any logger output. - */ - def isLevelEnabled(logLevel: LogLevel): Boolean = logLevel match { - case ErrorLevel => isErrorEnabled - case WarningLevel => isWarningEnabled - case InfoLevel => isInfoEnabled - case DebugLevel => isDebugEnabled - case _ => false - } - - /** - * Whether a log level with this marker is enabled on the actor system level, may not represent the setting all the - * way to the logger implementation, but when it does it allows avoiding unnecessary resource usage for log entries - * that will not actually end up in any logger output. - */ - def isLevelEnabled(logLevel: LogLevel, marker: LogMarker): Boolean = logLevel match { - case ErrorLevel => isErrorEnabled(marker) - case WarningLevel => isWarningEnabled(marker) - case InfoLevel => isInfoEnabled(marker) - case DebugLevel => isDebugEnabled(marker) - case _ => false - } - - // message only error logging - - /** - * Log message at error level, without providing the exception that caused the error. - * - * @see [[Logger]] - */ - def error(message: String): Unit - - /** - * Message template with 1 replacement argument. - * - * If `arg1` is an `Array` it will be expanded into replacement arguments, which is useful when - * there are more than four arguments. - * - * @see [[Logger]] - */ - def error(template: String, arg1: Any): Unit - - /** - * Message template with 2 replacement arguments. - * - * @see [[Logger]] - */ - def error(template: String, arg1: Any, arg2: Any): Unit - - /** - * Message template with 3 replacement arguments. - * - * @see [[Logger]] - */ - def error(template: String, arg1: Any, arg2: Any, arg3: Any): Unit - - /** - * Message template with 4 replacement arguments. For more parameters see the single replacement version of this method. - * - * @see [[Logger]] - */ - def error(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit - - // exception error logging - - /** - * Log message at error level, including the exception that caused the error. - * - * @see [[Logger]] - */ - def error(cause: Throwable, message: String): Unit - - /** - * Message template with 1 replacement argument. - * - * If `arg1` is an `Array` it will be expanded into replacement arguments, which is useful when - * there are more than four arguments. - * - * @see [[Logger]] - */ - def error(cause: Throwable, template: String, arg1: Any): Unit - - /** - * Message template with 2 replacement arguments. - * - * @see [[Logger]] - */ - def error(cause: Throwable, template: String, arg1: Any, arg2: Any): Unit - - /** - * Message template with 3 replacement arguments. - * - * @see [[Logger]] - */ - def error(cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any): Unit - - /** - * Message template with 4 replacement arguments. For more parameters see the single replacement version of this method. - * - * @see [[Logger]] - */ - def error(cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit - - // marker error logging - - /** - * Log message at error level, including the exception that caused the error. - * - * @see [[Logger]] - */ - def error(marker: LogMarker, cause: Throwable, message: String): Unit - - /** - * Message template with 1 replacement argument. - * - * If `arg1` is an `Array` it will be expanded into replacement arguments, which is useful when - * there are more than four arguments. - * - * @see [[Logger]] - */ - def error(marker: LogMarker, cause: Throwable, template: String, arg1: Any): Unit - - /** - * Message template with 2 replacement arguments. - * - * @see [[Logger]] - */ - def error(marker: LogMarker, cause: Throwable, template: String, arg1: Any, arg2: Any): Unit - - /** - * Message template with 3 replacement arguments. - * - * @see [[Logger]] - */ - def error(marker: LogMarker, cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any): Unit - - /** - * Message template with 4 replacement arguments. For more parameters see the single replacement version of this method. - * - * @see [[Logger]] - */ - def error(marker: LogMarker, cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit - - /** - * Log message at error level, without providing the exception that caused the error. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def error(marker: LogMarker, message: String): Unit - - /** - * Message template with 1 replacement argument. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * If `arg1` is an `Array` it will be expanded into replacement arguments, which is useful when - * there are more than four arguments. - * - * @see [[Logger]] - */ - def error(marker: LogMarker, template: String, arg1: Any): Unit - - /** - * Message template with 2 replacement arguments. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def error(marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit - - /** - * Message template with 3 replacement arguments. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def error(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit - - /** - * Message template with 4 replacement arguments. For more parameters see the single replacement version of this method. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def error(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit - - // message only warning logging - - /** - * Log message at warning level. - */ - def warning(message: String): Unit - - /** - * Message template with 1 replacement argument. - * - * If `arg1` is an `Array` it will be expanded into replacement arguments, which is useful when - * there are more than four arguments. - * - * @see [[Logger]] - */ - def warning(template: String, arg1: Any): Unit - - /** - * Message template with 2 replacement arguments. - * - * @see [[Logger]] - */ - def warning(template: String, arg1: Any, arg2: Any): Unit - - /** - * Message template with 3 replacement arguments. - * - * @see [[Logger]] - */ - def warning(template: String, arg1: Any, arg2: Any, arg3: Any): Unit - - /** - * Message template with 4 replacement arguments. For more parameters see the single replacement version of this method. - * - * @see [[Logger]] - */ - def warning(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit - - /** - * Log message at warning level. - */ - def warning(cause: Throwable, message: String): Unit - - /** - * Message template with 1 replacement argument. - * - * If `arg1` is an `Array` it will be expanded into replacement arguments, which is useful when - * there are more than four arguments. - * - * @see [[Logger]] - */ - def warning(cause: Throwable, template: String, arg1: Any): Unit - - /** - * Message template with 2 replacement arguments. - * @see [[Logger]] - */ - def warning(cause: Throwable, template: String, arg1: Any, arg2: Any): Unit - - /** - * Message template with 3 replacement arguments. - * @see [[Logger]] - */ - def warning(cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any): Unit - - /** - * Message template with 4 replacement arguments. For more parameters see the single replacement version of this method. - * @see [[Logger]] - */ - def warning(cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit - - // marker warning logging - /** - * Log message at warning level. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - */ - def warning(marker: LogMarker, message: String): Unit - - /** - * Message template with 1 replacement argument. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * If `arg1` is an `Array` it will be expanded into replacement arguments, which is useful when - * there are more than four arguments. - * - * @see [[Logger]] - */ - def warning(marker: LogMarker, template: String, arg1: Any): Unit - - /** - * Message template with 2 replacement arguments. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def warning(marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit - - /** - * Message template with 3 replacement arguments. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def warning(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit - - /** - * Message template with 4 replacement arguments. For more parameters see the single replacement version of this method. - * - * @see [[Logger]] - */ - def warning(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit - - /** - * Log message at warning level. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * @see [[Logger]] - */ - def warning(marker: LogMarker, cause: Throwable, message: String): Unit - - /** - * Message template with 1 replacement argument. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * If `arg1` is an `Array` it will be expanded into replacement arguments, which is useful when - * there are more than four arguments. - * - * @see [[Logger]] - */ - def warning(marker: LogMarker, cause: Throwable, template: String, arg1: Any): Unit - - /** - * Message template with 2 replacement arguments. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def warning(marker: LogMarker, cause: Throwable, template: String, arg1: Any, arg2: Any): Unit - - /** - * Message template with 3 replacement arguments. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def warning(marker: LogMarker, cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any): Unit - - /** - * Message template with 4 replacement arguments. For more parameters see the single replacement version of this method. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def warning(marker: LogMarker, cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit - - // message only info logging - - /** - * Log message at info level. - * - * @see [[Logger]] - */ - def info(message: String): Unit - - /** - * Message template with 1 replacement argument. - * - * If `arg1` is an `Array` it will be expanded into replacement arguments, which is useful when - * there are more than four arguments. - * - * @see [[Logger]] - */ - def info(template: String, arg1: Any): Unit - - /** - * Message template with 2 replacement arguments. - * - * @see [[Logger]] - */ - def info(template: String, arg1: Any, arg2: Any): Unit - - /** - * Message template with 3 replacement arguments. - * - * @see [[Logger]] - */ - def info(template: String, arg1: Any, arg2: Any, arg3: Any): Unit - - /** - * Message template with 4 replacement arguments. For more parameters see the single replacement version of this method. - * - * @see [[Logger]] - */ - def info(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit - - // marker info logging - - /** - * Log message at info level. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def info(marker: LogMarker, message: String): Unit - - /** - * Message template with 1 replacement argument. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * If `arg1` is an `Array` it will be expanded into replacement arguments, which is useful when - * there are more than four arguments. - * - * @see [[Logger]] - */ - def info(marker: LogMarker, template: String, arg1: Any): Unit - - /** - * Message template with 2 replacement arguments. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def info(marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit - - /** - * Message template with 3 replacement arguments. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def info(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit - - /** - * Message template with 4 replacement arguments. For more parameters see the single replacement version of this method. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def info(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit - - // message only debug logging - - /** - * Log message at debug level. - * - * @see [[Logger]] - */ - def debug(message: String): Unit - - /** - * Message template with 1 replacement argument. - * - * If `arg1` is an `Array` it will be expanded into replacement arguments, which is useful when - * there are more than four arguments. - * - * @see [[Logger]] - */ - def debug(template: String, arg1: Any): Unit - - /** - * Message template with 2 replacement arguments. - * - * @see [[Logger]] - */ - def debug(template: String, arg1: Any, arg2: Any): Unit - - /** - * Message template with 3 replacement arguments. - * - * @see [[Logger]] - */ - def debug(template: String, arg1: Any, arg2: Any, arg3: Any): Unit - - /** - * Message template with 4 replacement arguments. For more parameters see the single replacement version of this method. - * - * @see [[Logger]] - */ - def debug(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit - - // marker debug logging - - /** - * Log message at debug level. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def debug(marker: LogMarker, message: String): Unit - - /** - * Message template with 1 replacement argument. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * If `arg1` is an `Array` it will be expanded into replacement arguments, which is useful when - * there are more than four arguments. - * - * @see [[Logger]] - */ - def debug(marker: LogMarker, template: String, arg1: Any): Unit - - /** - * Message template with 2 replacement arguments. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def debug(marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit - - /** - * Message template with 3 replacement arguments. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def debug(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit - - /** - * Message template with 4 replacement arguments. For more parameters see the single replacement version of this method. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def debug(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit - - // message any level logging - - /** - * Log message at the specified level. - * - * @see [[Logger]] - */ - def log(level: LogLevel, message: String): Unit - - /** - * Message template with 1 replacement argument. - * - * If `arg1` is an `Array` it will be expanded into replacement arguments, which is useful when - * there are more than four arguments. - * - * @see [[Logger]] - */ - def log(level: LogLevel, template: String, arg1: Any): Unit - - /** - * Message template with 2 replacement arguments. - * - * @see [[Logger]] - */ - def log(level: LogLevel, template: String, arg1: Any, arg2: Any): Unit - - /** - * Message template with 3 replacement arguments. - * - * @see [[Logger]] - */ - def log(level: LogLevel, template: String, arg1: Any, arg2: Any, arg3: Any): Unit - - /** - * Message template with 4 replacement arguments. For more parameters see the single replacement version of this method. - * - * @see [[Logger]] - */ - def log(level: LogLevel, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit - - // marker logging at any level - - /** - * Log message at the specified level. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def log(level: LogLevel, marker: LogMarker, message: String): Unit - - /** - * Message template with 1 replacement argument. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * If `arg1` is an `Array` it will be expanded into replacement arguments, which is useful when - * there are more than four arguments. - * - * @see [[Logger]] - */ - def log(level: LogLevel, marker: LogMarker, template: String, arg1: Any): Unit - - /** - * Message template with 2 replacement arguments. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def log(level: LogLevel, marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit - - /** - * Message template with 3 replacement arguments. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def log(level: LogLevel, marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit - - /** - * Message template with 4 replacement arguments. For more parameters see the single replacement version of this method. - * - * The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special". - * - * @see [[Logger]] - */ - def log(level: LogLevel, marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit - - /** - * Scala API: the returned logger will add the given MDC (Mapped Diagnostic Context) to any log entry logged - * - * See also [[akka.actor.typed.scaladsl.Behaviors.withMdc]] - */ - def withMdc(mdc: Map[String, Any]): Logger - - /** - * Java API: the returned logger will add the given MDC (Mapped Diagnostic Context) to any log entry logged - * - * See also [[akka.actor.typed.javadsl.Behaviors.withMdc]] - */ - def withMdc(mdc: java.util.Map[String, Any]): Logger - - /** - * Return a new logger sharing properties of this logger except the logger class - */ - def withLoggerClass(clazz: Class[_]): Logger - - /** - * Return a new logger sharing properties of this logger except the log source - */ - def withLogSource(logSource: String): Logger -} diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/SupervisorStrategy.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/SupervisorStrategy.scala index 9fe1c535c6..8e67a761b9 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/SupervisorStrategy.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/SupervisorStrategy.scala @@ -5,12 +5,11 @@ package akka.actor.typed import akka.annotation.InternalApi -import akka.event.Logging -import akka.event.Logging.LogLevel import scala.concurrent.duration.FiniteDuration import scala.concurrent.duration.Duration import akka.util.JavaDurationConverters._ +import org.slf4j.event.Level object SupervisorStrategy { @@ -21,7 +20,7 @@ object SupervisorStrategy { * If the actor behavior is deferred and throws an exception on startup the actor is stopped * (restarting would be dangerous as it could lead to an infinite restart-loop) */ - val resume: SupervisorStrategy = Resume(loggingEnabled = true, logLevel = Logging.ErrorLevel) + val resume: SupervisorStrategy = Resume(loggingEnabled = true, logLevel = Level.ERROR) /** * Restart immediately without any limit on number of restart retries. A limit can be @@ -36,7 +35,7 @@ object SupervisorStrategy { /** * Stop the actor */ - val stop: SupervisorStrategy = Stop(loggingEnabled = true, logLevel = Logging.ErrorLevel) + val stop: SupervisorStrategy = Stop(loggingEnabled = true, logLevel = Level.ERROR) /** * Scala API: It supports exponential back-off between the given `minBackoff` and @@ -109,20 +108,20 @@ object SupervisorStrategy { /** * INTERNAL API */ - @InternalApi private[akka] case class Resume(loggingEnabled: Boolean, logLevel: LogLevel) extends SupervisorStrategy { + @InternalApi private[akka] case class Resume(loggingEnabled: Boolean, logLevel: Level) extends SupervisorStrategy { override def withLoggingEnabled(enabled: Boolean): SupervisorStrategy = copy(loggingEnabled = enabled) - override def withLogLevel(level: LogLevel): SupervisorStrategy = + override def withLogLevel(level: Level): SupervisorStrategy = copy(logLevel = level) } /** * INTERNAL API */ - @InternalApi private[akka] case class Stop(loggingEnabled: Boolean, logLevel: LogLevel) extends SupervisorStrategy { + @InternalApi private[akka] case class Stop(loggingEnabled: Boolean, logLevel: Level) extends SupervisorStrategy { override def withLoggingEnabled(enabled: Boolean) = copy(loggingEnabled = enabled) - override def withLogLevel(level: LogLevel): SupervisorStrategy = + override def withLogLevel(level: Level): SupervisorStrategy = copy(logLevel = level) } @@ -145,7 +144,7 @@ object SupervisorStrategy { maxRestarts: Int, withinTimeRange: FiniteDuration, loggingEnabled: Boolean = true, - logLevel: LogLevel = Logging.ErrorLevel, + logLevel: Level = Level.ERROR, stopChildren: Boolean = true, stashCapacity: Int = -1) extends RestartSupervisorStrategy @@ -166,7 +165,7 @@ object SupervisorStrategy { override def withLoggingEnabled(enabled: Boolean): RestartSupervisorStrategy = copy(loggingEnabled = enabled) - override def withLogLevel(level: LogLevel): RestartSupervisorStrategy = + override def withLogLevel(level: Level): RestartSupervisorStrategy = copy(logLevel = level) } @@ -179,7 +178,7 @@ object SupervisorStrategy { randomFactor: Double, resetBackoffAfter: FiniteDuration, loggingEnabled: Boolean = true, - logLevel: LogLevel = Logging.ErrorLevel, + logLevel: Level = Level.ERROR, maxRestarts: Int = -1, stopChildren: Boolean = true, stashCapacity: Int = -1) @@ -206,7 +205,7 @@ object SupervisorStrategy { override def withLoggingEnabled(enabled: Boolean): BackoffSupervisorStrategy = copy(loggingEnabled = enabled) - override def withLogLevel(level: LogLevel): BackoffSupervisorStrategy = + override def withLogLevel(level: Level): BackoffSupervisorStrategy = copy(logLevel = logLevel) } @@ -214,11 +213,11 @@ object SupervisorStrategy { sealed abstract class SupervisorStrategy { def loggingEnabled: Boolean - def logLevel: LogLevel + def logLevel: Level def withLoggingEnabled(enabled: Boolean): SupervisorStrategy - def withLogLevel(level: LogLevel): SupervisorStrategy + def withLogLevel(level: Level): SupervisorStrategy } @@ -273,7 +272,7 @@ sealed abstract class RestartSupervisorStrategy extends SupervisorStrategy { override def withLoggingEnabled(enabled: Boolean): RestartSupervisorStrategy - override def withLogLevel(level: LogLevel): RestartSupervisorStrategy + override def withLogLevel(level: Level): RestartSupervisorStrategy } @@ -321,6 +320,6 @@ sealed abstract class BackoffSupervisorStrategy extends SupervisorStrategy { override def withLoggingEnabled(enabled: Boolean): BackoffSupervisorStrategy - override def withLogLevel(level: LogLevel): BackoffSupervisorStrategy + override def withLogLevel(level: Level): BackoffSupervisorStrategy } diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/ActorContextImpl.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/ActorContextImpl.scala index c4df8b6b48..a33455a314 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/ActorContextImpl.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/ActorContextImpl.scala @@ -16,10 +16,13 @@ import java.util.function.BiFunction import scala.concurrent.{ ExecutionContextExecutor, Future } import scala.reflect.ClassTag import scala.util.Try + import akka.annotation.InternalApi import akka.util.OptionVal import akka.util.Timeout import akka.util.JavaDurationConverters._ +import org.slf4j.Logger +import org.slf4j.LoggerFactory /** * INTERNAL API @@ -29,6 +32,10 @@ import akka.util.JavaDurationConverters._ with javadsl.ActorContext[T] with scaladsl.ActorContext[T] { + // lazily initialized + private var logger: OptionVal[Logger] = OptionVal.None + private var mdcUsed: Boolean = false + private var messageAdapterRef: OptionVal[ActorRef[Any]] = OptionVal.None private var _messageAdapters: List[(Class[_], Any => T)] = Nil private var _timer: OptionVal[TimerSchedulerImpl[T]] = OptionVal.None @@ -76,8 +83,42 @@ import akka.util.JavaDurationConverters._ override def getSystem: akka.actor.typed.ActorSystem[Void] = system.asInstanceOf[ActorSystem[Void]] + override def log: Logger = { + val l = logger match { + case OptionVal.Some(l) => l + case OptionVal.None => + val logClass = LoggerClass.detectLoggerClassFromStack(classOf[Behavior[_]]) + initLoggerWithName(logClass.getName) + } + // avoid access to MDC ThreadLocal if not needed + mdcUsed = true + ActorMdc.setMdc(self.path.toString) + l + } + override def getLog: Logger = log + override def setLoggerName(name: String): Unit = + initLoggerWithName(name) + + override def setLoggerName(clazz: Class[_]): Unit = + setLoggerName(clazz.getName) + + // MDC is cleared (if used) from aroundReceive in ActorAdapter after processing each message + override private[akka] def clearMdc(): Unit = { + // avoid access to MDC ThreadLocal if not needed + if (mdcUsed) { + ActorMdc.clearMdc() + mdcUsed = false + } + } + + private def initLoggerWithName(name: String): Logger = { + val l = LoggerFactory.getLogger(name) + logger = OptionVal.Some(l) + l + } + override def setReceiveTimeout(d: java.time.Duration, msg: T): Unit = setReceiveTimeout(d.asScala, msg) diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/ActorMdc.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/ActorMdc.scala new file mode 100644 index 0000000000..e1c4c6c98a --- /dev/null +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/ActorMdc.scala @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 Lightbend Inc. + */ + +package akka.actor.typed.internal + +import akka.annotation.InternalApi +import org.slf4j.MDC + +/** + * INTERNAL API + */ +@InternalApi private[akka] object ActorMdc { + val SourceKey = "akkaSource" + + def setMdc(source: String): Unit = { + val mdcAdpater = MDC.getMDCAdapter + mdcAdpater.put(SourceKey, source) + } + + // MDC is cleared (if used) from aroundReceive in ActorAdapter after processing each message, + // via ActorContextImpl.clearMdc() + def clearMdc(): Unit = + MDC.clear() + +} diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/ExtensionsImpl.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/ExtensionsImpl.scala index e4a7de1a05..8ab3291f53 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/ExtensionsImpl.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/ExtensionsImpl.scala @@ -49,7 +49,7 @@ trait ExtensionsImpl extends Extensions { self: ActorSystem[_] => else throw new RuntimeException(s"[$extensionIdFQCN] is not an 'ExtensionId'") case Failure(problem) => if (!throwOnLoadFail) - log.error(problem, "While trying to load extension [{}], skipping...", extensionIdFQCN) + log.error(s"While trying to load extension $extensionIdFQCN, skipping...", problem) else throw new RuntimeException(s"While trying to load extension [$extensionIdFQCN]", problem) } } diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/InterceptorImpl.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/InterceptorImpl.scala index 92b8c788de..8960838526 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/InterceptorImpl.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/InterceptorImpl.scala @@ -5,14 +5,14 @@ package akka.actor.typed.internal import scala.reflect.ClassTag - import akka.actor.typed - import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.LogOptions import akka.actor.typed._ import akka.annotation.InternalApi import akka.util.LineNumbers +import org.slf4j.LoggerFactory +import org.slf4j.event.Level /** * Provides the impl of any behavior that could nest another behavior @@ -142,6 +142,9 @@ private[akka] final case class MonitorInterceptor[T: ClassTag](actorRef: ActorRe def apply[T](opts: LogOptions): BehaviorInterceptor[T, T] = { new LogMessagesInterceptor(opts).asInstanceOf[BehaviorInterceptor[T, T]] } + + private val LogMessageTemplate = "actor [{}] received message: {}" + private val LogSignalTemplate = "actor [{}] received signal: {}" } /** @@ -153,19 +156,34 @@ private[akka] final case class MonitorInterceptor[T: ClassTag](actorRef: ActorRe private[akka] final class LogMessagesInterceptor(val opts: LogOptions) extends BehaviorInterceptor[Any, Any] { import BehaviorInterceptor._ + import LogMessagesInterceptor._ + + private val logger = opts.getLogger.orElse(LoggerFactory.getLogger(getClass)) override def aroundReceive(ctx: TypedActorContext[Any], msg: Any, target: ReceiveTarget[Any]): Behavior[Any] = { - if (opts.enabled) - opts.logger.getOrElse(ctx.asScala.log).log(opts.level, "received message {}", msg) + log(LogMessageTemplate, msg, ctx) target(ctx, msg) } override def aroundSignal(ctx: TypedActorContext[Any], signal: Signal, target: SignalTarget[Any]): Behavior[Any] = { - if (opts.enabled) - opts.logger.getOrElse(ctx.asScala.log).log(opts.level, "received signal {}", signal) + log(LogSignalTemplate, signal, ctx) target(ctx, signal) } + private def log(template: String, messageOrSignal: Any, context: TypedActorContext[Any]): Unit = { + if (opts.enabled) { + val selfPath = context.asScala.self.path + opts.level match { + case Level.ERROR => logger.error(template, selfPath, messageOrSignal) + case Level.WARN => logger.warn(template, selfPath, messageOrSignal) + case Level.INFO => logger.info(template, selfPath, messageOrSignal) + case Level.DEBUG => logger.debug(template, selfPath, messageOrSignal) + case Level.TRACE => logger.trace(template, selfPath, messageOrSignal) + case other => throw new IllegalArgumentException(s"Unknown log level [$other].") + } + } + } + // only once in the same behavior stack override def isSame(other: BehaviorInterceptor[Any, Any]): Boolean = other match { case a: LogMessagesInterceptor => a.opts == opts diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/Supervision.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/Supervision.scala index ebfad48073..1feca5dd4a 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/Supervision.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/Supervision.scala @@ -25,6 +25,7 @@ import akka.annotation.InternalApi import akka.event.Logging import akka.util.OptionVal import akka.util.unused +import org.slf4j.event.Level /** * INTERNAL API @@ -79,12 +80,15 @@ private abstract class AbstractSupervisor[I, Thr <: Throwable](strategy: Supervi def log(ctx: TypedActorContext[_], t: Throwable): Unit = { if (strategy.loggingEnabled) { val unwrapped = UnstashException.unwrap(t) + val logMessage = s"Supervisor $this saw failure: ${unwrapped.getMessage}" + val logger = ctx.asScala.log strategy.logLevel match { - case Logging.ErrorLevel => - ctx.asScala.log.error(unwrapped, "Supervisor {} saw failure: {}", this, unwrapped.getMessage) - case Logging.WarningLevel => - ctx.asScala.log.warning(unwrapped, "Supervisor {} saw failure: {}", this, unwrapped.getMessage) - case level => ctx.asScala.log.log(level, "Supervisor {} saw failure: {}", this, unwrapped.getMessage) + case Level.ERROR => logger.error(logMessage, unwrapped) + case Level.WARN => logger.warn(logMessage, unwrapped) + case Level.INFO => logger.info(logMessage, unwrapped) + case Level.DEBUG => logger.debug(logMessage, unwrapped) + case Level.TRACE => logger.trace(logMessage, unwrapped) + case other => throw new IllegalArgumentException(s"Unknown log level [$other].") } } } @@ -311,7 +315,7 @@ private class RestartSupervisor[T, Thr <: Throwable: ClassTag](initial: Behavior } else { try signalRestart(t) catch { - case NonFatal(ex) => ctx.asScala.log.error(ex, "failure during PreRestart") + case NonFatal(ex) => ctx.asScala.log.error("failure during PreRestart", ex) } prepareRestart(ctx, t) diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/TimerSchedulerImpl.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/TimerSchedulerImpl.scala index 6a6bcef980..90cfca4a4c 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/TimerSchedulerImpl.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/TimerSchedulerImpl.scala @@ -8,14 +8,15 @@ package internal import java.time.Duration import scala.concurrent.duration.FiniteDuration - import akka.actor.Cancellable import akka.actor.NotInfluenceReceiveTimeout import akka.actor.typed.scaladsl.ActorContext +import akka.actor.typed.scaladsl.LoggerOps import akka.annotation.InternalApi import akka.dispatch.ExecutionContexts import akka.util.JavaDurationConverters._ import akka.util.OptionVal +import org.slf4j.Logger /** * INTERNAL API @@ -158,11 +159,12 @@ import akka.util.OptionVal OptionVal.Some(t.msg) } else { // it was from an old timer that was enqueued in mailbox before canceled - log.debug( - "Received timer [{}] from old generation [{}], expected generation [{}], discarding", - timerMsg.key, - timerMsg.generation, - t.generation) + if (log.isDebugEnabled) + log.debugN( + "Received timer [{}] from old generation [{}], expected generation [{}], discarding", + timerMsg.key, + timerMsg.generation, + t.generation) OptionVal.none // message should be ignored } } diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/WithMdcBehaviorInterceptor.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/WithMdcBehaviorInterceptor.scala index 35aa728f5e..9a947abe0c 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/WithMdcBehaviorInterceptor.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/WithMdcBehaviorInterceptor.scala @@ -4,23 +4,22 @@ package akka.actor.typed.internal -import akka.actor.typed.internal.adapter.AbstractLogger import akka.actor.typed.{ Behavior, BehaviorInterceptor, Signal, TypedActorContext } import akka.annotation.InternalApi -import scala.collection.immutable.HashMap +import org.slf4j.MDC + import scala.reflect.ClassTag /** * INTERNAL API */ @InternalApi private[akka] object WithMdcBehaviorInterceptor { - val noMdcPerMessage = (_: Any) => Map.empty[String, Any] + val noMdcPerMessage = (_: Any) => Map.empty[String, String] def apply[T: ClassTag]( - staticMdc: Map[String, Any], - mdcForMessage: T => Map[String, Any], + staticMdc: Map[String, String], + mdcForMessage: T => Map[String, String], behavior: Behavior[T]): Behavior[T] = { - BehaviorImpl.intercept(() => new WithMdcBehaviorInterceptor[T](staticMdc, mdcForMessage))(behavior) } @@ -32,8 +31,8 @@ import scala.reflect.ClassTag * INTERNAL API */ @InternalApi private[akka] final class WithMdcBehaviorInterceptor[T: ClassTag] private ( - staticMdc: Map[String, Any], - mdcForMessage: T => Map[String, Any]) + staticMdc: Map[String, String], + mdcForMessage: T => Map[String, String]) extends BehaviorInterceptor[T, T] { import BehaviorInterceptor._ @@ -73,34 +72,30 @@ import scala.reflect.ClassTag } override def aroundReceive(ctx: TypedActorContext[T], msg: T, target: ReceiveTarget[T]): Behavior[T] = { - val mdc = merge(staticMdc, mdcForMessage(msg)) - ctx.asScala.log.asInstanceOf[AbstractLogger].mdc = mdc - val next = - try { - target(ctx, msg) - } finally { - ctx.asScala.log.asInstanceOf[AbstractLogger].mdc = Map.empty - } - next - } - - override def aroundSignal(ctx: TypedActorContext[T], signal: Signal, target: SignalTarget[T]): Behavior[T] = { - ctx.asScala.log.asInstanceOf[AbstractLogger].mdc = staticMdc try { - target(ctx, signal) + setMdcValues(mdcForMessage(msg)) + target(ctx, msg) } finally { - ctx.asScala.log.asInstanceOf[AbstractLogger].mdc = Map.empty + MDC.clear() } } - private def merge(staticMdc: Map[String, Any], mdcForMessage: Map[String, Any]): Map[String, Any] = { - if (staticMdc.isEmpty) mdcForMessage - else if (mdcForMessage.isEmpty) staticMdc - else if (staticMdc.isInstanceOf[HashMap[String, Any]] && mdcForMessage.isInstanceOf[HashMap[String, Any]]) { - // merged is more efficient than ++ - mdcForMessage.asInstanceOf[HashMap[String, Any]].merged(staticMdc.asInstanceOf[HashMap[String, Any]])(null) - } else { - staticMdc ++ mdcForMessage + override def aroundSignal(ctx: TypedActorContext[T], signal: Signal, target: SignalTarget[T]): Behavior[T] = { + try { + setMdcValues(Map.empty) + target(ctx, signal) + } finally { + MDC.clear() + } + } + + private def setMdcValues(dynamicMdc: Map[String, String]): Unit = { + val mdcAdapter = MDC.getMDCAdapter + if (staticMdc.nonEmpty) staticMdc.foreach { + case (key, value) => mdcAdapter.put(key, value) + } + if (dynamicMdc.nonEmpty) dynamicMdc.foreach { + case (key, value) => mdcAdapter.put(key, value) } } diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/adapter/ActorAdapter.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/adapter/ActorAdapter.scala index 25794370b2..900ba9cee1 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/adapter/ActorAdapter.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/adapter/ActorAdapter.scala @@ -72,34 +72,36 @@ import akka.util.OptionVal def receive: Receive = ActorAdapter.DummyReceive override protected[akka] def aroundReceive(receive: Receive, msg: Any): Unit = { - // as we know we never become in "normal" typed actors, it is just the current behavior that - // changes, we can avoid some overhead with the partial function/behavior stack of classic entirely - // we also know that the receive is total, so we can avoid the orElse part as well. - msg match { - case classic.Terminated(ref) => - val msg = - if (failures contains ref) { - val ex = failures(ref) - failures -= ref - ChildFailed(ActorRefAdapter(ref), ex) - } else Terminated(ActorRefAdapter(ref)) - handleSignal(msg) - case classic.ReceiveTimeout => - handleMessage(ctx.receiveTimeoutMsg) - case wrapped: AdaptMessage[Any, T] @unchecked => - withSafelyAdapted(() => wrapped.adapt()) { - case AdaptWithRegisteredMessageAdapter(msg) => - adaptAndHandle(msg) - case msg: T @unchecked => - handleMessage(msg) - } - case AdaptWithRegisteredMessageAdapter(msg) => - adaptAndHandle(msg) - case signal: Signal => - handleSignal(signal) - case msg: T @unchecked => - handleMessage(msg) - } + try { + // as we know we never become in "normal" typed actors, it is just the current behavior that + // changes, we can avoid some overhead with the partial function/behavior stack of untyped entirely + // we also know that the receive is total, so we can avoid the orElse part as well. + msg match { + case classic.Terminated(ref) => + val msg = + if (failures contains ref) { + val ex = failures(ref) + failures -= ref + ChildFailed(ActorRefAdapter(ref), ex) + } else Terminated(ActorRefAdapter(ref)) + handleSignal(msg) + case classic.ReceiveTimeout => + handleMessage(ctx.receiveTimeoutMsg) + case wrapped: AdaptMessage[Any, T] @unchecked => + withSafelyAdapted(() => wrapped.adapt()) { + case AdaptWithRegisteredMessageAdapter(msg) => + adaptAndHandle(msg) + case msg: T @unchecked => + handleMessage(msg) + } + case AdaptWithRegisteredMessageAdapter(msg) => + adaptAndHandle(msg) + case signal: Signal => + handleSignal(signal) + case msg: T @unchecked => + handleMessage(msg) + } + } finally ctx.clearMdc() } private def handleMessage(msg: T): Unit = { @@ -179,7 +181,7 @@ import akka.util.OptionVal case Success(a) => body(a) case Failure(ex) => - log.error(ex, "Exception thrown out of adapter. Stopping myself.") + log.error(ex, s"Exception thrown out of adapter. Stopping myself. ${ex.getMessage}") context.stop(self) } } @@ -233,33 +235,41 @@ import akka.util.OptionVal } override def preStart(): Unit = { - if (Behavior.isAlive(behavior)) { - behavior = Behavior.validateAsInitial(Behavior.start(behavior, ctx)) - } - // either was stopped initially or became stopped on start - if (!Behavior.isAlive(behavior)) context.stop(self) + try { + if (Behavior.isAlive(behavior)) { + behavior = Behavior.validateAsInitial(Behavior.start(behavior, ctx)) + } + // either was stopped initially or became stopped on start + if (!Behavior.isAlive(behavior)) context.stop(self) + } finally ctx.clearMdc() } override def preRestart(reason: Throwable, message: Option[Any]): Unit = { - ctx.cancelAllTimers() - Behavior.interpretSignal(behavior, ctx, PreRestart) - behavior = BehaviorImpl.stopped + try { + ctx.cancelAllTimers() + Behavior.interpretSignal(behavior, ctx, PreRestart) + behavior = BehaviorImpl.stopped + } finally ctx.clearMdc() } override def postRestart(reason: Throwable): Unit = { - ctx.cancelAllTimers() - behavior = Behavior.validateAsInitial(Behavior.start(behavior, ctx)) - if (!Behavior.isAlive(behavior)) context.stop(self) + try { + ctx.cancelAllTimers() + behavior = Behavior.validateAsInitial(Behavior.start(behavior, ctx)) + if (!Behavior.isAlive(behavior)) context.stop(self) + } finally ctx.clearMdc() } override def postStop(): Unit = { - ctx.cancelAllTimers() - behavior match { - case _: DeferredBehavior[_] => - // Do not undefer a DeferredBehavior as that may cause creation side-effects, which we do not want on termination. - case b => Behavior.interpretSignal(b, ctx, PostStop) - } - behavior = BehaviorImpl.stopped + try { + ctx.cancelAllTimers() + behavior match { + case _: DeferredBehavior[_] => + // Do not undefer a DeferredBehavior as that may cause creation side-effects, which we do not want on termination. + case b => Behavior.interpretSignal(b, ctx, PostStop) + } + behavior = BehaviorImpl.stopped + } finally ctx.clearMdc() } } diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/adapter/ActorContextAdapter.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/adapter/ActorContextAdapter.scala index b12bc8c85a..e7971186d5 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/adapter/ActorContextAdapter.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/adapter/ActorContextAdapter.scala @@ -6,10 +6,7 @@ package akka.actor.typed package internal package adapter -import akka.actor.ExtendedActorSystem import akka.annotation.InternalApi -import akka.event.LoggingFilterWithMarker -import akka.util.OptionVal import akka.{ actor => classic } import scala.concurrent.ExecutionContextExecutor @@ -58,9 +55,6 @@ private[akka] object ActorContextAdapter { private[akka] override def currentBehavior: Behavior[T] = adapter.currentBehavior - // lazily initialized - private var actorLogger: OptionVal[Logger] = OptionVal.None - final override val self = ActorRefAdapter(classicContext.self) final override val system = ActorSystemAdapter(classicContext.system) private[akka] def classicActorContext = classicContext @@ -121,28 +115,6 @@ private[akka] object ActorContextAdapter { ActorRefAdapter[U](ref) } - private def initLoggerWithClass(logClass: Class[_]): LoggerAdapterImpl = { - val logSource = self.path.toString - val system = classicContext.system.asInstanceOf[ExtendedActorSystem] - val logger = - new LoggerAdapterImpl(system.eventStream, logClass, logSource, LoggingFilterWithMarker.wrap(system.logFilter)) - actorLogger = OptionVal.Some(logger) - logger - } - - override def log: Logger = { - actorLogger match { - case OptionVal.Some(logger) => logger - case OptionVal.None => - val logClass = LoggerClass.detectLoggerClassFromStack(classOf[Behavior[_]]) - initLoggerWithClass(logClass) - } - } - - override def setLoggerClass(clazz: Class[_]): Unit = { - initLoggerWithClass(clazz) - } - /** * Made accessible to allow stash to deal with unhandled messages as though they were interpreted by * the adapter itself, even though the unstashing occurs inside the behavior stack. diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/adapter/ActorSystemAdapter.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/adapter/ActorSystemAdapter.scala index 56ef53c160..c372eca4d4 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/adapter/ActorSystemAdapter.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/adapter/ActorSystemAdapter.scala @@ -19,7 +19,6 @@ import akka.actor.typed.ActorSystem import akka.actor.typed.Behavior import akka.actor.typed.DispatcherSelector import akka.actor.typed.Dispatchers -import akka.actor.typed.Logger import akka.actor.typed.Props import akka.actor.typed.Scheduler import akka.actor.typed.Settings @@ -31,8 +30,8 @@ import akka.actor.typed.internal.PropsImpl.DispatcherFromConfig import akka.actor.typed.internal.PropsImpl.DispatcherSameAsParent import akka.actor.typed.internal.SystemMessage import akka.annotation.InternalApi -import akka.event.LoggingFilterWithMarker import akka.{ actor => classic } +import org.slf4j.{ Logger, LoggerFactory } /** * INTERNAL API. Lightweight wrapper for presenting a classic ActorSystem to a Behavior (via the context). @@ -88,19 +87,15 @@ import akka.{ actor => classic } override def shutdown(): Unit = () // there was no shutdown in classic Akka } override def dynamicAccess: classic.DynamicAccess = system.dynamicAccess - implicit override def executionContext: scala.concurrent.ExecutionContextExecutor = system.dispatcher - override val log: Logger = new LoggerAdapterImpl( - system.eventStream, - classOf[ActorSystem[_]], - name, - LoggingFilterWithMarker.wrap(system.logFilter)) - override def logConfiguration(): Unit = system.logConfiguration() - override def name: String = system.name - override val scheduler: Scheduler = new SchedulerAdapter(system.scheduler) - override def settings: Settings = new Settings(system.settings) - override def startTime: Long = system.startTime + implicit override def executionContext: scala.concurrent.ExecutionContextExecutor = classicSystem.dispatcher + override val log: Logger = LoggerFactory.getLogger(classOf[ActorSystem[_]]) + override def logConfiguration(): Unit = classicSystem.logConfiguration() + override def name: String = classicSystem.name + override val scheduler: Scheduler = new SchedulerAdapter(classicSystem.scheduler) + override def settings: Settings = new Settings(classicSystem.settings) + override def startTime: Long = classicSystem.startTime override def threadFactory: java.util.concurrent.ThreadFactory = system.threadFactory - override def uptime: Long = system.uptime + override def uptime: Long = classicSystem.uptime override def printTree: String = system.printTree import akka.dispatch.ExecutionContexts.sameThreadExecutionContext diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/adapter/LoggerAdapterImpl.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/adapter/LoggerAdapterImpl.scala deleted file mode 100644 index 4b66374809..0000000000 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/adapter/LoggerAdapterImpl.scala +++ /dev/null @@ -1,469 +0,0 @@ -/* - * Copyright (C) 2009-2019 Lightbend Inc. - */ - -package akka.actor.typed.internal.adapter - -import akka.actor.typed.{ LogMarker, Logger } -import akka.annotation.InternalApi -import akka.event.Logging._ -import akka.event.{ LoggingBus, LoggingFilterWithMarker, LogMarker => ClassicLM } -import akka.util.OptionVal - -import akka.util.ccompat.JavaConverters._ - -/** - * INTERNAL API - */ -private[akka] abstract class AbstractLogger extends Logger { - - // actual log entry emitting methods - private[akka] def notifyError(message: String, cause: OptionVal[Throwable], marker: OptionVal[LogMarker]): Unit - private[akka] def notifyWarning(message: String, cause: OptionVal[Throwable], marker: OptionVal[LogMarker]): Unit - private[akka] def notifyInfo(message: String, marker: OptionVal[LogMarker]): Unit - private[akka] def notifyDebug(message: String, marker: OptionVal[LogMarker]): Unit - // is set directly by Behaviors.withMdc - private[akka] var mdc: Map[String, Any] = Map.empty - - // user api implementations - override def withMdc(mdc: java.util.Map[String, Any]): Logger = - withMdc(mdc.asScala.toMap) - - override def error(message: String): Unit = { - if (isErrorEnabled) notifyError(message, OptionVal.None, OptionVal.None) - } - - override def error(template: String, arg1: Any): Unit = { - if (isErrorEnabled) notifyError(format(template, arg1), OptionVal.None, OptionVal.None) - } - - override def error(template: String, arg1: Any, arg2: Any): Unit = { - if (isErrorEnabled) notifyError(format(template, arg1, arg2), OptionVal.None, OptionVal.None) - } - - override def error(template: String, arg1: Any, arg2: Any, arg3: Any): Unit = { - if (isErrorEnabled) notifyError(format(template, arg1, arg2, arg3), OptionVal.None, OptionVal.None) - } - - override def error(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = { - if (isErrorEnabled) notifyError(format(template, arg1, arg2, arg3, arg4), OptionVal.None, OptionVal.None) - } - - override def error(cause: Throwable, message: String): Unit = { - if (isErrorEnabled) notifyError(message, OptionVal.Some(cause), OptionVal.None) - } - - override def error(cause: Throwable, template: String, arg1: Any): Unit = { - if (isErrorEnabled) notifyError(format(template, arg1), OptionVal.Some(cause), OptionVal.None) - } - - override def error(cause: Throwable, template: String, arg1: Any, arg2: Any): Unit = { - if (isErrorEnabled) notifyError(format(template, arg1, arg2), OptionVal.Some(cause), OptionVal.None) - } - - override def error(cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any): Unit = { - if (isErrorEnabled) notifyError(format(template, arg1, arg2, arg3), OptionVal.Some(cause), OptionVal.None) - } - - override def error(cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = { - if (isErrorEnabled) notifyError(format(template, arg1, arg2, arg3, arg4), OptionVal.Some(cause), OptionVal.None) - } - - override def error(marker: LogMarker, cause: Throwable, message: String): Unit = { - if (isErrorEnabled) notifyError(message, OptionVal.Some(cause), OptionVal.Some(marker)) - } - - override def error(marker: LogMarker, cause: Throwable, template: String, arg1: Any): Unit = { - if (isErrorEnabled) notifyError(format(template, arg1), OptionVal.Some(cause), OptionVal.Some(marker)) - } - - override def error(marker: LogMarker, cause: Throwable, template: String, arg1: Any, arg2: Any): Unit = { - if (isErrorEnabled) notifyError(format(template, arg1, arg2), OptionVal.Some(cause), OptionVal.Some(marker)) - } - - override def error(marker: LogMarker, cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any): Unit = { - if (isErrorEnabled) notifyError(format(template, arg1, arg2, arg3), OptionVal.Some(cause), OptionVal.Some(marker)) - } - - override def error( - marker: LogMarker, - cause: Throwable, - template: String, - arg1: Any, - arg2: Any, - arg3: Any, - arg4: Any): Unit = { - if (isErrorEnabled) - notifyError(format(template, arg1, arg2, arg3, arg4), OptionVal.Some(cause), OptionVal.Some(marker)) - } - - override def error(marker: LogMarker, message: String): Unit = { - if (isErrorEnabled) notifyError(message, OptionVal.None, OptionVal.Some(marker)) - } - - override def error(marker: LogMarker, template: String, arg1: Any): Unit = { - if (isErrorEnabled) notifyError(format(template, arg1), OptionVal.None, OptionVal.Some(marker)) - } - - override def error(marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit = { - if (isErrorEnabled) notifyError(format(template, arg1, arg2), OptionVal.None, OptionVal.Some(marker)) - } - - override def error(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit = { - if (isErrorEnabled) notifyError(format(template, arg1, arg2, arg3), OptionVal.None, OptionVal.Some(marker)) - } - - override def error(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = { - if (isErrorEnabled) notifyError(format(template, arg1, arg2, arg3, arg4), OptionVal.None, OptionVal.Some(marker)) - } - - override def warning(message: String): Unit = { - if (isWarningEnabled) notifyWarning(message, OptionVal.None, OptionVal.None) - } - - override def warning(template: String, arg1: Any): Unit = { - if (isWarningEnabled) notifyWarning(format(template, arg1), OptionVal.None, OptionVal.None) - } - - override def warning(template: String, arg1: Any, arg2: Any): Unit = { - if (isWarningEnabled) notifyWarning(format(template, arg1, arg2), OptionVal.None, OptionVal.None) - } - - override def warning(template: String, arg1: Any, arg2: Any, arg3: Any): Unit = { - if (isWarningEnabled) notifyWarning(format(template, arg1, arg2, arg3), OptionVal.None, OptionVal.None) - } - - override def warning(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = { - if (isWarningEnabled) notifyWarning(format(template, arg1, arg2, arg3, arg4), OptionVal.None, OptionVal.None) - } - - override def warning(cause: Throwable, message: String): Unit = { - if (isWarningEnabled) notifyWarning(message, OptionVal.Some(cause), OptionVal.None) - } - - override def warning(cause: Throwable, template: String, arg1: Any): Unit = { - if (isWarningEnabled) notifyWarning(format(template, arg1), OptionVal.Some(cause), OptionVal.None) - } - - override def warning(cause: Throwable, template: String, arg1: Any, arg2: Any): Unit = { - if (isWarningEnabled) notifyWarning(format(template, arg1, arg2), OptionVal.Some(cause), OptionVal.None) - } - - override def warning(cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any): Unit = { - if (isWarningEnabled) notifyWarning(format(template, arg1, arg2, arg3), OptionVal.Some(cause), OptionVal.None) - } - - override def warning(cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = { - if (isWarningEnabled) notifyWarning(format(template, arg1, arg2, arg3, arg4), OptionVal.Some(cause), OptionVal.None) - } - - override def warning(marker: LogMarker, cause: Throwable, template: String, arg1: Any): Unit = { - if (isWarningEnabled) notifyWarning(format(template, arg1), OptionVal.Some(cause), OptionVal.Some(marker)) - } - - override def warning(marker: LogMarker, cause: Throwable, template: String, arg1: Any, arg2: Any): Unit = { - if (isWarningEnabled) notifyWarning(format(template, arg1, arg2), OptionVal.Some(cause), OptionVal.Some(marker)) - } - - override def warning(marker: LogMarker, cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any): Unit = { - if (isWarningEnabled) - notifyWarning(format(template, arg1, arg2, arg3), OptionVal.Some(cause), OptionVal.Some(marker)) - } - - override def warning( - marker: LogMarker, - cause: Throwable, - template: String, - arg1: Any, - arg2: Any, - arg3: Any, - arg4: Any): Unit = { - if (isWarningEnabled) - notifyWarning(format(template, arg1, arg2, arg3, arg4), OptionVal.Some(cause), OptionVal.Some(marker)) - } - - override def warning(marker: LogMarker, cause: Throwable, message: String): Unit = { - if (isWarningEnabled) notifyWarning(message, OptionVal.Some(cause), OptionVal.Some(marker)) - } - - override def warning(marker: LogMarker, message: String): Unit = { - if (isWarningEnabled) notifyWarning(message, OptionVal.None, OptionVal.Some(marker)) - } - - override def warning(marker: LogMarker, template: String, arg1: Any): Unit = { - if (isWarningEnabled) notifyWarning(format(template, arg1), OptionVal.None, OptionVal.Some(marker)) - } - - override def warning(marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit = { - if (isWarningEnabled) notifyWarning(format(template, arg1, arg2), OptionVal.None, OptionVal.Some(marker)) - } - - override def warning(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit = { - if (isWarningEnabled) notifyWarning(format(template, arg1, arg2, arg3), OptionVal.None, OptionVal.Some(marker)) - } - - override def warning(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = { - if (isWarningEnabled) - notifyWarning(format(template, arg1, arg2, arg3, arg4), OptionVal.None, OptionVal.Some(marker)) - } - - override def info(message: String): Unit = { - if (isInfoEnabled) notifyInfo(message, OptionVal.None) - } - - override def info(template: String, arg1: Any): Unit = { - if (isInfoEnabled) notifyInfo(format(template, arg1), OptionVal.None) - } - - override def info(template: String, arg1: Any, arg2: Any): Unit = { - if (isInfoEnabled) notifyInfo(format(template, arg1, arg2), OptionVal.None) - } - - override def info(template: String, arg1: Any, arg2: Any, arg3: Any): Unit = { - if (isInfoEnabled) notifyInfo(format(template, arg1, arg2, arg3), OptionVal.None) - } - - override def info(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = { - if (isInfoEnabled) notifyInfo(format(template, arg1, arg2, arg3, arg4), OptionVal.None) - } - - override def info(marker: LogMarker, message: String): Unit = { - if (isInfoEnabled) notifyInfo(message, OptionVal.Some(marker)) - } - - override def info(marker: LogMarker, template: String, arg1: Any): Unit = { - if (isInfoEnabled) notifyInfo(format(template, arg1), OptionVal.Some(marker)) - } - - override def info(marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit = { - if (isInfoEnabled) notifyInfo(format(template, arg1, arg2), OptionVal.Some(marker)) - } - - override def info(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit = { - if (isInfoEnabled) notifyInfo(format(template, arg1, arg2, arg3), OptionVal.Some(marker)) - } - - override def info(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = { - if (isInfoEnabled) notifyInfo(format(template, arg1, arg2, arg3, arg4), OptionVal.Some(marker)) - } - - override def debug(message: String): Unit = { - if (isDebugEnabled) notifyDebug(message, OptionVal.None) - } - - override def debug(template: String, arg1: Any): Unit = { - if (isDebugEnabled) notifyDebug(format(template, arg1), OptionVal.None) - } - - override def debug(template: String, arg1: Any, arg2: Any): Unit = { - if (isDebugEnabled) notifyDebug(format(template, arg1, arg2), OptionVal.None) - } - - override def debug(template: String, arg1: Any, arg2: Any, arg3: Any): Unit = { - if (isDebugEnabled) notifyDebug(format(template, arg1, arg2, arg3), OptionVal.None) - } - - override def debug(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = { - if (isDebugEnabled) notifyDebug(format(template, arg1, arg2, arg3, arg4), OptionVal.None) - } - - override def debug(marker: LogMarker, message: String): Unit = { - if (isDebugEnabled) notifyDebug(message, OptionVal.Some(marker)) - } - - override def debug(marker: LogMarker, template: String, arg1: Any): Unit = { - if (isDebugEnabled) notifyDebug(format(template, arg1), OptionVal.Some(marker)) - } - - override def debug(marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit = { - if (isDebugEnabled) notifyDebug(format(template, arg1, arg2), OptionVal.Some(marker)) - } - - override def debug(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit = { - if (isDebugEnabled) notifyDebug(format(template, arg1, arg2, arg3), OptionVal.Some(marker)) - } - - override def debug(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = { - if (isDebugEnabled) notifyDebug(format(template, arg1, arg2, arg3, arg4), OptionVal.Some(marker)) - } - - override def log(level: LogLevel, message: String): Unit = { - if (isLevelEnabled(level)) notify(level, message, OptionVal.None) - } - - override def log(level: LogLevel, template: String, arg1: Any): Unit = { - if (isLevelEnabled(level)) notify(level, format(template, arg1), OptionVal.None) - } - - override def log(level: LogLevel, template: String, arg1: Any, arg2: Any): Unit = { - if (isLevelEnabled(level)) notify(level, format(template, arg1, arg2), OptionVal.None) - } - - override def log(level: LogLevel, template: String, arg1: Any, arg2: Any, arg3: Any): Unit = { - if (isLevelEnabled(level)) notify(level, format(template, arg1, arg2, arg3), OptionVal.None) - } - - override def log(level: LogLevel, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = { - if (isLevelEnabled(level)) notify(level, format(template, arg1, arg2, arg3, arg4), OptionVal.None) - } - - override def log(level: LogLevel, marker: LogMarker, message: String): Unit = { - if (isLevelEnabled(level)) notify(level, message, OptionVal.Some(marker)) - } - - override def log(level: LogLevel, marker: LogMarker, template: String, arg1: Any): Unit = { - if (isLevelEnabled(level)) notify(level, format(template, arg1), OptionVal.Some(marker)) - } - - override def log(level: LogLevel, marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit = { - if (isLevelEnabled(level)) notify(level, format(template, arg1, arg2), OptionVal.Some(marker)) - } - - override def log(level: LogLevel, marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit = { - if (isLevelEnabled(level)) notify(level, format(template, arg1, arg2, arg3), OptionVal.Some(marker)) - } - - override def log( - level: LogLevel, - marker: LogMarker, - template: String, - arg1: Any, - arg2: Any, - arg3: Any, - arg4: Any): Unit = { - if (isLevelEnabled(level)) notify(level, format(template, arg1, arg2, arg3, arg4), OptionVal.Some(marker)) - } - - protected def notify(level: LogLevel, message: String, marker: OptionVal[LogMarker]): Unit = level match { - case ErrorLevel => notifyDebug(message, marker) - case WarningLevel => notifyWarning(message, OptionVal.None, marker) - case InfoLevel => notifyInfo(message, marker) - case DebugLevel => notifyDebug(message, marker) - case _ => () - } - - /** - * If `arg` is an `Array` it will be expanded into replacement arguments, which is useful when - * there are more than four arguments. - */ - private def format(t: String, arg1: Any): String = arg1 match { - case a: Array[_] if !a.getClass.getComponentType.isPrimitive => formatArrayImpl(t, a.toSeq) - case a: Array[_] => formatArrayImpl(t, a.map(_.asInstanceOf[AnyRef]).toSeq) - case x => formatArray(t, x) - } - private def format(t: String, arg1: Any, arg2: Any): String = formatArray(t, arg1, arg2) - private def format(t: String, arg1: Any, arg2: Any, arg3: Any): String = formatArray(t, arg1, arg2, arg3) - private def format(t: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): String = - formatArray(t, arg1, arg2, arg3, arg4) - - private def formatArray(t: String, arg: Any*): String = - formatArrayImpl(t, arg) - - private def formatArrayImpl(t: String, arg: Seq[Any]): String = { - val sb = new java.lang.StringBuilder(64) - var p = 0 - var startIndex = 0 - while (p < arg.length) { - val index = t.indexOf("{}", startIndex) - if (index == -1) { - sb.append(t.substring(startIndex, t.length)).append(" WARNING arguments left: ").append(arg.length - p) - p = arg.length - startIndex = t.length - } else { - sb.append(t.substring(startIndex, index)).append(arg(p)) - startIndex = index + 2 - p += 1 - } - } - sb.append(t.substring(startIndex, t.length)).toString - } - -} - -/** - * INTERNAL API - */ -@InternalApi -private[akka] final class LoggerAdapterImpl( - bus: LoggingBus, - logClass: Class[_], - logSource: String, - loggingFilter: LoggingFilterWithMarker) - extends AbstractLogger { - - override def isErrorEnabled = loggingFilter.isErrorEnabled(logClass, logSource) - override def isWarningEnabled = loggingFilter.isWarningEnabled(logClass, logSource) - override def isInfoEnabled = loggingFilter.isInfoEnabled(logClass, logSource) - override def isDebugEnabled = loggingFilter.isDebugEnabled(logClass, logSource) - - override def isErrorEnabled(marker: LogMarker): Boolean = - loggingFilter.isErrorEnabled(logClass, logSource, marker.asInstanceOf[ClassicLM]) - override def isWarningEnabled(marker: LogMarker): Boolean = - loggingFilter.isWarningEnabled(logClass, logSource, marker.asInstanceOf[ClassicLM]) - override def isInfoEnabled(marker: LogMarker): Boolean = - loggingFilter.isInfoEnabled(logClass, logSource, marker.asInstanceOf[ClassicLM]) - override def isDebugEnabled(marker: LogMarker): Boolean = - loggingFilter.isDebugEnabled(logClass, logSource, marker.asInstanceOf[ClassicLM]) - - override def withMdc(mdc: Map[String, Any]): Logger = { - val mdcAdapter = new LoggerAdapterImpl(bus, logClass, logSource, loggingFilter) - mdcAdapter.mdc = mdc - mdcAdapter - } - - def withLoggerClass(clazz: Class[_]): Logger = { - val withClass = new LoggerAdapterImpl(bus, clazz, logSource, loggingFilter) - withClass.mdc = mdc - withClass - } - - def withLogSource(logSource: String): Logger = { - val withSource = new LoggerAdapterImpl(bus, logClass, logSource, loggingFilter) - withSource.mdc = mdc - withSource - } - - private[akka] def notifyError(message: String, cause: OptionVal[Throwable], marker: OptionVal[LogMarker]): Unit = { - val error = cause match { - case OptionVal.Some(cause) => - marker match { - case OptionVal.Some(m) => Error(cause, logSource, logClass, message, mdc, m.asInstanceOf[ClassicLM]) - case OptionVal.None => Error(cause, logSource, logClass, message, mdc) - } - case OptionVal.None => - marker match { - case OptionVal.Some(m) => Error(logSource, logClass, message, mdc, m.asInstanceOf[ClassicLM]) - case OptionVal.None => Error(logSource, logClass, message, mdc) - } - } - bus.publish(error) - } - - private[akka] def notifyWarning(message: String, cause: OptionVal[Throwable], marker: OptionVal[LogMarker]): Unit = { - val warning = - if (cause.isDefined) Warning(cause.get, logSource, logClass, message, mdc, marker.orNull.asInstanceOf[ClassicLM]) - else - marker match { - case OptionVal.Some(m) => Warning(logSource, logClass, message, mdc, m.asInstanceOf[ClassicLM]) - case OptionVal.None => Warning(logSource, logClass, message, mdc) - } - bus.publish(warning) - } - - private[akka] def notifyInfo(message: String, marker: OptionVal[LogMarker]): Unit = { - val info = marker match { - case OptionVal.Some(m) => Info(logSource, logClass, message, mdc, m.asInstanceOf[ClassicLM]) - case OptionVal.None => Info(logSource, logClass, message, mdc) - } - bus.publish(info) - } - - private[akka] def notifyDebug(message: String, marker: OptionVal[LogMarker]): Unit = { - val debug = marker match { - case OptionVal.Some(m) => Debug(logSource, logClass, message, mdc, m.asInstanceOf[ClassicLM]) - case OptionVal.None => Debug(logSource, logClass, message, mdc) - } - bus.publish(debug) - } - -} diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/receptionist/LocalReceptionist.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/receptionist/LocalReceptionist.scala index cd98cf9085..2d83dcef7d 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/receptionist/LocalReceptionist.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/receptionist/LocalReceptionist.scala @@ -4,11 +4,14 @@ package akka.actor.typed.internal.receptionist -import akka.actor.typed.{ ActorRef, Behavior, Terminated } +import akka.actor.typed.ActorRef +import akka.actor.typed.Behavior +import akka.actor.typed.Terminated import akka.actor.typed.receptionist.Receptionist._ import akka.actor.typed.receptionist.ServiceKey -import akka.actor.typed.scaladsl.{ ActorContext, Behaviors } -import akka.actor.typed.scaladsl.Behaviors.{ receive, same } +import akka.actor.typed.scaladsl.ActorContext +import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.LoggerOps import akka.annotation.InternalApi import akka.util.TypedMultiMap @@ -45,7 +48,7 @@ private[akka] object LocalReceptionist extends ReceptionistBehaviorProvider { extends InternalCommand override def behavior: Behavior[Command] = Behaviors.setup { ctx => - ctx.setLoggerClass(classOf[LocalReceptionist]) + ctx.setLoggerName(classOf[LocalReceptionist]) behavior(TypedMultiMap.empty[AbstractServiceKey, KV], TypedMultiMap.empty[AbstractServiceKey, SubscriptionsKV]) .narrow[Command] } @@ -98,7 +101,7 @@ private[akka] object LocalReceptionist extends ReceptionistBehaviorProvider { def onCommand(ctx: ActorContext[Any], cmd: Command): Behavior[Any] = cmd match { case ReceptionistMessages.Register(key, serviceInstance, maybeReplyTo) => - ctx.log.debug("Actor was registered: {} {}", key, serviceInstance) + ctx.log.debug2("Actor was registered: {} {}", key, serviceInstance) watchWith(ctx, serviceInstance, RegisteredActorTerminated(key, serviceInstance)) maybeReplyTo match { case Some(replyTo) => replyTo ! ReceptionistMessages.Registered(key, serviceInstance) @@ -108,7 +111,7 @@ private[akka] object LocalReceptionist extends ReceptionistBehaviorProvider { case ReceptionistMessages.Find(key, replyTo) => replyWithListing(key, replyTo) - same + Behaviors.same case ReceptionistMessages.Subscribe(key, subscriber) => watchWith(ctx, subscriber, SubscriberTerminated(key, subscriber)) @@ -121,14 +124,14 @@ private[akka] object LocalReceptionist extends ReceptionistBehaviorProvider { def onInternal(ctx: ActorContext[Any], cmd: InternalCommand): Behavior[Any] = cmd match { case RegisteredActorTerminated(key, serviceInstance) => - ctx.log.debug("Registered actor terminated: {} {}", key, serviceInstance) + ctx.log.debug2("Registered actor terminated: {} {}", key, serviceInstance) updateRegistry(Set(key), _.removed(key)(serviceInstance)) case SubscriberTerminated(key, subscriber) => next(newSubscriptions = subscriptions.removed(key)(subscriber)) } - receive[Any] { (ctx, msg) => + Behaviors.receive[Any] { (ctx, msg) => msg match { case cmd: Command => onCommand(ctx, cmd) case cmd: InternalCommand => onInternal(ctx, cmd) diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/ActorContext.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/ActorContext.scala index b56453a096..70628d41ff 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/ActorContext.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/ActorContext.scala @@ -8,11 +8,12 @@ import java.time.Duration import java.util.function.{ BiFunction, Function => JFunction } import akka.annotation.DoNotInherit +import akka.actor.ClassicActorContextProvider import akka.actor.typed._ import java.util.Optional import java.util.concurrent.CompletionStage -import akka.actor.ClassicActorContextProvider +import org.slf4j.Logger import scala.concurrent.ExecutionContextExecutor @@ -67,7 +68,11 @@ trait ActorContext[T] extends TypedActorContext[T] with ClassicActorContextProvi def getSystem: ActorSystem[Void] /** - * An actor specific logger + * An actor specific logger. + * + * The logger name will be an estimated source class for the actor which is calculated when the + * logger is first used (the logger is lazily created upon first use). If this yields the wrong + * class or another class is preferred this can be changed with `setLoggerName`. * * *Warning*: This method is not thread-safe and must not be accessed from threads other * than the ordinary actor message processing thread, such as [[java.util.concurrent.CompletionStage]] callbacks. @@ -76,12 +81,21 @@ trait ActorContext[T] extends TypedActorContext[T] with ClassicActorContextProvi /** * Replace the current logger (or initialize a new logger if the logger was not touched before) with one that - * has ghe given class as logging class. Logger source will be actor path. + * has ghe given name as logger name. Logger source MDC entry "akkaSource" will be the actor path. * * *Warning*: This method is not thread-safe and must not be accessed from threads other * than the ordinary actor message processing thread, such as [[java.util.concurrent.CompletionStage]] callbacks. */ - def setLoggerClass(clazz: Class[_]): Unit + def setLoggerName(name: String): Unit + + /** + * Replace the current logger (or initialize a new logger if the logger was not touched before) with one that + * has ghe given class name as logger name. Logger source MDC entry "akkaSource" will be the actor path. + * + * *Warning*: This method is not thread-safe and must not be accessed from threads other + * than the ordinary actor message processing thread, such as [[java.util.concurrent.CompletionStage]] callbacks. + */ + def setLoggerName(clazz: Class[_]): Unit /** * The list of child Actors created by this Actor during its lifetime that diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/Behaviors.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/Behaviors.scala index eba5af2fae..ada4861703 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/Behaviors.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/Behaviors.scala @@ -325,13 +325,12 @@ object Behaviors { * @param behavior The actual behavior handling the messages, the MDC is used for the log entries logged through * `ActorContext.log` * - * See also [[akka.actor.typed.Logger.withMdc]] */ def withMdc[T]( interceptMessageClass: Class[T], - mdcForMessage: akka.japi.function.Function[T, java.util.Map[String, Any]], + mdcForMessage: akka.japi.function.Function[T, java.util.Map[String, String]], behavior: Behavior[T]): Behavior[T] = - withMdc(interceptMessageClass, Collections.emptyMap[String, Any], mdcForMessage, behavior) + withMdc(interceptMessageClass, Collections.emptyMap[String, String], mdcForMessage, behavior) /** * Static MDC (Mapped Diagnostic Context) @@ -343,11 +342,10 @@ object Behaviors { * @param behavior The actual behavior handling the messages, the MDC is used for the log entries logged through * `ActorContext.log` * - * See also [[akka.actor.typed.Logger.withMdc]] */ def withMdc[T]( interceptMessageClass: Class[T], - staticMdc: java.util.Map[String, Any], + staticMdc: java.util.Map[String, String], behavior: Behavior[T]): Behavior[T] = withMdc(interceptMessageClass, staticMdc, null, behavior) @@ -369,20 +367,19 @@ object Behaviors { * @param behavior The actual behavior handling the messages, the MDC is used for the log entries logged through * `ActorContext.log` * - * See also [[akka.actor.typed.Logger.withMdc]] */ def withMdc[T]( interceptMessageClass: Class[T], - staticMdc: java.util.Map[String, Any], - mdcForMessage: akka.japi.function.Function[T, java.util.Map[String, Any]], + staticMdc: java.util.Map[String, String], + mdcForMessage: akka.japi.function.Function[T, java.util.Map[String, String]], behavior: Behavior[T]): Behavior[T] = { - def asScalaMap(m: java.util.Map[String, Any]): Map[String, Any] = { - if (m == null || m.isEmpty) Map.empty[String, Any] + def asScalaMap(m: java.util.Map[String, String]): Map[String, String] = { + if (m == null || m.isEmpty) Map.empty[String, String] else m.asScala.toMap } - val mdcForMessageFun: T => Map[String, Any] = + val mdcForMessageFun: T => Map[String, String] = if (mdcForMessage == null) Map.empty else { message => asScalaMap(mdcForMessage.apply(message)) diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/scaladsl/ActorContext.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/scaladsl/ActorContext.scala index 52ab00f9b9..97d9c174f8 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/scaladsl/ActorContext.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/scaladsl/ActorContext.scala @@ -14,6 +14,7 @@ import scala.concurrent.duration.FiniteDuration import scala.reflect.ClassTag import scala.util.Try import akka.annotation.InternalApi +import org.slf4j.Logger /** * An Actor is given by the combination of a [[Behavior]] and a context in @@ -67,10 +68,9 @@ trait ActorContext[T] extends TypedActorContext[T] with ClassicActorContextProvi /** * An actor specific logger. * - * The logger will have the actor path as `logSource` and will an estimated source class for the actor - * which is calculated when the logger is first used (the logger is lazily created upon first use). If this - * yields the wrong class or another class is preferred this can be achieved through `Logger.withLoggerClass` - * or `setLoggerClass`. + * The logger name will be an estimated source class for the actor which is calculated when the + * logger is first used (the logger is lazily created upon first use). If this yields the wrong + * class or another class is preferred this can be changed with `setLoggerName`. * * *Warning*: This method is not thread-safe and must not be accessed from threads other * than the ordinary actor message processing thread, such as [[scala.concurrent.Future]] callbacks. @@ -79,12 +79,21 @@ trait ActorContext[T] extends TypedActorContext[T] with ClassicActorContextProvi /** * Replace the current logger (or initialize a new logger if the logger was not touched before) with one that - * has ghe given class as logging class. Logger source will be actor path. + * has ghe given name as logger name. Logger source MDC entry "akkaSource" will be the actor path. + * + * *Warning*: This method is not thread-safe and must not be accessed from threads other + * than the ordinary actor message processing thread, such as [[java.util.concurrent.CompletionStage]] callbacks. + */ + def setLoggerName(name: String): Unit + + /** + * Replace the current logger (or initialize a new logger if the logger was not touched before) with one that + * has ghe given class name as logger name. Logger source MDC entry "akkaSource" will be the actor path. * * *Warning*: This method is not thread-safe and must not be accessed from threads other * than the ordinary actor message processing thread, such as [[scala.concurrent.Future]] callbacks. */ - def setLoggerClass(clazz: Class[_]): Unit + def setLoggerName(clazz: Class[_]): Unit /** * The list of child Actors created by this Actor during its lifetime that @@ -323,4 +332,10 @@ trait ActorContext[T] extends TypedActorContext[T] with ClassicActorContextProvi @InternalApi private[akka] def cancelAllTimers(): Unit + /** + * INTERNAL API + */ + @InternalApi + private[akka] def clearMdc(): Unit + } diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/scaladsl/Behaviors.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/scaladsl/Behaviors.scala index 6cce00e0f4..8639a0ac7d 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/scaladsl/Behaviors.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/scaladsl/Behaviors.scala @@ -252,10 +252,9 @@ object Behaviors { * @param behavior The actual behavior handling the messages, the MDC is used for the log entries logged through * `ActorContext.log` * - * See also [[akka.actor.typed.Logger.withMdc]] */ - def withMdc[T: ClassTag](mdcForMessage: T => Map[String, Any])(behavior: Behavior[T]): Behavior[T] = - withMdc[T](Map.empty[String, Any], mdcForMessage)(behavior) + def withMdc[T: ClassTag](mdcForMessage: T => Map[String, String])(behavior: Behavior[T]): Behavior[T] = + withMdc[T](Map.empty[String, String], mdcForMessage)(behavior) /** * Static MDC (Mapped Diagnostic Context) @@ -268,10 +267,9 @@ object Behaviors { * @param behavior The actual behavior handling the messages, the MDC is used for the log entries logged through * `ActorContext.log` * - * See also [[akka.actor.typed.Logger.withMdc]] */ - def withMdc[T: ClassTag](staticMdc: Map[String, Any])(behavior: Behavior[T]): Behavior[T] = - withMdc[T](staticMdc, (_: T) => Map.empty[String, Any])(behavior) + def withMdc[T: ClassTag](staticMdc: Map[String, String])(behavior: Behavior[T]): Behavior[T] = + withMdc[T](staticMdc, (_: T) => Map.empty[String, String])(behavior) /** * Combination of static and per message MDC (Mapped Diagnostic Context). @@ -292,9 +290,8 @@ object Behaviors { * @param behavior The actual behavior handling the messages, the MDC is used for the log entries logged through * `ActorContext.log` * - * See also [[akka.actor.typed.Logger.withMdc]] */ - def withMdc[T: ClassTag](staticMdc: Map[String, Any], mdcForMessage: T => Map[String, Any])( + def withMdc[T: ClassTag](staticMdc: Map[String, String], mdcForMessage: T => Map[String, String])( behavior: Behavior[T]): Behavior[T] = WithMdcBehaviorInterceptor[T](staticMdc, mdcForMessage, behavior) diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/scaladsl/package.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/scaladsl/package.scala new file mode 100644 index 0000000000..7995583e39 --- /dev/null +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/scaladsl/package.scala @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2019 Lightbend Inc. + */ + +package akka.actor.typed + +import org.slf4j.Logger +import org.slf4j.Marker + +package object scaladsl { + + /** + * Extension methods to [[org.slf4j.Logger]] that are useful because the Scala + * compiler can't select the right overloaded methods for some cases when using + * 2 template arguments and varargs (>= 3 arguments) with primitive types. + * + * Enable these extension methods with: + * + * {{{ + * import akka.actor.typed.scaladsl.LoggerOps + * }}} + * or + * {{{ + *import akka.actor.typed.scaladsl._ + * }}} + * + * @param log the underlying [[org.slf4j.Logger]] + */ + implicit class LoggerOps(val log: Logger) extends AnyVal { + + /** + * Log a message at the TRACE level according to the specified format + * and 2 arguments. + * + * This form avoids superfluous object creation when the logger + * is disabled for the TRACE level. + * + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + def trace2(format: String, arg1: Any, arg2: Any): Unit = + log.trace(format, arg1, arg2) + + /** + * Log marker data and message at the TRACE level according to the specified format + * and 2 arguments. + * + * This form avoids superfluous object creation when the logger + * is disabled for the TRACE level. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + def trace2(marker: Marker, format: String, arg1: Any, arg2: Any): Unit = + log.trace(marker, format, arg1, arg2) + + /** + * Log a message at the TRACE level according to the specified format + * and arguments. + * + * This form avoids superfluous string concatenation when the logger + * is disabled for the TRACE level. However, this variant incurs the hidden + * (and relatively small) cost of creating an `Array[Object]` before invoking the method, + * even if this logger is disabled for TRACE. The `trace` variants taking + * one and `trace2` taking two arguments exist solely in order to avoid this hidden cost. + * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + def traceN(format: String, arguments: Any*): Unit = { + val arr = arguments.toArray + if (arr.isInstanceOf[Array[Object]]) + log.trace(format, arr.asInstanceOf[Array[Object]]: _*) // this seems to always be the case + else + log.trace(format, arr.map(_.asInstanceOf[AnyRef]): _*) + } + + /** + * Log marker data and message at the TRACE level according to the specified format + * and arguments. + * + * This form avoids superfluous string concatenation when the logger + * is disabled for the TRACE level. However, this variant incurs the hidden + * (and relatively small) cost of creating an `Array[Object]` before invoking the method, + * even if this logger is disabled for TRACE. The `trace` variants taking + * one and `trace2` taking two arguments exist solely in order to avoid this hidden cost. + * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + def traceN(marker: Marker, format: String, arguments: Any*): Unit = { + val arr = arguments.toArray + if (arr.isInstanceOf[Array[Object]]) + log.trace(marker, format, arr.asInstanceOf[Array[Object]]: _*) // this seems to always be the case + else + log.trace(marker, format, arr.map(_.asInstanceOf[AnyRef]): _*) + } + + /** + * Log a message at the DEBUG level according to the specified format + * and 2 arguments. + * + * This form avoids superfluous object creation when the logger + * is disabled for the DEBUG level. + * + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + def debug2(format: String, arg1: Any, arg2: Any): Unit = + log.debug(format, arg1, arg2) + + /** + * Log marker data and message at the DEBUG level according to the specified format + * and 2 arguments. + * + * This form avoids superfluous object creation when the logger + * is disabled for the DEBUG level. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + def debug2(marker: Marker, format: String, arg1: Any, arg2: Any): Unit = + log.debug(marker, format, arg1, arg2) + + /** + * Log a message at the DEBUG level according to the specified format + * and arguments. + * + * This form avoids superfluous string concatenation when the logger + * is disabled for the DEBUG level. However, this variant incurs the hidden + * (and relatively small) cost of creating an `Array[Object]` before invoking the method, + * even if this logger is disabled for DEBUG. The `debug` variants taking + * one and `debug2` taking two arguments exist solely in order to avoid this hidden cost. + * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + def debugN(format: String, arguments: Any*): Unit = { + val arr = arguments.toArray + if (arr.isInstanceOf[Array[Object]]) + log.debug(format, arr.asInstanceOf[Array[Object]]: _*) // this seems to always be the case + else + log.debug(format, arr.map(_.asInstanceOf[AnyRef]): _*) + } + + /** + * Log marker data and message at the DEBUG level according to the specified format + * and arguments. + * + * This form avoids superfluous string concatenation when the logger + * is disabled for the DEBUG level. However, this variant incurs the hidden + * (and relatively small) cost of creating an `Array[Object]` before invoking the method, + * even if this logger is disabled for DEBUG. The `debug` variants taking + * one and `debug2` taking two arguments exist solely in order to avoid this hidden cost. + * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + def debugN(marker: Marker, format: String, arguments: Any*): Unit = { + val arr = arguments.toArray + if (arr.isInstanceOf[Array[Object]]) + log.debug(marker, format, arr.asInstanceOf[Array[Object]]: _*) // this seems to always be the case + else + log.debug(marker, format, arr.map(_.asInstanceOf[AnyRef]): _*) + } + + /** + * Log a message at the INFO level according to the specified format + * and 2 arguments. + * + * This form avoids superfluous object creation when the logger + * is disabled for the INFO level. + * + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + def info2(format: String, arg1: Any, arg2: Any): Unit = + log.info(format, arg1, arg2) + + /** + * Log marker data and message at the INFO level according to the specified format + * and 2 arguments. + * + * This form avoids superfluous object creation when the logger + * is disabled for the INFO level. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + def info2(marker: Marker, format: String, arg1: Any, arg2: Any): Unit = + log.info(marker, format, arg1, arg2) + + /** + * Log a message at the INFO level according to the specified format + * and arguments. + * + * This form avoids superfluous string concatenation when the logger + * is disabled for the INFO level. However, this variant incurs the hidden + * (and relatively small) cost of creating an `Array[Object]` before invoking the method, + * even if this logger is disabled for INFO. The `info` variants taking + * one and `info2` taking two arguments exist solely in order to avoid this hidden cost. + * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + def infoN(format: String, arguments: Any*): Unit = { + val arr = arguments.toArray + if (arr.isInstanceOf[Array[Object]]) + log.info(format, arr.asInstanceOf[Array[Object]]: _*) // this seems to always be the case + else + log.info(format, arr.map(_.asInstanceOf[AnyRef]): _*) + } + + /** + * Log marker data and message at the INFO level according to the specified format + * and arguments. + * + * This form avoids superfluous string concatenation when the logger + * is disabled for the INFO level. However, this variant incurs the hidden + * (and relatively small) cost of creating an `Array[Object]` before invoking the method, + * even if this logger is disabled for INFO. The `info` variants taking + * one and `info2` taking two arguments exist solely in order to avoid this hidden cost. + * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + def infoN(marker: Marker, format: String, arguments: Any*): Unit = { + val arr = arguments.toArray + if (arr.isInstanceOf[Array[Object]]) + log.info(marker, format, arr.asInstanceOf[Array[Object]]: _*) // this seems to always be the case + else + log.info(marker, format, arr.map(_.asInstanceOf[AnyRef]): _*) + } + + /** + * Log a message at the WARN level according to the specified format + * and 2 arguments. + * + * This form avoids superfluous object creation when the logger + * is disabled for the WARN level. + * + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + def warn2(format: String, arg1: Any, arg2: Any): Unit = + log.warn(format, arg1, arg2) + + /** + * Log marker data and message at the WARN level according to the specified format + * and 2 arguments. + * + * This form avoids superfluous object creation when the logger + * is disabled for the WARN level. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + def warn2(marker: Marker, format: String, arg1: Any, arg2: Any): Unit = + log.warn(marker, format, arg1, arg2) + + /** + * Log a message at the WARN level according to the specified format + * and arguments. + * + * This form avoids superfluous string concatenation when the logger + * is disabled for the WARN level. However, this variant incurs the hidden + * (and relatively small) cost of creating an `Array[Object]` before invoking the method, + * even if this logger is disabled for WARN. The `warn` variants taking + * one and `warn2` taking two arguments exist solely in order to avoid this hidden cost. + * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + def warnN(format: String, arguments: Any*): Unit = { + val arr = arguments.toArray + if (arr.isInstanceOf[Array[Object]]) + log.warn(format, arr.asInstanceOf[Array[Object]]: _*) // this seems to always be the case + else + log.warn(format, arr.map(_.asInstanceOf[AnyRef]): _*) + } + + /** + * Log marker data and message at the WARN level according to the specified format + * and arguments. + * + * This form avoids superfluous string concatenation when the logger + * is disabled for the WARN level. However, this variant incurs the hidden + * (and relatively small) cost of creating an `Array[Object]` before invoking the method, + * even if this logger is disabled for WARN. The `warn` variants taking + * one and `warn2` taking two arguments exist solely in order to avoid this hidden cost. + * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + def warnN(marker: Marker, format: String, arguments: Any*): Unit = { + val arr = arguments.toArray + if (arr.isInstanceOf[Array[Object]]) + log.warn(marker, format, arr.asInstanceOf[Array[Object]]: _*) // this seems to always be the case + else + log.warn(marker, format, arr.map(_.asInstanceOf[AnyRef]): _*) + } + + /** + * Log a message at the ERROR level according to the specified format + * and 2 arguments. + * + * This form avoids superfluous object creation when the logger + * is disabled for the ERROR level. + * + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + def error2(format: String, arg1: Any, arg2: Any): Unit = + log.error(format, arg1, arg2) + + /** + * Log marker data and message at the ERROR level according to the specified format + * and 2 arguments. + * + * This form avoids superfluous object creation when the logger + * is disabled for the ERROR level. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + def error2(marker: Marker, format: String, arg1: Any, arg2: Any): Unit = + log.error(marker, format, arg1, arg2) + + /** + * Log a message at the ERROR level according to the specified format + * and arguments. + * + * This form avoids superfluous string concatenation when the logger + * is disabled for the ERROR level. However, this variant incurs the hidden + * (and relatively small) cost of creating an `Array[Object]` before invoking the method, + * even if this logger is disabled for ERROR. The `error` variants taking + * one and `error2` taking two arguments exist solely in order to avoid this hidden cost. + * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + def errorN(format: String, arguments: Any*): Unit = { + val arr = arguments.toArray + if (arr.isInstanceOf[Array[Object]]) + log.error(format, arr.asInstanceOf[Array[Object]]: _*) // this seems to always be the case + else + log.error(format, arr.map(_.asInstanceOf[AnyRef]): _*) + } + + /** + * Log marker data and message at the ERROR level according to the specified format + * and arguments. + * + * This form avoids superfluous string concatenation when the logger + * is disabled for the ERROR level. However, this variant incurs the hidden + * (and relatively small) cost of creating an `Array[Object]` before invoking the method, + * even if this logger is disabled for ERROR. The `error` variants taking + * one and `error2` taking two arguments exist solely in order to avoid this hidden cost. + * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + def errorN(marker: Marker, format: String, arguments: Any*): Unit = { + val arr = arguments.toArray + if (arr.isInstanceOf[Array[Object]]) + log.error(marker, format, arr.asInstanceOf[Array[Object]]: _*) // this seems to always be the case + else + log.error(marker, format, arr.map(_.asInstanceOf[AnyRef]): _*) + } + + } + +} diff --git a/akka-actor/src/main/scala/akka/actor/ActorSystem.scala b/akka-actor/src/main/scala/akka/actor/ActorSystem.scala index ce7efc67ea..0374caa8da 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorSystem.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorSystem.scala @@ -20,7 +20,6 @@ import akka.japi.Util.immutableSeq import akka.util.Helpers.toRootLowerCase import akka.util._ import com.typesafe.config.{ Config, ConfigFactory } - import scala.annotation.tailrec import scala.collection.immutable import scala.compat.java8.FutureConverters @@ -30,6 +29,8 @@ import scala.concurrent.{ ExecutionContext, ExecutionContextExecutor, Future, Pr import scala.util.control.{ ControlThrowable, NonFatal } import scala.util.{ Failure, Success, Try } +import akka.event.Logging.DefaultLogger + object BootstrapSetup { /** @@ -323,6 +324,55 @@ object ActorSystem { defaultExecutionContext: Option[ExecutionContext] = None): ActorSystem = apply(name, ActorSystemSetup(BootstrapSetup(classLoader, config, defaultExecutionContext))) + /** + * INTERNAL API + */ + @InternalApi private[akka] object Settings { + + /** + * INTERNAL API + * + * When using Akka Typed the Slf4jLogger should be used by default. + * Looking for config property `akka.use-slf4j` (defined in akka-actor-typed) and + * that `Slf4jLogger` (akka-slf4j) is in classpath. + * Then adds `Slf4jLogger` to configured loggers and removes `DefaultLogger`. + */ + @InternalApi private[akka] def amendSlf4jConfig(config: Config, dynamicAccess: DynamicAccess): Config = { + val slf4jLoggerClassName = "akka.event.slf4j.Slf4jLogger" + val slf4jLoggingFilterClassName = "akka.event.slf4j.Slf4jLoggingFilter" + val loggersConfKey = "akka.loggers" + val loggingFilterConfKey = "akka.logging-filter" + val configuredLoggers = immutableSeq(config.getStringList(loggersConfKey)) + val configuredLoggingFilter = config.getString(loggingFilterConfKey) + + val loggingFilterAlreadyConfigured = + configuredLoggingFilter == slf4jLoggingFilterClassName || configuredLoggingFilter != classOf[ + DefaultLoggingFilter].getName + + def newLoggingFilterConfStr = s"""$loggingFilterConfKey = "$slf4jLoggingFilterClassName"""" + + if (configuredLoggers.contains(slf4jLoggerClassName)) { + // already configured explicitly + if (loggingFilterAlreadyConfigured) + config + else + ConfigFactory.parseString(newLoggingFilterConfStr).withFallback(config) + } else { + val confKey = "akka.use-slf4j" + if (config.hasPath(confKey) && config.getBoolean(confKey) && dynamicAccess.classIsOnClasspath( + slf4jLoggerClassName)) { + val newLoggers = slf4jLoggerClassName +: configuredLoggers.filterNot(_ == classOf[DefaultLogger].getName) + val newLoggersConfStr = s"$loggersConfKey = [${newLoggers.mkString("\"", "\", \"", "\"")}]" + val newConfStr = + if (loggingFilterAlreadyConfigured) newLoggersConfStr + else newLoggersConfStr + "\n" + newLoggingFilterConfStr + ConfigFactory.parseString(newConfStr).withFallback(config) + } else + config + } + } + } + /** * Settings are the overall ActorSystem Settings which also provides a convenient access to the Config object. * @@ -340,14 +390,12 @@ object ActorSystem { * @see The Typesafe Config Library API Documentation */ final val config: Config = { - val config = cfg.withFallback(ConfigFactory.defaultReference(classLoader)) - - config.checkValid( + cfg.checkValid( ConfigFactory .defaultReference(classLoader) .withoutPath(Dispatchers.InternalDispatcherId), // allow this to be both string and config object "akka") - config + cfg } import akka.util.Helpers.ConfigOps @@ -758,7 +806,15 @@ private[akka] class ActorSystemImpl( import ActorSystem._ @volatile private var logDeadLetterListener: Option[ActorRef] = None - final val settings: Settings = new Settings(classLoader, applicationConfig, name, setup) + + private val _dynamicAccess: DynamicAccess = createDynamicAccess() + + final val settings: Settings = { + val config = Settings.amendSlf4jConfig( + applicationConfig.withFallback(ConfigFactory.defaultReference(classLoader)), + _dynamicAccess) + new Settings(classLoader, config, name, setup) + } protected def uncaughtExceptionHandler: Thread.UncaughtExceptionHandler = new Thread.UncaughtExceptionHandler() { @@ -821,8 +877,7 @@ private[akka] class ActorSystemImpl( */ protected def createDynamicAccess(): DynamicAccess = new ReflectiveDynamicAccess(classLoader) - private val _pm: DynamicAccess = createDynamicAccess() - def dynamicAccess: DynamicAccess = _pm + def dynamicAccess: DynamicAccess = _dynamicAccess def logConfiguration(): Unit = log.info(settings.toString) diff --git a/akka-bench-jmh-typed/src/main/resources/logback-test.xml b/akka-bench-jmh-typed/src/main/resources/logback-test.xml new file mode 100644 index 0000000000..10814d5a0c --- /dev/null +++ b/akka-bench-jmh-typed/src/main/resources/logback-test.xml @@ -0,0 +1,18 @@ + + + + + + + + DEBUG + DENY + + + %date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n + + + + + + diff --git a/akka-cluster-sharding-typed/src/multi-jvm/resources/logback-test.xml b/akka-cluster-sharding-typed/src/multi-jvm/resources/logback-test.xml new file mode 100644 index 0000000000..b8aaf237b2 --- /dev/null +++ b/akka-cluster-sharding-typed/src/multi-jvm/resources/logback-test.xml @@ -0,0 +1,15 @@ + + + + + + + + %date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n + + + + + + + diff --git a/akka-cluster-sharding-typed/src/test/java/akka/cluster/sharding/typed/javadsl/ClusterShardingPersistenceTest.java b/akka-cluster-sharding-typed/src/test/java/akka/cluster/sharding/typed/javadsl/ClusterShardingPersistenceTest.java index 6af13e7d8f..8c1f461278 100644 --- a/akka-cluster-sharding-typed/src/test/java/akka/cluster/sharding/typed/javadsl/ClusterShardingPersistenceTest.java +++ b/akka-cluster-sharding-typed/src/test/java/akka/cluster/sharding/typed/javadsl/ClusterShardingPersistenceTest.java @@ -5,6 +5,7 @@ package akka.cluster.sharding.typed.javadsl; import akka.Done; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import akka.actor.typed.ActorRef; @@ -18,6 +19,7 @@ import akka.util.Timeout; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -36,6 +38,8 @@ public class ClusterShardingPersistenceTest extends JUnitSuite { @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(config); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + interface Command {} static class Add implements Command { diff --git a/akka-cluster-sharding-typed/src/test/java/akka/cluster/sharding/typed/javadsl/ShardingEventSourcedEntityWithEnforcedRepliesCompileOnlyTest.java b/akka-cluster-sharding-typed/src/test/java/akka/cluster/sharding/typed/javadsl/ShardingEventSourcedEntityWithEnforcedRepliesCompileOnlyTest.java index bf30d8ba4e..c36c031dc6 100644 --- a/akka-cluster-sharding-typed/src/test/java/akka/cluster/sharding/typed/javadsl/ShardingEventSourcedEntityWithEnforcedRepliesCompileOnlyTest.java +++ b/akka-cluster-sharding-typed/src/test/java/akka/cluster/sharding/typed/javadsl/ShardingEventSourcedEntityWithEnforcedRepliesCompileOnlyTest.java @@ -4,6 +4,7 @@ package akka.cluster.sharding.typed.javadsl; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.typed.ActorRef; import akka.cluster.typed.Cluster; @@ -11,11 +12,14 @@ import akka.cluster.typed.Join; import akka.persistence.typed.ExpectingReply; import akka.persistence.typed.javadsl.*; import org.junit.ClassRule; +import org.junit.Rule; public class ShardingEventSourcedEntityWithEnforcedRepliesCompileOnlyTest { @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + interface Command extends ExpectingReply {} static class Append implements Command { diff --git a/akka-cluster-sharding-typed/src/test/java/jdocs/akka/cluster/sharding/typed/AccountExampleTest.java b/akka-cluster-sharding-typed/src/test/java/jdocs/akka/cluster/sharding/typed/AccountExampleTest.java index b9694e69f5..8522fb1211 100644 --- a/akka-cluster-sharding-typed/src/test/java/jdocs/akka/cluster/sharding/typed/AccountExampleTest.java +++ b/akka-cluster-sharding-typed/src/test/java/jdocs/akka/cluster/sharding/typed/AccountExampleTest.java @@ -4,6 +4,7 @@ package jdocs.akka.cluster.sharding.typed; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import akka.actor.typed.ActorRef; @@ -15,6 +16,7 @@ import akka.cluster.typed.Join; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -40,6 +42,8 @@ public class AccountExampleTest extends JUnitSuite { @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(config); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + private ClusterSharding _sharding = null; private ClusterSharding sharding() { diff --git a/akka-cluster-sharding-typed/src/test/java/jdocs/akka/cluster/sharding/typed/HelloWorldEventSourcedEntityExampleTest.java b/akka-cluster-sharding-typed/src/test/java/jdocs/akka/cluster/sharding/typed/HelloWorldEventSourcedEntityExampleTest.java index c2023d0b43..69b0cf9ee7 100644 --- a/akka-cluster-sharding-typed/src/test/java/jdocs/akka/cluster/sharding/typed/HelloWorldEventSourcedEntityExampleTest.java +++ b/akka-cluster-sharding-typed/src/test/java/jdocs/akka/cluster/sharding/typed/HelloWorldEventSourcedEntityExampleTest.java @@ -4,6 +4,7 @@ package jdocs.akka.cluster.sharding.typed; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import akka.cluster.sharding.typed.javadsl.ClusterSharding; @@ -14,6 +15,7 @@ import akka.cluster.typed.Join; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -33,6 +35,8 @@ public class HelloWorldEventSourcedEntityExampleTest extends JUnitSuite { @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(config); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + private ClusterSharding _sharding = null; private ClusterSharding sharding() { diff --git a/akka-cluster-sharding-typed/src/test/resources/logback-test.xml b/akka-cluster-sharding-typed/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..5e80aa8ee0 --- /dev/null +++ b/akka-cluster-sharding-typed/src/test/resources/logback-test.xml @@ -0,0 +1,36 @@ + + + + + + + + DEBUG + DENY + + + %date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} { "persistenceId": "%X{persistenceId}", "persistencePhase": "%X{persistencePhase}" } - %msg%n + + + + + + + + + + + + + + + + diff --git a/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/ShardingSerializerSpec.scala b/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/ShardingSerializerSpec.scala index 7c530a0506..24ca6c2ef6 100644 --- a/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/ShardingSerializerSpec.scala +++ b/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/ShardingSerializerSpec.scala @@ -8,9 +8,10 @@ import akka.actor.typed.internal.adapter.ActorSystemAdapter import akka.cluster.sharding.typed.internal.ShardingSerializer import akka.serialization.SerializationExtension import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.WordSpecLike -class ShardingSerializerSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class ShardingSerializerSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { "The typed ShardingSerializer" must { diff --git a/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/scaladsl/ClusterShardingPersistenceSpec.scala b/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/scaladsl/ClusterShardingPersistenceSpec.scala index a42eb8a10a..cb3172d731 100644 --- a/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/scaladsl/ClusterShardingPersistenceSpec.scala +++ b/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/scaladsl/ClusterShardingPersistenceSpec.scala @@ -12,9 +12,11 @@ import scala.concurrent.ExecutionContext import scala.concurrent.Future import scala.concurrent.Promise import scala.concurrent.duration._ + import akka.Done import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.ActorRef import akka.actor.typed.Behavior import akka.actor.typed.PostStop @@ -147,7 +149,8 @@ object ClusterShardingPersistenceSpec { class ClusterShardingPersistenceSpec extends ScalaTestWithActorTestKit(ClusterShardingPersistenceSpec.config) - with WordSpecLike { + with WordSpecLike + with LogCapturing { import ClusterShardingPersistenceSpec._ private var _entityId = 0 diff --git a/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/scaladsl/ClusterShardingSpec.scala b/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/scaladsl/ClusterShardingSpec.scala index ecef47b48f..75ed9997ca 100644 --- a/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/scaladsl/ClusterShardingSpec.scala +++ b/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/scaladsl/ClusterShardingSpec.scala @@ -9,10 +9,12 @@ import java.nio.charset.StandardCharsets import scala.concurrent.duration._ import scala.util.Failure import scala.util.Success + import akka.actor.ExtendedActorSystem import akka.actor.testkit.typed.scaladsl.ActorTestKit import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.ActorRef import akka.actor.typed.ActorRefResolver import akka.actor.typed.ActorSystem @@ -179,7 +181,10 @@ object ClusterShardingSpec { } } -class ClusterShardingSpec extends ScalaTestWithActorTestKit(ClusterShardingSpec.config) with WordSpecLike { +class ClusterShardingSpec + extends ScalaTestWithActorTestKit(ClusterShardingSpec.config) + with WordSpecLike + with LogCapturing { import ClusterShardingSpec._ diff --git a/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/scaladsl/ClusterShardingStateSpec.scala b/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/scaladsl/ClusterShardingStateSpec.scala index a855640f10..064916031d 100644 --- a/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/scaladsl/ClusterShardingStateSpec.scala +++ b/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/scaladsl/ClusterShardingStateSpec.scala @@ -4,6 +4,7 @@ package akka.cluster.sharding.typed.scaladsl +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.testkit.typed.scaladsl.{ ScalaTestWithActorTestKit, TestProbe } import akka.actor.typed.ActorRef import akka.cluster.sharding.ShardRegion.{ CurrentShardRegionState, ShardState } @@ -12,7 +13,10 @@ import akka.cluster.sharding.typed.{ GetShardRegionState, ShardingMessageExtract import akka.cluster.typed.{ Cluster, Join } import org.scalatest.WordSpecLike -class ClusterShardingStateSpec extends ScalaTestWithActorTestKit(ClusterShardingSpec.config) with WordSpecLike { +class ClusterShardingStateSpec + extends ScalaTestWithActorTestKit(ClusterShardingSpec.config) + with WordSpecLike + with LogCapturing { val sharding = ClusterSharding(system) diff --git a/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/scaladsl/EntityTypeKeySpec.scala b/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/scaladsl/EntityTypeKeySpec.scala index 9bb42ad569..498372565f 100644 --- a/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/scaladsl/EntityTypeKeySpec.scala +++ b/akka-cluster-sharding-typed/src/test/scala/akka/cluster/sharding/typed/scaladsl/EntityTypeKeySpec.scala @@ -4,11 +4,12 @@ package akka.cluster.sharding.typed.scaladsl +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.persistence.typed.PersistenceId import org.scalatest.Matchers import org.scalatest.WordSpec -class EntityTypeKeySpec extends WordSpec with Matchers { +class EntityTypeKeySpec extends WordSpec with Matchers with LogCapturing { "EntityTypeKey" must { "use | as default entityIdSeparator for compatibility with Lagom's scaladsl" in { diff --git a/akka-cluster-sharding-typed/src/test/scala/docs/akka/cluster/sharding/typed/AccountExampleSpec.scala b/akka-cluster-sharding-typed/src/test/scala/docs/akka/cluster/sharding/typed/AccountExampleSpec.scala index 94da0a0fad..404db0c18e 100644 --- a/akka-cluster-sharding-typed/src/test/scala/docs/akka/cluster/sharding/typed/AccountExampleSpec.scala +++ b/akka-cluster-sharding-typed/src/test/scala/docs/akka/cluster/sharding/typed/AccountExampleSpec.scala @@ -8,6 +8,7 @@ import scala.concurrent.ExecutionContext import scala.concurrent.Future import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.cluster.sharding.typed.scaladsl.ClusterSharding import akka.cluster.sharding.typed.scaladsl.Entity import akka.cluster.typed.Cluster @@ -29,7 +30,10 @@ object AccountExampleSpec { } -class AccountExampleSpec extends ScalaTestWithActorTestKit(AccountExampleSpec.config) with WordSpecLike { +class AccountExampleSpec + extends ScalaTestWithActorTestKit(AccountExampleSpec.config) + with WordSpecLike + with LogCapturing { import AccountExampleWithEventHandlersInState.AccountEntity import AccountExampleWithEventHandlersInState.AccountEntity._ diff --git a/akka-cluster-sharding-typed/src/test/scala/docs/akka/cluster/sharding/typed/HelloWorldEventSourcedEntityExampleSpec.scala b/akka-cluster-sharding-typed/src/test/scala/docs/akka/cluster/sharding/typed/HelloWorldEventSourcedEntityExampleSpec.scala index bac1eadbd3..184075a3c8 100644 --- a/akka-cluster-sharding-typed/src/test/scala/docs/akka/cluster/sharding/typed/HelloWorldEventSourcedEntityExampleSpec.scala +++ b/akka-cluster-sharding-typed/src/test/scala/docs/akka/cluster/sharding/typed/HelloWorldEventSourcedEntityExampleSpec.scala @@ -5,6 +5,7 @@ package docs.akka.cluster.sharding.typed import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.cluster.sharding.typed.scaladsl.ClusterSharding import akka.cluster.sharding.typed.scaladsl.Entity import akka.cluster.typed.Cluster @@ -27,7 +28,8 @@ object HelloWorldEventSourcedEntityExampleSpec { class HelloWorldEventSourcedEntityExampleSpec extends ScalaTestWithActorTestKit(HelloWorldEventSourcedEntityExampleSpec.config) - with WordSpecLike { + with WordSpecLike + with LogCapturing { import HelloWorldPersistentEntityExample.HelloWorld import HelloWorldPersistentEntityExample.HelloWorld._ diff --git a/akka-cluster-typed/src/main/scala/akka/cluster/ddata/typed/scaladsl/DistributedData.scala b/akka-cluster-typed/src/main/scala/akka/cluster/ddata/typed/scaladsl/DistributedData.scala index 838b965e1e..65f023506e 100644 --- a/akka-cluster-typed/src/main/scala/akka/cluster/ddata/typed/scaladsl/DistributedData.scala +++ b/akka-cluster-typed/src/main/scala/akka/cluster/ddata/typed/scaladsl/DistributedData.scala @@ -10,12 +10,14 @@ import akka.actor.typed.{ ActorRef, ActorSystem, Extension, ExtensionId, Props } import akka.actor.ExtendedActorSystem import akka.actor.typed.Behavior import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.LoggerOps import akka.annotation.InternalApi import akka.cluster.Cluster import akka.cluster.ddata.ReplicatedData import akka.cluster.{ ddata => dd } import akka.cluster.ddata.SelfUniqueAddress import akka.util.JavaDurationConverters._ +import org.slf4j.LoggerFactory object DistributedData extends ExtensionId[DistributedData] { def get(system: ActorSystem[_]): DistributedData = apply(system) @@ -85,13 +87,13 @@ class DistributedData(system: ActorSystem[_]) extends Extension { */ val replicator: ActorRef[Replicator.Command] = if (isTerminated) { - val log = system.log.withLoggerClass(getClass) + val log = LoggerFactory.getLogger(getClass) if (Cluster(classicSystem).isTerminated) - log.warning("Replicator points to dead letters, because Cluster is terminated.") + log.warn("Replicator points to dead letters, because Cluster is terminated.") else - log.warning( + log.warn2( "Replicator points to dead letters. Make sure the cluster node has the proper role. " + - "Node has roles [], Distributed Data is configured for roles []", + "Node has roles [{}], Distributed Data is configured for roles [{}].", Cluster(classicSystem).selfRoles.mkString(","), settings.roles.mkString(",")) system.deadLetters diff --git a/akka-cluster-typed/src/main/scala/akka/cluster/typed/internal/receptionist/ClusterReceptionist.scala b/akka-cluster-typed/src/main/scala/akka/cluster/typed/internal/receptionist/ClusterReceptionist.scala index 45bcb391d8..697478ad4e 100644 --- a/akka-cluster-typed/src/main/scala/akka/cluster/typed/internal/receptionist/ClusterReceptionist.scala +++ b/akka-cluster-typed/src/main/scala/akka/cluster/typed/internal/receptionist/ClusterReceptionist.scala @@ -8,7 +8,7 @@ import akka.actor.typed.internal.receptionist.{ AbstractServiceKey, Receptionist import akka.actor.typed.receptionist.Receptionist.Command import akka.actor.typed.receptionist.ServiceKey import akka.actor.typed.scaladsl.adapter._ -import akka.actor.typed.scaladsl.{ ActorContext, Behaviors } +import akka.actor.typed.scaladsl.{ ActorContext, Behaviors, LoggerOps } import akka.actor.typed.{ ActorRef, Behavior, Terminated } import akka.annotation.InternalApi import akka.cluster.ClusterEvent.MemberRemoved @@ -92,7 +92,7 @@ private[typed] object ClusterReceptionist extends ReceptionistBehaviorProvider { override def behavior: Behavior[Command] = Behaviors.setup { ctx => - ctx.setLoggerClass(classOf[ClusterReceptionist]) + ctx.setLoggerName(classOf[ClusterReceptionist]) Behaviors.withTimers { timers => val setup = new Setup(ctx) // include selfUniqueAddress so that it can be used locally before joining cluster @@ -194,7 +194,7 @@ private[typed] object ClusterReceptionist extends ReceptionistBehaviorProvider { if (removals.nonEmpty) { if (ctx.log.isDebugEnabled) - ctx.log.debug( + ctx.log.debugN( "ClusterReceptionist [{}] - Node(s) removed [{}], updating registry removing entries: [{}]", cluster.selfAddress, addresses.mkString(","), @@ -237,7 +237,8 @@ private[typed] object ClusterReceptionist extends ReceptionistBehaviorProvider { case ReceptionistMessages.Register(key, serviceInstance, maybeReplyTo) => if (serviceInstance.path.address.hasLocalScope) { val entry = Entry(serviceInstance, setup.selfSystemUid) - ctx.log.debug("ClusterReceptionist [{}] - Actor was registered: [{}] [{}]", cluster.selfAddress, key, entry) + ctx.log + .debugN("ClusterReceptionist [{}] - Actor was registered: [{}] [{}]", cluster.selfAddress, key, entry) watchWith(ctx, serviceInstance, RegisteredActorTerminated(key, serviceInstance)) maybeReplyTo match { case Some(replyTo) => replyTo ! ReceptionistMessages.Registered(key, serviceInstance) @@ -248,7 +249,7 @@ private[typed] object ClusterReceptionist extends ReceptionistBehaviorProvider { ServiceRegistry(registry).addBinding(key, entry).toORMultiMap } } else { - ctx.log.error(s"ClusterReceptionist [{}] - Register of non-local [{}] is not supported", serviceInstance) + ctx.log.error("ClusterReceptionist [{}] - Register of non-local [{}] is not supported", serviceInstance) } Behaviors.same @@ -277,7 +278,7 @@ private[typed] object ClusterReceptionist extends ReceptionistBehaviorProvider { case RegisteredActorTerminated(key, serviceInstance) => val entry = Entry(serviceInstance, setup.selfSystemUid) - ctx.log.debug( + ctx.log.debugN( "ClusterReceptionist [{}] - Registered actor terminated: [{}] [{}]", cluster.selfAddress, key.asServiceKey.id, @@ -297,7 +298,7 @@ private[typed] object ClusterReceptionist extends ReceptionistBehaviorProvider { val newRegistry = registry.withServiceRegistry(ddataKey, newState) if (changedKeys.nonEmpty) { if (ctx.log.isDebugEnabled) { - ctx.log.debug( + ctx.log.debugN( "ClusterReceptionist [{}] - Change from replicator: [{}], changes: [{}], tombstones [{}]", cluster.selfAddress, newState.entries.entries, @@ -323,7 +324,7 @@ private[typed] object ClusterReceptionist extends ReceptionistBehaviorProvider { val tombstonedButReAdded = newRegistry.actorRefsFor(serviceKey).filter(newRegistry.hasTombstone) if (tombstonedButReAdded.nonEmpty) { if (ctx.log.isDebugEnabled) - ctx.log.debug( + ctx.log.debug2( "ClusterReceptionist [{}] - Saw ActorRefs that were tomstoned [{}], re-removing.", cluster.selfAddress, tombstonedButReAdded.mkString(", ")) @@ -355,7 +356,7 @@ private[typed] object ClusterReceptionist extends ReceptionistBehaviorProvider { } else { // Ok to update from several nodes but more efficient to try to do it from one node. if (isLeader) { - ctx.log.debug( + ctx.log.debug2( "ClusterReceptionist [{}] - Leader node observed removed node [{}]", cluster.selfAddress, uniqueAddress) @@ -369,7 +370,7 @@ private[typed] object ClusterReceptionist extends ReceptionistBehaviorProvider { val keysForNode = registry.keysFor(uniqueAddress) val newRegistry = registry.addUnreachable(uniqueAddress) if (keysForNode.nonEmpty) { - ctx.log.debug( + ctx.log.debug2( "ClusterReceptionist [{}] - Node with registered services unreachable [{}]", cluster.selfAddress, uniqueAddress) @@ -381,7 +382,7 @@ private[typed] object ClusterReceptionist extends ReceptionistBehaviorProvider { val keysForNode = registry.keysFor(uniqueAddress) val newRegistry = registry.removeUnreachable(uniqueAddress) if (keysForNode.nonEmpty) { - ctx.log.debug( + ctx.log.debug2( "ClusterReceptionist [{}] - Node with registered services reachable again [{}]", cluster.selfAddress, uniqueAddress) @@ -398,7 +399,7 @@ private[typed] object ClusterReceptionist extends ReceptionistBehaviorProvider { if (notInCluster.isEmpty) Behaviors.same else { if (ctx.log.isDebugEnabled) - ctx.log.debug( + ctx.log.debug2( "ClusterReceptionist [{}] - Leader node cleanup tick, removed nodes: [{}]", cluster.selfAddress, notInCluster.mkString(",")) diff --git a/akka-cluster-typed/src/multi-jvm/resources/logback-test.xml b/akka-cluster-typed/src/multi-jvm/resources/logback-test.xml new file mode 100644 index 0000000000..b8aaf237b2 --- /dev/null +++ b/akka-cluster-typed/src/multi-jvm/resources/logback-test.xml @@ -0,0 +1,15 @@ + + + + + + + + %date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n + + + + + + + diff --git a/akka-cluster-typed/src/test/java/akka/cluster/ddata/typed/javadsl/ReplicatorTest.java b/akka-cluster-typed/src/test/java/akka/cluster/ddata/typed/javadsl/ReplicatorTest.java index d349d12662..344ddcd62c 100644 --- a/akka-cluster-typed/src/test/java/akka/cluster/ddata/typed/javadsl/ReplicatorTest.java +++ b/akka-cluster-typed/src/test/java/akka/cluster/ddata/typed/javadsl/ReplicatorTest.java @@ -6,12 +6,14 @@ package akka.cluster.ddata.typed.javadsl; // FIXME move to doc package +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import akka.cluster.ddata.*; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -179,6 +181,8 @@ public class ReplicatorTest extends JUnitSuite { @ClassRule public static TestKitJunitResource testKit = new TestKitJunitResource(config); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + @Test public void shouldHaveApiForUpdateAndGet() { TestProbe probe = testKit.createTestProbe(Integer.class); diff --git a/akka-cluster-typed/src/test/resources/logback-test.xml b/akka-cluster-typed/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..f7f657efd7 --- /dev/null +++ b/akka-cluster-typed/src/test/resources/logback-test.xml @@ -0,0 +1,36 @@ + + + + + + + + DEBUG + DENY + + + %date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n + + + + + + + + + + + + + + + + diff --git a/akka-cluster-typed/src/test/scala/akka/cluster/ddata/typed/scaladsl/ReplicatorSpec.scala b/akka-cluster-typed/src/test/scala/akka/cluster/ddata/typed/scaladsl/ReplicatorSpec.scala index 4361d57eff..c0ac375302 100644 --- a/akka-cluster-typed/src/test/scala/akka/cluster/ddata/typed/scaladsl/ReplicatorSpec.scala +++ b/akka-cluster-typed/src/test/scala/akka/cluster/ddata/typed/scaladsl/ReplicatorSpec.scala @@ -185,7 +185,7 @@ object ReplicatorSpec { } -class ReplicatorSpec extends ScalaTestWithActorTestKit(ReplicatorSpec.config) with WordSpecLike { +class ReplicatorSpec extends ScalaTestWithActorTestKit(ReplicatorSpec.config) with WordSpecLike with LogCapturing { import ReplicatorSpec._ diff --git a/akka-cluster-typed/src/test/scala/akka/cluster/typed/ActorSystemSpec.scala b/akka-cluster-typed/src/test/scala/akka/cluster/typed/ActorSystemSpec.scala index 3b7057a569..719ff5c127 100644 --- a/akka-cluster-typed/src/test/scala/akka/cluster/typed/ActorSystemSpec.scala +++ b/akka-cluster-typed/src/test/scala/akka/cluster/typed/ActorSystemSpec.scala @@ -13,6 +13,7 @@ import akka.Done import akka.actor.CoordinatedShutdown import akka.actor.InvalidMessageException import akka.actor.testkit.typed.scaladsl.TestInbox +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.ActorRef import akka.actor.typed.ActorSystem import akka.actor.typed.Behavior @@ -25,7 +26,13 @@ import org.scalatest.concurrent.Eventually import org.scalatest.concurrent.ScalaFutures import org.scalatest.time.Span -class ActorSystemSpec extends WordSpec with Matchers with BeforeAndAfterAll with ScalaFutures with Eventually { +class ActorSystemSpec + extends WordSpec + with Matchers + with BeforeAndAfterAll + with ScalaFutures + with Eventually + with LogCapturing { implicit val patience: PatienceConfig = PatienceConfig(3.seconds, Span(100, org.scalatest.time.Millis)) diff --git a/akka-cluster-typed/src/test/scala/akka/cluster/typed/ClusterApiSpec.scala b/akka-cluster-typed/src/test/scala/akka/cluster/typed/ClusterApiSpec.scala index 79626da331..525798fc9f 100644 --- a/akka-cluster-typed/src/test/scala/akka/cluster/typed/ClusterApiSpec.scala +++ b/akka-cluster-typed/src/test/scala/akka/cluster/typed/ClusterApiSpec.scala @@ -13,6 +13,7 @@ import akka.actor.testkit.typed.TestKitSettings import akka.actor.testkit.typed.scaladsl.ActorTestKit import com.typesafe.config.ConfigFactory import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.WordSpecLike object ClusterApiSpec { @@ -35,7 +36,7 @@ object ClusterApiSpec { """) } -class ClusterApiSpec extends ScalaTestWithActorTestKit(ClusterApiSpec.config) with WordSpecLike { +class ClusterApiSpec extends ScalaTestWithActorTestKit(ClusterApiSpec.config) with WordSpecLike with LogCapturing { val testSettings = TestKitSettings(system) val clusterNode1 = Cluster(system) diff --git a/akka-cluster-typed/src/test/scala/akka/cluster/typed/ClusterSingletonApiSpec.scala b/akka-cluster-typed/src/test/scala/akka/cluster/typed/ClusterSingletonApiSpec.scala index 6412b47ff9..364436ee46 100644 --- a/akka-cluster-typed/src/test/scala/akka/cluster/typed/ClusterSingletonApiSpec.scala +++ b/akka-cluster-typed/src/test/scala/akka/cluster/typed/ClusterSingletonApiSpec.scala @@ -14,10 +14,11 @@ import akka.actor.testkit.typed.scaladsl.TestProbe import akka.actor.typed.{ ActorRef, ActorRefResolver } import akka.serialization.SerializerWithStringManifest import com.typesafe.config.ConfigFactory - import scala.concurrent.Await import scala.concurrent.duration._ + import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.WordSpecLike object ClusterSingletonApiSpec { @@ -85,7 +86,10 @@ object ClusterSingletonApiSpec { } } -class ClusterSingletonApiSpec extends ScalaTestWithActorTestKit(ClusterSingletonApiSpec.config) with WordSpecLike { +class ClusterSingletonApiSpec + extends ScalaTestWithActorTestKit(ClusterSingletonApiSpec.config) + with WordSpecLike + with LogCapturing { import ClusterSingletonApiSpec._ implicit val testSettings = TestKitSettings(system) diff --git a/akka-cluster-typed/src/test/scala/akka/cluster/typed/ClusterSingletonPersistenceSpec.scala b/akka-cluster-typed/src/test/scala/akka/cluster/typed/ClusterSingletonPersistenceSpec.scala index ae0ed90db9..dd32e7e29b 100644 --- a/akka-cluster-typed/src/test/scala/akka/cluster/typed/ClusterSingletonPersistenceSpec.scala +++ b/akka-cluster-typed/src/test/scala/akka/cluster/typed/ClusterSingletonPersistenceSpec.scala @@ -8,6 +8,7 @@ import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.typed.{ ActorRef, Behavior } import akka.persistence.typed.scaladsl.{ Effect, EventSourcedBehavior } import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.persistence.typed.PersistenceId import com.typesafe.config.ConfigFactory import org.scalatest.WordSpecLike @@ -52,7 +53,8 @@ object ClusterSingletonPersistenceSpec { class ClusterSingletonPersistenceSpec extends ScalaTestWithActorTestKit(ClusterSingletonPersistenceSpec.config) - with WordSpecLike { + with WordSpecLike + with LogCapturing { import ClusterSingletonPersistenceSpec._ import akka.actor.typed.scaladsl.adapter._ diff --git a/akka-cluster-typed/src/test/scala/akka/cluster/typed/ClusterSingletonPoisonPillSpec.scala b/akka-cluster-typed/src/test/scala/akka/cluster/typed/ClusterSingletonPoisonPillSpec.scala index 80c9051490..dba81b23ab 100644 --- a/akka-cluster-typed/src/test/scala/akka/cluster/typed/ClusterSingletonPoisonPillSpec.scala +++ b/akka-cluster-typed/src/test/scala/akka/cluster/typed/ClusterSingletonPoisonPillSpec.scala @@ -12,9 +12,10 @@ import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.adapter._ import akka.cluster.typed.ClusterSingletonPoisonPillSpec.GetSelf import org.scalatest.WordSpecLike - import scala.concurrent.duration._ +import akka.actor.testkit.typed.scaladsl.LogCapturing + object ClusterSingletonPoisonPillSpec { final case class GetSelf(replyTo: ActorRef[ActorRef[Any]]) @@ -27,7 +28,8 @@ object ClusterSingletonPoisonPillSpec { class ClusterSingletonPoisonPillSpec extends ScalaTestWithActorTestKit(ClusterSingletonApiSpec.config) - with WordSpecLike { + with WordSpecLike + with LogCapturing { implicit val testSettings = TestKitSettings(system) val clusterNode1 = Cluster(system) diff --git a/akka-cluster-typed/src/test/scala/akka/cluster/typed/RemoteContextAskSpec.scala b/akka-cluster-typed/src/test/scala/akka/cluster/typed/RemoteContextAskSpec.scala index 21cebbe67f..4f89475661 100644 --- a/akka-cluster-typed/src/test/scala/akka/cluster/typed/RemoteContextAskSpec.scala +++ b/akka-cluster-typed/src/test/scala/akka/cluster/typed/RemoteContextAskSpec.scala @@ -16,10 +16,11 @@ import akka.actor.testkit.typed.scaladsl.TestProbe import akka.actor.typed.scaladsl.adapter._ import akka.util.Timeout import com.typesafe.config.ConfigFactory - import scala.concurrent.duration._ import scala.util.{ Failure, Success } + import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.WordSpecLike class RemoteContextAskSpecSerializer(system: ExtendedActorSystem) extends SerializerWithStringManifest { @@ -83,7 +84,10 @@ object RemoteContextAskSpec { } -class RemoteContextAskSpec extends ScalaTestWithActorTestKit(RemoteContextAskSpec.config) with WordSpecLike { +class RemoteContextAskSpec + extends ScalaTestWithActorTestKit(RemoteContextAskSpec.config) + with WordSpecLike + with LogCapturing { import RemoteContextAskSpec._ diff --git a/akka-cluster-typed/src/test/scala/akka/cluster/typed/RemoteDeployNotAllowedSpec.scala b/akka-cluster-typed/src/test/scala/akka/cluster/typed/RemoteDeployNotAllowedSpec.scala index df5f7bfaea..004fff9d32 100644 --- a/akka-cluster-typed/src/test/scala/akka/cluster/typed/RemoteDeployNotAllowedSpec.scala +++ b/akka-cluster-typed/src/test/scala/akka/cluster/typed/RemoteDeployNotAllowedSpec.scala @@ -8,10 +8,11 @@ import akka.actor.typed.ActorSystem import akka.actor.typed.scaladsl.Behaviors import akka.actor.testkit.typed.scaladsl.TestProbe import com.typesafe.config.ConfigFactory - import scala.concurrent.duration._ + import akka.actor.testkit.typed.scaladsl.ActorTestKit import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.WordSpecLike object RemoteDeployNotAllowedSpec { @@ -44,7 +45,8 @@ object RemoteDeployNotAllowedSpec { class RemoteDeployNotAllowedSpec extends ScalaTestWithActorTestKit(RemoteDeployNotAllowedSpec.config) - with WordSpecLike { + with WordSpecLike + with LogCapturing { "Typed cluster" must { diff --git a/akka-cluster-typed/src/test/scala/akka/cluster/typed/internal/AkkaClusterTypedSerializerSpec.scala b/akka-cluster-typed/src/test/scala/akka/cluster/typed/internal/AkkaClusterTypedSerializerSpec.scala index 8a29ed6417..5f9209dfda 100644 --- a/akka-cluster-typed/src/test/scala/akka/cluster/typed/internal/AkkaClusterTypedSerializerSpec.scala +++ b/akka-cluster-typed/src/test/scala/akka/cluster/typed/internal/AkkaClusterTypedSerializerSpec.scala @@ -9,10 +9,11 @@ import akka.actor.typed.scaladsl.adapter._ import akka.cluster.typed.internal.receptionist.ClusterReceptionist import akka.serialization.SerializationExtension import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.scaladsl.Behaviors import org.scalatest.WordSpecLike -class AkkaClusterTypedSerializerSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class AkkaClusterTypedSerializerSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { val ref = spawn(Behaviors.empty[String]) val classicSystem = system.toClassic diff --git a/akka-cluster-typed/src/test/scala/akka/cluster/typed/internal/receptionist/ClusterReceptionistSpec.scala b/akka-cluster-typed/src/test/scala/akka/cluster/typed/internal/receptionist/ClusterReceptionistSpec.scala index 6758bd6679..299e1a03b1 100644 --- a/akka-cluster-typed/src/test/scala/akka/cluster/typed/internal/receptionist/ClusterReceptionistSpec.scala +++ b/akka-cluster-typed/src/test/scala/akka/cluster/typed/internal/receptionist/ClusterReceptionistSpec.scala @@ -21,6 +21,7 @@ import org.scalatest.{ Matchers, WordSpec } import scala.concurrent.Await import scala.concurrent.duration._ +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.cluster.typed.Down import akka.cluster.typed.JoinSeedNodes import akka.cluster.typed.Leave @@ -99,7 +100,7 @@ object ClusterReceptionistSpec { val PingKey = ServiceKey[PingProtocol]("pingy") } -class ClusterReceptionistSpec extends WordSpec with Matchers { +class ClusterReceptionistSpec extends WordSpec with Matchers with LogCapturing { import ClusterReceptionistSpec._ import Receptionist._ diff --git a/akka-cluster-typed/src/test/scala/docs/akka/cluster/typed/BasicClusterExampleSpec.scala b/akka-cluster-typed/src/test/scala/docs/akka/cluster/typed/BasicClusterExampleSpec.scala index fe6029d1cc..5401de1af9 100644 --- a/akka-cluster-typed/src/test/scala/docs/akka/cluster/typed/BasicClusterExampleSpec.scala +++ b/akka-cluster-typed/src/test/scala/docs/akka/cluster/typed/BasicClusterExampleSpec.scala @@ -4,6 +4,7 @@ package docs.akka.cluster.typed +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.testkit.SocketUtil import com.typesafe.config.ConfigFactory import org.scalatest.{ Matchers, WordSpec } @@ -49,7 +50,7 @@ akka { """).withFallback(configSystem1) } -class BasicClusterConfigSpec extends WordSpec with ScalaFutures with Eventually with Matchers { +class BasicClusterConfigSpec extends WordSpec with ScalaFutures with Eventually with Matchers with LogCapturing { import BasicClusterExampleSpec._ implicit override val patienceConfig = @@ -103,7 +104,7 @@ akka { } -class BasicClusterManualSpec extends WordSpec with ScalaFutures with Eventually with Matchers { +class BasicClusterManualSpec extends WordSpec with ScalaFutures with Eventually with Matchers with LogCapturing { import BasicClusterManualSpec._ diff --git a/akka-distributed-data/src/main/scala/akka/cluster/ddata/DistributedData.scala b/akka-distributed-data/src/main/scala/akka/cluster/ddata/DistributedData.scala index ac381f66b1..d69d79d727 100644 --- a/akka-distributed-data/src/main/scala/akka/cluster/ddata/DistributedData.scala +++ b/akka-distributed-data/src/main/scala/akka/cluster/ddata/DistributedData.scala @@ -44,9 +44,9 @@ class DistributedData(system: ExtendedActorSystem) extends Extension { else log.warning( "Replicator points to dead letters. Make sure the cluster node has the proper role. " + - "Node has roles [], Distributed Data is configured for roles []", + "Node has roles [{}], Distributed Data is configured for roles [{}].", Cluster(system).selfRoles.mkString(","), - settings.roles.mkString(",")) + settings.roles.mkString(","): Any) system.deadLetters } else { system.systemActorOf(Replicator.props(settings), ReplicatorSettings.name(system, None)) diff --git a/akka-docs/src/main/paradox/project/migration-guide-2.5.x-2.6.x.md b/akka-docs/src/main/paradox/project/migration-guide-2.5.x-2.6.x.md index fea8fc2ae6..8e31d180f2 100644 --- a/akka-docs/src/main/paradox/project/migration-guide-2.5.x-2.6.x.md +++ b/akka-docs/src/main/paradox/project/migration-guide-2.5.x-2.6.x.md @@ -476,12 +476,17 @@ made before finalizing the APIs. Compared to Akka 2.5.x the source incompatible * To align with the Akka Typed style guide `SpawnProtocol` is now created through @scala[`SpawnProtocol()`]@java[`SpawnProtocol.create()`], the special `Spawn` message factories has been removed and the top level of the actor protocol is now `SpawnProtocol.Command` * `Future` removed from `ActorSystem.systemActorOf`. +* `toUntyped` has been renamed to `toClassic`. +* Akka Typed is now using SLF4J as the logging API. @scala[`ActorContext.log`]@java[`ActorContext.getLog`] returns + an `org.slf4j.Logger`. MDC has been changed to only support `String` values. +* `setLoggerClass` in `ActorContext` has been renamed to `setLoggerName`. #### Akka Typed Stream API changes * `ActorSource.actorRef` relying on `PartialFunction` has been replaced in the Java API with a variant more suitable to be called by Java. -* `toUntyped` has been renamed to `toClassic`. - +* Factories for creating a materializer from an `akka.actor.typed.ActorSystem` have been removed. + A stream can be run with an `akka.actor.typed.ActorSystem` @scala[in implicit scope]@java[parameter] + and therefore the need for creating a materializer has been reduced. ## Akka Stream changes diff --git a/akka-docs/src/test/java/jdocs/typed/tutorial_4/DeviceGroup.java b/akka-docs/src/test/java/jdocs/typed/tutorial_4/DeviceGroup.java index 2170a6cb47..db2ceed759 100644 --- a/akka-docs/src/test/java/jdocs/typed/tutorial_4/DeviceGroup.java +++ b/akka-docs/src/test/java/jdocs/typed/tutorial_4/DeviceGroup.java @@ -69,7 +69,7 @@ public class DeviceGroup extends AbstractBehavior { } else { context .getLog() - .warning( + .warn( "Ignoring TrackDevice request for {}. This actor is responsible for {}.", groupId, this.groupId); diff --git a/akka-docs/src/test/java/jdocs/typed/tutorial_5/DeviceGroup.java b/akka-docs/src/test/java/jdocs/typed/tutorial_5/DeviceGroup.java index e3b699306b..ae1be3dfa3 100644 --- a/akka-docs/src/test/java/jdocs/typed/tutorial_5/DeviceGroup.java +++ b/akka-docs/src/test/java/jdocs/typed/tutorial_5/DeviceGroup.java @@ -65,7 +65,7 @@ public class DeviceGroup extends AbstractBehavior { } else { context .getLog() - .warning( + .warn( "Ignoring TrackDevice request for {}. This actor is responsible for {}.", groupId, this.groupId); diff --git a/akka-docs/src/test/resources/logback-test.xml b/akka-docs/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..1982b856a3 --- /dev/null +++ b/akka-docs/src/test/resources/logback-test.xml @@ -0,0 +1,19 @@ + + + + + + + + DEBUG + DENY + + + %date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n + + + + + + + diff --git a/akka-docs/src/test/scala/typed/tutorial_3/Device.scala b/akka-docs/src/test/scala/typed/tutorial_3/Device.scala index 245b828920..6a0b25c2dd 100644 --- a/akka-docs/src/test/scala/typed/tutorial_3/Device.scala +++ b/akka-docs/src/test/scala/typed/tutorial_3/Device.scala @@ -12,6 +12,7 @@ import akka.actor.typed.Signal import akka.actor.typed.scaladsl.AbstractBehavior import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.LoggerOps object Device { def apply(groupId: String, deviceId: String): Behavior[Command] = @@ -35,12 +36,12 @@ class Device(context: ActorContext[Device.Command], groupId: String, deviceId: S var lastTemperatureReading: Option[Double] = None - context.log.info("Device actor {}-{} started", groupId, deviceId) + context.log.info2("Device actor {}-{} started", groupId, deviceId) override def onMessage(msg: Command): Behavior[Command] = { msg match { case RecordTemperature(id, value, replyTo) => - context.log.info("Recorded temperature reading {} with {}", value, id) + context.log.info2("Recorded temperature reading {} with {}", value, id) lastTemperatureReading = Some(value) replyTo ! TemperatureRecorded(id) this @@ -53,7 +54,7 @@ class Device(context: ActorContext[Device.Command], groupId: String, deviceId: S override def onSignal: PartialFunction[Signal, Behavior[Command]] = { case PostStop => - context.log.info("Device actor {}-{} stopped", groupId, deviceId) + context.log.info2("Device actor {}-{} stopped", groupId, deviceId) this } diff --git a/akka-docs/src/test/scala/typed/tutorial_3/DeviceInProgress.scala b/akka-docs/src/test/scala/typed/tutorial_3/DeviceInProgress.scala index d41e6ea2de..629d753de3 100644 --- a/akka-docs/src/test/scala/typed/tutorial_3/DeviceInProgress.scala +++ b/akka-docs/src/test/scala/typed/tutorial_3/DeviceInProgress.scala @@ -4,9 +4,9 @@ package typed.tutorial_3 -import akka.actor.typed.ActorRef import akka.actor.typed.PostStop import akka.actor.typed.Signal +import akka.actor.typed.scaladsl.LoggerOps object DeviceInProgress1 { @@ -48,7 +48,7 @@ object DeviceInProgress2 { var lastTemperatureReading: Option[Double] = None - context.log.info("Device actor {}-{} started", groupId, deviceId) + context.log.info2("Device actor {}-{} started", groupId, deviceId) override def onMessage(msg: Command): Behavior[Command] = { msg match { @@ -60,7 +60,7 @@ object DeviceInProgress2 { override def onSignal: PartialFunction[Signal, Behavior[Command]] = { case PostStop => - context.log.info("Device actor {}-{} stopped", groupId, deviceId) + context.log.info2("Device actor {}-{} stopped", groupId, deviceId) this } diff --git a/akka-docs/src/test/scala/typed/tutorial_4/Device.scala b/akka-docs/src/test/scala/typed/tutorial_4/Device.scala index 3c3bdefd6e..197a68090c 100644 --- a/akka-docs/src/test/scala/typed/tutorial_4/Device.scala +++ b/akka-docs/src/test/scala/typed/tutorial_4/Device.scala @@ -12,6 +12,7 @@ import akka.actor.typed.Signal import akka.actor.typed.scaladsl.AbstractBehavior import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.LoggerOps object Device { def apply(groupId: String, deviceId: String): Behavior[Command] = @@ -37,12 +38,12 @@ class Device(context: ActorContext[Device.Command], groupId: String, deviceId: S var lastTemperatureReading: Option[Double] = None - context.log.info("Device actor {}-{} started", groupId, deviceId) + context.log.info2("Device actor {}-{} started", groupId, deviceId) override def onMessage(msg: Command): Behavior[Command] = { msg match { case RecordTemperature(id, value, replyTo) => - context.log.info("Recorded temperature reading {} with {}", value, id) + context.log.info2("Recorded temperature reading {} with {}", value, id) lastTemperatureReading = Some(value) replyTo ! TemperatureRecorded(id) this @@ -58,7 +59,7 @@ class Device(context: ActorContext[Device.Command], groupId: String, deviceId: S override def onSignal: PartialFunction[Signal, Behavior[Command]] = { case PostStop => - context.log.info("Device actor {}-{} stopped", groupId, deviceId) + context.log.info2("Device actor {}-{} stopped", groupId, deviceId) this } diff --git a/akka-docs/src/test/scala/typed/tutorial_4/DeviceGroup.scala b/akka-docs/src/test/scala/typed/tutorial_4/DeviceGroup.scala index 29b2c87d3d..4ab9ed8141 100644 --- a/akka-docs/src/test/scala/typed/tutorial_4/DeviceGroup.scala +++ b/akka-docs/src/test/scala/typed/tutorial_4/DeviceGroup.scala @@ -11,6 +11,7 @@ import akka.actor.typed.Signal import akka.actor.typed.scaladsl.AbstractBehavior import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.LoggerOps //#device-group-full //#device-group-register @@ -55,7 +56,7 @@ class DeviceGroup(context: ActorContext[DeviceGroup.Command], groupId: String) this case RequestTrackDevice(gId, _, _) => - context.log.warning("Ignoring TrackDevice request for {}. This actor is responsible for {}.", gId, groupId) + context.log.warn2("Ignoring TrackDevice request for {}. This actor is responsible for {}.", gId, groupId) this //#device-group-register //#device-group-remove diff --git a/akka-docs/src/test/scala/typed/tutorial_5/Device.scala b/akka-docs/src/test/scala/typed/tutorial_5/Device.scala index 7c5439a040..52a690f3bc 100644 --- a/akka-docs/src/test/scala/typed/tutorial_5/Device.scala +++ b/akka-docs/src/test/scala/typed/tutorial_5/Device.scala @@ -12,6 +12,7 @@ import akka.actor.typed.Signal import akka.actor.typed.scaladsl.AbstractBehavior import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.LoggerOps object Device { def apply(groupId: String, deviceId: String): Behavior[Command] = @@ -35,12 +36,12 @@ class Device(context: ActorContext[Device.Command], groupId: String, deviceId: S var lastTemperatureReading: Option[Double] = None - context.log.info("Device actor {}-{} started", groupId, deviceId) + context.log.info2("Device actor {}-{} started", groupId, deviceId) override def onMessage(msg: Command): Behavior[Command] = { msg match { case RecordTemperature(id, value, replyTo) => - context.log.info("Recorded temperature reading {} with {}", value, id) + context.log.info2("Recorded temperature reading {} with {}", value, id) lastTemperatureReading = Some(value) replyTo ! TemperatureRecorded(id) this @@ -56,7 +57,7 @@ class Device(context: ActorContext[Device.Command], groupId: String, deviceId: S override def onSignal: PartialFunction[Signal, Behavior[Command]] = { case PostStop => - context.log.info("Device actor {}-{} stopped", groupId, deviceId) + context.log.info2("Device actor {}-{} stopped", groupId, deviceId) this } diff --git a/akka-docs/src/test/scala/typed/tutorial_5/DeviceGroup.scala b/akka-docs/src/test/scala/typed/tutorial_5/DeviceGroup.scala index 334dc552c9..e226eea063 100644 --- a/akka-docs/src/test/scala/typed/tutorial_5/DeviceGroup.scala +++ b/akka-docs/src/test/scala/typed/tutorial_5/DeviceGroup.scala @@ -12,6 +12,7 @@ import akka.actor.typed.Signal import akka.actor.typed.scaladsl.AbstractBehavior import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.LoggerOps object DeviceGroup { def apply(groupId: String): Behavior[Command] = @@ -59,7 +60,7 @@ class DeviceGroup(context: ActorContext[DeviceGroup.Command], groupId: String) this case RequestTrackDevice(gId, _, _) => - context.log.warning("Ignoring TrackDevice request for {}. This actor is responsible for {}.", gId, groupId) + context.log.warn2("Ignoring TrackDevice request for {}. This actor is responsible for {}.", gId, groupId) this case RequestDeviceList(requestId, gId, replyTo) => diff --git a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/BehaviorSetup.scala b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/BehaviorSetup.scala index 6e7b3ad9dd..bc148c7928 100644 --- a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/BehaviorSetup.scala +++ b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/BehaviorSetup.scala @@ -6,8 +6,8 @@ package akka.persistence.typed.internal import scala.concurrent.ExecutionContext import scala.util.control.NonFatal + import akka.actor.Cancellable -import akka.actor.typed.Logger import akka.actor.typed.scaladsl.ActorContext import akka.actor.ActorRef import akka.actor.typed.Signal @@ -18,6 +18,8 @@ import akka.persistence.typed.scaladsl.EventSourcedBehavior import akka.persistence.typed.scaladsl.RetentionCriteria import akka.util.ConstantFun import akka.util.OptionVal +import org.slf4j.Logger +import org.slf4j.MDC /** * INTERNAL API @@ -62,28 +64,16 @@ private[akka] final class BehaviorSetup[C, E, S]( def selfClassic: ActorRef = context.self.toClassic - private var mdc: Map[String, Any] = Map.empty - private var _log: OptionVal[Logger] = OptionVal.Some(context.log) // changed when mdc is changed + private var mdcPhase = PersistenceMdc.Initializing def log: Logger = { - _log match { - case OptionVal.Some(l) => l - case OptionVal.None => - // lazy init if mdc changed - val l = context.log.withMdc(mdc) - _log = OptionVal.Some(l) - l - } + // MDC is cleared (if used) from aroundReceive in ActorAdapter after processing each message, + // but important to call `context.log` to mark MDC as used + PersistenceMdc.setMdc(persistenceId, mdcPhase) + context.log } - def setMdc(newMdc: Map[String, Any]): BehaviorSetup[C, E, S] = { - mdc = newMdc - // mdc is changed often, for each persisted event, but logging is rare, so lazy init of Logger - _log = OptionVal.None - this - } - - def setMdc(phaseName: String): BehaviorSetup[C, E, S] = { - setMdc(MDC.create(persistenceId, phaseName)) + def setMdcPhase(phaseName: String): BehaviorSetup[C, E, S] = { + mdcPhase = phaseName this } @@ -121,9 +111,10 @@ private[akka] final class BehaviorSetup[C, E, S]( } catch { case NonFatal(ex) => if (catchAndLog) - log.error(ex, s"Error while processing signal [{}]", signal) + log.error(s"Error while processing signal [$signal]: $ex", ex) else { - log.debug(s"Error while processing signal [{}]: {}", signal, ex) + if (log.isDebugEnabled) + log.debug(s"Error while processing signal [$signal]: $ex", ex) throw ex } } @@ -147,17 +138,26 @@ private[akka] final class BehaviorSetup[C, E, S]( * INTERNAL API */ @InternalApi -private[akka] object MDC { +private[akka] object PersistenceMdc { // format: OFF + val Initializing = "initializing" val AwaitingPermit = "get-permit" - val ReplayingSnapshot = "replay-snap" - val ReplayingEvents = "replay-evts" - val RunningCmds = "running-cmnds" - val PersistingEvents = "persist-evts" - val StoringSnapshot = "storing-snapshot" + val ReplayingSnapshot = "load-snap" + val ReplayingEvents = "replay-evt" + val RunningCmds = "running-cmd" + val PersistingEvents = "persist-evt" + val StoringSnapshot = "storing-snap" // format: ON - def create(persistenceId: PersistenceId, phaseName: String): Map[String, Any] = { - Map("persistenceId" -> persistenceId.id, "phase" -> phaseName) + val PersistencePhaseKey = "persistencePhase" + val PersistenceIdKey = "persistenceId" + + // MDC is cleared (if used) from aroundReceive in ActorAdapter after processing each message, + // but important to call `context.log` to mark MDC as used + def setMdc(persistenceId: PersistenceId, phase: String): Unit = { + val mdcAdpater = MDC.getMDCAdapter + mdcAdpater.put(PersistenceIdKey, persistenceId.id) + mdcAdpater.put(PersistencePhaseKey, phase) } + } diff --git a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/EventSourcedBehaviorImpl.scala b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/EventSourcedBehaviorImpl.scala index beff3063ad..49316f7e33 100644 --- a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/EventSourcedBehaviorImpl.scala +++ b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/EventSourcedBehaviorImpl.scala @@ -16,6 +16,7 @@ import akka.actor.typed.Signal import akka.actor.typed.SupervisorStrategy import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.LoggerOps import akka.annotation._ import akka.persistence.JournalProtocol import akka.persistence.Recovery @@ -82,7 +83,7 @@ private[akka] final case class EventSourcedBehaviorImpl[Command, Event, State]( override def apply(context: typed.TypedActorContext[Command]): Behavior[Command] = { val ctx = context.asScala - ctx.setLoggerClass(loggerClass) + ctx.setLoggerName(loggerClass) val settings = EventSourcedSettings(ctx.system, journalPluginId.getOrElse(""), snapshotPluginId.getOrElse("")) // stashState outside supervise because StashState should survive restarts due to persist failures @@ -93,19 +94,19 @@ private[akka] final case class EventSourcedBehaviorImpl[Command, Event, State]( case (_, SnapshotCompleted(meta)) => ctx.log.debug("Save snapshot successful, snapshot metadata [{}].", meta) case (_, SnapshotFailed(meta, failure)) => - ctx.log.error(failure, "Save snapshot failed, snapshot metadata [{}].", meta) + ctx.log.error(s"Save snapshot failed, snapshot metadata [$meta] due to: ${failure.getMessage}", failure) case (_, DeleteSnapshotsCompleted(DeletionTarget.Individual(meta))) => ctx.log.debug("Persistent snapshot [{}] deleted successfully.", meta) case (_, DeleteSnapshotsCompleted(DeletionTarget.Criteria(criteria))) => ctx.log.debug("Persistent snapshots given criteria [{}] deleted successfully.", criteria) case (_, DeleteSnapshotsFailed(DeletionTarget.Individual(meta), failure)) => - ctx.log.warning("Failed to delete snapshot with meta [{}] due to [{}].", meta, failure) + ctx.log.warn2("Failed to delete snapshot with meta [{}] due to: {}", meta, failure.getMessage) case (_, DeleteSnapshotsFailed(DeletionTarget.Criteria(criteria), failure)) => - ctx.log.warning("Failed to delete snapshots given criteria [{}] due to [{}].", criteria, failure) + ctx.log.warn2("Failed to delete snapshots given criteria [{}] due to: {}", criteria, failure.getMessage) case (_, DeleteEventsCompleted(toSequenceNr)) => ctx.log.debug("Events successfully deleted to sequence number [{}].", toSequenceNr) case (_, DeleteEventsFailed(toSequenceNr, failure)) => - ctx.log.warning("Failed to delete events to sequence number [{}] due to [{}].", toSequenceNr, failure) + ctx.log.warn2("Failed to delete events to sequence number [{}] due to: {}", toSequenceNr, failure.getMessage) } // do this once, even if the actor is restarted diff --git a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/ExternalInteractions.scala b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/ExternalInteractions.scala index 59c98cef9e..755a4d5786 100644 --- a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/ExternalInteractions.scala +++ b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/ExternalInteractions.scala @@ -13,6 +13,7 @@ import akka.actor.typed.PreRestart import akka.actor.typed.Signal import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.LoggerOps import akka.annotation.InternalApi import akka.annotation.InternalStableApi @@ -100,7 +101,7 @@ private[akka] trait JournalInteractions[C, E, S] { @unused repr: immutable.Seq[PersistentRepr]): Unit = () protected def replayEvents(fromSeqNr: Long, toSeqNr: Long): Unit = { - setup.log.debug("Replaying messages: from: {}, to: {}", fromSeqNr, toSeqNr) + setup.log.debug2("Replaying messages: from: {}, to: {}", fromSeqNr, toSeqNr) setup.journal ! ReplayMessages( fromSeqNr, toSeqNr, @@ -182,7 +183,7 @@ private[akka] trait SnapshotInteractions[C, E, S] { protected def internalDeleteSnapshots(fromSequenceNr: Long, toSequenceNr: Long): Unit = { if (toSequenceNr > 0) { val snapshotCriteria = SnapshotSelectionCriteria(minSequenceNr = fromSequenceNr, maxSequenceNr = toSequenceNr) - setup.log.debug("Deleting snapshots from sequenceNr [{}] to [{}]", fromSequenceNr, toSequenceNr) + setup.log.debug2("Deleting snapshots from sequenceNr [{}] to [{}]", fromSequenceNr, toSequenceNr) setup.snapshotStore .tell(SnapshotProtocol.DeleteSnapshots(setup.persistenceId.id, snapshotCriteria), setup.selfClassic) } diff --git a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/ReplayingEvents.scala b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/ReplayingEvents.scala index f2351bad11..d91afde722 100644 --- a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/ReplayingEvents.scala +++ b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/ReplayingEvents.scala @@ -10,7 +10,7 @@ import scala.concurrent.duration._ import akka.actor.typed.{ Behavior, Signal } import akka.actor.typed.internal.PoisonPill import akka.actor.typed.internal.UnstashException -import akka.actor.typed.scaladsl.{ AbstractBehavior, ActorContext, Behaviors } +import akka.actor.typed.scaladsl.{ AbstractBehavior, ActorContext, Behaviors, LoggerOps } import akka.annotation.{ InternalApi, InternalStableApi } import akka.event.Logging import akka.persistence.JournalProtocol._ @@ -56,7 +56,7 @@ private[akka] object ReplayingEvents { Behaviors.setup { _ => // protect against event recovery stalling forever because of journal overloaded and such setup.startRecoveryTimer(snapshot = false) - new ReplayingEvents[C, E, S](setup.setMdc(MDC.ReplayingEvents), state) + new ReplayingEvents[C, E, S](setup.setMdcPhase(PersistenceMdc.ReplayingEvents), state) } } @@ -181,7 +181,7 @@ private[akka] final class ReplayingEvents[C, E, S]( def onSnapshotterResponse(response: SnapshotProtocol.Response): Behavior[InternalProtocol] = { setup.log - .warning("Unexpected [{}] from SnapshotStore, already in replaying events state.", Logging.simpleName(response)) + .warn("Unexpected [{}] from SnapshotStore, already in replaying events state.", Logging.simpleName(response)) Behaviors.unhandled // ignore the response } @@ -199,7 +199,7 @@ private[akka] final class ReplayingEvents[C, E, S]( setup.cancelRecoveryTimer() tryReturnRecoveryPermit("on replay failure: " + cause.getMessage) if (setup.log.isDebugEnabled) { - setup.log.debug( + setup.log.debug2( "Recovery failure for persistenceId [{}] after {}", setup.persistenceId, (System.nanoTime() - state.recoveryStartTime).nanos.pretty) @@ -209,10 +209,10 @@ private[akka] final class ReplayingEvents[C, E, S]( val msg = event match { case Some(_: Message) | None => s"Exception during recovery. Last known sequence number [$sequenceNr]. " + - s"PersistenceId [${setup.persistenceId.id}]. ${cause.getMessage}" + s"PersistenceId [${setup.persistenceId.id}], due to: ${cause.getMessage}" case Some(evt) => s"Exception during recovery while handling [${evt.getClass.getName}] with sequence number [$sequenceNr]. " + - s"PersistenceId [${setup.persistenceId.id}]. ${cause.getMessage}" + s"PersistenceId [${setup.persistenceId.id}], due to: ${cause.getMessage}" } throw new JournalFailureException(msg, cause) @@ -223,7 +223,7 @@ private[akka] final class ReplayingEvents[C, E, S]( onRecoveryComplete(setup.context) tryReturnRecoveryPermit("replay completed successfully") if (setup.log.isDebugEnabled) { - setup.log.debug( + setup.log.debug2( "Recovery for persistenceId [{}] took {}", setup.persistenceId, (System.nanoTime() - state.recoveryStartTime).nanos.pretty) diff --git a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/ReplayingSnapshot.scala b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/ReplayingSnapshot.scala index fd56dbca91..c790efe587 100644 --- a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/ReplayingSnapshot.scala +++ b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/ReplayingSnapshot.scala @@ -32,7 +32,7 @@ import akka.util.unused private[akka] object ReplayingSnapshot { def apply[C, E, S](setup: BehaviorSetup[C, E, S], receivedPoisonPill: Boolean): Behavior[InternalProtocol] = - new ReplayingSnapshot(setup.setMdc(MDC.ReplayingSnapshot)).createBehavior(receivedPoisonPill) + new ReplayingSnapshot(setup.setMdcPhase(PersistenceMdc.ReplayingSnapshot)).createBehavior(receivedPoisonPill) } @@ -86,7 +86,7 @@ private[akka] class ReplayingSnapshot[C, E, S](override val setup: BehaviorSetup private def onRecoveryFailure(cause: Throwable): Behavior[InternalProtocol] = { onRecoveryFailed(setup.context, cause) setup.cancelRecoveryTimer() - setup.log.error(cause, s"Persistence failure when replaying snapshot. ${cause.getMessage}") + setup.log.error(s"Persistence failure when replaying snapshot, due to: ${cause.getMessage}", cause) Behaviors.stopped } diff --git a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/RequestingRecoveryPermit.scala b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/RequestingRecoveryPermit.scala index 8b874dd041..7de1ff24ce 100644 --- a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/RequestingRecoveryPermit.scala +++ b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/RequestingRecoveryPermit.scala @@ -23,7 +23,7 @@ import akka.annotation.InternalApi private[akka] object RequestingRecoveryPermit { def apply[C, E, S](setup: BehaviorSetup[C, E, S]): Behavior[InternalProtocol] = - new RequestingRecoveryPermit(setup.setMdc(MDC.AwaitingPermit)).createBehavior() + new RequestingRecoveryPermit(setup.setMdcPhase(PersistenceMdc.AwaitingPermit)).createBehavior() } diff --git a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/Running.scala b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/Running.scala index dcaf3a70f2..8c44b551ed 100644 --- a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/Running.scala +++ b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/Running.scala @@ -10,7 +10,7 @@ import akka.actor.UnhandledMessage import akka.actor.typed.Behavior import akka.actor.typed.Signal import akka.actor.typed.internal.PoisonPill -import akka.actor.typed.scaladsl.{ AbstractBehavior, ActorContext, Behaviors } +import akka.actor.typed.scaladsl.{ AbstractBehavior, ActorContext, Behaviors, LoggerOps } import akka.annotation.{ InternalApi, InternalStableApi } import akka.persistence.DeleteMessagesFailure import akka.persistence.DeleteMessagesSuccess @@ -79,7 +79,7 @@ private[akka] object Running { } def apply[C, E, S](setup: BehaviorSetup[C, E, S], state: RunningState[S]): Behavior[InternalProtocol] = { - val running = new Running(setup.setMdc(MDC.RunningCmds)) + val running = new Running(setup.setMdcPhase(PersistenceMdc.RunningCmds)) new running.HandlingCommands(state) } } @@ -95,10 +95,6 @@ private[akka] object Running { import Running.RunningState import BehaviorSetup._ - private val runningCmdsMdc = MDC.create(setup.persistenceId, MDC.RunningCmds) - private val persistingEventsMdc = MDC.create(setup.persistenceId, MDC.PersistingEvents) - private val storingSnapshotMdc = MDC.create(setup.persistenceId, MDC.StoringSnapshot) - final class HandlingCommands(state: RunningState[S]) extends AbstractBehavior[InternalProtocol] with WithSeqNrAccessible { @@ -130,7 +126,7 @@ private[akka] object Running { effect: Effect[E, S], sideEffects: immutable.Seq[SideEffect[S]] = Nil): Behavior[InternalProtocol] = { if (setup.log.isDebugEnabled && !effect.isInstanceOf[CompositeEffect[_, _]]) - setup.log.debug( + setup.log.debugN( s"Handled command [{}], resulting effect: [{}], side effects: [{}]", msg.getClass.getName, effect, @@ -205,7 +201,7 @@ private[akka] object Running { Tagged(adaptedEvent, tags) } - setup.setMdc(runningCmdsMdc) + setup.setMdcPhase(PersistenceMdc.RunningCmds) override def currentSequenceNumber: Long = state.seqNr } @@ -218,7 +214,7 @@ private[akka] object Running { numberOfEvents: Int, shouldSnapshotAfterPersist: SnapshotAfterPersist, sideEffects: immutable.Seq[SideEffect[S]]): Behavior[InternalProtocol] = { - setup.setMdc(persistingEventsMdc) + setup.setMdcPhase(PersistenceMdc.PersistingEvents) new PersistingEvents(state, visibleState, numberOfEvents, shouldSnapshotAfterPersist, sideEffects) } @@ -258,7 +254,10 @@ private[akka] object Running { final def onJournalResponse(response: Response): Behavior[InternalProtocol] = { if (setup.log.isDebugEnabled) { - setup.log.debug("Received Journal response: {} after: {} nanos", response, System.nanoTime() - persistStartTime) + setup.log.debug2( + "Received Journal response: {} after: {} nanos", + response, + System.nanoTime() - persistStartTime) } def onWriteResponse(p: PersistentRepr): Behavior[InternalProtocol] = { @@ -336,7 +335,7 @@ private[akka] object Running { state: RunningState[S], sideEffects: immutable.Seq[SideEffect[S]], snapshotReason: SnapshotAfterPersist): Behavior[InternalProtocol] = { - setup.setMdc(storingSnapshotMdc) + setup.setMdcPhase(PersistenceMdc.StoringSnapshot) def onCommand(cmd: IncomingCommand[C]): Behavior[InternalProtocol] = { if (state.receivedPoisonPill) { @@ -371,14 +370,14 @@ private[akka] object Running { Some(SnapshotCompleted(SnapshotMetadata.fromClassic(meta))) case SaveSnapshotFailure(meta, error) => - setup.log.warning("Failed to save snapshot given metadata [{}] due to [{}]", meta, error.getMessage) + setup.log.warn2("Failed to save snapshot given metadata [{}] due to: {}", meta, error.getMessage) Some(SnapshotFailed(SnapshotMetadata.fromClassic(meta), error)) case _ => None } - setup.log.debug("Received snapshot response [{}], emitting signal [{}].", response, signal) + setup.log.debug2("Received snapshot response [{}], emitting signal [{}].", response, signal) signal.foreach(setup.onSignal(state.state, _, catchAndLog = false)) } diff --git a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/StashManagement.scala b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/StashManagement.scala index adb6714fa1..13018a68c4 100644 --- a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/StashManagement.scala +++ b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/StashManagement.scala @@ -9,6 +9,7 @@ import akka.actor.Dropped import akka.actor.typed.scaladsl.adapter._ import akka.actor.typed.scaladsl.StashOverflowException import akka.actor.typed.scaladsl.ActorContext +import akka.actor.typed.scaladsl.LoggerOps import akka.actor.typed.scaladsl.StashBuffer import akka.annotation.InternalApi import akka.util.ConstantFun @@ -45,13 +46,11 @@ private[akka] trait StashManagement[C, E, S] { case e: StashOverflowException => setup.settings.stashOverflowStrategy match { case StashOverflowStrategy.Drop => - if (context.log.isWarningEnabled) { - val dropName = msg match { - case InternalProtocol.IncomingCommand(actual) => actual.getClass.getName - case other => other.getClass.getName - } - context.log.warning("Stash buffer is full, dropping message [{}]", dropName) + val dropName = msg match { + case InternalProtocol.IncomingCommand(actual) => actual.getClass.getName + case other => other.getClass.getName } + context.log.warn("Stash buffer is full, dropping message [{}]", dropName) context.system.toClassic.eventStream.publish(Dropped(msg, "Stash buffer is full", context.self.toClassic)) case StashOverflowStrategy.Fail => throw e @@ -97,7 +96,7 @@ private[akka] trait StashManagement[C, E, S] { private def logStashMessage(msg: InternalProtocol, buffer: StashBuffer[InternalProtocol]): Unit = { if (setup.settings.logOnStashing) - setup.log.debug( + setup.log.debugN( "Stashing message to {} stash: [{}] ", if (buffer eq stashState.internalStashBuffer) "internal" else "user", msg) @@ -105,7 +104,7 @@ private[akka] trait StashManagement[C, E, S] { private def logUnstashMessage(buffer: StashBuffer[InternalProtocol]): Unit = { if (setup.settings.logOnStashing) - setup.log.debug( + setup.log.debugN( "Unstashing message from {} stash: [{}]", if (buffer eq stashState.internalStashBuffer) "internal" else "user", buffer.head) @@ -113,7 +112,7 @@ private[akka] trait StashManagement[C, E, S] { private def logUnstashAll(): Unit = { if (setup.settings.logOnStashing) - setup.log.debug( + setup.log.debug2( "Unstashing all [{}] messages from user stash, first is: [{}]", stashState.userStashBuffer.size, stashState.userStashBuffer.head) diff --git a/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/EventSourcedActorFailureTest.java b/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/EventSourcedActorFailureTest.java index a9c2e59af3..4d8b84f58c 100644 --- a/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/EventSourcedActorFailureTest.java +++ b/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/EventSourcedActorFailureTest.java @@ -5,6 +5,7 @@ package akka.persistence.typed.javadsl; import akka.actor.testkit.typed.TestException; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import akka.actor.typed.ActorRef; @@ -13,11 +14,10 @@ import akka.actor.typed.SupervisorStrategy; import akka.persistence.typed.PersistenceId; import akka.persistence.typed.RecoveryCompleted; import akka.persistence.typed.RecoveryFailed; -import akka.testkit.EventFilter; -import akka.testkit.TestEvent; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -86,6 +86,8 @@ public class EventSourcedActorFailureTest extends JUnitSuite { @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(config); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + public static Behavior fail( PersistenceId pid, ActorRef probe, ActorRef recoveryFailureProbe) { return new FailingEventSourcedActor(pid, probe, recoveryFailureProbe); @@ -95,24 +97,6 @@ public class EventSourcedActorFailureTest extends JUnitSuite { return fail(pid, probe, testKit.createTestProbe().ref()); } - public EventSourcedActorFailureTest() { - // FIXME ##24348 silence logging in a proper way - akka.actor.typed.javadsl.Adapter.toClassic(testKit.system()) - .eventStream() - .publish( - new TestEvent.Mute( - akka.japi.Util.immutableSeq( - new EventFilter[] { - EventFilter.warning(null, null, "No default snapshot store", null, 1) - }))); - akka.actor.typed.javadsl.Adapter.toClassic(testKit.system()) - .eventStream() - .publish( - new TestEvent.Mute( - akka.japi.Util.immutableSeq( - new EventFilter[] {EventFilter.error(null, null, "", ".*saw failure.*", 1)}))); - } - @Test public void notifyRecoveryFailure() { TestProbe probe = testKit.createTestProbe(); diff --git a/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/LoggerSourceTest.java b/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/LoggerSourceTest.java index 8ceb5db24c..c7ade1337e 100644 --- a/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/LoggerSourceTest.java +++ b/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/LoggerSourceTest.java @@ -4,6 +4,8 @@ package akka.persistence.typed.javadsl; +import akka.actor.testkit.typed.javadsl.LogCapturing; +import akka.actor.testkit.typed.javadsl.LoggingEventFilter; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import akka.actor.typed.ActorRef; @@ -12,26 +14,28 @@ import akka.actor.typed.javadsl.ActorContext; import akka.actor.typed.javadsl.Behaviors; import akka.persistence.typed.PersistenceId; import akka.persistence.typed.RecoveryCompleted; -import akka.testkit.EventFilter; -import akka.testkit.TestEvent; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; public class LoggerSourceTest extends JUnitSuite { private static final Config config = ConfigFactory.parseString( - "akka.loggers = [akka.testkit.TestEventListener] \n" - + "akka.persistence.journal.plugin = \"akka.persistence.journal.inmem\" \n" + "akka.persistence.journal.plugin = \"akka.persistence.journal.inmem\" \n" + "akka.persistence.journal.inmem.test-serialization = on \n"); @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(config); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + private static final AtomicInteger idCounter = new AtomicInteger(0); public static PersistenceId nextId() { @@ -94,26 +98,58 @@ public class LoggerSourceTest extends JUnitSuite { return new LoggingBehavior(nextId(), ctx); }); - public LoggerSourceTest() { - // FIXME ##24348 silence logging in a proper way - akka.actor.typed.javadsl.Adapter.toClassic(testKit.system()) - .eventStream() - .publish( - new TestEvent.Mute( - akka.japi.Util.immutableSeq( - new EventFilter[] { - EventFilter.warning(null, null, "No default snapshot store", null, 1) - }))); - } - @Test - public void verifyLoggerClass() { - ActorRef ref = testKit.spawn(behavior); - ref.tell("command"); - // no Java testkit support for custom event filters to look at the logger class - // so this is a manual-occular test for now (additionally it requires a small change to - // stdoutlogger to print log event class name) - // FIXME #24348: eventfilter support in typed testkit + public void verifyLogging() { + Map expectedMdc1 = new HashMap<>(); + expectedMdc1.put("persistenceId", "1"); + expectedMdc1.put("persistencePhase", "replay-evt"); + + ActorRef ref = + LoggingEventFilter.info("recovery-completed") + .withMdc(expectedMdc1) + .withCustom(event -> event.loggerName().equals(LoggingBehavior.class.getName())) + .intercept( + testKit.system(), + () -> { + return testKit.spawn(behavior); + }); + + // MDC persistenceId ajd persistencePhase for the "command-received" not included in the + // "command-received" logging, because that is via ActorContext.log directly and + // EventSourcedBehaviorImpl + // isn't involved. + + LoggingEventFilter.info("command-received") + .withCustom( + event -> { + return event.loggerName().equals(LoggingBehavior.class.getName()) + && event.getMdc().get("akkaSource").equals(ref.path().toString()); + }) + .intercept( + testKit.system(), + () -> { + ref.tell("command"); + return null; + }); + + Map expectedMdc3 = new HashMap<>(); + expectedMdc3.put("persistenceId", "1"); + expectedMdc3.put("persistencePhase", "running-cmd"); + + LoggingEventFilter.info("event-received") + .withMdc(expectedMdc3) + .withCustom( + event -> { + return event.loggerName().equals(LoggingBehavior.class.getName()) + && event.getMdc().get("akkaSource").equals(ref.path().toString()); + }) + .intercept( + testKit.system(), + () -> { + ref.tell("command"); + return null; + }); + TestProbe probe = testKit.createTestProbe(); ref.tell("stop"); probe.expectTerminated(ref); diff --git a/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/NullEmptyStateTest.java b/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/NullEmptyStateTest.java index a34dcc809d..2cdb05e174 100644 --- a/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/NullEmptyStateTest.java +++ b/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/NullEmptyStateTest.java @@ -4,6 +4,7 @@ package akka.persistence.typed.javadsl; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import akka.actor.typed.ActorRef; @@ -14,6 +15,7 @@ import akka.persistence.typed.RecoveryCompleted; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -26,6 +28,8 @@ public class NullEmptyStateTest extends JUnitSuite { @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(config); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + static class NullEmptyState extends EventSourcedBehavior { private final ActorRef probe; diff --git a/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/PersistentActorJavaDslTest.java b/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/PersistentActorJavaDslTest.java index 3946695872..10d0bc885b 100644 --- a/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/PersistentActorJavaDslTest.java +++ b/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/PersistentActorJavaDslTest.java @@ -5,6 +5,8 @@ package akka.persistence.typed.javadsl; import akka.Done; +import akka.actor.testkit.typed.javadsl.LogCapturing; +import akka.actor.testkit.typed.javadsl.LoggingEventFilter; import akka.actor.typed.*; import akka.actor.typed.javadsl.ActorContext; import akka.actor.typed.javadsl.Adapter; @@ -31,8 +33,11 @@ import com.google.common.collect.Sets; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; +import org.slf4j.event.Level; import java.time.Duration; import java.util.*; @@ -44,11 +49,12 @@ import static org.junit.Assert.assertEquals; public class PersistentActorJavaDslTest extends JUnitSuite { public static final Config config = - ConfigFactory.parseString("akka.loggers = [akka.testkit.TestEventListener]") - .withFallback(EventSourcedBehaviorSpec.conf().withFallback(ConfigFactory.load())); + EventSourcedBehaviorSpec.conf().withFallback(ConfigFactory.load()); @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(config); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + private LeveldbReadJournal queries = PersistenceQuery.get(Adapter.toClassic(testKit.system())) .getReadJournalFor(LeveldbReadJournal.class, LeveldbReadJournal.Identifier()); @@ -697,13 +703,15 @@ public class PersistentActorJavaDslTest extends JUnitSuite { testKit.spawn( new IncorrectExpectedStateForThenRun(probe.getRef(), new PersistenceId("foiesftr"))); - probe.expectMessage("started!"); // workaround for #26256 + probe.expectMessage("started!"); - new EventFilter(Logging.Error.class, Adapter.toClassic(testKit.system())) - .occurrences(1) + LoggingEventFilter.empty() + .withLogLevel(Level.ERROR) // the error messages slightly changed in later JDKs - .matches("(class )?java.lang.Integer cannot be cast to (class )?java.lang.String.*") + .withMessageRegex( + "(class )?java.lang.Integer cannot be cast to (class )?java.lang.String.*") .intercept( + testKit.system(), () -> { c.tell("expect wrong type"); return null; diff --git a/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/PrimitiveStateTest.java b/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/PrimitiveStateTest.java index 037b438bf3..541b14bbdd 100644 --- a/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/PrimitiveStateTest.java +++ b/akka-persistence-typed/src/test/java/akka/persistence/typed/javadsl/PrimitiveStateTest.java @@ -4,6 +4,7 @@ package akka.persistence.typed.javadsl; +import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.testkit.typed.javadsl.TestProbe; import akka.actor.typed.ActorRef; @@ -14,6 +15,7 @@ import akka.persistence.typed.RecoveryCompleted; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.scalatest.junit.JUnitSuite; @@ -26,6 +28,8 @@ public class PrimitiveStateTest extends JUnitSuite { @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(config); + @Rule public final LogCapturing logCapturing = new LogCapturing(); + static class PrimitiveState extends EventSourcedBehavior { private final ActorRef probe; diff --git a/akka-persistence-typed/src/test/resources/logback-test.xml b/akka-persistence-typed/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..5e80aa8ee0 --- /dev/null +++ b/akka-persistence-typed/src/test/resources/logback-test.xml @@ -0,0 +1,36 @@ + + + + + + + + DEBUG + DENY + + + %date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} { "persistenceId": "%X{persistenceId}", "persistencePhase": "%X{persistencePhase}" } - %msg%n + + + + + + + + + + + + + + + + diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/ManyRecoveriesSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/ManyRecoveriesSpec.scala index 00cdc92b59..46e4127596 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/ManyRecoveriesSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/ManyRecoveriesSpec.scala @@ -14,8 +14,7 @@ import scala.concurrent.Await import scala.concurrent.duration._ import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit -import akka.testkit.EventFilter -import akka.testkit.TestEvent.Mute +import akka.actor.testkit.typed.scaladsl.LogCapturing import org.scalatest.WordSpecLike object ManyRecoveriesSpec { @@ -50,7 +49,6 @@ object ManyRecoveriesSpec { } class ManyRecoveriesSpec extends ScalaTestWithActorTestKit(s""" - akka.loggers = [akka.testkit.TestEventListener] akka.actor.default-dispatcher { type = Dispatcher executor = "thread-pool-executor" @@ -60,13 +58,10 @@ class ManyRecoveriesSpec extends ScalaTestWithActorTestKit(s""" } akka.persistence.max-concurrent-recoveries = 3 akka.persistence.journal.plugin = "akka.persistence.journal.inmem" - """) with WordSpecLike { + """) with WordSpecLike with LogCapturing { import ManyRecoveriesSpec._ - import akka.actor.typed.scaladsl.adapter._ - system.toClassic.eventStream.publish(Mute(EventFilter.warning(start = "No default snapshot store", occurrences = 1))) - "Many persistent actors" must { "be able to recover without overloading" in { val probe = TestProbe[String]() diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/StashingWhenSnapshottingSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/StashingWhenSnapshottingSpec.scala index 724119525e..6a38643f89 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/StashingWhenSnapshottingSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/StashingWhenSnapshottingSpec.scala @@ -20,11 +20,12 @@ import akka.persistence.typed.StashingWhenSnapshottingSpec.ControllableSnapshotS import akka.persistence.typed.scaladsl.Effect import akka.persistence.typed.scaladsl.EventSourcedBehavior import org.scalatest.WordSpecLike - import scala.concurrent.Future import scala.concurrent.Promise import scala.util.Success +import akka.actor.testkit.typed.scaladsl.LogCapturing + object StashingWhenSnapshottingSpec { object ControllableSnapshotStoreExt extends ExtensionId[ControllableSnapshotStoreExt] { @@ -85,7 +86,8 @@ object StashingWhenSnapshottingSpec { class StashingWhenSnapshottingSpec extends ScalaTestWithActorTestKit(StashingWhenSnapshottingSpec.config) - with WordSpecLike { + with WordSpecLike + with LogCapturing { "A persistent actor" should { "stash messages and automatically replay when snapshot is in progress" in { val eventProbe = TestProbe[String]() diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/internal/RecoveryPermitterSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/internal/RecoveryPermitterSpec.scala index f915ee364f..046302c548 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/internal/RecoveryPermitterSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/internal/RecoveryPermitterSpec.scala @@ -13,14 +13,14 @@ import akka.persistence.Persistence import akka.persistence.RecoveryPermitter.{ RecoveryPermitGranted, RequestRecoveryPermit, ReturnRecoveryPermit } import akka.persistence.typed.scaladsl.EventSourcedBehavior.CommandHandler import akka.persistence.typed.scaladsl.{ Effect, EventSourcedBehavior } -import akka.testkit.EventFilter import scala.concurrent.duration._ import scala.util.control.NoStackTrace +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.persistence.typed.PersistenceId import akka.persistence.typed.RecoveryCompleted -import akka.testkit.TestEvent.Mute import org.scalatest.WordSpecLike object RecoveryPermitterSpec { @@ -68,19 +68,16 @@ object RecoveryPermitterSpec { } class RecoveryPermitterSpec extends ScalaTestWithActorTestKit(s""" - akka.loggers = [akka.testkit.TestEventListener] akka.persistence.max-concurrent-recoveries = 3 akka.persistence.journal.plugin = "akka.persistence.journal.inmem" akka.persistence.journal.inmem.test-serialization = on akka.loggers = ["akka.testkit.TestEventListener"] - """) with WordSpecLike { + """) with WordSpecLike with LogCapturing { import RecoveryPermitterSpec._ implicit val classicSystem = system.toClassic - classicSystem.eventStream.publish(Mute(EventFilter.warning(start = "No default snapshot store", occurrences = 1))) - private val permitter = Persistence(classicSystem).recoveryPermitter def requestPermit(p: TestProbe[Any]): Unit = { @@ -192,7 +189,7 @@ class RecoveryPermitterSpec extends ScalaTestWithActorTestKit(s""" val stopProbe = createTestProbe[ActorRef[Command]]() val parent = - EventFilter.error(occurrences = 1, start = "Exception during recovery.").intercept { + LoggingEventFilter.error("Exception during recovery.").intercept { spawn(Behaviors.setup[Command](ctx => { val persistentActor = ctx.spawnAnonymous(persistentBehavior("p3", p3, p3, throwOnRecovery = true)) diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/internal/RetentionCriteriaSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/internal/RetentionCriteriaSpec.scala index daaa4d8a37..f567a41b60 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/internal/RetentionCriteriaSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/internal/RetentionCriteriaSpec.scala @@ -4,12 +4,13 @@ package akka.persistence.typed.internal +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.persistence.typed.scaladsl.RetentionCriteria import org.scalatest.Matchers import org.scalatest.TestSuite import org.scalatest.WordSpecLike -class RetentionCriteriaSpec extends TestSuite with Matchers with WordSpecLike { +class RetentionCriteriaSpec extends TestSuite with Matchers with WordSpecLike with LogCapturing { "RetentionCriteria" must { diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/internal/StashStateSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/internal/StashStateSpec.scala index 3351488af9..58567fda2c 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/internal/StashStateSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/internal/StashStateSpec.scala @@ -8,13 +8,14 @@ import scala.concurrent.duration._ import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.Behavior import akka.actor.typed.scaladsl.Behaviors import akka.persistence.typed.internal.InternalProtocol.IncomingCommand import akka.persistence.typed.internal.InternalProtocol.RecoveryPermitGranted import org.scalatest.WordSpecLike -class StashStateSpec extends ScalaTestWithActorTestKit with WordSpecLike { +class StashStateSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapturing { "StashState" should { diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorFailureSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorFailureSpec.scala index c593e9690d..26b3a2ec35 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorFailureSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorFailureSpec.scala @@ -27,8 +27,6 @@ import akka.persistence.typed.RecoveryCompleted import akka.persistence.typed.RecoveryCompleted import akka.persistence.typed.RecoveryFailed import akka.persistence.typed.internal.JournalFailureException -import akka.testkit.EventFilter -import akka.testkit.TestEvent.Mute import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import org.scalatest.WordSpecLike @@ -74,7 +72,6 @@ object EventSourcedBehaviorFailureSpec { val conf: Config = ConfigFactory.parseString(s""" akka.loglevel = INFO - akka.loggers = [akka.testkit.TestEventListener] akka.persistence.journal.plugin = "failure-journal" failure-journal = $${akka.persistence.journal.inmem} failure-journal { @@ -85,15 +82,11 @@ object EventSourcedBehaviorFailureSpec { class EventSourcedBehaviorFailureSpec extends ScalaTestWithActorTestKit(EventSourcedBehaviorFailureSpec.conf) - with WordSpecLike { + with WordSpecLike + with LogCapturing { implicit val testSettings: TestKitSettings = TestKitSettings(system) - // Needed for the classic event filter - implicit val classic = system.toClassic - - classic.eventStream.publish(Mute(EventFilter.warning(start = "No default snapshot store", occurrences = 1))) - def failingPersistentActor( pid: PersistenceId, probe: ActorRef[String], @@ -128,7 +121,7 @@ class EventSourcedBehaviorFailureSpec "A typed persistent actor (failures)" must { "signal RecoveryFailure when replay fails" in { - EventFilter[JournalFailureException](occurrences = 1).intercept { + LoggingEventFilter.error[JournalFailureException].intercept { val probe = TestProbe[String]() val excProbe = TestProbe[Throwable]() spawn(failingPersistentActor(PersistenceId("fail-recovery"), probe.ref, { @@ -166,7 +159,7 @@ class EventSourcedBehaviorFailureSpec probe.expectMessage("malicious") probe.expectMessage("persisted") - EventFilter[JournalFailureException](occurrences = 1).intercept { + LoggingEventFilter.error[JournalFailureException].intercept { // start again and then the event handler will throw spawn(failingPersistentActor(pid, probe.ref, { case (_, RecoveryFailed(t)) => @@ -180,7 +173,7 @@ class EventSourcedBehaviorFailureSpec "fail recovery if exception from RecoveryCompleted signal handler" in { val probe = TestProbe[String]() - EventFilter[JournalFailureException](occurrences = 1).intercept { + LoggingEventFilter.error[JournalFailureException].intercept { spawn( Behaviors .supervise(failingPersistentActor(PersistenceId("recovery-ok"), probe.ref, { @@ -257,7 +250,7 @@ class EventSourcedBehaviorFailureSpec } "stop (default supervisor strategy) if command handler throws" in { - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { val probe = TestProbe[String]() val behav = failingPersistentActor(PersistenceId("wrong-command-1"), probe.ref) val c = spawn(behav) @@ -268,7 +261,7 @@ class EventSourcedBehaviorFailureSpec } "restart supervisor strategy if command handler throws" in { - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { val probe = TestProbe[String]() val behav = Behaviors .supervise(failingPersistentActor(PersistenceId("wrong-command-2"), probe.ref)) @@ -281,7 +274,7 @@ class EventSourcedBehaviorFailureSpec } "stop (default supervisor strategy) if side effect callback throws" in { - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { val probe = TestProbe[String]() val behav = failingPersistentActor(PersistenceId("wrong-command-3"), probe.ref) val c = spawn(behav) @@ -296,7 +289,7 @@ class EventSourcedBehaviorFailureSpec "stop (default supervisor strategy) if signal handler throws" in { case object SomeSignal extends Signal - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { val probe = TestProbe[String]() val behav = failingPersistentActor(PersistenceId("wrong-signal-handler"), probe.ref, { case (_, SomeSignal) => throw TestException("from signal") @@ -309,7 +302,7 @@ class EventSourcedBehaviorFailureSpec } "not accept wrong event, before persisting it" in { - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { val probe = TestProbe[String]() val behav = failingPersistentActor(PersistenceId("wrong-event-2"), probe.ref) val c = spawn(behav) diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorInterceptorSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorInterceptorSpec.scala index 2bcaa41512..0d6bcf62a1 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorInterceptorSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorInterceptorSpec.scala @@ -13,8 +13,6 @@ import akka.actor.typed.BehaviorInterceptor import akka.actor.typed.TypedActorContext import akka.actor.typed.scaladsl.Behaviors import akka.persistence.typed.PersistenceId -import akka.testkit.EventFilter -import akka.testkit.TestEvent.Mute import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import org.scalatest.WordSpecLike @@ -25,7 +23,6 @@ object EventSourcedBehaviorInterceptorSpec { def config: Config = ConfigFactory.parseString(s""" akka.loglevel = INFO - akka.loggers = [akka.testkit.TestEventListener] akka.persistence.journal.plugin = "akka.persistence.journal.inmem" akka.persistence.journal.inmem.test-serialization = on """) @@ -47,19 +44,14 @@ object EventSourcedBehaviorInterceptorSpec { class EventSourcedBehaviorInterceptorSpec extends ScalaTestWithActorTestKit(EventSourcedBehaviorTimersSpec.config) - with WordSpecLike { + with WordSpecLike + with LogCapturing { import EventSourcedBehaviorInterceptorSpec._ val pidCounter = new AtomicInteger(0) private def nextPid(): PersistenceId = PersistenceId(s"c${pidCounter.incrementAndGet()})") - import akka.actor.typed.scaladsl.adapter._ - // needed for the classic event filter - private implicit val classicSystem: akka.actor.ActorSystem = system.toClassic - - classicSystem.eventStream.publish(Mute(EventFilter.warning(start = "No default snapshot store", occurrences = 1))) - "EventSourcedBehavior interceptor" must { "be possible to combine with another interceptor" in { @@ -101,10 +93,11 @@ class EventSourcedBehaviorInterceptorSpec val probe = createTestProbe[String]() val pid = nextPid() val ref = spawn(Behaviors.setup[String] { _ => - Behaviors - .withMdc(staticMdc = Map("pid" -> pid), mdcForMessage = (msg: String) => Map("msg" -> msg.toUpperCase())) { - testBehavior(pid, probe.ref) - } + Behaviors.withMdc( + staticMdc = Map("pid" -> pid.toString), + mdcForMessage = (msg: String) => Map("msg" -> msg.toUpperCase())) { + testBehavior(pid, probe.ref) + } }) ref ! "a" diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorRecoveryTimeoutSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorRecoveryTimeoutSpec.scala index afe397a90f..95d50a00fa 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorRecoveryTimeoutSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorRecoveryTimeoutSpec.scala @@ -17,8 +17,6 @@ import akka.persistence.journal.SteppingInmemJournal import akka.persistence.typed.PersistenceId import akka.persistence.typed.RecoveryFailed import akka.persistence.typed.internal.JournalFailureException -import akka.testkit.EventFilter -import akka.testkit.TestEvent.Mute import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import org.scalatest.WordSpecLike @@ -35,7 +33,6 @@ object EventSourcedBehaviorRecoveryTimeoutSpec { """)) .withFallback(ConfigFactory.parseString(s""" akka.loglevel = INFO - akka.loggers = [akka.testkit.TestEventListener] """)) def testBehavior(persistenceId: PersistenceId, probe: ActorRef[AnyRef]): Behavior[String] = @@ -54,7 +51,8 @@ object EventSourcedBehaviorRecoveryTimeoutSpec { class EventSourcedBehaviorRecoveryTimeoutSpec extends ScalaTestWithActorTestKit(EventSourcedBehaviorRecoveryTimeoutSpec.config) - with WordSpecLike { + with WordSpecLike + with LogCapturing { import EventSourcedBehaviorRecoveryTimeoutSpec._ @@ -65,8 +63,6 @@ class EventSourcedBehaviorRecoveryTimeoutSpec // needed for SteppingInmemJournal.step private implicit val classicSystem: akka.actor.ActorSystem = system.toClassic - classicSystem.eventStream.publish(Mute(EventFilter.warning(start = "No default snapshot store", occurrences = 1))) - "The recovery timeout" must { "fail recovery if timeout is not met when recovering" in { @@ -89,7 +85,9 @@ class EventSourcedBehaviorRecoveryTimeoutSpec // now replay, but don't give the journal any tokens to replay events // so that we cause the timeout to trigger - EventFilter[JournalFailureException](pattern = "Exception during recovery.*Replay timed out", occurrences = 1) + LoggingEventFilter + .error[JournalFailureException] + .withMessageRegex("Exception during recovery.*Replay timed out") .intercept { val replaying = spawn(testBehavior(pid, probe.ref)) diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorReplySpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorReplySpec.scala index 86920fbf8d..3551097fa5 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorReplySpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorReplySpec.scala @@ -76,7 +76,8 @@ object EventSourcedBehaviorReplySpec { class EventSourcedBehaviorReplySpec extends ScalaTestWithActorTestKit(EventSourcedBehaviorReplySpec.conf) - with WordSpecLike { + with WordSpecLike + with LogCapturing { import EventSourcedBehaviorReplySpec._ diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorRetentionSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorRetentionSpec.scala index 3b1691d125..3a8b6bcde9 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorRetentionSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorRetentionSpec.scala @@ -29,8 +29,6 @@ import akka.persistence.typed.SnapshotFailed import akka.persistence.typed.SnapshotMetadata import akka.persistence.typed.SnapshotSelectionCriteria import akka.serialization.jackson.CborSerializable -import akka.testkit.EventFilter -import akka.testkit.TestEvent.Mute import akka.util.unused import com.typesafe.config.Config import com.typesafe.config.ConfigFactory @@ -40,7 +38,6 @@ object EventSourcedBehaviorRetentionSpec { def conf: Config = ConfigFactory.parseString(s""" akka.loglevel = INFO - akka.loggers = [akka.testkit.TestEventListener] # akka.persistence.typed.log-stashing = on akka.persistence.journal.leveldb.dir = "target/typed-persistence-${UUID.randomUUID().toString}" akka.persistence.journal.plugin = "akka.persistence.journal.leveldb" @@ -147,20 +144,14 @@ object EventSourcedBehaviorRetentionSpec { class EventSourcedBehaviorRetentionSpec extends ScalaTestWithActorTestKit(EventSourcedBehaviorRetentionSpec.conf) - with WordSpecLike { + with WordSpecLike + with LogCapturing { import EventSourcedBehaviorRetentionSpec._ - import akka.actor.typed.scaladsl.adapter._ - - // needed for the classic event filter - implicit val actorSystem = system.toClassic val pidCounter = new AtomicInteger(0) private def nextPid(): PersistenceId = PersistenceId(s"c${pidCounter.incrementAndGet()})") - actorSystem.eventStream.publish(Mute(EventFilter.info(pattern = ".*was not delivered.*", occurrences = 100))) - actorSystem.eventStream.publish(Mute(EventFilter.warning(pattern = ".*received dead letter.*", occurrences = 100))) - "EventSourcedBehavior with retention" must { "snapshot every N sequence nrs" in { diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorSpec.scala index 991367b2ee..d9b90a56fd 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorSpec.scala @@ -42,8 +42,6 @@ import akka.persistence.{ SnapshotMetadata => ClassicSnapshotMetadata } import akka.persistence.{ SnapshotSelectionCriteria => ClassicSnapshotSelectionCriteria } import akka.serialization.jackson.CborSerializable import akka.stream.scaladsl.Sink -import akka.testkit.EventFilter -import akka.testkit.TestEvent.Mute import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import org.scalatest.WordSpecLike @@ -80,7 +78,6 @@ object EventSourcedBehaviorSpec { // also used from PersistentActorTest def conf: Config = ConfigFactory.parseString(s""" akka.loglevel = INFO - akka.loggers = [akka.testkit.TestEventListener] # akka.persistence.typed.log-stashing = on akka.persistence.journal.leveldb.dir = "target/typed-persistence-${UUID.randomUUID().toString}" akka.persistence.journal.plugin = "akka.persistence.journal.leveldb" @@ -283,7 +280,10 @@ object EventSourcedBehaviorSpec { } } -class EventSourcedBehaviorSpec extends ScalaTestWithActorTestKit(EventSourcedBehaviorSpec.conf) with WordSpecLike { +class EventSourcedBehaviorSpec + extends ScalaTestWithActorTestKit(EventSourcedBehaviorSpec.conf) + with WordSpecLike + with LogCapturing { import EventSourcedBehaviorSpec._ import akka.actor.typed.scaladsl.adapter._ @@ -291,15 +291,9 @@ class EventSourcedBehaviorSpec extends ScalaTestWithActorTestKit(EventSourcedBeh val queries: LeveldbReadJournal = PersistenceQuery(system.toClassic).readJournalFor[LeveldbReadJournal](LeveldbReadJournal.Identifier) - // needed for the classic event filter - implicit val actorSystem = system.toClassic - val pidCounter = new AtomicInteger(0) private def nextPid(): PersistenceId = PersistenceId(s"c${pidCounter.incrementAndGet()})") - actorSystem.eventStream.publish(Mute(EventFilter.info(pattern = ".*was not delivered.*", occurrences = 100))) - actorSystem.eventStream.publish(Mute(EventFilter.warning(pattern = ".*received dead letter.*", occurrences = 100))) - "A typed persistent actor" must { "persist an event" in { @@ -505,7 +499,7 @@ class EventSourcedBehaviorSpec extends ScalaTestWithActorTestKit(EventSourcedBeh } "fail after recovery timeout" in { - EventFilter.error(start = "Persistence failure when replaying snapshot", occurrences = 1).intercept { + LoggingEventFilter.error("Persistence failure when replaying snapshot").intercept { val c = spawn( Behaviors.setup[Command](ctx => counter(ctx, nextPid) @@ -531,7 +525,7 @@ class EventSourcedBehaviorSpec extends ScalaTestWithActorTestKit(EventSourcedBeh c ! StopIt probe.expectTerminated(c) - EventFilter[TestException](occurrences = 1).intercept { + LoggingEventFilter.error[TestException].intercept { val c2 = spawn(Behaviors.setup[Command](counter(_, pid))) c2 ! Fail probe.expectTerminated(c2) // should fail @@ -543,14 +537,20 @@ class EventSourcedBehaviorSpec extends ScalaTestWithActorTestKit(EventSourcedBeh PersistenceId(null) } val probe = TestProbe[AnyRef] - EventFilter[ActorInitializationException](start = "persistenceId must not be null", occurrences = 1).intercept { - val ref = spawn(Behaviors.setup[Command](counter(_, persistenceId = PersistenceId(null)))) - probe.expectTerminated(ref) - } - EventFilter[ActorInitializationException](start = "persistenceId must not be null", occurrences = 1).intercept { - val ref = spawn(Behaviors.setup[Command](counter(_, persistenceId = null))) - probe.expectTerminated(ref) - } + LoggingEventFilter + .error[ActorInitializationException] + .withMessageContains("persistenceId must not be null") + .intercept { + val ref = spawn(Behaviors.setup[Command](counter(_, persistenceId = PersistenceId(null)))) + probe.expectTerminated(ref) + } + LoggingEventFilter + .error[ActorInitializationException] + .withMessageContains("persistenceId must not be null") + .intercept { + val ref = spawn(Behaviors.setup[Command](counter(_, persistenceId = null))) + probe.expectTerminated(ref) + } } "fail fast if persistenceId is empty" in { @@ -558,26 +558,27 @@ class EventSourcedBehaviorSpec extends ScalaTestWithActorTestKit(EventSourcedBeh PersistenceId("") } val probe = TestProbe[AnyRef] - EventFilter[ActorInitializationException](start = "persistenceId must not be empty", occurrences = 1).intercept { - val ref = spawn(Behaviors.setup[Command](counter(_, persistenceId = PersistenceId("")))) - probe.expectTerminated(ref) - } + LoggingEventFilter + .error[ActorInitializationException] + .withMessageContains("persistenceId must not be empty") + .intercept { + val ref = spawn(Behaviors.setup[Command](counter(_, persistenceId = PersistenceId("")))) + probe.expectTerminated(ref) + } } "fail fast if default journal plugin is not defined" in { // new ActorSystem without persistence config - val testkit2 = ActorTestKit( - ActorTestKitBase.testNameFromCallStack(), - ConfigFactory.parseString(""" - akka.loggers = [akka.testkit.TestEventListener] - """)) + val testkit2 = ActorTestKit(ActorTestKitBase.testNameFromCallStack(), ConfigFactory.parseString("")) try { - EventFilter[ActorInitializationException](start = "Default journal plugin is not configured", occurrences = 1) + LoggingEventFilter + .error[ActorInitializationException] + .withMessageContains("Default journal plugin is not configured") .intercept { val ref = testkit2.spawn(Behaviors.setup[Command](counter(_, nextPid()))) val probe = testkit2.createTestProbe() probe.expectTerminated(ref) - }(testkit2.system.toClassic) + }(testkit2.system) } finally { testkit2.shutdownTestKit() } @@ -585,19 +586,16 @@ class EventSourcedBehaviorSpec extends ScalaTestWithActorTestKit(EventSourcedBeh "fail fast if given journal plugin is not defined" in { // new ActorSystem without persistence config - val testkit2 = ActorTestKit( - ActorTestKitBase.testNameFromCallStack(), - ConfigFactory.parseString(""" - akka.loggers = [akka.testkit.TestEventListener] - """)) + val testkit2 = ActorTestKit(ActorTestKitBase.testNameFromCallStack(), ConfigFactory.parseString("")) try { - EventFilter[ActorInitializationException]( - start = "Journal plugin [missing] configuration doesn't exist", - occurrences = 1).intercept { - val ref = testkit2.spawn(Behaviors.setup[Command](counter(_, nextPid()).withJournalPluginId("missing"))) - val probe = testkit2.createTestProbe() - probe.expectTerminated(ref) - }(testkit2.system.toClassic) + LoggingEventFilter + .error[ActorInitializationException] + .withMessageContains("Journal plugin [missing] configuration doesn't exist") + .intercept { + val ref = testkit2.spawn(Behaviors.setup[Command](counter(_, nextPid()).withJournalPluginId("missing"))) + val probe = testkit2.createTestProbe() + probe.expectTerminated(ref) + }(testkit2.system) } finally { testkit2.shutdownTestKit() } @@ -608,20 +606,19 @@ class EventSourcedBehaviorSpec extends ScalaTestWithActorTestKit(EventSourcedBeh val testkit2 = ActorTestKit( ActorTestKitBase.testNameFromCallStack(), ConfigFactory.parseString(s""" - akka.loggers = [akka.testkit.TestEventListener] akka.persistence.journal.leveldb.dir = "target/typed-persistence-${UUID.randomUUID().toString}" akka.persistence.journal.plugin = "akka.persistence.journal.leveldb" """)) try { - EventFilter - .warning(start = "No default snapshot store configured", occurrences = 1) + LoggingEventFilter + .warn("No default snapshot store configured") .intercept { val ref = testkit2.spawn(Behaviors.setup[Command](counter(_, nextPid()))) val probe = testkit2.createTestProbe[State]() // verify that it's not terminated ref ! GetValue(probe.ref) probe.expectMessage(State(0, Vector.empty)) - }(testkit2.system.toClassic) + }(testkit2.system) } finally { testkit2.shutdownTestKit() } @@ -632,18 +629,18 @@ class EventSourcedBehaviorSpec extends ScalaTestWithActorTestKit(EventSourcedBeh val testkit2 = ActorTestKit( ActorTestKitBase.testNameFromCallStack(), ConfigFactory.parseString(s""" - akka.loggers = [akka.testkit.TestEventListener] akka.persistence.journal.leveldb.dir = "target/typed-persistence-${UUID.randomUUID().toString}" akka.persistence.journal.plugin = "akka.persistence.journal.leveldb" """)) try { - EventFilter[ActorInitializationException]( - start = "Snapshot store plugin [missing] configuration doesn't exist", - occurrences = 1).intercept { - val ref = testkit2.spawn(Behaviors.setup[Command](counter(_, nextPid()).withSnapshotPluginId("missing"))) - val probe = testkit2.createTestProbe() - probe.expectTerminated(ref) - }(testkit2.system.toClassic) + LoggingEventFilter + .error[ActorInitializationException] + .withMessageContains("Snapshot store plugin [missing] configuration doesn't exist") + .intercept { + val ref = testkit2.spawn(Behaviors.setup[Command](counter(_, nextPid()).withSnapshotPluginId("missing"))) + val probe = testkit2.createTestProbe() + probe.expectTerminated(ref) + }(testkit2.system) } finally { testkit2.shutdownTestKit() } diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorStashSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorStashSpec.scala index bbc3c6aa01..edf43a60ae 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorStashSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorStashSpec.scala @@ -16,8 +16,10 @@ import akka.actor.testkit.typed.scaladsl._ import akka.actor.typed.ActorRef import akka.actor.typed.Behavior import akka.actor.Dropped +import akka.actor.UnhandledMessage import akka.actor.typed.PostStop import akka.actor.typed.SupervisorStrategy +import akka.actor.typed.eventstream.EventStream import akka.actor.typed.internal.PoisonPill import akka.actor.typed.javadsl.StashOverflowException import akka.actor.typed.scaladsl.Behaviors @@ -25,8 +27,6 @@ import akka.actor.typed.scaladsl.adapter._ import akka.persistence.typed.ExpectingReply import akka.persistence.typed.PersistenceId import akka.persistence.typed.RecoveryCompleted -import akka.testkit.EventFilter -import akka.testkit.TestEvent.Mute import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import org.scalatest.WordSpecLike @@ -34,7 +34,6 @@ import org.scalatest.WordSpecLike object EventSourcedBehaviorStashSpec { def conf: Config = ConfigFactory.parseString(s""" #akka.loglevel = DEBUG - akka.loggers = [akka.testkit.TestEventListener] #akka.persistence.typed.log-stashing = on akka.persistence.journal.plugin = "akka.persistence.journal.inmem" akka.persistence.journal.plugin = "failure-journal" @@ -168,18 +167,14 @@ object EventSourcedBehaviorStashSpec { class EventSourcedBehaviorStashSpec extends ScalaTestWithActorTestKit(EventSourcedBehaviorStashSpec.conf) - with WordSpecLike { + with WordSpecLike + with LogCapturing { import EventSourcedBehaviorStashSpec._ val pidCounter = new AtomicInteger(0) private def nextPid(): PersistenceId = PersistenceId(s"c${pidCounter.incrementAndGet()})") - // Needed for the classic event filter - implicit val classic = system.toClassic - - classic.eventStream.publish(Mute(EventFilter.warning(start = "No default snapshot store", occurrences = 1))) - "A typed persistent actor that is stashing commands" must { "stash and unstash" in { @@ -280,6 +275,8 @@ class EventSourcedBehaviorStashSpec val ackProbe = TestProbe[Ack] val stateProbe = TestProbe[State] val notUsedProbe = TestProbe[NotUsed] + val unhandledProbe = createTestProbe[UnhandledMessage]() + system.eventStream ! EventStream.Subscribe(unhandledProbe.ref) (1 to 100).foreach { n => c ! Increment(s"inc-1-$n", ackProbe.ref) @@ -295,42 +292,42 @@ class EventSourcedBehaviorStashSpec c ! Increment(s"inc-3-$n", ackProbe.ref) } - EventFilter.warning(start = "unhandled message", occurrences = 10).intercept { - c ! GetValue(stateProbe.ref) + c ! GetValue(stateProbe.ref) - (1 to 5).foreach { n => - c ! UpdateValue(s"upd-4-$n", n * 1000, ackProbe.ref) - } - - (1 to 3).foreach { n => - c ! Activate(s"act-5-$n", ackProbe.ref) - } - - (1 to 100).foreach { n => - c ! Increment(s"inc-6-$n", ackProbe.ref) - } - - c ! GetValue(stateProbe.ref) - - (6 to 8).foreach { n => - c ! UpdateValue(s"upd-7-$n", n * 1000, ackProbe.ref) - } - - (1 to 3).foreach { n => - c ! Deactivate(s"deact-8-$n", ackProbe.ref) - } - - (1 to 100).foreach { n => - c ! Increment(s"inc-9-$n", ackProbe.ref) - } - - (1 to 3).foreach { n => - c ! Activate(s"act-10-$n", ackProbe.ref) - } - - c ! GetValue(stateProbe.ref) + (1 to 5).foreach { n => + c ! UpdateValue(s"upd-4-$n", n * 1000, ackProbe.ref) } + (1 to 3).foreach { n => + c ! Activate(s"act-5-$n", ackProbe.ref) + } + + (1 to 100).foreach { n => + c ! Increment(s"inc-6-$n", ackProbe.ref) + } + + c ! GetValue(stateProbe.ref) + + (6 to 8).foreach { n => + c ! UpdateValue(s"upd-7-$n", n * 1000, ackProbe.ref) + } + + (1 to 3).foreach { n => + c ! Deactivate(s"deact-8-$n", ackProbe.ref) + } + + (1 to 100).foreach { n => + c ! Increment(s"inc-9-$n", ackProbe.ref) + } + + (1 to 3).foreach { n => + c ! Activate(s"act-10-$n", ackProbe.ref) + } + + c ! GetValue(stateProbe.ref) + + unhandledProbe.receiveMessages(10) + val value1 = stateProbe.expectMessageType[State](5.seconds).value val value2 = stateProbe.expectMessageType[State](5.seconds).value val value3 = stateProbe.expectMessageType[State](5.seconds).value @@ -547,7 +544,7 @@ class EventSourcedBehaviorStashSpec c ! "start-stashing" val limit = system.settings.config.getInt("akka.persistence.typed.stash-capacity") - EventFilter.warning(start = "Stash buffer is full, dropping message").intercept { + LoggingEventFilter.warn("Stash buffer is full, dropping message").intercept { (0 to limit).foreach { n => c ! s"cmd-$n" // limit triggers overflow } @@ -572,8 +569,6 @@ class EventSourcedBehaviorStashSpec ConfigFactory .parseString("akka.persistence.typed.stash-overflow-strategy=fail") .withFallback(EventSourcedBehaviorStashSpec.conf)) - failStashTestKit.system.toClassic.eventStream - .publish(Mute(EventFilter.warning(start = "No default snapshot store", occurrences = 1))) try { val probe = failStashTestKit.createTestProbe[AnyRef]() val behavior = @@ -591,13 +586,15 @@ class EventSourcedBehaviorStashSpec c ! "ping" probe.expectMessage("pong") - EventFilter[StashOverflowException](occurrences = 1).intercept { - val limit = system.settings.config.getInt("akka.persistence.typed.stash-capacity") - (0 to limit).foreach { n => - c ! s"cmd-$n" // limit triggers overflow - } - probe.expectTerminated(c, 10.seconds) - }(failStashTestKit.system.toClassic) + LoggingEventFilter + .error[StashOverflowException] + .intercept { + val limit = system.settings.config.getInt("akka.persistence.typed.stash-capacity") + (0 to limit).foreach { n => + c ! s"cmd-$n" // limit triggers overflow + } + probe.expectTerminated(c, 10.seconds) + }(failStashTestKit.system) } finally { failStashTestKit.shutdownTestKit() } @@ -624,6 +621,8 @@ class EventSourcedBehaviorStashSpec "stop from PoisonPill after unstashing completed" in { val c = spawn(counter(nextPid())) val ackProbe = TestProbe[Ack] + val unhandledProbe = createTestProbe[UnhandledMessage]() + system.eventStream ! EventStream.Subscribe(unhandledProbe.ref) c ! Increment("1", ackProbe.ref) ackProbe.expectMessage(Ack("1")) @@ -635,20 +634,20 @@ class EventSourcedBehaviorStashSpec c ! Increment("3", ackProbe.ref) c ! Increment("4", ackProbe.ref) - EventFilter.warning(start = "unhandled message", occurrences = 1).intercept { - // start unstashing - c ! Activate("5", ackProbe.ref) - c.toClassic ! PoisonPill - // 6 shouldn't make it, already stopped - c ! Increment("6", ackProbe.ref) + // start unstashing + c ! Activate("5", ackProbe.ref) + c.toClassic ! PoisonPill + // 6 shouldn't make it, already stopped + c ! Increment("6", ackProbe.ref) - ackProbe.expectMessage(Ack("5")) - // not stopped before 3 and 4 were processed - ackProbe.expectMessage(Ack("3")) - ackProbe.expectMessage(Ack("4")) + ackProbe.expectMessage(Ack("5")) + // not stopped before 3 and 4 were processed + ackProbe.expectMessage(Ack("3")) + ackProbe.expectMessage(Ack("4")) - ackProbe.expectTerminated(c) - } + ackProbe.expectTerminated(c) + + unhandledProbe.receiveMessage() // 6 shouldn't make it, already stopped ackProbe.expectNoMessage(100.millis) @@ -666,11 +665,6 @@ class EventSourcedBehaviorStashSpec ackProbe.expectMessage(Ack("2")) ackProbe.expectMessage(Ack("3")) - // silence some dead letters that may, or may not, occur depending on timing - system.toClassic.eventStream.publish(Mute(EventFilter.warning(start = "unhandled message", occurrences = 100))) - system.toClassic.eventStream.publish(Mute(EventFilter.warning(start = "received dead letter", occurrences = 100))) - system.toClassic.eventStream.publish(Mute(EventFilter.info(pattern = ".*was not delivered.*", occurrences = 100))) - val signalProbe = TestProbe[String] val c2 = spawn(counter(pid, Some(signalProbe.ref))) // this PoisonPill will most likely be received in RequestingRecoveryPermit since it's sent immediately diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorTimersSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorTimersSpec.scala index d83be80e36..1a5b3a8414 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorTimersSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorTimersSpec.scala @@ -14,8 +14,6 @@ import akka.actor.typed.ActorRef import akka.actor.typed.Behavior import akka.actor.typed.scaladsl.Behaviors import akka.persistence.typed.PersistenceId -import akka.testkit.EventFilter -import akka.testkit.TestEvent.Mute import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import org.scalatest.WordSpecLike @@ -26,7 +24,6 @@ object EventSourcedBehaviorTimersSpec { def config: Config = ConfigFactory.parseString(s""" akka.loglevel = INFO - akka.loggers = [akka.testkit.TestEventListener] akka.persistence.journal.leveldb.dir = "target/typed-persistence-${UUID.randomUUID().toString}" akka.persistence.journal.plugin = "akka.persistence.journal.leveldb" """) @@ -77,19 +74,14 @@ object EventSourcedBehaviorTimersSpec { class EventSourcedBehaviorTimersSpec extends ScalaTestWithActorTestKit(EventSourcedBehaviorTimersSpec.config) - with WordSpecLike { + with WordSpecLike + with LogCapturing { import EventSourcedBehaviorTimersSpec._ val pidCounter = new AtomicInteger(0) private def nextPid(): PersistenceId = PersistenceId(s"c${pidCounter.incrementAndGet()})") - import akka.actor.typed.scaladsl.adapter._ - // needed for the classic event filter - private implicit val classicSystem: akka.actor.ActorSystem = system.toClassic - - classicSystem.eventStream.publish(Mute(EventFilter.warning(start = "No default snapshot store", occurrences = 1))) - "EventSourcedBehavior withTimers" must { "be able to schedule message" in { diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedEventAdapterSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedEventAdapterSpec.scala index 83f9a0f7ed..d81aac7ee6 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedEventAdapterSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedEventAdapterSpec.scala @@ -9,6 +9,7 @@ import java.util.concurrent.atomic.AtomicInteger import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.ActorRef import akka.actor.typed.scaladsl.Behaviors import akka.persistence.query.EventEnvelope @@ -20,16 +21,13 @@ import akka.persistence.typed.EventSeq import akka.persistence.typed.PersistenceId import akka.serialization.jackson.CborSerializable import akka.stream.scaladsl.Sink -import akka.testkit.EventFilter import akka.testkit.JavaSerializable -import akka.testkit.TestEvent.Mute import com.typesafe.config.ConfigFactory import org.scalatest.WordSpecLike object EventSourcedEventAdapterSpec { private val conf = ConfigFactory.parseString(s""" - akka.loggers = [akka.testkit.TestEventListener] akka.persistence.journal.leveldb.dir = "target/typed-persistence-${UUID.randomUUID().toString}" akka.persistence.journal.plugin = "akka.persistence.journal.leveldb" """) @@ -84,7 +82,8 @@ object EventSourcedEventAdapterSpec { class EventSourcedEventAdapterSpec extends ScalaTestWithActorTestKit(EventSourcedEventAdapterSpec.conf) - with WordSpecLike { + with WordSpecLike + with LogCapturing { import EventSourcedEventAdapterSpec._ import EventSourcedBehaviorSpec.{ counter, @@ -98,7 +97,6 @@ class EventSourcedEventAdapterSpec } import akka.actor.typed.scaladsl.adapter._ - system.toClassic.eventStream.publish(Mute(EventFilter.warning(start = "No default snapshot store", occurrences = 1))) val pidCounter = new AtomicInteger(0) private def nextPid(): PersistenceId = PersistenceId(s"c${pidCounter.incrementAndGet()})") diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedSequenceNumberSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedSequenceNumberSpec.scala index 0e2f031e4d..eed8d2a67e 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedSequenceNumberSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedSequenceNumberSpec.scala @@ -4,20 +4,18 @@ package akka.persistence.typed.scaladsl +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.testkit.typed.scaladsl.{ ScalaTestWithActorTestKit, TestProbe } import akka.actor.typed.{ ActorRef, Behavior } import akka.actor.typed.scaladsl.Behaviors import akka.persistence.typed.PersistenceId import akka.persistence.typed.RecoveryCompleted -import akka.testkit.EventFilter -import akka.testkit.TestEvent.Mute import com.typesafe.config.ConfigFactory import org.scalatest.WordSpecLike object EventSourcedSequenceNumberSpec { private val conf = ConfigFactory.parseString(s""" - akka.loggers = [akka.testkit.TestEventListener] akka.persistence.journal.plugin = "akka.persistence.journal.inmem" akka.persistence.journal.inmem.test-serialization = on """) @@ -26,10 +24,8 @@ object EventSourcedSequenceNumberSpec { class EventSourcedSequenceNumberSpec extends ScalaTestWithActorTestKit(EventSourcedSequenceNumberSpec.conf) - with WordSpecLike { - - import akka.actor.typed.scaladsl.adapter._ - system.toClassic.eventStream.publish(Mute(EventFilter.warning(start = "No default snapshot store", occurrences = 1))) + with WordSpecLike + with LogCapturing { private def behavior(pid: PersistenceId, probe: ActorRef[String]): Behavior[String] = Behaviors.setup(ctx => diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedSnapshotAdapterSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedSnapshotAdapterSpec.scala index a8ac972b42..73705d7e8b 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedSnapshotAdapterSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedSnapshotAdapterSpec.scala @@ -7,6 +7,7 @@ package akka.persistence.typed.scaladsl import java.util.UUID import java.util.concurrent.atomic.AtomicInteger +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe import akka.actor.typed.ActorRef @@ -34,7 +35,8 @@ object EventSourcedSnapshotAdapterSpec { class EventSourcedSnapshotAdapterSpec extends ScalaTestWithActorTestKit(EventSourcedSnapshotAdapterSpec.conf) - with WordSpecLike { + with WordSpecLike + with LogCapturing { import EventSourcedSnapshotAdapterSpec._ import akka.actor.typed.scaladsl.adapter._ diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/LoggerSourceSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/LoggerSourceSpec.scala index 34796705cb..5e1a396e5a 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/LoggerSourceSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/LoggerSourceSpec.scala @@ -5,29 +5,24 @@ package akka.persistence.typed.scaladsl import java.util.concurrent.atomic.AtomicInteger +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.Behavior import akka.actor.typed.scaladsl.Behaviors -import akka.actor.typed.scaladsl.adapter._ -import akka.event.Logging.LogEvent import akka.persistence.typed.PersistenceId import akka.persistence.typed.RecoveryCompleted import akka.persistence.typed.SnapshotCompleted import akka.persistence.typed.SnapshotFailed -import akka.testkit.EventFilter -import com.typesafe.config.ConfigFactory import org.scalatest.WordSpecLike +import org.slf4j.event.Level // Note that the spec name here is important since there are heuristics in place to avoid names // starting with EventSourcedBehavior class LoggerSourceSpec - extends ScalaTestWithActorTestKit( - ConfigFactory - .parseString("akka.loggers = [akka.testkit.TestEventListener]") - .withFallback(EventSourcedBehaviorSpec.conf)) - with WordSpecLike { - - implicit val classic = system.toClassic // FIXME #24348: eventfilter support in testkit + extends ScalaTestWithActorTestKit(EventSourcedBehaviorSpec.conf) + with WordSpecLike + with LogCapturing { private val pidCounter = new AtomicInteger(0) private def nextPid(): PersistenceId = PersistenceId(s"c${pidCounter.incrementAndGet()})") @@ -53,7 +48,7 @@ class LoggerSourceSpec // one test case leaks to another, the actual log class is what is tested in each individual case "log from setup" in { - EventFilter.info("recovery-completed", occurrences = 1).intercept { + LoggingEventFilter.info("recovery-completed").intercept { eventFilterFor("setting-up-behavior").intercept { spawn(behavior) } @@ -62,7 +57,7 @@ class LoggerSourceSpec } "log from recovery completed" in { - EventFilter.info("setting-up-behavior", occurrences = 1).intercept { + LoggingEventFilter.info("setting-up-behavior").intercept { eventFilterFor("recovery-completed").intercept { spawn(behavior) } @@ -70,16 +65,22 @@ class LoggerSourceSpec } "log from command handler" in { - EventFilter.info(pattern = "(setting-up-behavior|recovery-completed|event-received)", occurrences = 3).intercept { - eventFilterFor("command-received").intercept { - spawn(behavior) ! "cmd" + LoggingEventFilter.empty + .withLogLevel(Level.INFO) + .withMessageRegex("(setting-up-behavior|recovery-completed|event-received)") + .withOccurrences(3) + .intercept { + eventFilterFor("command-received").intercept { + spawn(behavior) ! "cmd" + } } - } } "log from event handler" in { - EventFilter - .info(pattern = "(setting-up-behavior|recovery-completed|command-received)", occurrences = 3) + LoggingEventFilter.empty + .withLogLevel(Level.INFO) + .withMessageRegex("(setting-up-behavior|recovery-completed|command-received)") + .withOccurrences(3) .intercept { eventFilterFor("event-received").intercept { spawn(behavior) ! "cmd" @@ -89,13 +90,8 @@ class LoggerSourceSpec } def eventFilterFor(logMsg: String) = - EventFilter.custom( - { - case l: LogEvent if l.message == logMsg => - if (l.logClass == classOf[LoggerSourceSpec]) true - else fail(s"Unexpected log source: ${l.logClass} for message ${l.message}") - case _ => false - }, - occurrences = 1) + LoggingEventFilter.custom { logEvent => + logEvent.message == logMsg && logEvent.loggerName == classOf[LoggerSourceSpec].getName + } } diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/NullEmptyStateSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/NullEmptyStateSpec.scala index e7d5e00faf..69bd82f828 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/NullEmptyStateSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/NullEmptyStateSpec.scala @@ -10,27 +10,24 @@ import akka.actor.typed.ActorRef import akka.actor.typed.Behavior import akka.persistence.typed.PersistenceId import akka.persistence.typed.RecoveryCompleted -import akka.testkit.EventFilter -import akka.testkit.TestEvent.Mute import com.typesafe.config.ConfigFactory import org.scalatest.WordSpecLike object NullEmptyStateSpec { private val conf = ConfigFactory.parseString(s""" - akka.loggers = [akka.testkit.TestEventListener] akka.persistence.journal.plugin = "akka.persistence.journal.inmem" akka.persistence.journal.inmem.test-serialization = on """) } -class NullEmptyStateSpec extends ScalaTestWithActorTestKit(NullEmptyStateSpec.conf) with WordSpecLike { +class NullEmptyStateSpec + extends ScalaTestWithActorTestKit(NullEmptyStateSpec.conf) + with WordSpecLike + with LogCapturing { implicit val testSettings = TestKitSettings(system) - import akka.actor.typed.scaladsl.adapter._ - system.toClassic.eventStream.publish(Mute(EventFilter.warning(start = "No default snapshot store", occurrences = 1))) - def primitiveState(persistenceId: PersistenceId, probe: ActorRef[String]): Behavior[String] = EventSourcedBehavior[String, String, String]( persistenceId, diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/OptionalSnapshotStoreSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/OptionalSnapshotStoreSpec.scala index 26932df94c..7721e969b0 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/OptionalSnapshotStoreSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/OptionalSnapshotStoreSpec.scala @@ -6,13 +6,13 @@ package akka.persistence.typed.scaladsl import java.util.UUID +import akka.actor.testkit.typed.scaladsl.LoggingEventFilter import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe -import akka.actor.typed.scaladsl.adapter.TypedActorSystemOps +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.persistence.typed.PersistenceId import akka.persistence.typed.scaladsl.EventSourcedBehavior.CommandHandler import akka.serialization.jackson.CborSerializable -import akka.testkit.EventFilter import org.scalatest.WordSpecLike object OptionalSnapshotStoreSpec { @@ -42,23 +42,19 @@ object OptionalSnapshotStoreSpec { } class OptionalSnapshotStoreSpec extends ScalaTestWithActorTestKit(s""" - akka.loggers = [akka.testkit.TestEventListener] akka.persistence.publish-plugin-commands = on akka.persistence.journal.plugin = "akka.persistence.journal.inmem" akka.persistence.journal.inmem.test-serialization = on # snapshot store plugin is NOT defined, things should still work akka.persistence.snapshot-store.local.dir = "target/snapshots-${classOf[OptionalSnapshotStoreSpec].getName}/" - """) with WordSpecLike { + """) with WordSpecLike with LogCapturing { import OptionalSnapshotStoreSpec._ - // Needed for the classic event filter - implicit val classic = system.toClassic - "Persistence extension" must { "initialize properly even in absence of configured snapshot store" in { - EventFilter.warning(start = "No default snapshot store configured", occurrences = 1).intercept { + LoggingEventFilter.warn("No default snapshot store configured").intercept { val stateProbe = TestProbe[State]() spawn(persistentBehavior(stateProbe)) stateProbe.expectNoMessage() @@ -66,8 +62,8 @@ class OptionalSnapshotStoreSpec extends ScalaTestWithActorTestKit(s""" } "fail if PersistentActor tries to saveSnapshot without snapshot-store available" in { - EventFilter.error(pattern = ".*No snapshot store configured.*", occurrences = 1).intercept { - EventFilter.warning(pattern = ".*Failed to save snapshot.*", occurrences = 1).intercept { + LoggingEventFilter.error("No snapshot store configured").intercept { + LoggingEventFilter.warn("Failed to save snapshot").intercept { val stateProbe = TestProbe[State]() val persistentActor = spawn(persistentBehavior(stateProbe)) persistentActor ! AnyCommand diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/PerformanceSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/PerformanceSpec.scala index e54f56194f..f39a82aa11 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/PerformanceSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/PerformanceSpec.scala @@ -11,6 +11,7 @@ import scala.concurrent.duration._ import akka.actor.testkit.typed.TestException import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.TestProbe +import akka.actor.testkit.typed.scaladsl.LogCapturing import akka.actor.typed.ActorRef import akka.actor.typed.SupervisorStrategy import akka.actor.typed.scaladsl.Behaviors @@ -118,7 +119,7 @@ class PerformanceSpec extends ScalaTestWithActorTestKit(ConfigFactory.parseStrin akka.persistence.snapshot-store.plugin = "akka.persistence.snapshot-store.local" akka.persistence.snapshot-store.local.dir = "target/snapshots-PerformanceSpec/" akka.test.single-expect-default = 10s - """).withFallback(ConfigFactory.parseString(PerformanceSpec.config))) with WordSpecLike { + """).withFallback(ConfigFactory.parseString(PerformanceSpec.config))) with WordSpecLike with LogCapturing { import PerformanceSpec._ diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/PrimitiveStateSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/PrimitiveStateSpec.scala index ac18b6a923..05fe00c18f 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/PrimitiveStateSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/PrimitiveStateSpec.scala @@ -9,24 +9,21 @@ import akka.actor.typed.ActorRef import akka.actor.typed.Behavior import akka.persistence.typed.PersistenceId import akka.persistence.typed.RecoveryCompleted -import akka.testkit.EventFilter -import akka.testkit.TestEvent.Mute import com.typesafe.config.ConfigFactory import org.scalatest.WordSpecLike object PrimitiveStateSpec { private val conf = ConfigFactory.parseString(s""" - akka.loggers = [akka.testkit.TestEventListener] akka.persistence.journal.plugin = "akka.persistence.journal.inmem" akka.persistence.journal.inmem.test-serialization = on """) } -class PrimitiveStateSpec extends ScalaTestWithActorTestKit(PrimitiveStateSpec.conf) with WordSpecLike { - - import akka.actor.typed.scaladsl.adapter._ - system.toClassic.eventStream.publish(Mute(EventFilter.warning(start = "No default snapshot store", occurrences = 1))) +class PrimitiveStateSpec + extends ScalaTestWithActorTestKit(PrimitiveStateSpec.conf) + with WordSpecLike + with LogCapturing { def primitiveState(persistenceId: PersistenceId, probe: ActorRef[String]): Behavior[Int] = EventSourcedBehavior[Int, Int, Int]( diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/SnapshotMutableStateSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/SnapshotMutableStateSpec.scala index 9c0039cd25..1b8a21a2a5 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/SnapshotMutableStateSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/SnapshotMutableStateSpec.scala @@ -117,7 +117,10 @@ object SnapshotMutableStateSpec { } -class SnapshotMutableStateSpec extends ScalaTestWithActorTestKit(SnapshotMutableStateSpec.conf) with WordSpecLike { +class SnapshotMutableStateSpec + extends ScalaTestWithActorTestKit(SnapshotMutableStateSpec.conf) + with WordSpecLike + with LogCapturing { import SnapshotMutableStateSpec._ diff --git a/akka-persistence-typed/src/test/scala/docs/akka/persistence/typed/BasicPersistentBehaviorCompileOnly.scala b/akka-persistence-typed/src/test/scala/docs/akka/persistence/typed/BasicPersistentBehaviorCompileOnly.scala index 925ba8d93a..87d087a081 100644 --- a/akka-persistence-typed/src/test/scala/docs/akka/persistence/typed/BasicPersistentBehaviorCompileOnly.scala +++ b/akka-persistence-typed/src/test/scala/docs/akka/persistence/typed/BasicPersistentBehaviorCompileOnly.scala @@ -9,6 +9,7 @@ import scala.concurrent.duration._ import akka.actor.typed.Behavior import akka.actor.typed.SupervisorStrategy import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.LoggerOps import akka.persistence.typed.DeleteEventsFailed import akka.persistence.typed.DeleteSnapshotsFailed import akka.persistence.typed.EventAdapter @@ -123,7 +124,7 @@ object BasicPersistentBehaviorCompileOnly { val debugAlwaysSnapshot: Behavior[Command] = Behaviors.setup { context => samplePersistentBehavior.snapshotWhen((state, _, _) => { - context.log.info("Snapshot actor {} => state: {}", context.self.path.name, state) + context.log.info2("Snapshot actor {} => state: {}", context.self.path.name, state) true }) } diff --git a/akka-persistence-typed/src/test/scala/docs/akka/persistence/typed/PersistentFsmToTypedMigrationSpec.scala b/akka-persistence-typed/src/test/scala/docs/akka/persistence/typed/PersistentFsmToTypedMigrationSpec.scala index 6ffb2d2b78..3128d81141 100644 --- a/akka-persistence-typed/src/test/scala/docs/akka/persistence/typed/PersistentFsmToTypedMigrationSpec.scala +++ b/akka-persistence-typed/src/test/scala/docs/akka/persistence/typed/PersistentFsmToTypedMigrationSpec.scala @@ -32,9 +32,10 @@ import akka.persistence.typed.SnapshotAdapter import com.typesafe.config.ConfigFactory import org.scalatest.WordSpec import org.scalatest.concurrent.ScalaFutures - import scala.concurrent.duration._ +import akka.actor.testkit.typed.scaladsl.LogCapturing + object PersistentFsmToTypedMigrationSpec { val config = ConfigFactory.parseString(s""" akka.actor.allow-java-serialization = on @@ -205,7 +206,7 @@ object ShoppingCartBehavior { } -class PersistentFsmToTypedMigrationSpec extends WordSpec with ScalaFutures { +class PersistentFsmToTypedMigrationSpec extends WordSpec with ScalaFutures with LogCapturing { import akka.persistence.fsm.PersistentFSMSpec._ diff --git a/build.sbt b/build.sbt index 7fa14e7742..ecf88bd06e 100644 --- a/build.sbt +++ b/build.sbt @@ -426,6 +426,7 @@ lazy val testkit = akkaModule("akka-testkit") lazy val actorTyped = akkaModule("akka-actor-typed") .dependsOn(actor) .settings(AutomaticModuleName.settings("akka.actor.typed")) // fine for now, eventually new module name to become typed.actor + .settings(Dependencies.actorTyped) .settings(OSGi.actorTyped) .settings(initialCommands := """ @@ -495,7 +496,9 @@ lazy val streamTyped = akkaModule("akka-stream-typed") .enablePlugins(ScaladocNoVerificationOfDiagrams) lazy val actorTestkitTyped = akkaModule("akka-actor-testkit-typed") - .dependsOn(actorTyped, testkit % "compile->compile;test->test") + .dependsOn(actorTyped, + slf4j, + testkit % "compile->compile;test->test") .settings(AutomaticModuleName.settings("akka.actor.testkit.typed")) .settings(Dependencies.actorTestkitTyped) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 0e2ed4ca23..5074a28701 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -15,7 +15,7 @@ object Dependencies { lazy val java8CompatVersion = settingKey[String]("The version of scala-java8-compat to use.") val junitVersion = "4.12" - val slf4jVersion = "1.7.25" + val slf4jVersion = "1.7.27" // check agrona version when updating this val aeronVersion = "1.19.1" // needs to be inline with the aeron version @@ -97,6 +97,8 @@ object Dependencies { val protobufRuntime = "com.google.protobuf" % "protobuf-java" % "3.9.0" + val logback = "ch.qos.logback" % "logback-classic" % "1.2.3" // EPL 1.0 + object Docs { val sprayJson = "io.spray" %% "spray-json" % "1.3.5" % "test" val gson = "com.google.code.gson" % "gson" % "2.8.5" % "test" @@ -107,7 +109,7 @@ object Dependencies { val commonsIo = "commons-io" % "commons-io" % "2.6" % "test" // ApacheV2 val commonsCodec = "commons-codec" % "commons-codec" % "1.11" % "test" // ApacheV2 val junit = "junit" % "junit" % junitVersion % "test" // Common Public License 1.0 - val logback = "ch.qos.logback" % "logback-classic" % "1.2.3" % "test" // EPL 1.0 / LGPL 2.1 + val logback = Compile.logback % "test" // EPL 1.0 val mockito = "org.mockito" % "mockito-core" % "2.19.1" % "test" // MIT // changing the scalatest dependency must be reflected in akka-docs/rst/dev/multi-jvm-testing.rst val scalatest = Def.setting { "org.scalatest" %% "scalatest" % scalaTestVersion.value % "test" } // ApacheV2 @@ -154,6 +156,8 @@ object Dependencies { val scalatest = Def.setting { "org.scalatest" %% "scalatest" % scalaTestVersion.value % "optional;provided;test" } // ApacheV2 + val logback = Compile.logback % "optional;provided;test" // EPL 1.0 + } } @@ -164,6 +168,8 @@ object Dependencies { val actor = l ++= Seq(config, java8Compat.value) + val actorTyped = l ++= Seq(slf4jApi) + val discovery = l ++= Seq(Test.junit, Test.scalatest.value) val coordination = l ++= Seq(Test.junit, Test.scalatest.value) @@ -181,7 +187,7 @@ object Dependencies { Provided.activation // dockerClient needs javax.activation.DataSource in JDK 11+ ) - val actorTestkitTyped = l ++= Seq(Provided.junit, Provided.scalatest.value) + val actorTestkitTyped = l ++= Seq(Provided.logback, Provided.junit, Provided.scalatest.value) val remoteDependencies = Seq(netty, aeronDriver, aeronClient) val remoteOptionalDependencies = remoteDependencies.map(_ % "optional")