Вхід у систему Scala


168

Який хороший спосіб зробити вхід у програму 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 Лісозаготівля

Запис лісоматеріалів написаний Хейко Зебергер як наступник своїх slf4 . Він використовує макрос для розширення викликів, якщо вираз, щоб уникнути потенційно дорогого журнального виклику.

Scala Logging - це зручна та ефективна бібліотека ведення журналів, яка обгортає бібліотеки журналів, такі як SLF4J та потенційно інші.

Історичні лісоруби

  • Logula , обгортка Log4J, написана Coda Hale. Раніше подобався цей, але тепер покинутий.
  • configgy - обгортка java.util.logging, яка раніше була популярною в Scala. Зараз покинутий.

1
Треба сказати, я люблю Логулу ... нарешті лісоруб, який не змушує мене забити викруткою у вухо. Без xml і без BS, просто налаштування scala при запуску
Arg

SLF4S Heiko Seeberger вже не підтримується. Я створив власну орієнтацію на Scala 2.10 та останню версію SLF4J. http://slf4s.org/
Метт Робертс

3
Логула покинута згідно з її README на Github.
Ерік Каплун

Oncue Journal за концепцією схожий на Scala Logging, але повідомлення журналу надсилаються актору, який дзвонить SLF4J з виділеного пулу потоків. Торгує час процесора для (набагато кращої) затримки. github.com/oncue/journal
Gary Coady

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

12
Використовуйте class MyClass with Logging.
Анджей Йозвік

1
Зверніть увагу, що використання журналу в ознаках призводить до того, що ознака має те саме ім’я для реєстратора, як і клас, до якого він змішаний. Я думаю, ви можете отримати реєстратор вручну замість зручності макросу, щоб вказати ім’я реєстратора вручну, якщо потрібно. Але будь ласка, виправте мене, якщо я помиляюся.
mkko

1
Чи означає це, що MyClass має відкриті методи для налагодження, попередження інформації тощо? Якщо так, то я вважаю за краще зробити додаткову ручну роботу з інстанціюванням лісоруба, приватного до класу. Методи ведення журналу не повинні просочуватися в інтерфейс класів.
ChucK

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 є використання грона або кластера для затримки конкатенації повідомлення про помилку. Хороший приклад цього - лісоруб ліфта

Log.scala Slf4jLog.scala

Як виглядає так:

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

Зауважте, що msg - це ім'я за назвою і не оцінюватиметься, якщо параметр "TraceEnabled "не відповідає дійсності, тому немає необхідності створювати хороший рядок повідомлень. Це працює навколо механізму інтерполяції 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

Не використовуйте Logula

Я насправді дотримувався рекомендації Євгена і спробував це, і виявив, що він має незграбну конфігурацію і піддається помилкам, які не виправляються (наприклад, ця ). Він виглядає не доглянутим, і він не підтримує 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." .. я дивлюся в неправильному місці?
nairbv

@Brian Використовуйте 2.9.1, як запропоновано у відповіді. Я перевірив, що він працює нормально з 2.10.0-M7
Микита Волков,

Я використовую 2.9.1, мені просто цікаво було виділено "ключову вигоду" і що це означає.
nairbv

Ретельно проаналізувавши відповіді про плюси і мінуси бібліотек журналу, я вибираю той, який підкаже, яку залежність імпортувати. Дякую, сер.
тео

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всередині папки ресурсів проекту.
Nadjib Mami

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 за замовчуванням) завжди виглядає приблизно так, замість класу, який насправді розширює реєстрацію: 22 липня 2011 3:02:17 AM redacted.util.Loggable $ class information ІНФОРМАЦІЯ: Якесь повідомлення ... Чи є шанс натрапити на це?
Томер Габель

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

але цей підхід, звичайно, використовує екземпляр реєстратора на екземпляр класу.


4

Ви повинні ознайомитись із бібліотекою масштабування: http://scalax.scalaforge.org/ У цій бібліотеці є риса журналу, яка використовує sl4j як бекенд. Використовуючи цю ознаку, ви можете легко входити в журнал (просто використовуйте поле реєстратора в класі, що успадковує ознаку).


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"

2

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

Він пропонує API, подібний до бібліотеки журналів Python, що робить загальні випадки (основний рядок, просте форматування) тривіальними і уникає форматування котлової панелі.

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


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"

}

На мій погляд, дуже корисний мікс з перевірених ява каркасів лісозаготівлі та химерного синтаксису Scala.


@sschaef, о, так. Вибачте. Я майже забув. Кілька днів з цим питанням велика боротьба, але схоже, що це справді неможливо. Я використав #! ! натомість "інформаційне повідомлення". Я відредагую редагування своєї відповіді.
Алекс Повар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.