簡體   English   中英

在 Scala 中登錄

[英]Logging in Scala

在 Scala 應用程序中進行日志記錄的好方法是什么? 符合語言哲學的東西,不會使代碼混亂,並且維護成本低且不引人注目。 這是一個基本的要求清單:

  • 簡單的
  • 不會使代碼混亂。 Scala 非常簡潔。 我不希望我的一半代碼記錄語句
  • 可以更改日志格式以適合我的其余企業日志和監控軟件
  • 支持日志級別(即調試、跟蹤、錯誤)
  • 可以登錄到磁盤以及其他目的地(即套接字、控制台等)
  • 最低配置,如果有的話
  • 在容器中工作(即網絡服務器)
  • (可選,但很高興)作為語言的一部分或作為 maven 工件,所以我不必破解我的構建來使用它

我知道我可以使用現有的 Java 日志記錄解決方案,但它們至少在上述兩個方面失敗,即混亂和配置。

感謝您的回復。

slf4j 包裝器

Scala 的大部分日志庫都是圍繞 Java 日志框架(slf4j、log4j 等)的一些包裝器,但截至 2015 年 3 月,幸存的日志庫都是 slf4j。 這些日志庫提供了某種log對象,您可以對其調用info(...)debug(...)等。我不是 slf4j 的忠實粉絲,但它現在似乎是主要的日志記錄框架. 這是SLF4J的描述:

Simple Logging Facade for Java 或 (SLF4J) 作為各種日志框架的簡單外觀或抽象,例如 java.util.logging、log4j 和 logback,允許最終用戶在部署時插入所需的日志框架。

在部署時更改底層日志庫的能力為整個 slf4j 記錄器系列帶來了獨特的特性,您需要注意:

  1. 類路徑作為配置方法。 slf4j 知道您正在使用哪個底層日志庫的方法是通過某個名稱加載一個類。 我遇到了在自定義類加載器時 slf4j 無法識別我的記錄器的問題。
  2. 因為簡單的外觀試圖成為公分母,所以它僅限於實際的日志調用。 換句話說,配置不能通過代碼來完成。

在大型項目中,如果每個人都使用 slf4j,那么能夠控制傳遞依賴的日志記錄行為實際上會很方便。

Scala 日志

Scala Logging由 Heiko Seeberger 作為他的slf4s的繼承者編寫 它使用宏將調用擴展為 if 表達式以避免潛在的昂貴的日志調用。

Scala Logging 是一個方便且高性能的日志庫,它封裝了像 SLF4J 和其他潛在的日志庫。

歷史記錄員

  • Logula ,由 Coda Hale 編寫的 Log4J 包裝器。 以前很喜歡這個,現在放棄了。
  • configgy ,一個 java.util.logging 包裝器,曾經在 Scala 的早期很流行。 現在放棄了。

使用 Scala 2.10+ 考慮使用類型安全的 ScalaLogging。 使用宏來提供非常干凈的 API

https://github.com/typesafehub/scala-logging

引用他們的維基:

幸運的是,Scala 宏可以讓我們的生活更輕松:ScalaLogging 為Logger類提供了輕量級日志記錄方法,這些方法將擴展為上述習慣用法。 所以我們只需要寫:

logger.debug(s"Some ${expensiveExpression} message!")

應用宏后,代碼將被轉換為上述習語。

此外,ScalaLogging 提供了 trait Logging ,它方便地提供了一個Logger實例,該實例用混合到的類名初始化:

import com.typesafe.scalalogging.slf4j.LazyLogging

class MyClass extends LazyLogging {
  logger.debug("This is very convenient ;-)")
}

使用 slf4j 和包裝器很好,但是當您有兩個以上的值要插值時,使用它內置的插值會崩潰,因為那時您需要創建一個值數組來插值。

更像 Scala 的解決方案是使用 thunk 或集群來延遲錯誤消息的串聯。 一個很好的例子是 Lift 的記錄器

Log.scala Slf4jLog.scala

看起來像這樣:

class Log4JLogger(val logger: Logger) extends LiftLogger {
  override def trace(msg: => AnyRef) = if (isTraceEnabled) logger.trace(msg)
}

請注意, msg 是按名稱調用,除非 isTraceEnabled 為真,否則不會被評估,因此生成一個好的消息字符串沒有成本。 這適用於需要解析錯誤消息的 slf4j 的插值機制。 使用此模型,您可以將任意數量的值插入到錯誤消息中。

如果您有一個單獨的特征將這個 Log4JLogger 混合到您的類中,那么您可以這樣做

trace("The foobar from " + a + " doesn't match the foobar from " +
      b + " and you should reset the baz from " + c")

代替

info("The foobar from {0} doesn't match the foobar from {1} and you should reset the baz from {c},
     Array(a, b, c))

不要使用 Logula

我實際上遵循了 Eugene 的建議並嘗試了它,發現它的配置很笨拙,並且存在無法修復的錯誤(例如這個)。 它看起來維護得不好,也不支持 Scala 2.10

使用 slf4s + slf4j-simple

主要優勢:

  • 支持最新的 Scala 2.10 (迄今為止是 M7)
  • 配置是通用的,但再簡單不過了。 它是通過系統屬性完成的,您可以通過將-Dorg.slf4j.simplelogger.defaultlog=trace類的-Dorg.slf4j.simplelogger.defaultlog=trace附加到執行命令或腳本中的硬代碼來設置: System.setProperty("org.slf4j.simplelogger.defaultlog", "trace") 無需管理垃圾配置文件!
  • 非常適合 IDE。 例如,在 IDEA 的特定運行配置中將日志記錄級別設置為“trace”,只需轉到Run/Debug Configurations並將-Dorg.slf4j.simplelogger.defaultlog=trace添加到VM options
  • 輕松設置:只需從這個答案的底部刪除依賴項

以下是使用 Maven 運行它所需的內容:

<dependency>
  <groupId>com.weiglewilczek.slf4s</groupId>
  <artifactId>slf4s_2.9.1</artifactId>
  <version>1.0.7</version>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-simple</artifactId>
  <version>1.6.6</version>
</dependency>

這就是我讓Scala Logging為我工作的方式:

把它放在你的build.sbt

libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.7.2",
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3"

然后,在執行sbt update ,這會打印出一條友好的日志消息:

import com.typesafe.scalalogging._
object MyApp extends App with LazyLogging {
  logger.info("Hello there")
}

如果您正在使用 Play,您當然可以簡單地import play.api.Logger來編寫日志消息: Logger.debug("Hi")

有關更多信息,請參閱 文檔

我使用 SLF4J + Logback classic 並像這樣應用它:

trait Logging {
  lazy val logger = LoggerFactory.getLogger(getClass)

  implicit def logging2Logger(anything: Logging): Logger = anything.logger
}

然后你可以使用更適合你的風格的它:

class X with Logging {
    logger.debug("foo")
    debug("bar")
}

但是這種方法當然對每個類實例使用一個記錄器實例。

我從scalaxLogging trait 中提取了一些工作,並創建了一個 trait,它也集成了一個MessageFormat-based庫。

然后東西看起來像這樣:

class Foo extends Loggable {
    info( "Dude, I'm an {0} with {1,number,#}", "Log message", 1234 )
}

到目前為止,我們喜歡這種方法。

執行:

trait Loggable {

    val logger:Logger = Logging.getLogger(this)

    def checkFormat(msg:String, refs:Seq[Any]):String =
        if (refs.size > 0) msgfmtSeq(msg, refs) else msg 

    def trace(msg:String, refs:Any*) = logger trace checkFormat(msg, refs)

    def trace(t:Throwable, msg:String, refs:Any*) = logger trace (checkFormat(msg, refs), t)

    def info(msg:String, refs:Any*) = logger info checkFormat(msg, refs)

    def info(t:Throwable, msg:String, refs:Any*) = logger info (checkFormat(msg, refs), t)

    def warn(msg:String, refs:Any*) = logger warn checkFormat(msg, refs)

    def warn(t:Throwable, msg:String, refs:Any*) = logger warn (checkFormat(msg, refs), t)

    def critical(msg:String, refs:Any*) = logger error checkFormat(msg, refs)

    def critical(t:Throwable, msg:String, refs:Any*) = logger error (checkFormat(msg, refs), t)

}

/**
 * Note: implementation taken from scalax.logging API
 */
object Logging {  

    def loggerNameForClass(className: String) = {  
        if (className endsWith "$") className.substring(0, className.length - 1)  
        else className  
    }  

    def getLogger(logging: AnyRef) = LoggerFactory.getLogger(loggerNameForClass(logging.getClass.getName))  
}

WriterMonoidMonad實現。

2020年登錄

我真的很驚訝我在工作中使用的Scribe日志框架在這里甚至沒有提到。 更重要的是,在搜索“scala logging”后,它甚至沒有出現在谷歌的第一頁。 但是在谷歌搜索時會出現這個頁面! 所以讓我把它留在這里。

Scribe 的主要優點:

你應該看看 scalax 庫: http ://scalax.scalaforge.org/ 在這個庫中,有一個 Logging trait,使用 sl4j 作為后端。 通過使用這個特性,你可以很容易地記錄日志(只需使用繼承該特性的類中的 logger 字段)。

快速簡便的表格。

Scala 2.10 及更早版本:

import com.typesafe.scalalogging.slf4j.Logger
import org.slf4j.LoggerFactory
val logger = Logger(LoggerFactory.getLogger("TheLoggerName"))
logger.debug("Useful message....")

和 build.sbt:

libraryDependencies += "com.typesafe" %% "scalalogging-slf4j" % "1.1.0"

Scala 2.11+ 及更新版本:

import import com.typesafe.scalalogging.Logger
import org.slf4j.LoggerFactory
val logger = Logger(LoggerFactory.getLogger("TheLoggerName"))
logger.debug("Useful message....")

和 build.sbt:

libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0"

還沒有嘗試過,但 Configgy 在配置和日志記錄方面看起來很有希望:

http://github.com/robey/configgy/tree/master

我發現使用某種 java 記錄器非常方便,例如 sl4j,帶有簡單的 scala 包裝器,這給我帶來了這樣的語法

val #! = new Logger(..) // somewhere deep in dsl.logging.

object User with dsl.logging {

  #! ! "info message"
  #! dbg "debug message"
  #! trace "var a=true"

}

在我看來,java 證明的日志框架和 scala 的花哨語法的混合非常有用。

在使用了 slf4s 和 logula 一段時間后,我寫了loglady ,一個簡單的記錄 trait 包裝 slf4j。

它提供了一個類似於 Python 日志庫的 API,這使得常見情況(基本字符串、簡單格式)變得微不足道,並避免了格式化樣板。

http://github.com/dln/loglady/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM