Anmelden in Scala

168

Was ist ein guter Weg, um sich in einer Scala-Anwendung anzumelden? Etwas, das mit der Sprachphilosophie übereinstimmt, den Code nicht überladen und wartungsarm und unauffällig ist. Hier ist eine grundlegende Anforderungsliste:

  • einfach
  • macht den Code nicht unübersichtlich. Scala ist großartig für seine Kürze. Ich möchte nicht, dass die Hälfte meines Codes Anweisungen protokolliert
  • Das Protokollformat kann an den Rest meiner Unternehmensprotokolle und Überwachungssoftware angepasst werden
  • unterstützt Protokollierungsstufen (dh Debug, Trace, Fehler)
  • kann sich auf der Festplatte sowie an anderen Zielen (z. B. Socket, Konsole usw.) anmelden.
  • Mindestkonfiguration, falls vorhanden
  • funktioniert in Containern (dh Webserver)
  • (optional, aber schön zu haben) kommt entweder als Teil der Sprache oder als Maven-Artefakt, sodass ich meine Builds nicht hacken muss, um es zu verwenden

Ich weiß, dass ich die vorhandenen Java-Protokollierungslösungen verwenden kann, aber sie schlagen bei mindestens zwei der oben genannten Probleme fehl, nämlich Unordnung und Konfiguration.

Vielen Dank für Ihre Antworten.

George
quelle

Antworten:

119

slf4j Wrapper

Die meisten Protokollierungsbibliotheken von Scala waren Wrapper für ein Java-Protokollierungsframework (slf4j, log4j usw.), aber seit März 2015 sind alle überlebenden Protokollbibliotheken slf4j. Diese Protokollbibliotheken bieten eine Art logObjekt, das Sie aufrufen info(...)können debug(...)usw. Ich bin kein großer Fan von slf4j, aber es scheint jetzt das vorherrschende Protokollierungsframework zu sein. Hier ist die Beschreibung von SLF4J :

Die Simple Logging Facade für Java oder (SLF4J) dient als einfache Fassade oder Abstraktion für verschiedene Protokollierungsframeworks, z. B. java.util.logging, log4j und logback, sodass der Endbenutzer das gewünschte Protokollierungsframework zur Bereitstellungszeit einstecken kann.

Die Möglichkeit, die zugrunde liegende Protokollbibliothek zur Bereitstellungszeit zu ändern, bringt einzigartige Merkmale für die gesamte slf4j-Protokolliererfamilie mit sich, die Sie beachten müssen:

  1. Klassenpfad als Konfigurationsansatz . Slf4j weiß, welche zugrunde liegende Protokollierungsbibliothek Sie verwenden, indem Sie eine Klasse mit einem Namen laden. Ich hatte Probleme, bei denen slf4j meinen Logger nicht erkannte, als der Classloader angepasst wurde.
  2. Da die einfache Fassade versucht, der gemeinsame Nenner zu sein, ist sie nur auf tatsächliche Protokollaufrufe beschränkt. Mit anderen Worten, die Konfiguration kann nicht über den Code erfolgen.

In einem großen Projekt könnte es tatsächlich praktisch sein, das Protokollierungsverhalten von transitiven Abhängigkeiten zu steuern, wenn alle Benutzer slf4j verwenden.

Scala-Protokollierung

Scala Logging wurde von Heiko Seeberger als Nachfolger seiner slf4s geschrieben . Es verwendet Makros, um Aufrufe in if-Ausdruck zu erweitern, um potenziell teure Protokollaufrufe zu vermeiden.

Scala Logging ist eine praktische und leistungsfähige Protokollierungsbibliothek, die Protokollierungsbibliotheken wie SLF4J und möglicherweise andere umschließt.

Historische Logger

  • Logula , ein Log4J-Wrapper von Coda Hale. Früher mochte ich dieses, aber jetzt ist es aufgegeben.
  • configgy , ein java.util.logging-Wrapper, der in den früheren Tagen von Scala sehr beliebt war. Jetzt verlassen.
