Вход в Scala

153

Что такое хороший способ ведения журнала в приложении Scala? Что-то, что согласуется с философией языка, не загромождает код и мало поддерживает и ненавязчиво. Вот список основных требований:

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

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

Спасибо за ваши ответы.

Теги:
logging

13 ответов

112
Лучший ответ

обертки slf4j

Большинство библиотек журналов Scala были некоторыми обертками вокруг фреймворка регистрации Java (slf4j, log4j и т.д.), но по состоянию на март 2015 года оставшиеся библиотеки журналов - все slf4j. Эти библиотеки журналов предоставляют какой-то объект log, к которому вы можете позвонить info(...), debug(...) и т.д. Я не являюсь большим поклонником slf4j, но теперь это, по-видимому, преобладающая структура ведения журнала. Здесь описание SLF4J:

Простой логический фасад для Java или (SLF4J) служит в качестве простого фасада или абстракции для различных фреймворков регистрации, например. java.util.logging, log4j и logback, позволяя конечному пользователю подключить требуемую структуру ведения журнала во время развертывания.

Возможность изменять базовую библиотеку журналов во время развертывания привносит уникальную характеристику для всего семейства журналов slf4j, о которых вам нужно знать:

  • classpath как метод настройки. Способ, которым slf4j знает, какая базовая библиотека ведения журнала вы используете, - это загрузка класса по имени. У меня были проблемы, в которых slf4j не распознавал мой регистратор при настройке загрузчика классов.
  • Поскольку простой фасад пытается стать общим знаменателем, он ограничивается только фактическими вызовами журнала. Другими словами, конфигурация не может быть выполнена с помощью кода.

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

Scala Запись

Scala Запись написана Хейко Зебергером как преемник его slf4s. Он использует макрос для расширения вызовов в выражение, чтобы избежать потенциально дорогого вызова журнала.

Scala Ведение журнала - это удобная и эффективная библиотека журналов регистрации журналов, такая как SLF4J и потенциально другие.

Исторические журналы

  • Logula, обертка Log4J, написанная Coda Hale. Используется как этот, но теперь он отказался.
  • configgy - обертка java.util.logging, которая раньше использовалась в Scala. Теперь заброшено.
  • 0
    Должен сказать, я люблю Логулу ... наконец, регистратор, который не заставляет меня хотеть заколоть отвертку мне в ухо. Нет xml и нет BS, просто конфигурация scala при запуске
  • 0
    SLF4S Хайко Сибергера больше не поддерживается. Я сделал свой собственный таргетинг на Scala 2.10 и последнюю версию SLF4J. http://slf4s.org/
Показать ещё 2 комментария
59

С Scala 2.10+ Рассмотрим ScalaLogging by Typesafe. Использует макросы для доставки очень чистого API

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

Цитата из их вики:

К счастью, макросы Scala могут использоваться для облегчения нашей жизни: ScalaLogging предлагает класс Logger с легкими методами ведения журнала, которые будут расширены до вышеуказанной идиомы. Итак, все, что мы должны написать:

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

После применения макроса код будет преобразован в описанную выше идиому.

Кроме того, ScalaLogging предлагает признак Logging, который удобно предоставляет экземпляр Logger, инициализированный именем класса, смешанного с:

import com.typesafe.scalalogging.slf4j.LazyLogging

class MyClass extends LazyLogging {
  logger.debug("This is very convenient ;-)")
}
  • 12
    Используйте class MyClass with Logging .
  • 1
    Обратите внимание, что использование признаков входа в систему приводит к тому, что признак имеет то же имя для регистратора, что и класс, в который он входит. Я думаю, что вы можете получить регистратор вручную вместо макроса, чтобы вручную указать имя регистратора, если это необходимо. Но, пожалуйста, поправьте меня, если я ошибаюсь.
Показать ещё 4 комментария
14

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

