登录 Scala

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2018528/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 01:50:41  来源:igfitidea点击:

logging in scala

loggingscala

提问by IttayD

In Java, the standard idiom for logging is to create a static variable for a logger object and use that in the various methods.

在 Java 中,记录的标准习惯用法是为记录器对象创建一个静态变量并在各种方法中使用它。

In Scala, it looks like the idiom is to create a Logging trait with a logger member and mixin the trait in concrete classes. This means that each time an object is created it calls the logging framework to get a logger and also the object is bigger due to the additional reference.

在 Scala 中,似乎习惯用法是创建一个带有记录器成员的 Logging trait 并将该 trait 混合到具体的类中。这意味着每次创建对象时,它都会调用日志框架来获取记录器,并且由于额外的引用,对象会更大。

Is there an alternative that allows the ease of use of "with Logging" while still using a per-class logger instance?

是否有替代方法可以在仍然使用每类记录器实例的同时轻松使用“带日志记录”?

EDIT: My question is not about how one can write a logging framework in Scala, but rather how to use an existing one (log4j) without incurring an overhead of performance (getting a reference for each instance) or code complexity. Also, yes, I want to use log4j, simply because I'll use 3rd party libraries written in Java that are likely to use log4j.

编辑:我的问题不是关于如何在 Scala 中编写日志框架,而是如何使用现有框架 (log4j) 而不会产生性能开销(获取每个实例的参考)或代码复杂性。另外,是的,我想使用 log4j,只是因为我将使用用 Java 编写的可能会使用 log4j 的 3rd 方库。

采纳答案by Kevin Wright

I'd just stick to the "with Logging" approach. Clean design wins every time - if you get the boilerplate out the way then chances are that you can find far more useful gains achievable in other areas.

我只是坚持“使用日志记录”方法。干净的设计每次都会获胜——如果你把样板弄出来,那么你很有可能会在其他领域找到更有用的收获。

Keep in mind that the logging framework will cache loggers, so you still have one per class, even if every instance of that class happens to hold a (inexpensive) reference.

请记住,日志记录框架将缓存记录器,因此每个类仍然有一个记录器,即使该类的每个实例碰巧都持有一个(廉价的)引用。

Without proof that logger references are harming your heap, this smells a lot like premature optimization... Just relax and don't worry about it, unless a profiler tells you otherwise.

没有证据表明记录器引用正在损害您的堆,这听起来很像过早的优化......放松一下,不要担心,除非分析器另有说明。

On an unrelated note, you might also want to look into using slf4j and logback instead of log4j. slf4j has a cleaner design that fits better with idiomatic scala.

在一个不相关的说明中,您可能还想考虑使用 slf4j 和 logback 而不是 log4j。slf4j 的设计更简洁,更适合惯用的 scala。

回答by davetron5000

I used log4j with Scala by creating a trait and having the logger by per-instances not per-class. With some Scala magic and manifests, you might be able to change the logger to be static (internal object), but I'm not 100% sure. Personally, I agree with @KevinWright that making the logger static is a premature optimization.

我通过创建 trait 并按实例而不是按类使用记录器来将 log4j 与 Scala 一起使用。使用一些 Scala 魔法和清单,您可能能够将记录器更改为静态(内部对象),但我不是 100% 确定。就个人而言,我同意 @KevinWright 的观点,即将记录器设为静态是一种过早的优化。

Also note that the code below has the log messages as by-name, meaning that your logger calls don't need to be wrapped in `if (log.isDebugEnabled()); complex log messages created via string concatenation won't be evaluated unless the log level is appropriate. See this link for more info: http://www.naildrivin5.com/scalatour/wiki_pages/TypeDependentClosures