Eugene Yokota
quelle
1
Ich muss sagen, ich liebe Logula ... endlich einen Logger, der mich nicht dazu bringt, mir einen Schraubenzieher ins Ohr zu stechen. Keine XML und keine BS, nur eine Scala-Konfiguration beim Start
Arg
Der SLF4S von Heiko Seeberger scheint nicht mehr gewartet zu werden. Ich habe mein eigenes Scala 2.10 und das neueste SLF4J erstellt. http://slf4s.org/
Matt Roberts
3
Logula wird gemäß seiner README auf Github aufgegeben.
Erik Kaplun
Oncue Journal ähnelt im Konzept der Scala-Protokollierung, die Protokollnachrichten werden jedoch an einen Akteur gesendet, der SLF4J aus einem dedizierten Thread-Pool aufruft. Tauscht die CPU-Zeit gegen (viel bessere) Latenz. github.com/oncue/journal
Gary Coady
64

Bei Scala 2.10+ sollten Sie ScalaLogging von Typesafe in Betracht ziehen. Verwendet Makros, um eine sehr saubere API bereitzustellen

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

Zitat aus ihrem Wiki:

Glücklicherweise können Scala-Makros verwendet werden, um unser Leben zu erleichtern: ScalaLogging bietet der Klasse Loggerleichtgewichtige Protokollierungsmethoden, die auf die oben genannte Sprache erweitert werden. Wir müssen also nur schreiben:

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

Nachdem das Makro angewendet wurde, wurde der Code in das oben beschriebene Idiom umgewandelt.

Zusätzlich bietet ScalaLogging das Merkmal, Loggingdas bequem eine LoggerInstanz bereitstellt, die mit dem Namen der Klasse initialisiert ist, gemischt in:

import com.typesafe.scalalogging.slf4j.LazyLogging

class MyClass extends LazyLogging {
  logger.debug("This is very convenient ;-)")
}
fracca
quelle
12
Verwenden Sie class MyClass with Logging.
Andrzej Jozwik
1
Bitte beachten Sie, dass die Verwendung von Anmeldeeigenschaften dazu führt, dass das Merkmal für den Logger denselben Namen hat wie die Klasse, in die es gemischt wird. Ich denke, Sie können den Logger manuell anstelle der Makro-Bequemlichkeit erhalten, um den Logger-Namen bei Bedarf manuell anzugeben. Aber bitte korrigieren Sie mich, wenn ich falsch liege.
mkko
1
Bedeutet dies, dass MyClass über öffentliche Methoden zum Debuggen, Info-Warnen usw. verfügt? Wenn ja, würde ich lieber die zusätzliche manuelle Arbeit erledigen, einen privaten Logger für die Klasse zu instanziieren. Protokollierungsmethoden sollten nicht in eine Klassenschnittstelle gelangen.
ChucK
2
Sie erhalten ein Logger-Feld, das Sie nach Belieben verwenden können. Siehe github.com/typesafehub/scalalogging/blob/master/… für Details
fracca
2
2.x erfordert Scala 2.11; hoffentlich gibt es nicht viel praktischen Unterschied; für Scala 2.10.x gibt es "com.typesafe" %% "scalalogging-slf4j" % "1.1.0".
Erik Kaplun
14

Die Verwendung von slf4j und eines Wrappers ist nett, aber die Verwendung der integrierten Interpolation bricht zusammen, wenn Sie mehr als zwei Werte interpolieren müssen. Seitdem müssen Sie ein Array von Werten zum Interpolieren erstellen.

Eine Scala-ähnliche Lösung besteht darin, einen Thunk oder Cluster zu verwenden, um die Verkettung der Fehlermeldung zu verzögern. Ein gutes Beispiel dafür ist der Logger von Lift

Log.scala Slf4jLog.scala

Welches sieht so aus:

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