Более Scala как решение должно использовать thunk или cluster для задержки конкатенации сообщения об ошибке. Хорошим примером этого является лифт-регистратор

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))
  • 2
    Можно ли получить точные номера строк для записи операторов в классах Scala?
12

Не используйте Logula

Я действительно следовал рекомендациям Юджина и пробовал его и выяснил, что он имеет неуклюжую конфигурацию и подвергается ошибкам, которые не фиксируются (например, this один). Он не выглядит хорошо поддержанным и не поддерживает Scala 2.10.

Использовать slf4s + slf4j-simple

Основные преимущества:

  • Поддержка последних Scala 2.10 (на данный момент это M7)
  • Конфигурация универсальна, но не может быть проще. Это сделано с помощью свойств системы, которую вы можете установить, добавив что-то вроде -Dorg.slf4j.simplelogger.defaultlog=trace к команде выполнения или жесткому коде в script: System.setProperty("org.slf4j.simplelogger.defaultlog", "trace"). Нет необходимости управлять конфигурационными файлами trashy!
  • Удобно подходит для IDE. Например, чтобы установить уровень ведения журнала на "трассировку" в конкретной конфигурации запуска в IDEA, просто перейдите к 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>
  • 0
    где вы найдете slf4s, который поддерживает 2.10? Я смотрю на github.com/weiglewilczek/slf4s и вижу «Поддерживаемые версии Scala: 2.8.0, 2.8.1, 2.9.0-1 и 2.9.1». .. я смотрю не в том месте?
  • 0
    @ Брайан Используйте 2.9.1, как предложено в ответе. Я проверил, чтобы он работал нормально с 2.10.0-M7
Показать ещё 2 комментария
9

Вот как я получил 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").

Подробнее см. docs.

7

Я немного поработал над формой Logging scalax и создал признак, который также интегрировал библиотеку 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))  
}
  • 0
    Милый, хотя у меня есть некоторые проблемы, заставляющие это работать - в частности, вывод (в конфигурации по умолчанию slf4j) всегда выглядит примерно так, а не класс, фактически расширяющий Loggable: 22 июля 2011 3:02:17 AM redacted.util.Loggable $ class info ИНФОРМАЦИЯ: Некоторые сообщения ... Есть шанс, что вы столкнулись с этим?
6

Я использую 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")
}

но этот подход, конечно, использует экземпляр регистратора для экземпляра класса.

3

Writer, Monoid и Monad.

  • 5
    Можете ли вы объяснить, что вы имеете в виду?
  • 0
    Комбинация из них может создать красивую привязку к вашему результату. ИМО, это немного излишне. Но если вы хотите узнать что-то новое blog.tmorris.net/the-writer-monad-using-scala-example
Показать ещё 1 комментарий
3

Еще не пробовал, но Configgy выглядит многообещающим как для конфигурации, так и для ведения журнала:

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

3

Вам следует взглянуть на библиотеку scalax: http://scalax.scalaforge.org/ В этой библиотеке есть свойство Logging, использующее sl4j в качестве backend. Используя этот признак, вы можете легко записывать (просто используйте поле регистратора в классе, наследующем признак).

2

Я нахожу очень удобным использование какого-то 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.

  • 0
    #! "info message" не скомпилируется ...
  • 0
    @sschaef, о да. Сожалею. Я почти забыл. Это была отличная борьба с этой проблемой в течение нескольких дней, но, похоже, это действительно невозможно. Я использовал #! ! "информационное сообщение" вместо. Я буду редактировать, редактировать мой ответ.
2

После использования slf4s и logula какое-то время я написал loglady, простую привязку к лог файлам slf4j.

Он предлагает API, аналогичный API библиотеки протоколов Python, что делает обычные случаи (базовую строку, простое форматирование) тривиальным и позволяет избежать форматирования шаблона.

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

1

Быстрые и легкие формы.

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"

Ещё вопросы

Сообщество Overcoder
Наверх
Меню