Вход в Скала

169

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

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

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

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

Джордж
источник

Ответы:

119

обертки slf4j

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

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

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

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

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

Scala Logging

Scala Logging написана Heiko Seeberger как преемница его slf4s . Он использует макрос для расширения вызовов в выражение if, чтобы избежать потенциально дорогих вызовов журнала.

Scala Logging - это удобная и эффективная библиотека журналирования, заключающая в себе библиотеки журналирования, такие как SLF4J и, возможно, другие.

Исторические регистраторы

  • Logula , обертка Log4J, написанная Coda Hale. Раньше нравился этот, но теперь заброшен.
  • configgy , оболочка java.util.logging, которая была популярна в ранние времена Scala. Сейчас заброшен.
Евгений Йокота
источник
1
Должен сказать, я люблю Логулу ... наконец, регистратор, который не заставляет меня хотеть заколоть отвертку мне в ухо. Нет xml и нет BS, просто конфигурация scala при запуске
Arg
SLF4S Хейко Сибергера больше не поддерживается. Я сделал свой собственный таргетинг на Scala 2.10 и последнюю версию SLF4J. http://slf4s.org/
Мэтт Робертс
3
Логула заброшена согласно README на Github.
Эрик Каплун
Oncue Journal по своей сути похож на Scala Logging, но сообщения журнала отправляются субъекту, который вызывает SLF4J из выделенного пула потоков. Меняет процессорное время (намного лучше) на задержку. github.com/oncue/journal
Гэри Коуди,
64

С Scala 2.10+ Рассмотрим ScalaLogging от 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 ;-)")
}
fracca
источник
12
Использование class MyClass with Logging.
Анджей Йозвик
1
Обратите внимание, что использование признаков входа в систему приводит к тому, что признак имеет то же имя для регистратора, что и класс, в который он входит. Я думаю, что вы можете получить регистратор вручную, вместо макроса удобство предоставления имени регистратора вручную, если это необходимо. Но, пожалуйста, поправьте меня, если я ошибаюсь.
mkko
1
Означает ли это, что в MyClass есть открытые методы для отладки, предупреждения о предупреждениях и т. Д.? Если так, то я бы предпочел выполнить дополнительную ручную работу по созданию экземпляра логгера, частного для класса. Методы ведения журнала не должны попадать в интерфейс классов.
Чек
2
Вы получаете поле логгера, которое можете использовать на досуге. См github.com/typesafehub/scalalogging/blob/master/... подробности
fracca
2
2.x требуется Scala 2.11; надеюсь, нет особой разницы; для Scala 2.10.x есть"com.typesafe" %% "scalalogging-slf4j" % "1.1.0" .
Эрик Каплун
14

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

Более похожее на Scala решение заключается в использовании thunk или кластера для задержки объединения сообщений об ошибках. Хорошим примером этого является регистратор лифта

Log.scala Slf4jLog.scala

Который выглядит так:

class Log4JLogger(val logger: Logger) extends LiftLogger {
  override def trace(msg: => AnyRef) = if (isTraceEnabled) logger.trace(msg)
}

Обратите внимание, что сообщение msg является вызовом по имени и не будет оцениваться, если isTraceEnabled не имеет значение true, поэтому генерация красивой строки сообщения не требует затрат. Это работает вокруг механизма интерполяции 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?
jpswain
13

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

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

Используйте slf4s + slf4j-simple

Ключевые преимущества:

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

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

Смотрите документы для получения дополнительной информации.

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

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

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

Кристоф Йожса
источник
4

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


источник
4

Writer, MonoidИ Monadреализация.

Тони Моррис
источник
5
Можете ли вы объяснить, что вы имеете в виду?
Дэвид Дж.
Комбинация из них может создать красивую привязку к вашему результату. ИМО, это немного излишне. Но если вы хотите узнать что-то новое blog.tmorris.net/the-writer-monad-using-scala-example
Tg.
@Tg. Ссылка в вашем комментарии мертва. Используйте blog.tmorris.net/posts/the-writer-monad-using-scala-example
jub0bs
3

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

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"
xgMz
источник
2

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

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

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

DLN
источник
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.

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