另请注意,下面的代码将日志消息作为名称,这意味着您的记录器调用不需要包含在 `if (log.isDebugEnabled()); 中。除非日志级别合适,否则不会评估通过字符串连接创建的复杂日志消息。有关更多信息,请参阅此链接:http: //www.naildrivin5.com/scalatour/wiki_pages/TypeDependentClosures

http://github.com/davetron5000/shorty/blob/master/src/main/scala/shorty/Logs.scala

http://github.com/davetron5000/shorty/blob/master/src/main/scala/shorty/Logs.scala

package shorty

import org.apache.log4j._

trait Logs {
  private[this] val logger = Logger.getLogger(getClass().getName());

  import org.apache.log4j.Level._

  def debug(message: => String) = if (logger.isEnabledFor(DEBUG)) logger.debug(message)
  def debug(message: => String, ex:Throwable) = if (logger.isEnabledFor(DEBUG)) logger.debug(message,ex)
  def debugValue[T](valueName: String, value: => T):T = {
    val result:T = value
    debug(valueName + " == " + result.toString)
    result
  }

  def info(message: => String) = if (logger.isEnabledFor(INFO)) logger.info(message)
  def info(message: => String, ex:Throwable) = if (logger.isEnabledFor(INFO)) logger.info(message,ex)

  def warn(message: => String) = if (logger.isEnabledFor(WARN)) logger.warn(message)
  def warn(message: => String, ex:Throwable) = if (logger.isEnabledFor(WARN)) logger.warn(message,ex)

  def error(ex:Throwable) = if (logger.isEnabledFor(ERROR)) logger.error(ex.toString,ex)
  def error(message: => String) = if (logger.isEnabledFor(ERROR)) logger.error(message)
  def error(message: => String, ex:Throwable) = if (logger.isEnabledFor(ERROR)) logger.error(message,ex)

  def fatal(ex:Throwable) = if (logger.isEnabledFor(FATAL)) logger.fatal(ex.toString,ex)
  def fatal(message: => String) = if (logger.isEnabledFor(FATAL)) logger.fatal(message)
  def fatal(message: => String, ex:Throwable) = if (logger.isEnabledFor(FATAL)) logger.fatal(message,ex)
}

class Foo extends SomeBaseClass with Logs {
  def doit(s:Option[String]) = {
    debug("Got param " + s)
    s match {
      case Some(string) => info(string)
      case None => error("Expected something!")
    } 
  }
}

回答by Geoff Reedy

If you are truly concerned about space overhead and/or extra time in object initializers, a good strategy can be to have a logging trait that leaves the logger abstract as in

如果你真的关心对象初始值设定项中的空间开销和/或额外时间,一个好的策略是拥有一个日志特征,使记录器抽象,如


trait Logging {
  def logger: Logger
  def debug(message: String) { logger.debug(message) }
  def warn(message: String) { logger.warn(message) }
}

For classes which need to be as lightweight as possible then you can do

对于需要尽可能轻量级的类,你可以这样做


object MustBeLightweight {
  val logger = Logger.getLog(classOf[MustBeLightweight])
}
class MustBeLightWeight extends Logging {
  final def logger = MustBeLightweight.logger
}

The JIT might even inline debugwarnand loggerin this case.

JIT 甚至可能内联debugwarnlogger在这种情况下。

You can also have a trait to mix in for classes where the overhead of an additional field is not a problem

你也可以有一个特性来混合额外字段的开销不是问题的类


trait PerInstanceLog {
  val logger = Logger.getLog(this.getClass())
}

A further option is to leave logging out of the class and put it completely in an object as in

另一种选择是不退出该类并将其完全放入一个对象中,如


object Foo {
  object log extends Logging {
    override val logger = Logger.getLogger(classOf[Foo])
  } 
}

class Foo {
  import Foo.log._
  def someMethod() = { warn("xyz") }
}

I agree with Kevin though, don't add the complexity unless you need it.

不过,我同意 Kevin 的观点,除非您需要,否则不要增加复杂性。

回答by F0RR

object Log {
    def log(message: String) = {
        .....
    }
}

No?

不?

回答by Arnold deVos

Sometimes logging at the package level is the right answer. Scala makes this easier than java because objects can be defined directly in a package. If you defined a Log like this:

有时在包级别进行日志记录是正确的答案。Scala 使这比 java 更容易,因为对象可以直接在包中定义。如果您定义了这样的日志:

package example 
object Log extends au.com.langdale.util.PackageLogger 

This Log is available everywhere in package example. To get more fine-grained logging, you can scatter similar definitions around the package hierarchy. Or you can defined all the package loggers together like this:

此日志在包示例中随处可用。要获得更细粒度的日志记录,您可以在包层次结构周围散布类似的定义。或者你可以像这样一起定义所有的包记录器:

package example {
  import au.com.langdale.util.PackageLogger

  object Log extends PackageLogger 

  package frobber {
    object Log extends PackageLogger 

    package undulater {
      object Log extends PackageLogger
    } 
  }
}

The PackageLogger class could be defined as follows (assuming SLF4J):

PackageLogger 类可以定义如下(假设 SLF4J):

package au.com.langdale.util
import org.slf4j.LoggerFactory

class PackageLogger {
  val name = { val c = getClass.getName; c.substring(0, c.lastIndexOf('.')) }
  val inner = LoggerFactory.getLogger(name)

  // various convenient logging methods follow....
  def apply( mesg: => Any ) = inner.info(mesg.toString)
  def info( mesg: String ) = inner.info(mesg)
  def warn( mesg: String ) = inner.warn(mesg)
  def error( mesg: String ) = inner.error(mesg)
}

回答by Arnold deVos

The typesafe logging API (https://github.com/typesafehub/scalalogging) has a trait for adding a logger val to a class but its use is optional. It initializes the variable using getClass getName which half the time will be worthless because frequently your actual class name will be gobbledygook.

类型安全日志 API ( https://github.com/typesafehub/scalalogging) 具有将记录器 val 添加到类的特性,但它的使用是可选的。它使用 getClass getName 初始化变量,这有一半时间将毫无价值,因为您的实际类名经常是 gobbledygook。

So if you don't want the trait adding the extra variable to each instance you certainly don't need to use it and can simply put the logger val in your companion object and do an import in the class of the companion objects members so you don't need to qualify it.

因此,如果您不希望 trait 向每个实例添加额外的变量,您当然不需要使用它,只需将记录器 val 放在您的伴生对象中并在伴生对象成员的类中进行导入,这样您不需要限定它。

回答by Don Mackenzie

Here's a quick hack (which I haven't actually been using, honest ;@)

这是一个快速的 hack(老实说,我实际上并没有使用过;@)

object LogLevel extends Enumeration {
  val Error   = Value(" ERROR   ")
  val Warning = Value(" WARNING ")                                                                                                      
  val Info    = Value(" INFO    ")
  val Debug   = Value(" DEBUG   ")
}

trait Identity {
  val id: String
}

trait Logging extends Identity {
  import LogLevel._

  abstract class LogWriter {
    protected val writer: Actor
    protected val tstampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ")

    def tstamp = tstampFormat.format(new Date)

    def log(id: String, logLevel: LogLevel.Value, msg: String) {
      writer ! (tstamp + id + logLevel + msg)
    }
  }

  object NullLogWriter extends LogWriter {
    protected val writer = actor{loop {react{case msg: String =>}}}
  }

  object ConsoleWriter extends LogWriter {
    protected val writer = actor{loop {react {case msg: String => Console.out.println(msg); Console.flush case _ =>}}}
  }

  class FileWriter(val file: File) extends LogWriter {
    require(file != null)
    require(file.canWrite)

    protected val writer = actor{loop {react {case msg: String => destFile.println(msg); destFile.flush case _ =>}}}

    private val destFile = {
      try {new PrintStream(new FileOutputStream(file))}
      catch {case e => ConsoleWriter.log("FileWriter", LogLevel.Error, "Unable to create FileWriter for file " + file +
                                         " exception was: " + e); Console.out}
    }
  }

  protected var logWriter: LogWriter = ConsoleWriter
  protected var logLevel             = Info

  def setLoggingLevel(level: LogLevel.Value) {logLevel = level}

  def setLogWriter(lw: LogWriter) {if (lw != null) logWriter = lw}

  def logError(msg: => String) {if (logLevel <= Error) logWriter.log(id, Error, msg)}

  def logWarning(msg: => String) {if (logLevel <= Warning) logWriter.log(id, Warning, msg)}

  def logInfo(msg: => String) {if (logLevel <= Info) logWriter.log(id, Info, msg)}

  def logDebug(msg: => String) {if (logLevel <= Debug) logWriter.log(id, Debug, msg)}
}

Hope it's of some use.

希望有点用。

回答by Thomas Jung

One way is to extends the Logger to the companion object:

一种方法是将 Logger 扩展到伴随对象:

object A extends LoggerSupport

class A {
    import A._
    log("hi")
}

trait LoggerSupport{
    val logger = LoggerFactory.getLogger(this.getClass)
    def log(msg : String)= logger.log(msg)
}

//classes of the logging framework
trait Logger{
    def log(msg : String) : Unit
}

object LoggerFactory{
    def getLogger(classOfLogger : Class[_]) : Logger = ...
}

Alternatively you can cache the logger instances:

或者,您可以缓存记录器实例:

import collection.mutable.Map
object LoggerCache{
    var cache : Map[Class[_], Logger] = Map()
    def logger(c : Class[_]) = cache.getOrElseUpdate(c, LoggerFactory.getLogger(c))
}

trait LoggerSupport{
    def log(msg : String) = LoggerCache.logger(this.getClass).log(msg)
}

class A extends LoggerSupport{
    log("hi")
}

This is easier to use but will have worse performance. Performance will be really bad when you're go to discard most of the log messages (because of the log level configuration).

这更容易使用,但性能会更差。当您要丢弃大部分日志消息(因为日志级别配置)时,性能会非常糟糕。