繁体   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