Skip to content

Commit

Permalink
fix: Support slf4j 2.0.6 and logback 1.4.5, #31593 (#31825)
Browse files Browse the repository at this point in the history
* slf4j 2.0 changed the API of getMarker (they say it was internal)
* we don't want to enforce an update to later slf4j and break downstream
  projects that still are on slf4j 1.7
* solution here is to support both by using reflection of the getMarker/getMarkers
  methods
* only usage is in StubbedActorContext, i.e. BehaviorTestKit
  • Loading branch information
patriknw committed Jan 26, 2023
1 parent f6a8ddd commit 45aaa40
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ import akka.util.Helpers
import akka.{ actor => classic }
import org.slf4j.Logger
import org.slf4j.helpers.{ MessageFormatter, SubstituteLoggerFactory }

import java.util.concurrent.ThreadLocalRandom.{ current => rnd }

import scala.collection.immutable.TreeMap
import scala.concurrent.ExecutionContextExecutor
import scala.concurrent.duration.FiniteDuration

import org.slf4j.Marker
import org.slf4j.event.SubstituteLoggingEvent

/**
* INTERNAL API
*
Expand Down Expand Up @@ -236,11 +239,30 @@ private[akka] final class FunctionRef[-T](override val path: ActorPath, send: (T
level = evt.getLevel,
message = MessageFormatter.arrayFormat(evt.getMessage, evt.getArgumentArray).getMessage,
cause = Option(evt.getThrowable),
marker = Option(evt.getMarker))
marker = marker(evt))
}
.toList
}

/**
* SL4FJ changed the API in SubstituteLoggingEvent from getMarker in 1.7 to getMarkers in 2.0.
* Using reflection to be able to support both.
*/
private def marker(evt: SubstituteLoggingEvent): Option[Marker] = {
try {
val slf4j1Method = evt.getClass.getMethod("getMarker")
Option(slf4j1Method.invoke(evt).asInstanceOf[Marker])
} catch {
case _: NoSuchMethodException =>
val slf4j2Method = evt.getClass.getMethod("getMarkers")
val markers = slf4j2Method.invoke(evt).asInstanceOf[java.util.List[Marker]]
if ((markers eq null) || markers.isEmpty)
None
else
Option(markers.get(0))
}
}

/**
* Clear the log entries.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ import akka.util.Timeout
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
import org.slf4j.event.Level

import scala.concurrent.duration.{ FiniteDuration, _ }
import scala.reflect.ClassTag

import org.slf4j.Marker
import org.slf4j.MarkerFactory

object BehaviorTestKitSpec {
object Parent {

Expand All @@ -45,7 +47,7 @@ object BehaviorTestKitSpec {
case class SpawnAndWatchWith(name: String) extends Command
case class SpawnSession(replyTo: ActorRef[ActorRef[String]], sessionHandler: ActorRef[String]) extends Command
case class KillSession(session: ActorRef[String], replyTo: ActorRef[Done]) extends Command
case class Log(what: String) extends Command
case class Log(what: String, marker: Option[Marker]) extends Command
case class RegisterWithReceptionist(name: String) extends Command
case class ScheduleCommand(key: Any, delay: FiniteDuration, mode: Effect.TimerScheduled.TimerMode, cmd: Command)
extends Command
Expand Down Expand Up @@ -117,8 +119,13 @@ object BehaviorTestKitSpec {
val adaptor = context.messageAdapter(f)(ClassTag(messageClass))
replyTo.foreach(_ ! adaptor.unsafeUpcast)
Behaviors.same
case Log(what) =>
context.log.info(what)
case Log(what, marker) =>
marker match {
case Some(m) =>
context.log.info(m, what)
case None =>
context.log.info(what)
}
Behaviors.same
case RegisterWithReceptionist(name: String) =>
context.system.receptionist ! Receptionist.Register(ServiceKey[Command](name), context.self)
Expand Down Expand Up @@ -149,8 +156,8 @@ object BehaviorTestKitSpec {
val nrCookies = randomNumerator / randomDenominator

context.ask[GiveMeCookies, CookiesForYou](distributor, GiveMeCookies(nrCookies, _)) {
case scala.util.Success(cfy) => Log(s"Got ${cfy.nrCookies} cookies from distributor")
case scala.util.Failure(ex) => Log(s"Failed to get cookies: ${ex.getMessage}")
case scala.util.Success(cfy) => Log(s"Got ${cfy.nrCookies} cookies from distributor", None)
case scala.util.Failure(ex) => Log(s"Failed to get cookies: ${ex.getMessage}", None)
}
Behaviors.same
case unexpected =>
Expand Down Expand Up @@ -250,14 +257,22 @@ class BehaviorTestKitSpec extends AnyWordSpec with Matchers with LogCapturing {
"allow retrieving log messages issued by behavior" in {
val what = "Hello!"
val testkit = BehaviorTestKit[Parent.Command](Parent.init)
testkit.run(Log(what))
testkit.run(Log(what, None))
testkit.logEntries() shouldBe Seq(CapturedLogEvent(Level.INFO, what))
}

"allow retrieving log messages with Marker issued by behavior" in {
val what = "Hello!"
val testkit = BehaviorTestKit[Parent.Command](Parent.init)
val someMarker = Some(MarkerFactory.getMarker("test"))
testkit.run(Log(what, someMarker))
testkit.logEntries() shouldBe Seq(CapturedLogEvent(Level.INFO, what, None, someMarker))
}

"allow clearing log messages issued by behavior" in {
val what = "Hi!"
val testkit = BehaviorTestKit[Parent.Command](Parent.init)
testkit.run(Log(what))
testkit.run(Log(what, None))
testkit.logEntries() shouldBe Seq(CapturedLogEvent(Level.INFO, what))
testkit.clearLog()
testkit.logEntries() shouldBe Seq.empty
Expand Down

0 comments on commit 45aaa40

Please sign in to comment.