-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
Slf4jLogger.scala
158 lines (136 loc) · 5.76 KB
/
Slf4jLogger.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/**
* Copyright (C) 2009-2018 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.event.slf4j
import org.slf4j.{ MDC, Marker, MarkerFactory, Logger ⇒ SLFLogger, LoggerFactory ⇒ SLFLoggerFactory }
import akka.event.Logging._
import akka.actor._
import akka.event.{ LogMarker, _ }
import akka.util.Helpers
import akka.dispatch.RequiresMessageQueue
/**
* Base trait for all classes that wants to be able use the SLF4J logging infrastructure.
*/
trait SLF4JLogging {
@transient
lazy val log = Logger(this.getClass.getName)
}
/**
* Logger is a factory for obtaining SLF4J-Loggers
*/
object Logger {
/**
* @param logger - which logger
* @return a Logger that corresponds for the given logger name
*/
def apply(logger: String): SLFLogger = SLFLoggerFactory getLogger logger
/**
* @param logClass - the class to log for
* @param logSource - the textual representation of the source of this log stream
* @return a Logger for the specified parameters
*/
def apply(logClass: Class[_], logSource: String): SLFLogger = logClass match {
case c if c == classOf[DummyClassForStringSources] ⇒ apply(logSource)
case _ ⇒ SLFLoggerFactory getLogger logClass
}
/**
* Returns the SLF4J Root Logger
*/
def root: SLFLogger = apply(SLFLogger.ROOT_LOGGER_NAME)
}
/**
* SLF4J logger.
*
* The thread in which the logging was performed is captured in
* Mapped Diagnostic Context (MDC) with attribute name "sourceThread".
*/
class Slf4jLogger extends Actor with SLF4JLogging with RequiresMessageQueue[LoggerMessageQueueSemantics] {
val mdcThreadAttributeName = "sourceThread"
val mdcActorSystemAttributeName = "sourceActorSystem"
val mdcAkkaSourceAttributeName = "akkaSource"
val mdcAkkaTimestamp = "akkaTimestamp"
def receive = {
case event @ Error(cause, logSource, logClass, message) ⇒
withMdc(logSource, event) {
cause match {
case Error.NoCause | null ⇒
Logger(logClass, logSource).error(markerIfPresent(event), if (message != null) message.toString else null)
case _ ⇒
Logger(logClass, logSource).error(markerIfPresent(event), if (message != null) message.toString else cause.getLocalizedMessage, cause)
}
}
case event @ Warning(logSource, logClass, message) ⇒
withMdc(logSource, event) {
event match {
case e: LogEventWithCause ⇒ Logger(logClass, logSource).warn(markerIfPresent(event), if (message != null) message.toString else null, e.cause)
case _ ⇒ Logger(logClass, logSource).warn(markerIfPresent(event), if (message != null) message.toString else null)
}
}
case event @ Info(logSource, logClass, message) ⇒
withMdc(logSource, event) {
Logger(logClass, logSource).info(markerIfPresent(event), "{}", message.asInstanceOf[AnyRef])
}
case event @ Debug(logSource, logClass, message) ⇒
withMdc(logSource, event) {
Logger(logClass, logSource).debug(markerIfPresent(event), "{}", message.asInstanceOf[AnyRef])
}
case InitializeLogger(_) ⇒
log.info("Slf4jLogger started")
sender() ! LoggerInitialized
}
@inline
final def withMdc(logSource: String, logEvent: LogEvent)(logStatement: ⇒ Unit) {
MDC.put(mdcAkkaSourceAttributeName, logSource)
MDC.put(mdcThreadAttributeName, logEvent.thread.getName)
MDC.put(mdcAkkaTimestamp, formatTimestamp(logEvent.timestamp))
MDC.put(mdcActorSystemAttributeName, actorSystemName)
logEvent.mdc foreach { case (k, v) ⇒ MDC.put(k, String.valueOf(v)) }
try logStatement finally {
MDC.remove(mdcAkkaSourceAttributeName)
MDC.remove(mdcThreadAttributeName)
MDC.remove(mdcAkkaTimestamp)
MDC.remove(mdcActorSystemAttributeName)
logEvent.mdc.keys.foreach(k ⇒ MDC.remove(k))
}
}
private final def markerIfPresent(event: LogEvent): Marker =
event match {
case m: LogEventWithMarker ⇒
m.marker match {
case slf4jMarker: Slf4jLogMarker ⇒ slf4jMarker.marker
case marker ⇒ MarkerFactory.getMarker(marker.name)
}
case _ ⇒ null
}
/**
* Override this method to provide a differently formatted timestamp
* @param timestamp a "currentTimeMillis"-obtained timestamp
* @return the given timestamp as a UTC String
*/
protected def formatTimestamp(timestamp: Long): String =
Helpers.currentTimeMillisToUTCString(timestamp)
private val actorSystemName = context.system.name
}
/**
* [[akka.event.LoggingFilter]] that uses the log level defined in the SLF4J
* backend configuration (e.g. logback.xml) to filter log events before publishing
* the log events to the `eventStream`.
*/
class Slf4jLoggingFilter(settings: ActorSystem.Settings, eventStream: EventStream) extends LoggingFilter {
def isErrorEnabled(logClass: Class[_], logSource: String) =
(eventStream.logLevel >= ErrorLevel) && Logger(logClass, logSource).isErrorEnabled
def isWarningEnabled(logClass: Class[_], logSource: String) =
(eventStream.logLevel >= WarningLevel) && Logger(logClass, logSource).isWarnEnabled
def isInfoEnabled(logClass: Class[_], logSource: String) =
(eventStream.logLevel >= InfoLevel) && Logger(logClass, logSource).isInfoEnabled
def isDebugEnabled(logClass: Class[_], logSource: String) =
(eventStream.logLevel >= DebugLevel) && Logger(logClass, logSource).isDebugEnabled
}
/** Wraps [[org.slf4j.Marker]] */
final class Slf4jLogMarker(val marker: org.slf4j.Marker) extends LogMarker(name = marker.getName)
/** Factory for creating [[LogMarker]] that wraps [[org.slf4j.Marker]] */
object Slf4jLogMarker {
def apply(marker: org.slf4j.Marker): Slf4jLogMarker = new Slf4jLogMarker(marker)
/** Java API */
def create(marker: org.slf4j.Marker): Slf4jLogMarker = apply(marker)
}