akka.event.Logging.scala Maven / Gradle / Ivy
/*
* Copyright (C) 2009-2019 Lightbend Inc.
*/
package akka.event
import java.util.concurrent.TimeoutException
import java.util.concurrent.atomic.AtomicInteger
import akka.actor.ActorSystem.Settings
import akka.actor._
import akka.annotation.{ DoNotInherit, InternalApi }
import akka.dispatch.RequiresMessageQueue
import akka.event.Logging._
import akka.util.{ unused, Helpers, ReentrantGuard }
import akka.{ AkkaException, ConfigurationException }
import com.github.ghik.silencer.silent
import scala.annotation.implicitNotFound
import scala.collection.immutable
import scala.concurrent.Await
import scala.language.existentials
import scala.util.control.{ NoStackTrace, NonFatal }
/**
* This trait brings log level handling to the EventStream: it reads the log
* levels for the initial logging (StandardOutLogger) and the loggers & level
* for after-init logging, possibly keeping the StandardOutLogger enabled if
* it is part of the configured loggers. All configured loggers are treated as
* system services and managed by this trait, i.e. subscribed/unsubscribed in
* response to changes of LoggingBus.logLevel.
*/
trait LoggingBus extends ActorEventBus {
type Event >: Logging.LogEvent
type Classifier >: Class[_]
import Logging._
private val guard = new ReentrantGuard
private var loggers = Seq.empty[ActorRef]
@volatile private var _logLevel: LogLevel = _
/**
* Query currently set log level. See object Logging for more information.
*/
def logLevel = _logLevel
/**
* Change log level: default loggers (i.e. from configuration file) are
* subscribed/unsubscribed as necessary so that they listen to all levels
* which are at least as severe as the given one. See object Logging for
* more information.
*
* NOTE: if the StandardOutLogger is configured also as normal logger, it
* will not participate in the automatic management of log level
* subscriptions!
*/
def setLogLevel(level: LogLevel): Unit = guard.withGuard {
val logLvl = _logLevel // saves (2 * AllLogLevel.size - 1) volatile reads (because of the loops below)
for {
l <- AllLogLevels
// subscribe if previously ignored and now requested
if l > logLvl && l <= level
log <- loggers
} subscribe(log, classFor(l))
for {
l <- AllLogLevels
// unsubscribe if previously registered and now ignored
if l <= logLvl && l > level
log <- loggers
} unsubscribe(log, classFor(l))
_logLevel = level
}
private def setUpStdoutLogger(config: Settings): Unit = {
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 :+= StandardOutLogger
_logLevel = level
}
}
/**
* Internal Akka use only
*/
private[akka] def startStdoutLogger(config: Settings): Unit = {
setUpStdoutLogger(config)
publish(Debug(simpleName(this), this.getClass, "StandardOutLogger started"))
}
/**
* Internal Akka use only
*/
private[akka] def startDefaultLoggers(system: ActorSystemImpl): Unit = {
val logName = simpleName(this) + "(" + system + ")"
val level = levelFor(system.settings.LogLevel).getOrElse {
// only log initialization errors directly with StandardOutLogger.print
StandardOutLogger.print(
Error(new LoggerException, logName, this.getClass, "unknown akka.loglevel " + system.settings.LogLevel))
ErrorLevel
}
try {
val defaultLoggers = system.settings.Loggers match {
case Nil => classOf[DefaultLogger].getName :: Nil
case loggers => loggers
}
val myloggers =
for {
loggerName <- defaultLoggers
if loggerName != StandardOutLogger.getClass.getName
} yield {
system.dynamicAccess
.getClassFor[Actor](loggerName)
.map({
case actorClass => addLogger(system, actorClass, level, logName)
})
.recover({
case e =>
throw new ConfigurationException(
"Logger specified in config can't be loaded [" + loggerName +
"] due to [" + e.toString + "]",
e)
})
.get
}
guard.withGuard {
loggers = myloggers
_logLevel = level
}
try {
if (system.settings.DebugUnhandledMessage)
subscribe(
system.systemActorOf(Props(new Actor {
def receive = {
case UnhandledMessage(msg, sender, rcp) =>
publish(Debug(rcp.path.toString, rcp.getClass, "unhandled message from " + sender + ": " + msg))
}
}), "UnhandledMessageForwarder"),
classOf[UnhandledMessage])
} catch {
case _: InvalidActorNameException => // ignore if it is already running
}
publish(Debug(logName, this.getClass, "Default Loggers started"))
if (!(defaultLoggers contains StandardOutLogger.getClass.getName)) {
unsubscribe(StandardOutLogger)
}
} catch {
case e: Exception =>
System.err.println("error while starting up loggers")
e.printStackTrace()
throw new ConfigurationException("Could not start logger due to [" + e.toString + "]")
}
}
/**
* Internal Akka use only
*/
private[akka] def stopDefaultLoggers(system: ActorSystem): Unit = {
@silent
val level = _logLevel // volatile access before reading loggers
if (!(loggers contains StandardOutLogger)) {
setUpStdoutLogger(system.settings)
publish(Debug(simpleName(this), this.getClass, "shutting down: StandardOutLogger"))
}
for {
logger <- loggers
if logger != StandardOutLogger
} {
// this is very necessary, else you get infinite loop with DeadLetter
unsubscribe(logger)
logger match {
case ref: InternalActorRef => ref.stop()
case _ =>
}
}
publish(Debug(simpleName(this), this.getClass, "all default loggers stopped"))
}
/**
* INTERNAL API
*/
private def addLogger(
system: ActorSystemImpl,
clazz: Class[_ <: Actor],
level: LogLevel,
logName: String): ActorRef = {
val name = "log" + LogExt(system).id() + "-" + simpleName(clazz)
val actor = system.systemActorOf(Props(clazz).withDispatcher(system.settings.LoggersDispatcher), name)
implicit def timeout = system.settings.LoggerStartTimeout
import akka.pattern.ask
val response = try Await.result(actor ? InitializeLogger(this), timeout.duration)
catch {
case _: TimeoutException =>
publish(
Warning(
logName,
this.getClass,
"Logger " + name + " did not respond within " + timeout + " to InitializeLogger(bus)"))
"[TIMEOUT]"
}
if (response != LoggerInitialized)
throw new LoggerInitializationException(
"Logger " + name + " did not respond with LoggerInitialized, sent instead " + response)
AllLogLevels.filter(level >= _).foreach(l => subscribe(actor, classFor(l)))
publish(Debug(logName, this.getClass, "logger " + name + " started"))
actor
}
}
/**
* This trait defines the interface to be provided by a “log source formatting
* rule” as used by [[akka.event.Logging]]’s `apply`/`create` method.
*
* See the companion object for default implementations.
*
* Example:
* {{{
* trait MyType { // as an example
* def name: String
* }
*
* implicit val myLogSourceType: LogSource[MyType] = new LogSource[MyType] {
* def genString(a: MyType) = a.name
* }
*
* class MyClass extends MyType {
* val log = Logging(eventStream, this) // will use "hallo" as logSource
* def name = "hallo"
* }
* }}}
*
* The second variant is used for including the actor system’s address:
* {{{
* trait MyType { // as an example
* def name: String
* }
*
* implicit val myLogSourceType: LogSource[MyType] = new LogSource[MyType] {
* def genString(a: MyType) = a.name
* def genString(a: MyType, s: ActorSystem) = a.name + "," + s
* }
*
* class MyClass extends MyType {
* val sys = ActorSystem("sys")
* val log = Logging(sys, this) // will use "hallo,akka://sys" as logSource
* def name = "hallo"
* }
* }}}
*
* The default implementation of the second variant will just call the first.
*/
@implicitNotFound(
"Cannot find LogSource for ${T} please see ScalaDoc for LogSource for how to obtain or construct one.") trait LogSource[
-T] {
def genString(t: T): String
def genString(t: T, @unused system: ActorSystem): String = genString(t)
def getClazz(t: T): Class[_] = t.getClass
}
/**
* This is a “marker” class which is inserted as originator class into
* [[akka.event.Logging.LogEvent]] when the string representation was supplied
* directly.
*/
class DummyClassForStringSources
/**
* This object holds predefined formatting rules for log sources.
*
* In case an [[akka.actor.ActorSystem]] is provided, the following apply:
*
* - [[akka.actor.Actor]] and [[akka.actor.ActorRef]] will be represented by their absolute physical path
* - providing a `String` as source will append "(<system address>)" and use the result
* - providing a `Class` will extract its simple name, append "(<system address>)" and use the result
* - anything else gives compile error unless implicit [[akka.event.LogSource]] is in scope for it
*
*
* In case a [[akka.event.LoggingBus]] is provided, the following apply:
*
* - [[akka.actor.Actor]] and [[akka.actor.ActorRef]] will be represented by their absolute physical path
* - providing a `String` as source will be used as is
* - providing a `Class` will extract its simple name
* - anything else gives compile error unless implicit [[akka.event.LogSource]] is in scope for it
*
*/
object LogSource {
implicit val fromString: LogSource[String] = new LogSource[String] {
def genString(s: String) = s
override def genString(s: String, system: ActorSystem) = s + "(" + system + ")"
override def getClazz(s: String) = classOf[DummyClassForStringSources]
}
implicit val fromActor: LogSource[Actor] = new LogSource[Actor] {
def genString(a: Actor) = fromActorRef.genString(a.self)
override def genString(a: Actor, system: ActorSystem) = fromActorRef.genString(a.self, system)
}
implicit val fromActorRef: LogSource[ActorRef] = new LogSource[ActorRef] {
def genString(a: ActorRef) = a.path.toString
override def genString(a: ActorRef, system: ActorSystem) =
try {
a.path.toStringWithAddress(system.asInstanceOf[ExtendedActorSystem].provider.getDefaultAddress)
} catch {
// it can fail if the ActorSystem (remoting) is not completely started yet
case NonFatal(_) => a.path.toString
}
}
// this one unfortunately does not work as implicit, because existential types have some weird behavior
val fromClass: LogSource[Class[_]] = new LogSource[Class[_]] {
def genString(c: Class[_]): String = Logging.simpleName(c)
override def genString(c: Class[_], system: ActorSystem): String = genString(c) + "(" + system + ")"
override def getClazz(c: Class[_]): Class[_] = c
}
implicit def fromAnyClass[T]: LogSource[Class[T]] = fromClass.asInstanceOf[LogSource[Class[T]]]
/**
* Convenience converter access: given an implicit `LogSource`, generate the
* string representation and originating class.
*/
def apply[T: LogSource](o: T): (String, Class[_]) = {
val ls = implicitly[LogSource[T]]
(ls.genString(o), ls.getClazz(o))
}
/**
* Convenience converter access: given an implicit `LogSource` and
* [[akka.actor.ActorSystem]], generate the string representation and
* originating class.
*/
def apply[T: LogSource](o: T, system: ActorSystem): (String, Class[_]) = {
val ls = implicitly[LogSource[T]]
(ls.genString(o, system), ls.getClazz(o))
}
/**
* construct string representation for any object according to
* rules above with fallback to its `Class`’s simple name.
*/
def fromAnyRef(o: AnyRef): (String, Class[_]) =
o match {
case c: Class[_] => apply(c)
case a: Actor => apply(a)
case a: ActorRef => apply(a)
case s: String => apply(s)
case x => (Logging.simpleName(x), x.getClass)
}
/**
* construct string representation for any object according to
* rules above (including the actor system’s address) with fallback to its
* `Class`’s simple name.
*/
def fromAnyRef(o: AnyRef, system: ActorSystem): (String, Class[_]) =
o match {
case c: Class[_] => apply(c)
case a: Actor => apply(a)
case a: ActorRef => apply(a)
case s: String => apply(s)
case x => (Logging.simpleName(x) + "(" + system + ")", x.getClass)
}
}
/**
* Main entry point for Akka logging: log levels and message types (aka
* channels) defined for the main transport medium, the main event bus. The
* recommended use is to obtain an implementation of the Logging trait with
* suitable and efficient methods for generating log events:
*
*
* val log = Logging(<bus>, <source object>)
* ...
* log.info("hello world!")
*
*
* The source object is used in two fashions: its `Class[_]` will be part of
* all log events produced by this logger, plus a string representation is
* generated which may contain per-instance information, see `apply` or `create`
* below.
*
* Loggers are attached to the level-specific channels Error
,
* Warning
, Info
and Debug
as
* appropriate for the configured (or set) log level. If you want to implement
* your own, make sure to handle these four event types plus the InitializeLogger
* message which is sent before actually attaching it to the logging bus.
*
* Logging is configured by setting (some of) the following:
*
*
* akka {
* loggers = ["akka.slf4j.Slf4jLogger"] # for example
* loglevel = "INFO" # used when normal logging ("loggers") has been started
* stdout-loglevel = "WARN" # used during application start-up until normal logging is available
* }
*
*/
object Logging {
/**
* Returns a 'safe' getSimpleName for the provided object's Class
* @return the simple name of the given object's Class
*/
def simpleName(obj: AnyRef): String = simpleName(obj.getClass)
/**
* Returns a 'safe' getSimpleName for the provided Class
* @return the simple name of the given Class
*/
def simpleName(clazz: Class[_]): String = {
val n = clazz.getName
val i = n.lastIndexOf('.')
n.substring(i + 1)
}
/**
* Class name representation of a message.
* `ActorSelectionMessage` representation includes class name of
* wrapped message.
*/
def messageClassName(message: Any): String = message match {
case null => "null"
case ActorSelectionMessage(m, _, _) => s"ActorSelectionMessage(${m.getClass.getName})"
case m => m.getClass.getName
}
/**
* INTERNAL API
*/
private[akka] object LogExt extends ExtensionId[LogExt] {
override def createExtension(system: ExtendedActorSystem): LogExt =
new LogExt(system)
}
/**
* INTERNAL API
*/
private[akka] class LogExt(@unused system: ExtendedActorSystem) extends Extension {
private val loggerId = new AtomicInteger
def id() = loggerId.incrementAndGet()
}
/**
* Marker trait for annotating LogLevel, which must be Int after erasure.
*/
final case class LogLevel(asInt: Int) extends AnyVal {
@inline final def >=(other: LogLevel): Boolean = asInt >= other.asInt
@inline final def <=(other: LogLevel): Boolean = asInt <= other.asInt
@inline final def >(other: LogLevel): Boolean = asInt > other.asInt
@inline final def <(other: LogLevel): Boolean = asInt < other.asInt
}
/**
* Log level in numeric form, used when deciding whether a certain log
* statement should generate a log event. Predefined levels are ErrorLevel (1)
* to DebugLevel (4). In case you want to add more levels, loggers need to
* be subscribed to their event bus channels manually.
*/
final val ErrorLevel = LogLevel(1)
final val WarningLevel = LogLevel(2)
final val InfoLevel = LogLevel(3)
final val DebugLevel = LogLevel(4)
/**
* INTERNAL API: Internal Akka use only
*
* Don't include the OffLevel in the AllLogLevels since we should never subscribe
* to some kind of OffEvent.
*/
@InternalApi
private[akka] 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] = Helpers.toRootLowerCase(s) match {
case "off" => Some(OffLevel)
case "error" => Some(ErrorLevel)
case "warning" => Some(WarningLevel)
case "info" => Some(InfoLevel)
case "debug" => Some(DebugLevel)
case _ => None
}
/**
* Returns the LogLevel associated with the given event class.
* Defaults to DebugLevel.
*/
def levelFor(eventClass: Class[_ <: LogEvent]): LogLevel = {
if (classOf[Error].isAssignableFrom(eventClass)) ErrorLevel
else if (classOf[Warning].isAssignableFrom(eventClass)) WarningLevel
else if (classOf[Info].isAssignableFrom(eventClass)) InfoLevel
else if (classOf[Debug].isAssignableFrom(eventClass)) DebugLevel
else DebugLevel
}
/**
* Returns the event class associated with the given LogLevel
*/
def classFor(level: LogLevel): Class[_ <: LogEvent] = level match {
case ErrorLevel => classOf[Error]
case WarningLevel => classOf[Warning]
case InfoLevel => classOf[Info]
case DebugLevel => classOf[Debug]
case level => throw new IllegalArgumentException(s"Unsupported log level [$level]")
}
// these type ascriptions/casts are necessary to avoid CCEs during construction while retaining correct type
val AllLogLevels: immutable.Seq[LogLevel] = Vector(ErrorLevel, WarningLevel, InfoLevel, DebugLevel)
/**
* Obtain LoggingAdapter for the given actor system and source object. This
* will use the system’s event stream and include the system’s address in the
* log source string.
*
* Do not use this if you want to supply a log category string (like
* “com.example.app.whatever”) unaltered, supply `system.eventStream` in this
* case or use
*
* {{{
* Logging(system, this.getClass)
* }}}
*
* The source is used to identify the source of this logging channel and
* must have a corresponding implicit LogSource[T] instance in scope; by
* default these are provided for Class[_], Actor, ActorRef and String types.
* See the companion object of [[akka.event.LogSource]] for details.
*
* You can add your own rules quite easily, see [[akka.event.LogSource]].
*/
def apply[T: LogSource](system: ActorSystem, logSource: T): LoggingAdapter = {
val (str, clazz) = LogSource(logSource, system)
new BusLogging(system.eventStream, str, clazz, system.asInstanceOf[ExtendedActorSystem].logFilter)
}
/**
* Obtain LoggingAdapter with additional "marker" support (which some logging frameworks are able to utilise)
* for the given actor system and source object. This will use the system’s event stream and include the system’s
* address in the log source string.
*
* Do not use this if you want to supply a log category string (like
* “com.example.app.whatever”) unaltered, supply `system.eventStream` in this
* case or use
*
* {{{
* Logging(system, this.getClass)
* }}}
*
* The source is used to identify the source of this logging channel and
* must have a corresponding implicit LogSource[T] instance in scope; by
* default these are provided for Class[_], Actor, ActorRef and String types.
* See the companion object of [[akka.event.LogSource]] for details.
*
* You can add your own rules quite easily, see [[akka.event.LogSource]].
*/
def withMarker[T: LogSource](system: ActorSystem, logSource: T): MarkerLoggingAdapter = {
val (str, clazz) = LogSource(logSource, system)
new MarkerLoggingAdapter(system.eventStream, str, clazz, system.asInstanceOf[ExtendedActorSystem].logFilter)
}
/**
* Obtain LoggingAdapter for the given logging bus and source object.
*
* The source is used to identify the source of this logging channel and
* must have a corresponding implicit LogSource[T] instance in scope; by
* default these are provided for Class[_], Actor, ActorRef and String types.
* See the companion object of [[akka.event.LogSource]] for details.
*
* You can add your own rules quite easily, see [[akka.event.LogSource]].
*
* Note that this `LoggingAdapter` will use the [[akka.event.DefaultLoggingFilter]],
* and not the [[akka.event.LoggingFilter]] configured for the system
* (if different from `DefaultLoggingFilter`).
*/
def apply[T: LogSource](bus: LoggingBus, logSource: T): LoggingAdapter = {
val (str, clazz) = LogSource(logSource)
new BusLogging(bus, str, clazz)
}
/**
* Obtain LoggingAdapter for the given logging bus and source object.
*
* The source is used to identify the source of this logging channel and
* must have a corresponding implicit LogSource[T] instance in scope; by
* default these are provided for Class[_], Actor, ActorRef and String types.
* See the companion object of [[akka.event.LogSource]] for details.
*
* You can add your own rules quite easily, see [[akka.event.LogSource]].
*
* Note that this `LoggingAdapter` will use the [[akka.event.DefaultLoggingFilter]],
* and not the [[akka.event.LoggingFilter]] configured for the system
* (if different from `DefaultLoggingFilter`).
*/
def withMarker[T: LogSource](bus: LoggingBus, logSource: T): MarkerLoggingAdapter = {
val (str, clazz) = LogSource(logSource)
new MarkerLoggingAdapter(bus, str, clazz)
}
/**
* Obtain LoggingAdapter with MDC support for the given actor.
* Don't use it outside its specific Actor as it isn't thread safe
*/
def apply(logSource: Actor): DiagnosticLoggingAdapter = {
val (str, clazz) = LogSource(logSource)
val system = logSource.context.system.asInstanceOf[ExtendedActorSystem]
new BusLogging(system.eventStream, str, clazz, system.logFilter) with DiagnosticLoggingAdapter
}
/**
* Obtain LoggingAdapter with marker and MDC support for the given actor.
* Don't use it outside its specific Actor as it isn't thread safe
*/
def withMarker(logSource: Actor): DiagnosticMarkerBusLoggingAdapter = {
val (str, clazz) = LogSource(logSource)
val system = logSource.context.system.asInstanceOf[ExtendedActorSystem]
new DiagnosticMarkerBusLoggingAdapter(system.eventStream, str, clazz, system.logFilter)
}
/**
* Obtain LoggingAdapter for the given actor system and source object. This
* will use the system’s event stream and include the system’s address in the
* log source string.
*
* Do not use this if you want to supply a log category string (like
* “com.example.app.whatever”) unaltered, supply `system.eventStream` in this
* case or use
*
* {{{
* Logging.getLogger(system, this.getClass());
* }}}
*
* The source is used to identify the source of this logging channel and
* must have a corresponding implicit LogSource[T] instance in scope; by
* default these are provided for Class[_], Actor, ActorRef and String types.
* See the companion object of [[akka.event.LogSource]] for details.
*/
def getLogger(system: ActorSystem, logSource: AnyRef): LoggingAdapter = {
val (str, clazz) = LogSource.fromAnyRef(logSource, system)
new BusLogging(system.eventStream, str, clazz, system.asInstanceOf[ExtendedActorSystem].logFilter)
}
/**
* Obtain LoggingAdapter for the given logging bus and source object.
*
* The source is used to identify the source of this logging channel and
* must have a corresponding implicit LogSource[T] instance in scope; by
* default these are provided for Class[_], Actor, ActorRef and String types.
* See the companion object of [[akka.event.LogSource]] for details.
*
* Note that this `LoggingAdapter` will use the [[akka.event.DefaultLoggingFilter]],
* and not the [[akka.event.LoggingFilter]] configured for the system
* (if different from `DefaultLoggingFilter`).
*/
def getLogger(bus: LoggingBus, logSource: AnyRef): LoggingAdapter = {
val (str, clazz) = LogSource.fromAnyRef(logSource)
new BusLogging(bus, str, clazz)
}
/**
* Obtain LoggingAdapter with MDC support for the given actor.
* Don't use it outside its specific Actor as it isn't thread safe
*/
def getLogger(logSource: Actor): DiagnosticLoggingAdapter = apply(logSource)
/**
* Obtain LoggingAdapter with MDC support for the given actor.
* Don't use it outside its specific Actor as it isn't thread safe
*/
@deprecated("Use AbstractActor instead of UntypedActor.", since = "2.5.0")
def getLogger(logSource: UntypedActor): DiagnosticLoggingAdapter = {
val (str, clazz) = LogSource.fromAnyRef(logSource)
val system = logSource.getContext().system.asInstanceOf[ExtendedActorSystem]
new BusLogging(system.eventStream, str, clazz, system.logFilter) with DiagnosticLoggingAdapter
}
/**
* Artificial exception injected into Error events if no Throwable is
* supplied; used for getting a stack dump of error locations.
*/
class LoggerException extends AkkaException("")
/**
* Exception that wraps a LogEvent.
*/
class LogEventException(val event: LogEvent, cause: Throwable) extends NoStackTrace {
override def getMessage: String = event.toString
override def getCause: Throwable = cause
}
/**
* Base type of LogEvents
*/
sealed trait LogEvent extends NoSerializationVerificationNeeded {
/**
* The thread that created this log event
*/
@transient
val thread: Thread = Thread.currentThread
/**
* When this LogEvent was created according to System.currentTimeMillis
*/
val timestamp: Long = System.currentTimeMillis
/**
* The LogLevel of this LogEvent
*/
def level: LogLevel
/**
* The source of this event
*/
def logSource: String
/**
* The class of the source of this event
*/
def logClass: Class[_]
/**
* The message, may be any object or null.
*/
def message: Any
/**
* Extra values for adding to MDC
*/
def mdc: MDC = emptyMDC
/**
* Java API: Retrieve the contents of the MDC.
*/
def getMDC: java.util.Map[String, Any] = {
import akka.util.ccompat.JavaConverters._
mdc.asJava
}
}
object LogEvent {
def apply(level: LogLevel, logSource: String, logClass: Class[_], message: Any): LogEvent = level match {
case ErrorLevel => Error(logSource, logClass, message)
case WarningLevel => Warning(logSource, logClass, message)
case InfoLevel => Info(logSource, logClass, message)
case DebugLevel => Debug(logSource, logClass, message)
case level => throw new IllegalArgumentException(s"Unsupported log level [$level]")
}
def apply(level: LogLevel, logSource: String, logClass: Class[_], message: Any, mdc: MDC): LogEvent = level match {
case ErrorLevel => Error(logSource, logClass, message, mdc)
case WarningLevel => Warning(logSource, logClass, message, mdc)
case InfoLevel => Info(logSource, logClass, message, mdc)
case DebugLevel => Debug(logSource, logClass, message, mdc)
case level => throw new IllegalArgumentException(s"Unsupported log level [$level]")
}
def apply(
level: LogLevel,
logSource: String,
logClass: Class[_],
message: Any,
mdc: MDC,
marker: LogMarker): LogEvent = level match {
case ErrorLevel => Error(logSource, logClass, message, mdc, marker)
case WarningLevel => Warning(logSource, logClass, message, mdc, marker)
case InfoLevel => Info(logSource, logClass, message, mdc, marker)
case DebugLevel => Debug(logSource, logClass, message, mdc, marker)
case level => throw new IllegalArgumentException(s"Unsupported log level [$level]")
}
}
trait LogEventWithCause {
def cause: Throwable
}
/**
* For ERROR Logging
*/
case class Error(override val cause: Throwable, logSource: String, logClass: Class[_], message: Any = "")
extends LogEvent
with LogEventWithCause {
def this(logSource: String, logClass: Class[_], message: Any) = this(Error.NoCause, logSource, logClass, message)
override def level = ErrorLevel
}
class Error2(
override val cause: Throwable,
logSource: String,
logClass: Class[_],
message: Any = "",
override val mdc: MDC)
extends Error(cause, logSource, logClass, message) {
def this(logSource: String, logClass: Class[_], message: Any, mdc: MDC) =
this(Error.NoCause, logSource, logClass, message, mdc)
}
class Error3(
override val cause: Throwable,
logSource: String,
logClass: Class[_],
message: Any,
override val mdc: MDC,
override val marker: LogMarker)
extends Error2(cause, logSource, logClass, message, mdc)
with LogEventWithMarker {
def this(logSource: String, logClass: Class[_], message: Any, mdc: MDC, marker: LogMarker) =
this(Error.NoCause, logSource, logClass, message, mdc, marker)
}
object Error {
def apply(logSource: String, logClass: Class[_], message: Any) =
new Error(NoCause, logSource, logClass, message)
def apply(logSource: String, logClass: Class[_], message: Any, marker: LogMarker) =
new Error3(NoCause, logSource, logClass, message, Map.empty, marker)
def apply(cause: Throwable, logSource: String, logClass: Class[_], message: Any, mdc: MDC) =
new Error2(cause, logSource, logClass, message, mdc)
def apply(cause: Throwable, logSource: String, logClass: Class[_], message: Any, mdc: MDC, marker: LogMarker) =
new Error3(cause, logSource, logClass, message, mdc, marker)
def apply(logSource: String, logClass: Class[_], message: Any, mdc: MDC) =
new Error2(NoCause, logSource, logClass, message, mdc)
def apply(logSource: String, logClass: Class[_], message: Any, mdc: MDC, marker: LogMarker) =
new Error3(NoCause, logSource, logClass, message, mdc, marker)
/** Null Object used for errors without cause Throwable */
object NoCause extends NoStackTrace
}
def noCause = Error.NoCause
/**
* For WARNING Logging
*/
case class Warning(logSource: String, logClass: Class[_], message: Any = "") extends LogEvent {
override def level = WarningLevel
}
class Warning2(logSource: String, logClass: Class[_], message: Any, override val mdc: MDC)
extends Warning(logSource, logClass, message)
class Warning3(
logSource: String,
logClass: Class[_],
message: Any,
override val mdc: MDC,
override val marker: LogMarker)
extends Warning2(logSource, logClass, message, mdc)
with LogEventWithMarker
class Warning4(
logSource: String,
logClass: Class[_],
message: Any,
override val mdc: MDC,
override val marker: LogMarker,
override val cause: Throwable)
extends Warning2(logSource, logClass, message, mdc)
with LogEventWithMarker
with LogEventWithCause
object Warning {
def apply(logSource: String, logClass: Class[_], message: Any, mdc: MDC) =
new Warning2(logSource, logClass, message, mdc)
def apply(logSource: String, logClass: Class[_], message: Any, mdc: MDC, marker: LogMarker) =
new Warning3(logSource, logClass, message, mdc, marker)
def apply(cause: Throwable, logSource: String, logClass: Class[_], message: Any, mdc: MDC) =
new Warning4(logSource, logClass, message, mdc, null, cause)
def apply(cause: Throwable, logSource: String, logClass: Class[_], message: Any, mdc: MDC, marker: LogMarker) =
new Warning4(logSource, logClass, message, mdc, marker, cause)
}
/**
* For INFO Logging
*/
case class Info(logSource: String, logClass: Class[_], message: Any = "") extends LogEvent {
override def level = InfoLevel
}
class Info2(logSource: String, logClass: Class[_], message: Any, override val mdc: MDC)
extends Info(logSource, logClass, message)
class Info3(
logSource: String,
logClass: Class[_],
message: Any,
override val mdc: MDC,
override val marker: LogMarker)
extends Info2(logSource, logClass, message, mdc)
with LogEventWithMarker
object Info {
def apply(logSource: String, logClass: Class[_], message: Any, mdc: MDC) =
new Info2(logSource, logClass, message, mdc)
def apply(logSource: String, logClass: Class[_], message: Any, mdc: MDC, marker: LogMarker) =
new Info3(logSource, logClass, message, mdc, marker)
}
/**
* For DEBUG Logging
*/
case class Debug(logSource: String, logClass: Class[_], message: Any = "") extends LogEvent {
override def level = DebugLevel
}
class Debug2(logSource: String, logClass: Class[_], message: Any, override val mdc: MDC)
extends Debug(logSource, logClass, message)
class Debug3(
logSource: String,
logClass: Class[_],
message: Any,
override val mdc: MDC,
override val marker: LogMarker)
extends Debug2(logSource, logClass, message, mdc)
with LogEventWithMarker
object Debug {
def apply(logSource: String, logClass: Class[_], message: Any, mdc: MDC) =
new Debug2(logSource, logClass, message, mdc)
def apply(logSource: String, logClass: Class[_], message: Any, mdc: MDC, marker: LogMarker) =
new Debug3(logSource, logClass, message, mdc, marker)
}
/** INTERNAL API, Marker interface for LogEvents containing Markers, which can be set for example on an slf4j logger */
sealed trait LogEventWithMarker extends LogEvent {
/** Marker attribute is nullable due to backward binary compatibility in the class `Warning4` */
def marker: LogMarker
/** Appends the marker to the Debug/Info/Warning/Error toString representations */
override def toString = {
val s = super.toString
s.substring(0, s.length - 1) + "," + marker + ")"
}
}
/**
* Message which is sent to each default logger (i.e. from configuration file)
* after its creation but before attaching it to the logging bus. The logger
* actor must handle this message, it can be used e.g. to register for more
* channels. When done, the logger must respond with a LoggerInitialized
* message. This is necessary to ensure that additional subscriptions are in
* effect when the logging system finished starting.
*/
final case class InitializeLogger(bus: LoggingBus) extends NoSerializationVerificationNeeded
/**
* Response message each logger must send within 1 second after receiving the
* InitializeLogger request. If initialization takes longer, send the reply
* as soon as subscriptions are set-up.
*/
abstract class LoggerInitialized
case object LoggerInitialized extends LoggerInitialized {
/**
* Java API: get the singleton instance
*/
def getInstance = this
}
/**
* Java API to create a LoggerInitialized message.
*/
// weird return type due to binary compatibility
def loggerInitialized(): LoggerInitialized.type = LoggerInitialized
/**
* LoggerInitializationException is thrown to indicate that there was a problem initializing a logger
* @param msg
*/
class LoggerInitializationException(msg: String) extends AkkaException(msg)
trait StdOutLogger {
import StdOutLogger._
def timestamp(event: LogEvent): String = Helpers.timestamp(event.timestamp)
def print(event: Any): Unit = event match {
case e: Error => error(e)
case e: Warning => warning(e)
case e: Info => info(e)
case e: Debug => debug(e)
case e =>
warning(Warning(simpleName(this), this.getClass, "received unexpected event of class " + e.getClass + ": " + e))
}
def error(event: Error): Unit = event match {
case e: Error3 => // has marker
val f = if (event.cause == Error.NoCause) ErrorWithoutCauseWithMarkerFormat else ErrorFormatWithMarker
println(
f.format(
e.marker.name,
timestamp(event),
event.thread.getName,
event.logSource,
formatMDC(event.mdc),
event.message,
stackTraceFor(event.cause)))
case _ =>
val f = if (event.cause == Error.NoCause) ErrorFormatWithoutCause else ErrorFormat
println(
f.format(
timestamp(event),
event.thread.getName,
event.logSource,
formatMDC(event.mdc),
event.message,
stackTraceFor(event.cause)))
}
def warning(event: Warning): Unit = event match {
case e: Warning3 => // has marker
println(
WarningWithMarkerFormat.format(
e.marker.name,
timestamp(event),
event.thread.getName,
event.logSource,
formatMDC(event.mdc),
event.message))
case _ =>
println(
WarningFormat
.format(timestamp(event), event.thread.getName, event.logSource, formatMDC(event.mdc), event.message))
}
def info(event: Info): Unit = event match {
case e: Info3 => // has marker
println(
InfoWithMarkerFormat.format(
e.marker.name,
timestamp(event),
event.thread.getName,
event.logSource,
formatMDC(event.mdc),
event.message))
case _ =>
println(
InfoFormat
.format(timestamp(event), event.thread.getName, event.logSource, formatMDC(event.mdc), event.message))
}
def debug(event: Debug): Unit = event match {
case e: Debug3 => // has marker
println(
DebugWithMarkerFormat.format(
e.marker.name,
timestamp(event),
event.thread.getName,
event.logSource,
formatMDC(event.mdc),
event.message))
case _ =>
println(
DebugFormat
.format(timestamp(event), event.thread.getName, event.logSource, formatMDC(event.mdc), event.message))
}
private def formatMDC(mdc: Map[String, Any]): String = {
val size = mdc.size
if (size == 0) ""
else if (size == 1) s"[${mdc.head._1}:${mdc.head._2}]"
else mdc.map({ case (k, v) => s"$k:$v" }).mkString("[", "][", "]")
}
}
object StdOutLogger {
// format: OFF
private final val ErrorFormat = "[ERROR] [%s] [%s] [%s]%s %s%s"
private final val ErrorFormatWithMarker = "[ERROR] [%s][%s] [%s] [%s]%s %s%s"
private final val ErrorFormatWithoutCause = "[ERROR] [%s] [%s] [%s]%s %s"
private final val ErrorWithoutCauseWithMarkerFormat = "[ERROR] [%s][%s] [%s] [%s]%s %s"
private final val WarningFormat = "[WARN] [%s] [%s] [%s]%s %s"
private final val WarningWithMarkerFormat = "[WARN] [%s][%s] [%s] [%s]%s %s"
private final val InfoFormat = "[INFO] [%s] [%s] [%s]%s %s"
private final val InfoWithMarkerFormat = "[INFO] [%s][%s] [%s] [%s]%s %s"
private final val DebugFormat = "[DEBUG] [%s] [%s] [%s]%s %s"
private final val DebugWithMarkerFormat = "[DEBUG] [%s][%s] [%s] [%s]%s %s"
// format: ON
}
/**
* Actor-less logging implementation for synchronous logging to standard
* output. This logger is always attached first in order to be able to log
* failures during application start-up, even before normal logging is
* started. Its log level can be defined by configuration setting
* akka.stdout-loglevel
.
*/
class StandardOutLogger extends MinimalActorRef with StdOutLogger {
val path: ActorPath = RootActorPath(Address("akka", "all-systems"), "/StandardOutLogger")
def provider: ActorRefProvider = throw new UnsupportedOperationException("StandardOutLogger does not provide")
override val toString = "StandardOutLogger"
override def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit =
if (message == null) throw InvalidMessageException("Message is null")
else print(message)
@throws(classOf[java.io.ObjectStreamException])
override protected def writeReplace(): AnyRef = serializedStandardOutLogger
}
private val serializedStandardOutLogger = new SerializedStandardOutLogger
/**
* INTERNAL API
*/
@SerialVersionUID(1L) private[akka] class SerializedStandardOutLogger extends Serializable {
@throws(classOf[java.io.ObjectStreamException])
private def readResolve(): AnyRef = Logging.StandardOutLogger
}
val StandardOutLogger = new StandardOutLogger
/**
* Actor wrapper around the standard output logger. If
* akka.loggers
is not set, it defaults to just this
* logger.
*/
class DefaultLogger extends Actor with StdOutLogger with RequiresMessageQueue[LoggerMessageQueueSemantics] {
override def receive: Receive = {
case InitializeLogger(_) => sender() ! LoggerInitialized
case event: LogEvent => print(event)
}
}
/**
* Returns the StackTrace for the given Throwable as a String
*/
def stackTraceFor(e: Throwable): String = e match {
case null | Error.NoCause => ""
case _: NoStackTrace => s" (${e.getClass.getName}: ${e.getMessage})"
case other =>
val sw = new java.io.StringWriter
val pw = new java.io.PrintWriter(sw)
pw.append('\n')
other.printStackTrace(pw)
sw.toString
}
type MDC = Map[String, Any]
val emptyMDC: MDC = Map()
}
/**
* Logging wrapper to make nicer and optimize: provide template versions which
* evaluate .toString only if the log level is actually enabled. Typically used
* by obtaining an implementation from the Logging object:
*
* {{{
* val log = Logging(<bus>, <source object>)
* ...
* log.info("hello world!")
* }}}
*
* All log-level methods support simple interpolation templates with up to four
* arguments placed by using {}
within the template (first string
* argument):
*
* {{{
* 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.
*/
trait LoggingAdapter {
type MDC = Logging.MDC
def mdc = Logging.emptyMDC
/*
* implement these as precisely as needed/possible: always returning true
* just makes the notify... methods be called every time.
*/
def isErrorEnabled: Boolean
def isWarningEnabled: Boolean
def isInfoEnabled: Boolean
def isDebugEnabled: Boolean
/*
* These actually implement the passing on of the messages to be logged.
* Will not be called if is...Enabled returned false.
*/
protected def notifyError(message: String): Unit
protected def notifyError(cause: Throwable, message: String): Unit
protected def notifyWarning(message: String): Unit
protected def notifyInfo(message: String): Unit
protected def notifyDebug(message: String): Unit
/*
* The rest is just the widening of the API for the user's convenience.
*/
/**
* Log message at error level, including the exception that caused the error.
* @see [[LoggingAdapter]]
*/
def error(cause: Throwable, message: String): Unit = { if (isErrorEnabled) notifyError(cause, message) }
/**
* 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 [[LoggingAdapter]]
*/
def error(cause: Throwable, template: String, arg1: Any): Unit = {
if (isErrorEnabled) notifyError(cause, format1(template, arg1))
}
/**
* Message template with 2 replacement arguments.
* @see [[LoggingAdapter]]
*/
def error(cause: Throwable, template: String, arg1: Any, arg2: Any): Unit = {
if (isErrorEnabled) notifyError(cause, format(template, arg1, arg2))
}
/**
* Message template with 3 replacement arguments.
* @see [[LoggingAdapter]]
*/
def error(cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any): Unit = {
if (isErrorEnabled) notifyError(cause, format(template, arg1, arg2, arg3))
}
/**
* Message template with 4 replacement arguments.
* @see [[LoggingAdapter]]
*/
def error(cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = {
if (isErrorEnabled) notifyError(cause, format(template, arg1, arg2, arg3, arg4))
}
/**
* Log message at error level, without providing the exception that caused the error.
* @see [[LoggingAdapter]]
*/
def error(message: String): Unit = { if (isErrorEnabled) notifyError(message) }
/**
* 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 [[LoggingAdapter]]
*/
def error(template: String, arg1: Any): Unit = { if (isErrorEnabled) notifyError(format1(template, arg1)) }
/**
* Message template with 2 replacement arguments.
* @see [[LoggingAdapter]]
*/
def error(template: String, arg1: Any, arg2: Any): Unit = {
if (isErrorEnabled) notifyError(format(template, arg1, arg2))
}
/**
* Message template with 3 replacement arguments.
* @see [[LoggingAdapter]]
*/
def error(template: String, arg1: Any, arg2: Any, arg3: Any): Unit = {
if (isErrorEnabled) notifyError(format(template, arg1, arg2, arg3))
}
/**
* Message template with 4 replacement arguments.
* @see [[LoggingAdapter]]
*/
def error(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = {
if (isErrorEnabled) notifyError(format(template, arg1, arg2, arg3, arg4))
}
/**
* Log message at warning level.
* @see [[LoggingAdapter]]
*/
def warning(message: String): Unit = { if (isWarningEnabled) notifyWarning(message) }
/**
* 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 [[LoggingAdapter]]
*/
def warning(template: String, arg1: Any): Unit = { if (isWarningEnabled) notifyWarning(format1(template, arg1)) }
/**
* Message template with 2 replacement arguments.
* @see [[LoggingAdapter]]
*/
def warning(template: String, arg1: Any, arg2: Any): Unit = {
if (isWarningEnabled) notifyWarning(format(template, arg1, arg2))
}
/**
* Message template with 3 replacement arguments.
* @see [[LoggingAdapter]]
*/
def warning(template: String, arg1: Any, arg2: Any, arg3: Any): Unit = {
if (isWarningEnabled) notifyWarning(format(template, arg1, arg2, arg3))
}
/**
* Message template with 4 replacement arguments.
* @see [[LoggingAdapter]]
*/
def warning(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = {
if (isWarningEnabled) notifyWarning(format(template, arg1, arg2, arg3, arg4))
}
/**
* Log message at info level.
* @see [[LoggingAdapter]]
*/
def info(message: String): Unit = { if (isInfoEnabled) notifyInfo(message) }
/**
* 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 [[LoggingAdapter]]
*/
def info(template: String, arg1: Any): Unit = { if (isInfoEnabled) notifyInfo(format1(template, arg1)) }
/**
* Message template with 2 replacement arguments.
* @see [[LoggingAdapter]]
*/
def info(template: String, arg1: Any, arg2: Any): Unit = {
if (isInfoEnabled) notifyInfo(format(template, arg1, arg2))
}
/**
* Message template with 3 replacement arguments.
* @see [[LoggingAdapter]]
*/
def info(template: String, arg1: Any, arg2: Any, arg3: Any): Unit = {
if (isInfoEnabled) notifyInfo(format(template, arg1, arg2, arg3))
}
/**
* Message template with 4 replacement arguments.
* @see [[LoggingAdapter]]
*/
def info(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = {
if (isInfoEnabled) notifyInfo(format(template, arg1, arg2, arg3, arg4))
}
/**
* Log message at debug level.
* @see [[LoggingAdapter]]
*/
def debug(message: String): Unit = { if (isDebugEnabled) notifyDebug(message) }
/**
* 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 [[LoggingAdapter]]
*/
def debug(template: String, arg1: Any): Unit = { if (isDebugEnabled) notifyDebug(format1(template, arg1)) }
/**
* Message template with 2 replacement arguments.
* @see [[LoggingAdapter]]
*/
def debug(template: String, arg1: Any, arg2: Any): Unit = {
if (isDebugEnabled) notifyDebug(format(template, arg1, arg2))
}
/**
* Message template with 3 replacement arguments.
* @see [[LoggingAdapter]]
*/
def debug(template: String, arg1: Any, arg2: Any, arg3: Any): Unit = {
if (isDebugEnabled) notifyDebug(format(template, arg1, arg2, arg3))
}
/**
* Message template with 4 replacement arguments.
* @see [[LoggingAdapter]]
*/
def debug(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = {
if (isDebugEnabled) notifyDebug(format(template, arg1, arg2, arg3, arg4))
}
/**
* Log message at the specified log level.
*/
def log(level: Logging.LogLevel, message: String): Unit = { if (isEnabled(level)) notifyLog(level, message) }
/**
* 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.
*/
def log(level: Logging.LogLevel, template: String, arg1: Any): Unit = {
if (isEnabled(level)) notifyLog(level, format1(template, arg1))
}
/**
* Message template with 2 replacement arguments.
*/
def log(level: Logging.LogLevel, template: String, arg1: Any, arg2: Any): Unit = {
if (isEnabled(level)) notifyLog(level, format(template, arg1, arg2))
}
/**
* Message template with 3 replacement arguments.
*/
def log(level: Logging.LogLevel, template: String, arg1: Any, arg2: Any, arg3: Any): Unit = {
if (isEnabled(level)) notifyLog(level, format(template, arg1, arg2, arg3))
}
/**
* Message template with 4 replacement arguments.
*/
def log(level: Logging.LogLevel, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = {
if (isEnabled(level)) notifyLog(level, format(template, arg1, arg2, arg3, arg4))
}
/**
* @return true if the specified log level is enabled
*/
final def isEnabled(level: Logging.LogLevel): Boolean = level match {
case Logging.ErrorLevel => isErrorEnabled
case Logging.WarningLevel => isWarningEnabled
case Logging.InfoLevel => isInfoEnabled
case Logging.DebugLevel => isDebugEnabled
case _ => false
}
final def notifyLog(level: Logging.LogLevel, message: String): Unit = level match {
case Logging.ErrorLevel => if (isErrorEnabled) notifyError(message)
case Logging.WarningLevel => if (isWarningEnabled) notifyWarning(message)
case Logging.InfoLevel => if (isInfoEnabled) notifyInfo(message)
case Logging.DebugLevel => if (isDebugEnabled) notifyDebug(message)
case level => throw new IllegalArgumentException(s"Unsupported log level [$level]")
}
/**
* If `arg` is an `Array` it will be expanded into replacement arguments, which is useful when
* there are more than four arguments.
*/
private def format1(t: String, arg: Any): String = arg match {
case a: Array[_] if !a.getClass.getComponentType.isPrimitive => formatImpl(t, a.toSeq)
case a: Array[_] => formatImpl(t, a.map(_.asInstanceOf[AnyRef]).toSeq)
case x => format(t, x)
}
def format(t: String, arg: Any*): String = {
formatImpl(t, arg)
}
private def formatImpl(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
}
}
/**
* Filter of log events that is used by the `LoggingAdapter` before
* publishing log events to the `eventStream`. It can perform
* fine grained filtering based on the log source.
*
* Note that the [[EventStream]] will only subscribe `loggers` to the events
* corresponding to the `logLevel` of the `EventStream`. Therefore it is good
* practice that the `LoggingFilter` implementation first filters using the
* `logLevel` of the `EventStream` before applying more fine grained filters.
*/
trait LoggingFilter {
// for backward-compatibility reason implementation of method without marker only must work
def isErrorEnabled(logClass: Class[_], logSource: String): Boolean
def isWarningEnabled(logClass: Class[_], logSource: String): Boolean
def isInfoEnabled(logClass: Class[_], logSource: String): Boolean
def isDebugEnabled(logClass: Class[_], logSource: String): Boolean
}
/**
* In retrospect should have been abstract, but we cannot change that
* without breaking binary compatibility
*/
@silent
trait LoggingFilterWithMarker extends LoggingFilter {
def isErrorEnabled(logClass: Class[_], logSource: String, marker: LogMarker): Boolean =
isErrorEnabled(logClass, logSource)
def isWarningEnabled(logClass: Class[_], logSource: String, marker: LogMarker): Boolean =
isWarningEnabled(logClass, logSource)
def isInfoEnabled(logClass: Class[_], logSource: String, marker: LogMarker): Boolean =
isInfoEnabled(logClass, logSource)
def isDebugEnabled(logClass: Class[_], logSource: String, marker: LogMarker): Boolean =
isDebugEnabled(logClass, logSource)
}
object LoggingFilterWithMarker {
def wrap(loggingFilter: LoggingFilter): LoggingFilterWithMarker =
loggingFilter match {
case lfwm: LoggingFilterWithMarker => lfwm
case _ => new LoggingFilterWithMarkerWrapper(loggingFilter)
}
}
class LoggingFilterWithMarkerWrapper(loggingFilter: LoggingFilter) extends LoggingFilterWithMarker {
override def isErrorEnabled(logClass: Class[_], logSource: String): Boolean =
loggingFilter.isErrorEnabled(logClass, logSource)
override def isWarningEnabled(logClass: Class[_], logSource: String): Boolean =
loggingFilter.isWarningEnabled(logClass, logSource)
override def isInfoEnabled(logClass: Class[_], logSource: String): Boolean =
loggingFilter.isInfoEnabled(logClass, logSource)
override def isDebugEnabled(logClass: Class[_], logSource: String): Boolean =
loggingFilter.isDebugEnabled(logClass, logSource)
}
/**
* Default [[LoggingFilter]] that uses the logLevel of the `eventStream`, which
* initial value is defined in configuration. The logLevel `eventStream` can be
* changed while the system is running.
*/
class DefaultLoggingFilter(logLevel: () => Logging.LogLevel) extends LoggingFilterWithMarker {
def this(settings: Settings, eventStream: EventStream) = this(() => eventStream.logLevel)
import Logging._
def isErrorEnabled(logClass: Class[_], logSource: String) = logLevel() >= ErrorLevel
def isWarningEnabled(logClass: Class[_], logSource: String) = logLevel() >= WarningLevel
def isInfoEnabled(logClass: Class[_], logSource: String) = logLevel() >= InfoLevel
def isDebugEnabled(logClass: Class[_], logSource: String) = logLevel() >= DebugLevel
}
/**
* LoggingAdapter extension which adds MDC support.
* Only recommended to be used within Actors as it isn't thread safe.
*/
trait DiagnosticLoggingAdapter extends LoggingAdapter {
import java.{ util => ju }
import Logging._
import akka.util.ccompat.JavaConverters._
private var _mdc = emptyMDC
/**
* Scala API:
* Mapped Diagnostic Context for application defined values
* which can be used in PatternLayout when `akka.event.slf4j.Slf4jLogger` is configured.
* Visit Logback Docs: MDC for more information.
*
* @return A Map containing the MDC values added by the application, or empty Map if no value was added.
*/
override def mdc: MDC = _mdc
/**
* Scala API:
* Sets the values to be added to the MDC (Mapped Diagnostic Context) before the log is appended.
* These values can be used in PatternLayout when `akka.event.slf4j.Slf4jLogger` is configured.
* Visit Logback Docs: MDC for more information.
*/
def mdc(mdc: MDC): Unit = _mdc = if (mdc != null) mdc else emptyMDC
/**
* Java API:
* Mapped Diagnostic Context for application defined values
* which can be used in PatternLayout when `akka.event.slf4j.Slf4jLogger` is configured.
* Visit Logback Docs: MDC for more information.
* Note tha it returns a COPY of the actual MDC values.
* You cannot modify any value by changing the returned Map.
* Code like the following won't have any effect unless you set back the modified Map.
*
* {{{
* Map mdc = log.getMDC();
* mdc.put("key", value);
* // NEEDED
* log.setMDC(mdc);
* }}}
*
* @return A copy of the actual MDC values
*/
def getMDC: ju.Map[String, Any] = mdc.asJava
/**
* Java API:
* Sets the values to be added to the MDC (Mapped Diagnostic Context) before the log is appended.
* These values can be used in PatternLayout when `akka.event.slf4j.Slf4jLogger` is configured.
* Visit Logback Docs: MDC for more information.
*/
def setMDC(jMdc: java.util.Map[String, Any]): Unit = mdc(if (jMdc != null) jMdc.asScala.toMap else emptyMDC)
/**
* Clear all entries in the MDC
*/
def clearMDC(): Unit = mdc(emptyMDC)
}
/** DO NOT INHERIT: Class is open only for use by akka-slf4j*/
@DoNotInherit
class LogMarker(val name: String)
object LogMarker {
/** The Marker is internally transferred via MDC using using this key */
private[akka] final val MDCKey = "marker"
def apply(name: String): LogMarker = new LogMarker(name)
/** Java API */
def create(name: String): LogMarker = apply(name)
@Deprecated
@deprecated("use akka.event.LogEventWithMarker#marker instead", since = "2.5.12")
def extractFromMDC(mdc: MDC): Option[String] =
mdc.get(MDCKey) match {
case Some(v) => Some(v.toString)
case None => None
}
private[akka] final val Security = apply("SECURITY")
}
/**
* [[LoggingAdapter]] extension which adds Marker support.
*/
class MarkerLoggingAdapter(
override val bus: LoggingBus,
override val logSource: String,
override val logClass: Class[_],
loggingFilter: LoggingFilter)
extends BusLogging(bus, logSource, logClass, loggingFilter) {
// TODO when breaking binary compatibility, these marker methods should become baked into LoggingAdapter itself
// For backwards compatibility, and when LoggingAdapter is created without direct
// association to an ActorSystem
def this(bus: LoggingBus, logSource: String, logClass: Class[_]) =
this(bus, logSource, logClass, new DefaultLoggingFilter(() => bus.logLevel))
val loggingFilterWithMarker: LoggingFilterWithMarker = LoggingFilterWithMarker.wrap(loggingFilter)
def isErrorEnabled(marker: LogMarker) = loggingFilterWithMarker.isErrorEnabled(logClass, logSource, marker)
def isWarningEnabled(marker: LogMarker) = loggingFilterWithMarker.isWarningEnabled(logClass, logSource, marker)
def isInfoEnabled(marker: LogMarker) = loggingFilterWithMarker.isInfoEnabled(logClass, logSource, marker)
def isDebugEnabled(marker: LogMarker) = loggingFilterWithMarker.isDebugEnabled(logClass, logSource, marker)
/**
* Log message at error level, including 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 [[LoggingAdapter]]
*/
def error(marker: LogMarker, cause: Throwable, message: String): Unit =
if (isErrorEnabled(marker)) bus.publish(Error(cause, logSource, logClass, message, mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def error(marker: LogMarker, cause: Throwable, template: String, arg1: Any): Unit =
if (isErrorEnabled(marker)) bus.publish(Error(cause, logSource, logClass, format1(template, arg1), mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def error(marker: LogMarker, cause: Throwable, template: String, arg1: Any, arg2: Any): Unit =
if (isErrorEnabled(marker))
bus.publish(Error(cause, logSource, logClass, format(template, arg1, arg2), mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def error(marker: LogMarker, cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any): Unit =
if (isErrorEnabled(marker))
bus.publish(Error(cause, logSource, logClass, format(template, arg1, arg2, arg3), mdc, marker))
/**
* Message template with 4 replacement arguments.
* The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special".
* @see [[LoggingAdapter]]
*/
def error(marker: LogMarker, cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit =
if (isErrorEnabled(marker))
bus.publish(Error(cause, logSource, logClass, format(template, arg1, arg2, arg3, arg4), mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def error(marker: LogMarker, message: String): Unit =
if (isErrorEnabled(marker)) bus.publish(Error(logSource, logClass, message, mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def error(marker: LogMarker, template: String, arg1: Any): Unit =
if (isErrorEnabled(marker)) bus.publish(Error(logSource, logClass, format1(template, arg1), mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def error(marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit =
if (isErrorEnabled(marker)) bus.publish(Error(logSource, logClass, format(template, arg1, arg2), mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def error(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit =
if (isErrorEnabled(marker)) bus.publish(Error(logSource, logClass, format(template, arg1, arg2, arg3), mdc, marker))
/**
* Message template with 4 replacement arguments.
* The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special".
* @see [[LoggingAdapter]]
*/
def error(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit =
if (isErrorEnabled(marker))
bus.publish(Error(logSource, logClass, format(template, arg1, arg2, arg3, arg4), mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def warning(marker: LogMarker, message: String): Unit =
if (isWarningEnabled(marker)) bus.publish(Warning(logSource, logClass, message, mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def warning(marker: LogMarker, template: String, arg1: Any): Unit =
if (isWarningEnabled(marker)) bus.publish(Warning(logSource, logClass, format1(template, arg1), mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def warning(marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit =
if (isWarningEnabled(marker)) bus.publish(Warning(logSource, logClass, format(template, arg1, arg2), mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def warning(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit =
if (isWarningEnabled(marker))
bus.publish(Warning(logSource, logClass, format(template, arg1, arg2, arg3), mdc, marker))
/**
* Message template with 4 replacement arguments.
* The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special".
* @see [[LoggingAdapter]]
*/
def warning(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit =
if (isWarningEnabled(marker))
bus.publish(Warning(logSource, logClass, format(template, arg1, arg2, arg3, arg4), mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def info(marker: LogMarker, message: String): Unit =
if (isInfoEnabled(marker)) bus.publish(Info(logSource, logClass, message, mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def info(marker: LogMarker, template: String, arg1: Any): Unit =
if (isInfoEnabled(marker)) bus.publish(Info(logSource, logClass, format1(template, arg1), mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def info(marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit =
if (isInfoEnabled(marker)) bus.publish(Info(logSource, logClass, format(template, arg1, arg2), mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def info(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit =
if (isInfoEnabled(marker)) bus.publish(Info(logSource, logClass, format(template, arg1, arg2, arg3), mdc, marker))
/**
* Message template with 4 replacement arguments.
* The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special".
* @see [[LoggingAdapter]]
*/
def info(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit =
if (isInfoEnabled(marker))
bus.publish(Info(logSource, logClass, format(template, arg1, arg2, arg3, arg4), mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def debug(marker: LogMarker, message: String): Unit =
if (isDebugEnabled(marker)) bus.publish(Debug(logSource, logClass, message, mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def debug(marker: LogMarker, template: String, arg1: Any): Unit =
if (isDebugEnabled(marker)) bus.publish(Debug(logSource, logClass, format1(template, arg1), mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def debug(marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit =
if (isDebugEnabled(marker)) bus.publish(Debug(logSource, logClass, format(template, arg1, arg2), mdc, marker))
/**
* 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 [[LoggingAdapter]]
*/
def debug(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit =
if (isDebugEnabled(marker)) bus.publish(Debug(logSource, logClass, format(template, arg1, arg2, arg3), mdc, marker))
/**
* Message template with 4 replacement arguments.
* The marker argument can be picked up by various logging frameworks such as slf4j to mark this log statement as "special".
* @see [[LoggingAdapter]]
*/
def debug(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit =
if (isDebugEnabled(marker))
bus.publish(Debug(logSource, logClass, format(template, arg1, arg2, arg3, arg4), mdc, marker))
// Copy of LoggingAdapter.format1 due to binary compatibility restrictions
private def format1(t: String, arg: Any): String = arg match {
case a: Array[_] if !a.getClass.getComponentType.isPrimitive => format(t, a.toIndexedSeq)
case a: Array[_] => format(t, a.map(_.asInstanceOf[AnyRef]).toIndexedSeq)
case x => format(t, x)
}
}
final class DiagnosticMarkerBusLoggingAdapter(
override val bus: LoggingBus,
override val logSource: String,
override val logClass: Class[_],
loggingFilter: LoggingFilter)
extends MarkerLoggingAdapter(bus, logSource, logClass, loggingFilter)
with DiagnosticLoggingAdapter
/**
* [[akka.event.LoggingAdapter]] that publishes [[akka.event.Logging.LogEvent]] to event stream.
*/
class BusLogging(val bus: LoggingBus, val logSource: String, val logClass: Class[_], loggingFilter: LoggingFilter)
extends LoggingAdapter {
// For backwards compatibility, and when LoggingAdapter is created without direct
// association to an ActorSystem
def this(bus: LoggingBus, logSource: String, logClass: Class[_]) =
this(bus, logSource, logClass, new DefaultLoggingFilter(() => bus.logLevel))
import Logging._
def isErrorEnabled = loggingFilter.isErrorEnabled(logClass, logSource)
def isWarningEnabled = loggingFilter.isWarningEnabled(logClass, logSource)
def isInfoEnabled = loggingFilter.isInfoEnabled(logClass, logSource)
def isDebugEnabled = loggingFilter.isDebugEnabled(logClass, logSource)
protected def notifyError(message: String): Unit =
bus.publish(Error(logSource, logClass, message, mdc))
protected def notifyError(cause: Throwable, message: String): Unit =
bus.publish(Error(cause, logSource, logClass, message, mdc))
protected def notifyWarning(message: String): Unit =
bus.publish(Warning(logSource, logClass, message, mdc))
protected def notifyInfo(message: String): Unit =
bus.publish(Info(logSource, logClass, message, mdc))
protected def notifyDebug(message: String): Unit =
bus.publish(Debug(logSource, logClass, message, mdc))
}
/**
* NoLogging is a LoggingAdapter that does absolutely nothing – no logging at all.
*/
object NoLogging extends LoggingAdapter {
/**
* Java API to return the reference to NoLogging
* @return The NoLogging instance
*/
def getInstance = this
final override def isErrorEnabled = false
final override def isWarningEnabled = false
final override def isInfoEnabled = false
final override def isDebugEnabled = false
final protected override def notifyError(message: String): Unit = ()
final protected override def notifyError(cause: Throwable, message: String): Unit = ()
final protected override def notifyWarning(message: String): Unit = ()
final protected override def notifyInfo(message: String): Unit = ()
final protected override def notifyDebug(message: String): Unit = ()
}
/**
* NoLogging is a MarkerLoggingAdapter that does absolutely nothing – no logging at all.
*/
object NoMarkerLogging extends MarkerLoggingAdapter(null, "source", classOf[String], null) {
/**
* Java API to return the reference to NoLogging
* @return The NoLogging instance
*/
def getInstance = this
final override def isErrorEnabled = false
final override def isWarningEnabled = false
final override def isInfoEnabled = false
final override def isDebugEnabled = false
final protected override def notifyError(message: String): Unit = ()
final protected override def notifyError(cause: Throwable, message: String): Unit = ()
final protected override def notifyWarning(message: String): Unit = ()
final protected override def notifyInfo(message: String): Unit = ()
final protected override def notifyDebug(message: String): Unit = ()
final override def error(marker: LogMarker, cause: Throwable, message: String): Unit = ()
final override def error(marker: LogMarker, cause: Throwable, template: String, arg1: Any): Unit = ()
final override def error(marker: LogMarker, cause: Throwable, template: String, arg1: Any, arg2: Any): Unit = ()
final override def error(
marker: LogMarker,
cause: Throwable,
template: String,
arg1: Any,
arg2: Any,
arg3: Any): Unit = ()
final override def error(
marker: LogMarker,
cause: Throwable,
template: String,
arg1: Any,
arg2: Any,
arg3: Any,
arg4: Any): Unit = ()
final override def error(marker: LogMarker, message: String): Unit = ()
final override def error(marker: LogMarker, template: String, arg1: Any): Unit = ()
final override def error(marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit = ()
final override def error(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit = ()
final override def error(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = ()
final override def warning(marker: LogMarker, message: String): Unit = ()
final override def warning(marker: LogMarker, template: String, arg1: Any): Unit = ()
final override def warning(marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit = ()
final override def warning(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit = ()
final override def warning(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = ()
final override def info(marker: LogMarker, message: String): Unit = ()
final override def info(marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit = ()
final override def info(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit = ()
final override def info(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = ()
final override def debug(marker: LogMarker, message: String): Unit = ()
final override def debug(marker: LogMarker, template: String, arg1: Any): Unit = ()
final override def debug(marker: LogMarker, template: String, arg1: Any, arg2: Any): Unit = ()
final override def debug(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any): Unit = ()
final override def debug(marker: LogMarker, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any): Unit = ()
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy