Подтвердить что ты не робот

Вход в систему scala

В Java стандартная идиома для ведения журнала - это создание статической переменной для объекта журнала и использование этого в различных методах.

В Scala, похоже, что идиома - создать черту Logging с членом журнала и микшировать признак в конкретных классах. Это означает, что каждый раз, когда объект создается, он вызывает фреймворк регистрации для получения регистратора, а также объект больше из-за дополнительной ссылки.

Есть ли альтернатива, которая позволяет легко использовать "с протоколированием" при использовании экземпляра журнала для каждого класса?

EDIT: Мой вопрос заключается не в том, как можно записать структуру ведения журнала в Scala, а в том, как использовать существующий (log4j), не наносимый служебной нагрузкой (получение ссылки для каждого экземпляра) или сложность кода, Кроме того, да, я хочу использовать log4j, просто потому, что буду использовать сторонние библиотеки, написанные на Java, которые могут использовать log4j.

4b9b3361

Ответ 1

Я бы просто придерживался подхода "с Logging". Чистый дизайн выигрывает каждый раз - если вы получаете шаблонный вариант, то, скорее всего, вы можете найти гораздо больше полезных достижений, достижимых в других областях.

Имейте в виду, что структура ведения журнала кэширует журналы, поэтому у вас все еще есть один для каждого класса, даже если каждый экземпляр этого класса содержит (недорогую) ссылку.

Без доказательства того, что ссылки на регистраторы вредят вашей куче, это сильно пахнет, как преждевременная оптимизация... Просто расслабьтесь и не беспокойтесь об этом, если только профайлер не говорит вам об этом.

В несвязанной заметке вам также может понадобиться использовать slf4j и logback вместо log4j. slf4j имеет более чистый дизайн, который лучше подходит для идиоматических scala.

Ответ 2

Я использовал log4j с Scala путем создания признака и наличия регистратора по каждому экземпляру, а не по классу. С помощью магии и манифеста Scala вы можете изменить регистратор на статический (внутренний объект), но я не уверен на 100%. Лично я согласен с @KevinWright в том, что создание статичного журнала является преждевременной оптимизацией.

Также обратите внимание, что приведенный ниже код имеет сообщения журнала как по имени, что означает, что ваши вызовы журналов не обязательно должны быть завернуты в `if (log.isDebugEnabled()); сложные сообщения журнала, созданные посредством конкатенации строк, не будут оцениваться, если уровень журнала не подходит. См. Эту ссылку для получения дополнительной информации: http://www.naildrivin5.com/scalatour/wiki_pages/TypeDependentClosures

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!")
    } 
  }
}

Ответ 3

Если вы действительно обеспокоены нехваткой места и/или дополнительным временем в инициализаторах объектов, хорошей стратегией может быть наличие каротажной черты, которая оставляет абстрактный журнал, как в


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

Для классов, которые должны быть как можно более легкими, вы можете сделать


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

В этом случае JIT может даже встраивать debug warn и logger.

У вас также может быть свойство для смешивания для классов, где накладные расходы дополнительного поля не являются проблемой


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

Еще один вариант - оставить выход из класса и полностью помещать его в объект, как в


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

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

Я согласен с Кевином, но не добавляйте сложность, если вам это не нужно.

Ответ 4

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

Нет

Ответ 5

Иногда регистрация на уровне пакета - правильный ответ. Scala делает это проще, чем java, потому что объекты могут быть определены непосредственно в пакете. Если вы определили Log следующим образом:

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

Этот журнал доступен везде в примере пакета. Чтобы получить более мелкозернистый журнал, вы можете разбросать похожие определения вокруг иерархии пакетов. Или вы можете определить все регистраторы пакетов следующим образом:

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

  object Log extends PackageLogger 

  package frobber {
    object Log extends PackageLogger 

    package undulater {
      object Log extends PackageLogger
    } 
  }
}

Класс 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)
}

Ответ 6

Вот быстрый хак (который я на самом деле не использовал, честно; @)

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)}
}

Надеюсь, что это будет полезно.

Ответ 7

API-интерфейс типов (https://github.com/typesafehub/scalalogging) имеет свойство добавления значения logger val в класс, но его использование является необязательным. Он инициализирует переменную, используя getClass getName, которая в течение половины времени будет бесполезной, потому что часто ваше фактическое имя класса будет gobbledygook.

Итак, если вы не хотите, чтобы черта добавляла дополнительную переменную к каждому экземпляру, вам, разумеется, не нужно ее использовать, и вы можете просто поместить логгер val в свой сопутствующий объект и выполнить импорт в классе объектов-компаньонов членов, поэтому вам не нужно его квалифицировать.

Ответ 8

Один из способов состоит в том, чтобы расширить 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 = ...
}

В качестве альтернативы вы можете кэшировать экземпляры журнала:

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")
}

Это проще в использовании, но будет иметь худшую производительность. Производительность будет очень плохой, если вы захотите отказаться от большинства сообщений журнала (из-за конфигурации уровня журнала).