diff --git a/akka-actor-tests/src/test/scala/akka/actor/SupervisorMiscSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/SupervisorMiscSpec.scala index caeacfb3db..f644842591 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/SupervisorMiscSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/SupervisorMiscSpec.scala @@ -60,5 +60,22 @@ class SupervisorMiscSpec extends AkkaSpec(SupervisorMiscSpec.config) with Defaul assert(Await.result(actor4 ? "status", timeout.duration) == "OK", "actor4 is shutdown") } } + + "be able to create named children in its constructor" in { + val a = system.actorOf(Props(new Actor { + context.actorOf(Props.empty, "bob") + def receive = { + case x: Exception ⇒ throw x + } + override def preStart(): Unit = testActor ! "preStart" + })) + val m = "weird message" + EventFilter[Exception](m, occurrences = 1) intercept { + a ! new Exception(m) + } + expectMsg("preStart") + expectMsg("preStart") + a.isTerminated must be(false) + } } } diff --git a/akka-actor/src/main/scala/akka/actor/ActorCell.scala b/akka-actor/src/main/scala/akka/actor/ActorCell.scala index 39bd52aa87..e659928743 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorCell.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorCell.scala @@ -389,7 +389,6 @@ private[akka] class ActorCell( def recreate(cause: Throwable): Unit = try { val failedActor = actor if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, clazz(failedActor), "restarting")) - val freshActor = newActor() if (failedActor ne null) { val c = currentMessage //One read only plz try { @@ -398,7 +397,8 @@ private[akka] class ActorCell( clearActorFields() } } - actor = freshActor // assign it here so if preStart fails, we can null out the sef-refs next call + val freshActor = newActor() // this must happen after failedActor.preRestart (to scrap those children) + actor = freshActor // this must happen before postRestart has a chance to fail freshActor.postRestart(cause) if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, clazz(freshActor), "restarted")) diff --git a/akka-actor/src/main/scala/akka/event/Logging.scala b/akka-actor/src/main/scala/akka/event/Logging.scala index a58ea3fdb9..93019318dd 100644 --- a/akka-actor/src/main/scala/akka/event/Logging.scala +++ b/akka-actor/src/main/scala/akka/event/Logging.scala @@ -734,37 +734,37 @@ trait LoggingAdapter { */ def error(cause: Throwable, message: String) { if (isErrorEnabled) notifyError(cause, message) } - def error(cause: Throwable, template: String, arg1: Any) { if (isErrorEnabled) notifyError(cause, format(template, arg1)) } + def error(cause: Throwable, template: String, arg1: Any) { if (isErrorEnabled) notifyError(cause, format1(template, arg1)) } def error(cause: Throwable, template: String, arg1: Any, arg2: Any) { if (isErrorEnabled) notifyError(cause, format(template, arg1, arg2)) } def error(cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any) { if (isErrorEnabled) notifyError(cause, format(template, arg1, arg2, arg3)) } def error(cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any) { if (isErrorEnabled) notifyError(cause, format(template, arg1, arg2, arg3, arg4)) } def error(message: String) { if (isErrorEnabled) notifyError(message) } - def error(template: String, arg1: Any) { if (isErrorEnabled) notifyError(format(template, arg1)) } + def error(template: String, arg1: Any) { if (isErrorEnabled) notifyError(format1(template, arg1)) } def error(template: String, arg1: Any, arg2: Any) { if (isErrorEnabled) notifyError(format(template, arg1, arg2)) } def error(template: String, arg1: Any, arg2: Any, arg3: Any) { if (isErrorEnabled) notifyError(format(template, arg1, arg2, arg3)) } def error(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any) { if (isErrorEnabled) notifyError(format(template, arg1, arg2, arg3, arg4)) } def warning(message: String) { if (isWarningEnabled) notifyWarning(message) } - def warning(template: String, arg1: Any) { if (isWarningEnabled) notifyWarning(format(template, arg1)) } + def warning(template: String, arg1: Any) { if (isWarningEnabled) notifyWarning(format1(template, arg1)) } def warning(template: String, arg1: Any, arg2: Any) { if (isWarningEnabled) notifyWarning(format(template, arg1, arg2)) } def warning(template: String, arg1: Any, arg2: Any, arg3: Any) { if (isWarningEnabled) notifyWarning(format(template, arg1, arg2, arg3)) } def warning(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any) { if (isWarningEnabled) notifyWarning(format(template, arg1, arg2, arg3, arg4)) } def info(message: String) { if (isInfoEnabled) notifyInfo(message) } - def info(template: String, arg1: Any) { if (isInfoEnabled) notifyInfo(format(template, arg1)) } + def info(template: String, arg1: Any) { if (isInfoEnabled) notifyInfo(format1(template, arg1)) } def info(template: String, arg1: Any, arg2: Any) { if (isInfoEnabled) notifyInfo(format(template, arg1, arg2)) } def info(template: String, arg1: Any, arg2: Any, arg3: Any) { if (isInfoEnabled) notifyInfo(format(template, arg1, arg2, arg3)) } def info(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any) { if (isInfoEnabled) notifyInfo(format(template, arg1, arg2, arg3, arg4)) } def debug(message: String) { if (isDebugEnabled) notifyDebug(message) } - def debug(template: String, arg1: Any) { if (isDebugEnabled) notifyDebug(format(template, arg1)) } + def debug(template: String, arg1: Any) { if (isDebugEnabled) notifyDebug(format1(template, arg1)) } def debug(template: String, arg1: Any, arg2: Any) { if (isDebugEnabled) notifyDebug(format(template, arg1, arg2)) } def debug(template: String, arg1: Any, arg2: Any, arg3: Any) { if (isDebugEnabled) notifyDebug(format(template, arg1, arg2, arg3)) } def debug(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any) { if (isDebugEnabled) notifyDebug(format(template, arg1, arg2, arg3, arg4)) } def log(level: Logging.LogLevel, message: String) { if (isEnabled(level)) notifyLog(level, message) } - def log(level: Logging.LogLevel, template: String, arg1: Any) { if (isEnabled(level)) notifyLog(level, format(template, arg1)) } + def log(level: Logging.LogLevel, template: String, arg1: Any) { if (isEnabled(level)) notifyLog(level, format1(template, arg1)) } def log(level: Logging.LogLevel, template: String, arg1: Any, arg2: Any) { if (isEnabled(level)) notifyLog(level, format(template, arg1, arg2)) } def log(level: Logging.LogLevel, template: String, arg1: Any, arg2: Any, arg3: Any) { if (isEnabled(level)) notifyLog(level, format(template, arg1, arg2, arg3)) } def log(level: Logging.LogLevel, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any) { if (isEnabled(level)) notifyLog(level, format(template, arg1, arg2, arg3, arg4)) } @@ -783,16 +783,28 @@ trait LoggingAdapter { case Logging.DebugLevel ⇒ if (isDebugEnabled) notifyDebug(message) } + private def format1(t: String, arg: Any) = arg match { + case a: Array[_] if !a.getClass.getComponentType.isPrimitive ⇒ format(t, a: _*) + case a: Array[_] ⇒ format(t, (a map (_.asInstanceOf[AnyRef]): _*)) + case x ⇒ format(t, x) + } + def format(t: String, arg: Any*) = { val sb = new StringBuilder var p = 0 var rest = t while (p < arg.length) { val index = rest.indexOf("{}") - sb.append(rest.substring(0, index)) - sb.append(arg(p)) - rest = rest.substring(index + 2) - p += 1 + if (index == -1) { + sb.append(rest).append(" WARNING arguments left: ").append(arg.length - p) + rest = "" + p = arg.length + } else { + sb.append(rest.substring(0, index)) + sb.append(arg(p)) + rest = rest.substring(index + 2) + p += 1 + } } sb.append(rest) sb.toString diff --git a/akka-docs/java/code/akka/docs/event/LoggingDocTestBase.java b/akka-docs/java/code/akka/docs/event/LoggingDocTestBase.java index 1a1f7867c0..8f7b63d8a8 100644 --- a/akka-docs/java/code/akka/docs/event/LoggingDocTestBase.java +++ b/akka-docs/java/code/akka/docs/event/LoggingDocTestBase.java @@ -33,7 +33,7 @@ import akka.actor.DeadLetter; //#imports-deadletter public class LoggingDocTestBase { - + @Test public void useLoggingActor() { ActorSystem system = ActorSystem.create("MySystem"); @@ -55,6 +55,16 @@ public class LoggingDocTestBase { //#deadletters system.shutdown(); } + + @Test + public void demonstrateMultipleArgs() { + final ActorSystem system = ActorSystem.create("multiArg"); + //#array + final Object[] args = new Object[] { "The", "brown", "fox", "jumps", 42 }; + system.log().debug("five parameters: {}, {}, {}, {}, {}", args); + //#array + system.shutdown(); + } //#my-actor class MyActor extends UntypedActor { diff --git a/akka-docs/java/logging.rst b/akka-docs/java/logging.rst index fabd9f6e92..333640a41f 100644 --- a/akka-docs/java/logging.rst +++ b/akka-docs/java/logging.rst @@ -30,8 +30,13 @@ object is translated to a String according to the following rules: * in case of a class an approximation of its simpleName * and in all other cases the simpleName of its class -The log message may contain argument placeholders ``{}``, which will be substituted if the log level -is enabled. +The log message may contain argument placeholders ``{}``, which will be +substituted if the log level is enabled. Giving more arguments as there are +placeholders results in a warning being appended to the log statement (i.e. on +the same line with the same severity). You may pass a Java array as the only +substitution argument to have its elements be treated individually: + +.. includecode:: code/akka/docs/event/LoggingDocTestBase.java#array The Java :class:`Class` of the log source is also included in the generated :class:`LogEvent`. In case of a simple string this is replaced with a “marker” diff --git a/akka-docs/scala/code/akka/docs/event/LoggingDocSpec.scala b/akka-docs/scala/code/akka/docs/event/LoggingDocSpec.scala index cd6ecdb162..0aa29549c9 100644 --- a/akka-docs/scala/code/akka/docs/event/LoggingDocSpec.scala +++ b/akka-docs/scala/code/akka/docs/event/LoggingDocSpec.scala @@ -89,4 +89,11 @@ class LoggingDocSpec extends AkkaSpec { //#deadletters } + "demonstrate logging more arguments" in { + //#array + val args = Array("The", "brown", "fox", "jumps", 42) + system.log.debug("five parameters: {}, {}, {}, {}, {}", args) + //#array + } + } diff --git a/akka-docs/scala/logging.rst b/akka-docs/scala/logging.rst index dd58b24fb4..1ccf9583dd 100644 --- a/akka-docs/scala/logging.rst +++ b/akka-docs/scala/logging.rst @@ -34,8 +34,20 @@ The source object is translated to a String according to the following rules: * and in all other cases a compile error occurs unless and implicit :class:`LogSource[T]` is in scope for the type in question. -The log message may contain argument placeholders ``{}``, which will be substituted if the log level -is enabled. +The log message may contain argument placeholders ``{}``, which will be +substituted if the log level is enabled. Giving more arguments as there are +placeholders results in a warning being appended to the log statement (i.e. on +the same line with the same severity). You may pass a Java array as the only +substitution argument to have its elements be treated individually: + +.. includecode:: code/akka/docs/event/LoggingDocSpec.scala#array + +The Java :class:`Class` of the log source is also included in the generated +:class:`LogEvent`. In case of a simple string this is replaced with a “marker” +class :class:`akka.event.DummyClassForStringSources` in order to allow special +treatment of this case, e.g. in the SLF4J event listener which will then use +the string instead of the class’ name for looking up the logger instance to +use. Auxiliary logging options -------------------------