[英]Logging in Scala
在 Scala 應用程序中進行日志記錄的好方法是什么? 符合語言哲學的東西,不會使代碼混亂,並且維護成本低且不引人注目。 這是一個基本的要求清單:
我知道我可以使用現有的 Java 日志記錄解決方案,但它們至少在上述兩個方面失敗,即混亂和配置。
感謝您的回復。
Scala 的大部分日志庫都是圍繞 Java 日志框架(slf4j、log4j 等)的一些包裝器,但截至 2015 年 3 月,幸存的日志庫都是 slf4j。 這些日志庫提供了某種log
對象,您可以對其調用info(...)
、 debug(...)
等。我不是 slf4j 的忠實粉絲,但它現在似乎是主要的日志記錄框架. 這是SLF4J的描述:
Simple Logging Facade for Java 或 (SLF4J) 作為各種日志框架的簡單外觀或抽象,例如 java.util.logging、log4j 和 logback,允許最終用戶在部署時插入所需的日志框架。
在部署時更改底層日志庫的能力為整個 slf4j 記錄器系列帶來了獨特的特性,您需要注意:
在大型項目中,如果每個人都使用 slf4j,那么能夠控制傳遞依賴的日志記錄行為實際上會很方便。
Scala Logging由 Heiko Seeberger 作為他的slf4s的繼承者編寫。 它使用宏將調用擴展為 if 表達式以避免潛在的昂貴的日志調用。
Scala Logging 是一個方便且高性能的日志庫,它封裝了像 SLF4J 和其他潛在的日志庫。
使用 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 的記錄器
看起來像這樣:
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))
我實際上遵循了 Eugene 的建議並嘗試了它,發現它的配置很笨拙,並且存在無法修復的錯誤(例如這個)。 它看起來維護得不好,也不支持 Scala 2.10 。
-Dorg.slf4j.simplelogger.defaultlog=trace
類的-Dorg.slf4j.simplelogger.defaultlog=trace
附加到執行命令或腳本中的硬代碼來設置: System.setProperty("org.slf4j.simplelogger.defaultlog", "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")
}
但是這種方法當然對每個類實例使用一個記錄器實例。
我從scalax
的Logging
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))
}
Writer
、 Monoid
和Monad
實現。
我真的很驚訝我在工作中使用的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 在配置和日志記錄方面看起來很有希望:
我發現使用某種 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,這使得常見情況(基本字符串、簡單格式)變得微不足道,並避免了格式化樣板。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.