diff --git a/akka-actor-tests/src/test/scala/akka/event/LoggerSpec.scala b/akka-actor-tests/src/test/scala/akka/event/LoggerSpec.scala new file mode 100644 index 0000000000..f4b6bf5dd9 --- /dev/null +++ b/akka-actor-tests/src/test/scala/akka/event/LoggerSpec.scala @@ -0,0 +1,96 @@ +/** + * Copyright (C) 2009-2013 Typesafe Inc. + */ +package akka.event + +import akka.testkit._ +import scala.concurrent.duration._ +import com.typesafe.config.{ Config, ConfigFactory } +import akka.actor.{ ActorRef, Actor, ActorSystem } +import org.scalatest.WordSpec +import org.scalatest.matchers.MustMatchers +import akka.event.Logging.{ LogEvent, LoggerInitialized, InitializeLogger } + +object LoggerSpec { + + val defaultConfig = ConfigFactory.parseString(""" + akka { + stdout-loglevel = "WARNING" + loglevel = "DEBUG" + loggers = ["akka.event.LoggerSpec$TestLogger"] + } + """).withFallback(AkkaSpec.testConf) + + val noLoggingConfig = ConfigFactory.parseString(""" + akka { + stdout-loglevel = "OFF" + loglevel = "OFF" + loggers = ["akka.event.LoggerSpec$TestLogger"] + } + """).withFallback(AkkaSpec.testConf) + + case class SetTarget(ref: ActorRef) + + class TestLogger extends Actor with Logging.StdOutLogger { + var target: Option[ActorRef] = None + override def receive: Receive = { + case InitializeLogger(bus) ⇒ + bus.subscribe(context.self, classOf[SetTarget]) + sender ! LoggerInitialized + case SetTarget(ref) ⇒ + target = Some(ref) + ref ! ("OK") + case event: LogEvent ⇒ + print(event) + target foreach { _ ! event.message } + } + } +} + +@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner]) +class LoggerSpec extends WordSpec with MustMatchers { + + import LoggerSpec._ + + private def createSystemAndLogToBuffer(name: String, config: Config, shouldLog: Boolean) = { + val out = new java.io.ByteArrayOutputStream() + Console.withOut(out) { + implicit val system = ActorSystem(name, config) + try { + val probe = TestProbe() + system.eventStream.publish(SetTarget(probe.ref)) + probe.expectMsg("OK") + system.log.error("Danger! Danger!") + // since logging is asynchronous ensure that it propagates + if (shouldLog) { + probe.fishForMessage(0.5.seconds.dilated) { + case "Danger! Danger!" ⇒ true + case _ ⇒ false + } + } else { + probe.expectNoMsg(0.5.seconds.dilated) + } + } finally { + system.shutdown() + system.awaitTermination(5.seconds.dilated) + } + } + out + } + + "A normally configured actor system" must { + + "log messages to standard output" in { + val out = createSystemAndLogToBuffer("defaultLogger", defaultConfig, true) + out.size must be > (0) + } + } + + "An actor system configured with the logging turned off" must { + + "not log messages to standard output" in { + val out = createSystemAndLogToBuffer("noLogging", noLoggingConfig, false) + out.size must be(0) + } + } +} diff --git a/akka-actor/src/main/resources/reference.conf b/akka-actor/src/main/resources/reference.conf index 1eed4db675..bff860665e 100644 --- a/akka-actor/src/main/resources/reference.conf +++ b/akka-actor/src/main/resources/reference.conf @@ -29,11 +29,11 @@ akka { # Log level used by the configured loggers (see "loggers") as soon # as they have been started; before that, see "stdout-loglevel" - # Options: ERROR, WARNING, INFO, DEBUG + # Options: OFF, ERROR, WARNING, INFO, DEBUG loglevel = "INFO" # Log level for the very basic logger activated during AkkaApplication startup - # Options: ERROR, WARNING, INFO, DEBUG + # Options: OFF, ERROR, WARNING, INFO, DEBUG stdout-loglevel = "WARNING" # Log the complete configuration at INFO level when the actor system is started. diff --git a/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala b/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala index 2d1c15d12d..58a8e86b1c 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala @@ -456,7 +456,7 @@ class LocalActorRefProvider private[akka] ( def stopWhenAllTerminationHooksDone(): Unit = if (terminationHooks.isEmpty) { - eventStream.stopDefaultLoggers() + eventStream.stopDefaultLoggers(system) context.stop(self) } diff --git a/akka-actor/src/main/scala/akka/event/Logging.scala b/akka-actor/src/main/scala/akka/event/Logging.scala index dab5b14504..53504c5d2c 100644 --- a/akka-actor/src/main/scala/akka/event/Logging.scala +++ b/akka-actor/src/main/scala/akka/event/Logging.scala @@ -67,19 +67,24 @@ trait LoggingBus extends ActorEventBus { _logLevel = level } - /** - * Internal Akka use only - */ - private[akka] def startStdoutLogger(config: Settings) { + private def setUpStdoutLogger(config: Settings) { val level = levelFor(config.StdoutLogLevel) getOrElse { + // only log initialization errors directly with StandardOutLogger.print StandardOutLogger.print(Error(new LoggerException, simpleName(this), this.getClass, "unknown akka.stdout-loglevel " + config.StdoutLogLevel)) ErrorLevel } AllLogLevels filter (level >= _) foreach (l ⇒ subscribe(StandardOutLogger, classFor(l))) guard.withGuard { - loggers = Seq(StandardOutLogger) + loggers :+= StandardOutLogger _logLevel = level } + } + + /** + * Internal Akka use only + */ + private[akka] def startStdoutLogger(config: Settings) { + setUpStdoutLogger(config) publish(Debug(simpleName(this), this.getClass, "StandardOutLogger started")) } @@ -89,7 +94,8 @@ trait LoggingBus extends ActorEventBus { private[akka] def startDefaultLoggers(system: ActorSystemImpl) { val logName = simpleName(this) + "(" + system + ")" val level = levelFor(system.settings.LogLevel) getOrElse { - StandardOutLogger.print(Error(new LoggerException, logName, this.getClass, "unknown akka.stdout-loglevel " + system.settings.LogLevel)) + // only log initialization errors directly with StandardOutLogger.print + StandardOutLogger.print(Error(new LoggerException, logName, this.getClass, "unknown akka.loglevel " + system.settings.LogLevel)) ErrorLevel } try { @@ -99,7 +105,7 @@ trait LoggingBus extends ActorEventBus { case loggers ⇒ loggers } case loggers ⇒ - StandardOutLogger.print(Warning(logName, this.getClass, "[akka.event-handlers] config is deprecated, use [akka.loggers]")) + publish(Warning(logName, this.getClass, "[akka.event-handlers] config is deprecated, use [akka.loggers]")) loggers } val myloggers = @@ -145,10 +151,10 @@ trait LoggingBus extends ActorEventBus { /** * Internal Akka use only */ - private[akka] def stopDefaultLoggers() { + private[akka] def stopDefaultLoggers(system: ActorSystem) { val level = _logLevel // volatile access before reading loggers if (!(loggers contains StandardOutLogger)) { - AllLogLevels filter (level >= _) foreach (l ⇒ subscribe(StandardOutLogger, classFor(l))) + setUpStdoutLogger(system.settings) publish(Debug(simpleName(this), this.getClass, "shutting down: StandardOutLogger started")) } for { @@ -173,7 +179,7 @@ trait LoggingBus extends ActorEventBus { val actor = system.systemActorOf(Props(clazz), name) implicit def timeout = if (system.settings.EventHandlerStartTimeout.duration >= Duration.Zero) { - StandardOutLogger.print(Warning(logName, this.getClass, + publish(Warning(logName, this.getClass, "[akka.event-handler-startup-timeout] config is deprecated, use [akka.logger-startup-timeout]")) system.settings.EventHandlerStartTimeout } else system.settings.LoggerStartTimeout @@ -424,17 +430,26 @@ object Logging { final val InfoLevel = LogLevel(3) final val DebugLevel = LogLevel(4) + /** + * Internal Akka use only + * + * Don't include the OffLevel in the AllLogLevels since we should never subscribe + * to some kind of OffEvent. + */ + private final val OffLevel = LogLevel(Int.MinValue) + /** * Returns the LogLevel associated with the given string, * valid inputs are upper or lowercase (not mixed) versions of: * "error", "warning", "info" and "debug" */ - def levelFor(s: String): Option[LogLevel] = s match { - case "ERROR" | "error" ⇒ Some(ErrorLevel) - case "WARNING" | "warning" ⇒ Some(WarningLevel) - case "INFO" | "info" ⇒ Some(InfoLevel) - case "DEBUG" | "debug" ⇒ Some(DebugLevel) - case unknown ⇒ None + def levelFor(s: String): Option[LogLevel] = s.toLowerCase match { + case "off" ⇒ Some(OffLevel) + case "error" ⇒ Some(ErrorLevel) + case "warning" ⇒ Some(WarningLevel) + case "info" ⇒ Some(InfoLevel) + case "debug" ⇒ Some(DebugLevel) + case unknown ⇒ None } /** diff --git a/akka-docs/rst/general/configuration.rst b/akka-docs/rst/general/configuration.rst index 94054ac007..d5feabe4d2 100644 --- a/akka-docs/rst/general/configuration.rst +++ b/akka-docs/rst/general/configuration.rst @@ -95,12 +95,12 @@ A custom ``application.conf`` might look like this:: # Log level used by the configured loggers (see "loggers") as soon # as they have been started; before that, see "stdout-loglevel" - # Options: ERROR, WARNING, INFO, DEBUG - loglevel = DEBUG + # Options: OFF, ERROR, WARNING, INFO, DEBUG + loglevel = "DEBUG" # Log level for the very basic logger activated during AkkaApplication startup - # Options: ERROR, WARNING, INFO, DEBUG - stdout-loglevel = DEBUG + # Options: OFF, ERROR, WARNING, INFO, DEBUG + stdout-loglevel = "DEBUG" actor { default-dispatcher { @@ -222,11 +222,11 @@ from the whole class path, it is easiest to utilize that functionality and differentiate actor systems within the hierarchy of the configuration:: myapp1 { - akka.loglevel = WARNING + akka.loglevel = "WARNING" my.own.setting = 43 } myapp2 { - akka.loglevel = ERROR + akka.loglevel = "ERROR" app2.setting = "appname" } my.own.setting = 42 @@ -245,15 +245,17 @@ system is this .. code-block:: ruby - akka.loglevel = WARNING + akka.loglevel = "WARNING" my.own.setting = 43 my.other.setting = "hello" // plus myapp1 and myapp2 subtrees while in the second one, only the “akka” subtree is lifted, with the following -result:: +result - akka.loglevel = ERROR +.. code-block:: ruby + + akka.loglevel = "ERROR" my.own.setting = 42 my.other.setting = "hello" // plus myapp1 and myapp2 subtrees diff --git a/akka-docs/rst/java/logging.rst b/akka-docs/rst/java/logging.rst index e75aff0e55..73bd2825eb 100644 --- a/akka-docs/rst/java/logging.rst +++ b/akka-docs/rst/java/logging.rst @@ -56,7 +56,7 @@ You almost definitely need to have logging set to DEBUG to use any of the option .. code-block:: ruby akka { - loglevel = DEBUG + loglevel = "DEBUG" } This config option is very good if you want to know what config settings are loaded by Akka: @@ -153,6 +153,20 @@ If you want to see all messages that are received through remoting at DEBUG log Also see the logging options for TestKit: :ref:`actor.logging-java`. +Turn Off Logging +---------------- + +To turn off logging you can configure the log levels to be ``OFF`` like this. + +.. code-block:: ruby + + akka { + stdout-loglevel = "OFF" + loglevel = "OFF" + } + +The ``stdout-loglevel`` is only in effect during system startup and shutdown, and setting +it to ``OFF`` as well, ensures that nothing gets logged during system startup or shutdown. Loggers ======= @@ -167,7 +181,7 @@ Here you can also define the log level. # Loggers to register at boot time (akka.event.Logging$DefaultLogger logs # to STDOUT) loggers = ["akka.event.Logging$DefaultLogger"] - # Options: ERROR, WARNING, INFO, DEBUG + # Options: OFF, ERROR, WARNING, INFO, DEBUG loglevel = "DEBUG" } diff --git a/akka-docs/rst/java/testing.rst b/akka-docs/rst/java/testing.rst index b0828c41d9..0f8311821a 100644 --- a/akka-docs/rst/java/testing.rst +++ b/akka-docs/rst/java/testing.rst @@ -618,7 +618,7 @@ All these messages are logged at ``DEBUG`` level. To summarize, you can enable full logging of actor activities using this configuration fragment:: akka { - loglevel = DEBUG + loglevel = "DEBUG" actor { debug { autoreceive = on diff --git a/akka-docs/rst/scala/code/docs/actor/FaultHandlingDocSample.scala b/akka-docs/rst/scala/code/docs/actor/FaultHandlingDocSample.scala index 5be1f9a542..fd54bbcf53 100644 --- a/akka-docs/rst/scala/code/docs/actor/FaultHandlingDocSample.scala +++ b/akka-docs/rst/scala/code/docs/actor/FaultHandlingDocSample.scala @@ -23,7 +23,7 @@ object FaultHandlingDocSample extends App { import Worker._ val config = ConfigFactory.parseString(""" - akka.loglevel = DEBUG + akka.loglevel = "DEBUG" akka.actor.debug { receive = on lifecycle = on diff --git a/akka-docs/rst/scala/logging.rst b/akka-docs/rst/scala/logging.rst index 130569aa4c..f004770f5c 100644 --- a/akka-docs/rst/scala/logging.rst +++ b/akka-docs/rst/scala/logging.rst @@ -57,7 +57,7 @@ You almost definitely need to have logging set to DEBUG to use any of the option .. code-block:: ruby akka { - loglevel = DEBUG + loglevel = "DEBUG" } This config option is very good if you want to know what config settings are loaded by Akka: @@ -196,6 +196,21 @@ purposes as it contains exactly the default behavior. to look up the logger instance to use instead of the class’ name), and you might want to do this also in case you implement your own logging adapter. +Turn Off Logging +---------------- + +To turn off logging you can configure the log levels to be ``OFF`` like this. + +.. code-block:: ruby + + akka { + stdout-loglevel = "OFF" + loglevel = "OFF" + } + +The ``stdout-loglevel`` is only in effect during system startup and shutdown, and setting +it to ``OFF`` as well, ensures that nothing gets logged during system startup or shutdown. + Loggers ======= @@ -210,7 +225,7 @@ also define the log level. # Loggers to register at boot time (akka.event.Logging$DefaultLogger logs # to STDOUT) loggers = ["akka.event.Logging$DefaultLogger"] - # Options: ERROR, WARNING, INFO, DEBUG + # Options: OFF, ERROR, WARNING, INFO, DEBUG loglevel = "DEBUG" } diff --git a/akka-docs/rst/scala/testing.rst b/akka-docs/rst/scala/testing.rst index ac6a63423d..0de33fc35c 100644 --- a/akka-docs/rst/scala/testing.rst +++ b/akka-docs/rst/scala/testing.rst @@ -698,7 +698,7 @@ All these messages are logged at ``DEBUG`` level. To summarize, you can enable full logging of actor activities using this configuration fragment:: akka { - loglevel = DEBUG + loglevel = "DEBUG" actor { debug { receive = on