簡體   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