Rename sbt akka modules

Co-authored-by: Sean Glover <sean@seanglover.com>
This commit is contained in:
Matthew de Detrich 2023-01-05 11:10:50 +01:00 committed by GitHub
parent b92b749946
commit 24c03cde19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2930 changed files with 1466 additions and 1462 deletions

View file

@ -0,0 +1,200 @@
/*
* Copyright (C) 2009-2022 Lightbend Inc. <https://www.lightbend.com>
*/
package org.apache.pekko.event.slf4j
import org.slf4j.{ Logger => SLFLogger, LoggerFactory => SLFLoggerFactory, MDC, Marker, MarkerFactory }
import org.apache.pekko
import pekko.actor._
import pekko.dispatch.RequiresMessageQueue
import pekko.event.{ LogMarker, _ }
import pekko.event.Logging._
import pekko.util.{ unused, Helpers }
/**
* 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"
val mdcAkkaAddressAttributeName = "akkaAddress"
val mdcAkkaUidAttributeName = "akkaUid"
private def akkaAddress = context.system.asInstanceOf[ExtendedActorSystem].provider.addressString
private val akkaUid: String = context.system.asInstanceOf[ExtendedActorSystem].uid.toString
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 e.cause.getLocalizedMessage,
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: Any)
}
case event @ Debug(logSource, logClass, message) =>
withMdc(logSource, event) {
Logger(logClass, logSource).debug(markerIfPresent(event), "{}", message: Any)
}
case InitializeLogger(_) =>
log.info("Slf4jLogger started")
sender() ! LoggerInitialized
}
@inline
final def withMdc(logSource: String, logEvent: LogEvent)(logStatement: => Unit): Unit = {
logEvent match {
case m: LogEventWithMarker if m.marker ne null =>
val properties = m.marker.properties
if (properties.nonEmpty) {
properties.foreach { case (k, v) => MDC.put(k, String.valueOf(v)) }
}
case _ =>
}
MDC.put(mdcAkkaSourceAttributeName, logSource)
MDC.put(mdcThreadAttributeName, logEvent.thread.getName)
MDC.put(mdcAkkaTimestamp, formatTimestamp(logEvent.timestamp))
MDC.put(mdcActorSystemAttributeName, context.system.name)
MDC.put(mdcAkkaAddressAttributeName, akkaAddress)
MDC.put(mdcAkkaUidAttributeName, akkaUid)
logEvent.mdc.foreach { case (k, v) => MDC.put(k, String.valueOf(v)) }
try logStatement
finally {
MDC.clear()
}
}
private final def markerIfPresent(event: LogEvent): Marker =
event match {
case m: LogEventWithMarker =>
m.marker match {
case null => null
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)
}
/**
* [[pekko.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(@unused settings: ActorSystem.Settings, eventStream: EventStream)
extends LoggingFilterWithMarker {
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
private def slf4jMarker(marker: LogMarker) = marker match {
case null => null
case slf4jMarker: Slf4jLogMarker => slf4jMarker.marker
case marker => MarkerFactory.getMarker(marker.name)
}
override def isErrorEnabled(logClass: Class[_], logSource: String, marker: LogMarker): Boolean =
(eventStream.logLevel >= ErrorLevel) && Logger(logClass, logSource).isErrorEnabled(slf4jMarker(marker))
override def isWarningEnabled(logClass: Class[_], logSource: String, marker: LogMarker): Boolean =
(eventStream.logLevel >= WarningLevel) && Logger(logClass, logSource).isWarnEnabled(slf4jMarker(marker))
override def isInfoEnabled(logClass: Class[_], logSource: String, marker: LogMarker): Boolean =
(eventStream.logLevel >= InfoLevel) && Logger(logClass, logSource).isInfoEnabled(slf4jMarker(marker))
override def isDebugEnabled(logClass: Class[_], logSource: String, marker: LogMarker): Boolean =
(eventStream.logLevel >= DebugLevel) && Logger(logClass, logSource).isDebugEnabled(slf4jMarker(marker))
}
/** Wraps [[org.slf4j.Marker]] */
final class Slf4jLogMarker(val marker: org.slf4j.Marker) extends LogMarker(name = marker.getName, Map.empty)
/** 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)
}

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n</pattern>
</encoder>
</appender>
<appender name="TEST" class="org.apache.pekko.event.slf4j.Slf4jLoggerSpec$TestAppender">
<encoder>
<pattern>%date{ISO8601} level=[%level] marker=[%marker] logger=[%logger] mdc=[%mdc] - msg=[%msg]%n----%n</pattern>
</encoder>
</appender>
<logger name="org.apache.pekko.event.slf4j.Slf4jLoggingFilterSpec$DebugLevelProducer"
level="debug" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.apache.pekko.event.slf4j.Slf4jLoggingFilterSpec$WarningLevelProducer"
level="warn" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.apache.pekko.event.slf4j.Slf4jLoggerSpec" level="info"
additivity="false">
<appender-ref ref="TEST" />
</logger>
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View file

@ -0,0 +1,246 @@
/*
* Copyright (C) 2009-2022 Lightbend Inc. <https://www.lightbend.com>
*/
package org.apache.pekko.event.slf4j
import java.io.ByteArrayOutputStream
import scala.concurrent.duration._
import ch.qos.logback.core.OutputStreamAppender
import language.postfixOps
import org.scalatest.BeforeAndAfterEach
import org.slf4j.{ Marker, MarkerFactory }
import org.apache.pekko
import pekko.actor.{ Actor, DiagnosticActorLogging, Props }
import pekko.event.{ LogMarker, Logging }
import pekko.testkit.PekkoSpec
object Slf4jLoggerSpec {
// This test depends on logback configuration in src/test/resources/logback-test.xml
val config = """
pekko {
loglevel = INFO
loggers = ["org.apache.pekko.event.slf4j.Slf4jLogger"]
logger-startup-timeout = 30s
}
"""
final case class StringWithMDC(s: String, mdc: Map[String, Any])
final case class StringWithMarker(s: String, marker: LogMarker)
final case class StringWithSlf4jMarker(s: String, marker: Marker)
final case class StringWithSlf4jMarkerMDC(s: String, marker: Marker, mdc: Map[String, Any])
final class LogProducer extends Actor with DiagnosticActorLogging {
val markLog = Logging.withMarker(this)
def receive = {
case e: Exception =>
log.error(e, e.getMessage)
case (s: String, x: Int, y: Int) =>
log.info(s, x, y)
case StringWithSlf4jMarker(s, m) =>
markLog.info(Slf4jLogMarker(m), s)
case StringWithSlf4jMarkerMDC(s, mark, mdc) =>
markLog.mdc(mdc)
markLog.info(Slf4jLogMarker(mark), s)
markLog.clearMDC()
case StringWithMDC(s, mdc) =>
log.mdc(mdc)
log.info(s)
log.clearMDC()
case StringWithMarker(s, marker) =>
markLog.info(marker, s)
}
}
class MyLogSource
val output = new ByteArrayOutputStream
def outputString: String = output.toString("UTF-8")
class TestAppender[E] extends OutputStreamAppender[E] {
override def start(): Unit = {
setOutputStream(output)
super.start()
}
}
}
class Slf4jLoggerSpec extends PekkoSpec(Slf4jLoggerSpec.config) with BeforeAndAfterEach {
import Slf4jLoggerSpec._
val producer = system.actorOf(Props[LogProducer](), name = "logProducer")
override def beforeEach(): Unit = {
output.reset()
}
val sourceThreadRegex = "sourceThread=Slf4jLoggerSpec-pekko.actor.default-dispatcher-[1-9][0-9]*"
"Slf4jLogger" must {
"log error with stackTrace" in {
producer ! new RuntimeException("Simulated error")
awaitCond(outputString.contains("----"), 5 seconds)
val s = outputString
s should include("akkaSource=akka://Slf4jLoggerSpec/user/logProducer")
s should include("akkaAddress=akka://Slf4jLoggerSpec")
s should include("akkaUid=")
s should include("level=[ERROR]")
s should include("logger=[org.apache.pekko.event.slf4j.Slf4jLoggerSpec$LogProducer]")
(s should include).regex(sourceThreadRegex)
s should include("msg=[Simulated error]")
s should include("java.lang.RuntimeException: Simulated error")
s should include("at org.apache.pekko.event.slf4j.Slf4jLoggerSpec")
}
"log info with parameters" in {
producer ! (("test x={} y={}", 3, 17))
awaitCond(outputString.contains("----"), 5 seconds)
val s = outputString
s should include("akkaSource=akka://Slf4jLoggerSpec/user/logProducer")
s should include("akkaAddress=akka://Slf4jLoggerSpec")
s should include("level=[INFO]")
s should include("logger=[org.apache.pekko.event.slf4j.Slf4jLoggerSpec$LogProducer]")
(s should include).regex(sourceThreadRegex)
s should include("msg=[test x=3 y=17]")
}
"log info with marker" in {
producer ! StringWithMarker("security-wise interesting message", LogMarker("SECURITY"))
awaitCond(outputString.contains("----"), 5 seconds)
val s = outputString
s should include("marker=[SECURITY]")
s should include("msg=[security-wise interesting message]")
}
"log info with marker and properties" in {
producer ! StringWithMarker("interesting message", LogMarker("testMarker", Map("p1" -> 1, "p2" -> "B")))
awaitCond(outputString.contains("----"), 5 seconds)
val s = outputString
s should include("marker=[testMarker]")
s should include("p1=1")
s should include("p2=B")
s should include("msg=[interesting message]")
}
"log info with slf4j marker" in {
val slf4jMarker = MarkerFactory.getMarker("SLF")
slf4jMarker.add(MarkerFactory.getMarker("ADDED")) // slf4j markers can have children
producer ! StringWithSlf4jMarker("security-wise interesting message", slf4jMarker)
awaitCond(outputString.contains("----"), 5 seconds)
val s = outputString
s should include("marker=[SLF [ ADDED ]]")
s should include("msg=[security-wise interesting message]")
}
"log info with slf4j marker and MDC" in {
val slf4jMarker = MarkerFactory.getMarker("SLF")
slf4jMarker.add(MarkerFactory.getMarker("ADDED")) // slf4j markers can have children
producer ! StringWithSlf4jMarkerMDC(
"security-wise interesting message",
slf4jMarker,
Map("ticketNumber" -> 3671, "ticketDesc" -> "Custom MDC Values"))
awaitCond(outputString.contains("----"), 5 seconds)
val s = outputString
s should include("marker=[SLF [ ADDED ]]")
s should include("ticketDesc=Custom MDC Values")
s should include("msg=[security-wise interesting message]")
}
"put custom MDC values when specified" in {
producer ! StringWithMDC(
"Message with custom MDC values",
Map("ticketNumber" -> 3671, "ticketDesc" -> "Custom MDC Values"))
awaitCond(outputString.contains("----"), 5 seconds)
val s = outputString
s should include("akkaSource=akka://Slf4jLoggerSpec/user/logProducer")
s should include("level=[INFO]")
s should include("logger=[org.apache.pekko.event.slf4j.Slf4jLoggerSpec$LogProducer]")
(s should include).regex(sourceThreadRegex)
s should include("ticketDesc=Custom MDC Values")
s should include("msg=[Message with custom MDC values]")
}
"support null marker" in {
producer ! StringWithMarker("security-wise interesting message", null)
awaitCond(outputString.contains("----"), 5 seconds)
val s = outputString
s should include("msg=[security-wise interesting message]")
}
"Support null values in custom MDC" in {
producer ! StringWithMDC("Message with null custom MDC values", Map("ticketNumber" -> 3671, "ticketDesc" -> null))
awaitCond(outputString.contains("----"), 5 seconds)
val s = outputString
s should include("akkaSource=akka://Slf4jLoggerSpec/user/logProducer")
s should include("level=[INFO]")
s should include("logger=[org.apache.pekko.event.slf4j.Slf4jLoggerSpec$LogProducer]")
println(s)
(s should include).regex(sourceThreadRegex)
s should include("ticketDesc=null")
s should include("msg=[Message with null custom MDC values]")
}
"include system info in akkaSource when creating Logging with system" in {
val log = Logging(system, "org.apache.pekko.event.slf4j.Slf4jLoggerSpec.MyLogSource")
log.info("test")
awaitCond(outputString.contains("----"), 5 seconds)
val s = outputString
s should include("akkaSource=org.apache.pekko.event.slf4j.Slf4jLoggerSpec.MyLogSource(akka://Slf4jLoggerSpec)")
s should include("logger=[org.apache.pekko.event.slf4j.Slf4jLoggerSpec.MyLogSource(akka://Slf4jLoggerSpec)]")
}
"not include system info in akkaSource when creating Logging with system.eventStream" in {
val log = Logging(system.eventStream, "org.apache.pekko.event.slf4j.Slf4jLoggerSpec.MyLogSource")
log.info("test")
awaitCond(outputString.contains("----"), 5 seconds)
val s = outputString
s should include("akkaSource=org.apache.pekko.event.slf4j.Slf4jLoggerSpec.MyLogSource")
s should include("logger=[org.apache.pekko.event.slf4j.Slf4jLoggerSpec.MyLogSource]")
}
"use short class name and include system info in akkaSource when creating Logging with system and class" in {
val log = Logging(system, classOf[MyLogSource])
log.info("test")
awaitCond(outputString.contains("----"), 5 seconds)
val s = outputString
s should include("akkaSource=Slf4jLoggerSpec$MyLogSource(akka://Slf4jLoggerSpec)")
s should include("logger=[org.apache.pekko.event.slf4j.Slf4jLoggerSpec$MyLogSource]")
}
"use short class name in akkaSource when creating Logging with system.eventStream and class" in {
val log = Logging(system.eventStream, classOf[MyLogSource])
log.info("test")
awaitCond(outputString.contains("----"), 5 seconds)
val s = outputString
s should include("akkaSource=Slf4jLoggerSpec$MyLogSource")
s should include("logger=[org.apache.pekko.event.slf4j.Slf4jLoggerSpec$MyLogSource]")
}
"include actorSystem name in sourceActorSystem" in {
val log = Logging(system.eventStream, classOf[MyLogSource])
log.info("test")
awaitCond(outputString.contains("----"), 5 seconds)
val s = outputString
s should include("sourceActorSystem=Slf4jLoggerSpec")
}
}
}

View file

@ -0,0 +1,117 @@
/*
* Copyright (C) 2009-2022 Lightbend Inc. <https://www.lightbend.com>
*/
package org.apache.pekko.event.slf4j
import scala.concurrent.duration._
import org.scalatest.BeforeAndAfterEach
import org.apache.pekko
import pekko.actor.{ Actor, ActorLogging, Props }
import pekko.actor.ActorRef
import pekko.event.Logging
import pekko.event.Logging.Debug
import pekko.event.Logging.Info
import pekko.event.Logging.InitializeLogger
import pekko.event.Logging.LogEvent
import pekko.event.Logging.LoggerInitialized
import pekko.event.Logging.Warning
import pekko.testkit.PekkoSpec
import pekko.testkit.TestProbe
object Slf4jLoggingFilterSpec {
// This test depends on logback configuration in src/test/resources/logback-test.xml
val config = """
pekko {
loglevel = DEBUG # test verifies debug
loggers = ["org.apache.pekko.event.slf4j.Slf4jLoggingFilterSpec$TestLogger"]
logging-filter = "org.apache.pekko.event.slf4j.Slf4jLoggingFilter"
}
"""
final case class SetTarget(ref: ActorRef)
class TestLogger extends Actor {
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 =>
println("# event: " + event)
target.foreach { _ ! event }
}
}
class DebugLevelProducer extends Actor with ActorLogging {
def receive = {
case s: String =>
log.warning(s)
log.info(s)
println("# DebugLevelProducer: " + log.isDebugEnabled)
log.debug(s)
}
}
class WarningLevelProducer extends Actor with ActorLogging {
def receive = {
case s: String =>
log.warning(s)
log.info(s)
log.debug(s)
}
}
}
class Slf4jLoggingFilterSpec extends PekkoSpec(Slf4jLoggingFilterSpec.config) with BeforeAndAfterEach {
import Slf4jLoggingFilterSpec._
"Slf4jLoggingFilter" must {
"use configured LoggingFilter at debug log level in logback conf" in {
val log1 = Logging(system, classOf[DebugLevelProducer])
log1.isDebugEnabled should be(true)
log1.isInfoEnabled should be(true)
log1.isWarningEnabled should be(true)
log1.isErrorEnabled should be(true)
}
"use configured LoggingFilter at warning log level in logback conf" in {
val log1 = Logging(system, classOf[WarningLevelProducer])
log1.isDebugEnabled should be(false)
log1.isInfoEnabled should be(false)
log1.isWarningEnabled should be(true)
log1.isErrorEnabled should be(true)
}
"filter ActorLogging at debug log level with logback conf" in {
val probe = TestProbe()
system.eventStream.publish(SetTarget(probe.ref))
probe.expectMsg("OK")
val debugLevelProducer = system.actorOf(Props[DebugLevelProducer](), name = "debugLevelProducer")
debugLevelProducer ! "test1"
probe.expectMsgType[Warning].message should be("test1")
probe.expectMsgType[Info].message should be("test1")
probe.expectMsgType[Debug].message should be("test1")
}
"filter ActorLogging at warning log level with logback conf" in {
val probe = TestProbe()
system.eventStream.publish(SetTarget(probe.ref))
probe.expectMsg("OK")
val debugLevelProducer = system.actorOf(Props[WarningLevelProducer](), name = "warningLevelProducer")
debugLevelProducer ! "test2"
probe.expectMsgType[Warning].message should be("test2")
probe.expectNoMessage(500.millis)
}
}
}