Beachten Sie, dass msg ein Call-by-Name ist und nur ausgewertet wird, wenn isTraceEnabled true ist, sodass das Generieren einer netten Nachrichtenzeichenfolge keine Kosten verursacht. Dies umgeht den Interpolationsmechanismus des slf4j, der das Parsen der Fehlermeldung erfordert. Mit diesem Modell können Sie eine beliebige Anzahl von Werten in die Fehlermeldung interpolieren.

Wenn Sie ein separates Merkmal haben, das diesen Log4JLogger in Ihre Klasse mischt, können Sie dies tun

trace("The foobar from " + a + " doesn't match the foobar from " +
      b + " and you should reset the baz from " + c")

anstatt

info("The foobar from {0} doesn't match the foobar from {1} and you should reset the baz from {c},
     Array(a, b, c))
Blair Zajac
quelle
2
Ist es möglich, genaue Zeilennummern für die Protokollierung von Anweisungen in Scala-Klassen zu erhalten?
Jpswain
13

Benutze Logula nicht

Ich bin tatsächlich der Empfehlung von Eugene gefolgt und habe es ausprobiert und festgestellt, dass es eine ungeschickte Konfiguration hat und Fehlern ausgesetzt ist, die nicht behoben werden (wie dieser ). Es scheint nicht gut gewartet zu sein und unterstützt Scala 2.10 nicht .

Verwenden Sie slf4s + slf4j-simple

Hauptvorteile:

  • Unterstützt die neueste Scala 2.10 (bis heute ist es M7)
  • Die Konfiguration ist vielseitig, könnte aber nicht einfacher sein. Dies geschieht mit Systemeigenschaften , die Sie entweder durch Anhängen eines -Dorg.slf4j.simplelogger.defaultlog=traceAusführungsbefehls oder eines Hardcodes in Ihrem Skript festlegen können : System.setProperty("org.slf4j.simplelogger.defaultlog", "trace"). Keine Notwendigkeit, trashige Konfigurationsdateien zu verwalten!
  • Passt gut zu IDEs. Zum Beispiel der Protokollebene auf „Spur“ in einer bestimmten Laufkonfiguration in IDEA einzustellen gehen Sie einfach auf Run/Debug Configurationsund fügen Sie -Dorg.slf4j.simplelogger.defaultlog=tracezu VM options.
  • Einfache Einrichtung: Geben Sie einfach die Abhängigkeiten am Ende dieser Antwort ein

Folgendes müssen Sie tun, um es mit Maven auszuführen:

<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>
Nikita Volkov
quelle
Wo finden Sie slf4s, die 2.10 unterstützen? Ich schaue auf github.com/weiglewilczek/slf4s und sehe "Unterstützte Scala-Versionen sind 2.8.0, 2.8.1, 2.9.0-1 und 2.9.1." .. suche ich am falschen Ort?
Nairbv
@Brian Verwenden Sie 2.9.1 wie in der Antwort vorgeschlagen. Ich habe es getestet, um mit 2.10.0-M7
Nikita Volkov
Ich benutze 2.9.1, ich war nur neugierig auf den hervorgehobenen "Hauptvorteil" und was das bedeutet.
Nairbv
Nachdem ich die Antworten sorgfältig nach den Vor- und Nachteilen der Protokollierungsbibliotheken durchsucht habe, wähle ich die aus, die mir sagt, welche Abhängigkeit importiert werden soll. Danke, Sir.
teo
11

So habe ich Scala Logging für mich arbeiten lassen:

Tragen Sie dies in Ihre build.sbt:

libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.7.2",
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3"

Anschließend sbt updatewird eine freundliche Protokollnachricht ausgedruckt:

import com.typesafe.scalalogging._
object MyApp extends App with LazyLogging {
  logger.info("Hello there")
}

Wenn Sie Play verwenden, können Sie natürlich einfach import play.api.LoggerProtokollnachrichten schreiben : Logger.debug("Hi").

Weitere Informationen finden Sie in den Dokumenten .

Matthias Braun
quelle
Funktionierte nur für mich, nachdem ich log4j.propertiesim Projektressourcenordner erstellt hatte .
Nadjib Mami
7

Ich habe ein bisschen Arbeit aus dem LoggingMerkmal gezogen scalaxund ein Merkmal erstellt, das auch eine MessageFormat-basedBibliothek integriert .

Dann sieht das so aus:

class Foo extends Loggable {
    info( "Dude, I'm an {0} with {1,number,#}", "Log message", 1234 )
}

Wir mögen den Ansatz bisher.

Implementierung:

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))  
}
Tristan Juricek
quelle
Süß, obwohl ich einige Probleme habe, damit dies funktioniert - insbesondere sieht die Ausgabe (in der Standardkonfiguration slf4j) immer wie folgt aus, anstatt dass die Klasse Loggable tatsächlich erweitert: 22. Juli 2011, 03:02:17 Uhr redacted.util.Loggable $ class info INFO: Eine Nachricht ... Gibt es eine Chance, dass Sie darauf gestoßen sind?
Tomer Gabel
6

Ich benutze SLF4J + Logback classic und wende es folgendermaßen an:

trait Logging {
  lazy val logger = LoggerFactory.getLogger(getClass)

  implicit def logging2Logger(anything: Logging): Logger = anything.logger
}

Dann können Sie es verwenden, je nachdem, was besser zu Ihrem Stil passt:

class X with Logging {
    logger.debug("foo")
    debug("bar")
}

Bei diesem Ansatz wird natürlich eine Logger-Instanz pro Klasseninstanz verwendet.

Kristof Jozsa
quelle
4

Sie sollten sich die Scalax-Bibliothek ansehen: http://scalax.scalaforge.org/ In dieser Bibliothek gibt es ein Protokollierungsmerkmal, bei dem sl4j als Backend verwendet wird. Mit diesem Merkmal können Sie ganz einfach protokollieren (verwenden Sie einfach das Logger-Feld in der Klasse, die das Merkmal erbt).


quelle
3

Ich habe es noch nicht ausprobiert, aber Configgy sieht sowohl für die Konfiguration als auch für die Protokollierung vielversprechend aus:

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

Dberesford
quelle
3

Schnelle und einfache Formulare.

Scala 2.10 und älter:

import com.typesafe.scalalogging.slf4j.Logger
import org.slf4j.LoggerFactory
val logger = Logger(LoggerFactory.getLogger("TheLoggerName"))
logger.debug("Useful message....")

Und build.sbt:

libraryDependencies += "com.typesafe" %% "scalalogging-slf4j" % "1.1.0"

Scala 2.11+ und neuer:

import import com.typesafe.scalalogging.Logger
import org.slf4j.LoggerFactory
val logger = Logger(LoggerFactory.getLogger("TheLoggerName"))
logger.debug("Useful message....")

Und build.sbt:

libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0"
xgMz
quelle
2

Nachdem ich eine Weile slf4s und logula verwendet hatte, schrieb ich logladyein einfaches Protokollierungsmerkmal, das slf4j umschließt.

Es bietet eine API ähnlich der Python-Protokollierungsbibliothek, die die gängigen Fälle (einfache Zeichenfolge, einfache Formatierung) trivial macht und das Formatieren von Boilerplates vermeidet.

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

dln
quelle
2

Ich finde es sehr praktisch, eine Art Java-Logger zu verwenden, zum Beispiel sl4j, mit einem einfachen Scala-Wrapper, der mir eine solche Syntax bringt

val #! = new Logger(..) // somewhere deep in dsl.logging.

object User with dsl.logging {

  #! ! "info message"
  #! dbg "debug message"
  #! trace "var a=true"

}

Meiner Meinung nach eine sehr nützliche Mischung aus Java-bewährten Protokollierungs-Frameworks und Scalas ausgefallener Syntax.

Alex Povar
quelle
@sschaef, oh ja. Es tut uns leid. Ich habe fast vergessen. Es war ein großartiger Kampf mit diesem Thema für ein paar Tage, aber es sieht so aus, als ob dies wirklich unmöglich ist. Ich habe # verwendet! ! "info message" stattdessen. Ich werde meine Antwort bearbeiten.
Alex Povar