繁体   English   中英

Sbt test: class org.apache.logging.slf4j.Log4jLogger cannot be cast to class ch.qos.logback.classic.Logger (org.apache.logging.slf4j.Log4jLogger

[英]Sbt test: class org.apache.logging.slf4j.Log4jLogger cannot be cast to class ch.qos.logback.classic.Logger (org.apache.logging.slf4j.Log4jLogger

我是 Scala 和 JVM 的新手,我想为记录器编写单元测试,但是当我从终端运行sbt test时出现此错误。

java.lang.ClassCastException: class org.apache.logging.slf4j.Log4jLogger cannot be cast to class ch.qos.logback.classic.Logger (org.apache.logging.slf4j.Log4jLogger and ch.qos.logback.classic.Logger are in unnamed module of loader 'app')和此警告

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/yl3/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/apache/logging/log4j/log4j-slf4j-impl/2.16.0/log4j-slf4j-impl-2.16.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/yl3/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.2.11/logback-classic-1.2.11.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]

但是当我从 IntelliJ 运行测试时,它通过了。

我的测试代码如下

import org.scalatest._
import flatspec._

import ch.qos.logback.classic.Logger
import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.read.ListAppender
import org.slf4j.LoggerFactory

class MyAppSuite extends AnyFlatSpec {
  it should "Log message" in {
    object MyApp {
      val LOGGER = LoggerFactory.getLogger(classOf[ILoggingEvent])
    }
    class MyApp {
      def hello(word: String): Unit = {
        MyApp.LOGGER.info(s"Word is ${word}")
      }
    }

    val appender = new ListAppender[ILoggingEvent]
    appender.start()
    val logger = MyApp.LOGGER.asInstanceOf[Logger]
    logger.addAppender(appender)

    val myApp = new MyApp
    myApp.hello("wow")

    val logsList = appender.list
    assert(logsList.size() === 1)
    val logEvent = logsList.get(0)
    assert(logEvent.getLevel.levelStr === "INFO")
    assert(logEvent.getMessage === "Word is wow")
  }
}

我在build.sbt中的依赖项

...

scalaVersion := "2.12.14"

val log4jVersion = "2.16.0"

val loggingDependencies = Seq(
  "org.slf4j" % "slf4j-api" % "1.7.36",
  "org.apache.logging.log4j" % "log4j-api" % log4jVersion,
  "org.apache.logging.log4j" % "log4j-core" % log4jVersion,
  "org.apache.logging.log4j" % "log4j-slf4j-impl" % log4jVersion
)

val testingDependencies = Seq(
  "org.scalamock" %% "scalamock" % "5.2.0" % Test,
  "org.scalatest" %% "scalatest" % "3.2.12" % Test,
  "ch.qos.logback" % "logback-classic" % "1.2.11" % Test,
)

libraryDependencies ++= loggingDependencies ++ testingDependencies
excludeDependencies ++= Seq(
  "org.slf4j" % "slf4j-log4j12"
)

当我从 IntelliJ 运行测试时,我看到了这个警告

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/yl3/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.2.11/logback-classic-1.2.11.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/yl3/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/apache/logging/log4j/log4j-slf4j-impl/2.16.0/log4j-slf4j-impl-2.16.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]

我在 Java 中发现了类似的日志记录测试

所以我认为它应该在 Scala 中工作,但不确定为什么铸造不起作用。 使用其他库测试日志记录的任何建议或其他方法?

SLF4J 的警告信息真的很有帮助。 在这两种情况下,它都说在类路径中加载了几个日志库,这可能会导致麻烦

在查看详细信息时,它甚至会说明选择哪个库作为 SLF4J 实现:

  • 在您的 IntelliJ 测试中,它选择了 Logback: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
  • 但在您的 SBT 运行中,它选择了 Log4j: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]

这解释了您希望日志库为 Logback 的代码在 IntelliJ 测试中有效,但在 SBT 运行时无效。

当类路径中有多个日志记录实现时,无法确定 SLF4J 将使用哪一个。


现在,让我们澄清一下构建定义中的依赖关系:

  • slf4j-api : SlF4J API,
  • log4j-api :log4j 2 API,
  • log4j-core :log4j 2 实现
  • log4j-slf4j-impl :使用 log4j 2 实现使 SLF4J 写入日志的绑定
  • logback-corelogback-classic :Logback 实现(Logback 带有一个绑定,可以使 SLF4J 自动使用 Logback 写入日志)并且您已经排除slf4j-log4j12 ,它是使 SLF4J 使用 log4j 1.2 实现写入日志的绑定。

解决方案

如果您不需要多个日志库(99% 的情况下),只需选择您要使用的实现,它似乎是 Logback,因为您有 Logback 特定代码。 然后删除其他库实现和绑定依赖项

这意味着在您的情况下删除log4j-corelog4j-slf4j-impl

但是你可能仍然有依赖 log4j 2 API 来记录内容的代码(例如其他依赖项)。 因此,您可能需要添加绑定库以使 log4j 2 日志通过 SlF4J 写入。 为此,请添加log4j-to-slf4j-2.x依赖项。

注意:如果出于某种奇怪的原因您需要多个日志记录实现,那么不要在您的代码中假设它是其中之一,或者通过处理强制转换异常来处理它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM