Connexion à Scala

169

Quelle est la bonne façon de se connecter dans une application Scala? Quelque chose qui est cohérent avec la philosophie du langage, n'encombre pas le code, nécessite peu d'entretien et est discret. Voici une liste d'exigences de base:

  • Facile
  • n'encombre pas le code. Scala est excellent pour sa brièveté. Je ne veux pas que la moitié de mon code consigne des instructions
  • le format du journal peut être modifié pour s'adapter au reste de mes journaux d'entreprise et de mon logiciel de surveillance
  • prend en charge les niveaux de journalisation (c'est-à-dire débogage, trace, erreur)
  • peut se connecter au disque ainsi qu'à d'autres destinations (c'est-à-dire socket, console, etc.)
  • configuration minimale, le cas échéant
  • fonctionne dans des conteneurs (c.-à-d. serveur Web)
  • (facultatif, mais agréable à avoir) vient soit dans le cadre du langage, soit comme un artefact maven, donc je n'ai pas à pirater mes builds pour l'utiliser

Je sais que je peux utiliser les solutions de journalisation Java existantes, mais elles échouent sur au moins deux des éléments ci-dessus, à savoir l'encombrement et la configuration.

Merci pour vos réponses.

George
la source

Réponses:

119

wrappers slf4j

La plupart des bibliothèques de journalisation de Scala ont été des wrappers autour d'un framework de journalisation Java (slf4j, log4j, etc.), mais en mars 2015, les bibliothèques de journaux survivantes sont toutes slf4j. Ces bibliothèques de journaux fournissent une sorte d' logobjet auquel vous pouvez appeler info(...), debug(...)etc. Je ne suis pas un grand fan de slf4j, mais il semble maintenant être le cadre de journalisation prédominant. Voici la description de SLF4J :

La façade de journalisation simple pour Java ou (SLF4J) sert de simple façade ou abstraction pour divers cadres de journalisation, par exemple java.util.logging, log4j et logback, permettant à l'utilisateur final de se connecter au cadre de journalisation souhaité au moment du déploiement.

La possibilité de modifier la bibliothèque de journaux sous-jacente au moment du déploiement apporte une caractéristique unique à toute la famille de loggers slf4j, dont vous devez être conscient:

  1. classpath comme approche de configuration . La façon dont slf4j sait quelle bibliothèque de journalisation sous-jacente vous utilisez consiste à charger une classe par un nom. J'ai eu des problèmes dans lesquels slf4j ne reconnaissait pas mon enregistreur lorsque le chargeur de classe était personnalisé.
  2. Parce que la façade simple essaie d'être le dénominateur commun, elle est limitée uniquement aux appels de journaux réels. En d'autres termes, la configuration ne peut pas se faire via le code.

Dans un grand projet, il pourrait en fait être pratique de pouvoir contrôler le comportement de journalisation des dépendances transitives si tout le monde utilisait slf4j.

Journalisation Scala

Scala Logging est écrit par Heiko Seeberger comme successeur de ses slf4s . Il utilise une macro pour développer les appels dans l'expression if afin d'éviter un appel de journal potentiellement coûteux.

Scala Logging est une bibliothèque de journalisation pratique et performante qui englobe des bibliothèques de journalisation comme SLF4J et potentiellement d'autres.

Enregistreurs historiques

  • Logula , un wrapper Log4J écrit par Coda Hale. J'aimais bien celui-ci, mais maintenant il est abandonné.
  • configgy , un wrapper java.util.logging qui était populaire dans les premiers jours de Scala. Maintenant abandonné.
Eugene Yokota
la source
1
Je dois dire, j'adore Logula ... enfin un enregistreur qui ne me donne pas envie de poignarder un tournevis dans l'oreille. Pas de xml et pas de BS, juste une configuration scala au démarrage
Arg
Le SLF4S de Heiko Seeberger ne semble plus être maintenu. J'ai fait mon propre ciblage Scala 2.10 et le dernier SLF4J. http://slf4s.org/
Matt Roberts
3
Logula est abandonnée selon son README sur Github.
Erik Kaplun
Oncue Journal est similaire dans son concept à Scala Logging, mais les messages du journal sont envoyés à un acteur qui appelle SLF4J à partir d'un pool de threads dédié. Traite le temps CPU pour une (bien meilleure) latence. github.com/oncue/journal
Gary Coady
64

Avec Scala 2.10+, pensez à ScalaLogging de Typesafe. Utilise des macros pour fournir une API très propre

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

Citant leur wiki:

Heureusement, les macros Scala peuvent être utilisées pour nous faciliter la vie: ScalaLogging propose la classe Loggeravec des méthodes de journalisation légères qui seront étendues à l'idiome ci-dessus. Donc, tout ce que nous avons à écrire est:

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

Une fois la macro appliquée, le code aura été transformé dans l'idiome décrit ci-dessus.

De plus, ScalaLogging offre le trait Loggingqui fournit commodément une Loggerinstance initialisée avec le nom de la classe mélangée dans:

import com.typesafe.scalalogging.slf4j.LazyLogging

class MyClass extends LazyLogging {
  logger.debug("This is very convenient ;-)")
}
fracca
la source
12
Utilisez class MyClass with Logging.
Andrzej Jozwik
1
Veuillez noter que l'utilisation de la journalisation des traits fait que le trait a le même nom pour l'enregistreur que la classe dans laquelle il est mélangé. Je pense que vous pouvez obtenir l'enregistreur manuellement au lieu de la macro commodité pour fournir le nom de l'enregistreur manuellement, si nécessaire. Mais s'il vous plaît, corrigez-moi si je me trompe.
mkko
1
Cela signifie-t-il que MyClass a des méthodes publiques de débogage, d'avertissement d'informations, etc.? Si tel est le cas, je préférerais faire le travail manuel supplémentaire d'instancier un enregistreur privé pour la classe. Les méthodes de journalisation ne doivent pas s'infiltrer dans une interface de classes.
ChucK
2
vous obtenez un champ d'enregistrement, que vous pouvez utiliser à votre guise. Voir github.com/typesafehub/scalalogging/blob/master/… pour plus de détails
fracca
2
2.x nécessite Scala 2.11; j'espère qu'il n'y a pas beaucoup de différence pratique; pour Scala 2.10.x, il y a "com.typesafe" %% "scalalogging-slf4j" % "1.1.0".
Erik Kaplun
14

Utiliser slf4j et un wrapper est bien mais l'utilisation de son interpolation intégrée se décompose lorsque vous avez plus de deux valeurs à interpoler, car vous devez créer un tableau de valeurs à interpoler.

Une solution plus Scala consiste à utiliser un thunk ou un cluster pour retarder la concaténation du message d'erreur. Un bon exemple de ceci est l'enregistreur de Lift

Log.scala Slf4jLog.scala

Ce qui ressemble à ceci:

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

Notez que msg est un appel par nom et ne sera pas évalué à moins que isTraceEnabled soit vrai, il n'y a donc aucun coût à générer une belle chaîne de message. Cela fonctionne autour du mécanisme d'interpolation de slf4j qui nécessite l'analyse du message d'erreur. Avec ce modèle, vous pouvez interpoler n'importe quel nombre de valeurs dans le message d'erreur.

Si vous avez un trait distinct qui mélange ce Log4JLogger dans votre classe, vous pouvez faire

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

au lieu de

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
la source
2
est-il possible d'obtenir des numéros de ligne précis pour la journalisation des instructions dans les classes scala ?????
jpswain
13

N'utilisez pas Logula

J'ai en fait suivi la recommandation d'Eugene et l'ai essayé et j'ai découvert qu'il avait une configuration maladroite et qu'il était soumis à des bogues, qui ne sont pas corrigés (comme celui-ci ). Il ne semble pas être bien entretenu et il ne prend pas en charge Scala 2.10 .

Utilisez slf4s + slf4j-simple

Avantages clés:

  • Prend en charge la dernière Scala 2.10 (à ce jour, c'est M7)
  • La configuration est polyvalente mais ne pourrait pas être plus simple. Il est fait avec les propriétés du système , que vous pouvez définir soit en ajoutant quelque chose comme -Dorg.slf4j.simplelogger.defaultlog=traceà commander ou hardcode exécution dans votre script: System.setProperty("org.slf4j.simplelogger.defaultlog", "trace"). Pas besoin de gérer les fichiers de configuration trash!
  • Convient bien aux IDE. Par exemple, pour définir le niveau de journalisation sur «trace» dans une configuration d'exécution spécifique dans IDEA, il suffit d'aller sur Run/Debug Configurationset d'ajouter -Dorg.slf4j.simplelogger.defaultlog=traceà VM options.
  • Configuration facile: déposez simplement les dépendances à partir du bas de cette réponse

Voici ce dont vous avez besoin pour l'exécuter avec 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>
Nikita Volkov
la source
où trouvez-vous des slf4 qui prennent en charge la version 2.10? Je regarde github.com/weiglewilczek/slf4s et je vois "Les versions Scala prises en charge sont 2.8.0, 2.8.1, 2.9.0-1 et 2.9.1." .. est-ce que je cherche au mauvais endroit?
nairbv
@Brian Utilisez le 2.9.1 comme suggéré dans la réponse. Je l'ai testé pour qu'il fonctionne correctement avec 2.10.0-M7
Nikita Volkov
J'utilise 2.9.1, j'étais simplement curieux de connaître le "principal avantage" mis en évidence et ce que cela signifie.
nairbv
Après avoir soigneusement cherché dans les réponses les avantages et les inconvénients des bibliothèques de journalisation, je choisis celle qui me dit quelle dépendance importer. Merci Monsieur.
teo
11

Voici comment j'ai fait fonctionner Scala Logging pour moi:

Mettez ceci dans votre build.sbt:

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

Ensuite, après avoir fait une sbt update, ceci imprime un message de journal convivial:

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

Si vous utilisez Play, vous pouvez bien sûr simplement import play.api.Loggerpour écrire des messages du journal: Logger.debug("Hi").

Consultez la documentation pour plus d'informations.

Matthias Braun
la source
Cela n'a fonctionné pour moi qu'après avoir créé le log4j.propertiesdossier de ressources du projet.
Nadjib Mami
7

J'ai tiré un peu de travail du Loggingtrait de scalax, et créé un trait qui a également intégré une MessageFormat-basedbibliothèque.

Ensuite, les choses ressemblent à ceci:

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

Nous aimons l'approche jusqu'à présent.

La mise en oeuvre:

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
la source
Mignon, même si j'ai quelques problèmes pour que cela fonctionne - en particulier, la sortie (sur la configuration par défaut de slf4j) ressemble toujours à ce qui suit, au lieu de la classe étendant réellement Loggable: 22 juillet 2011 3:02:17 AM rédigé.util.Loggable $ class info INFO: Un message ... Une chance que vous ayez rencontré ça?
Tomer Gabel
7

J'utilise SLF4J + Logback classic et je l'applique comme ceci:

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

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

Ensuite, vous pouvez l'utiliser selon ce qui correspond le mieux à votre style:

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

mais cette approche utilise bien sûr une instance de journalisation par instance de classe.

Kristof Jozsa
la source
4

Vous devriez jeter un œil à la bibliothèque scalax: http://scalax.scalaforge.org/ Dans cette bibliothèque, il y a un trait Logging, utilisant sl4j comme backend. En utilisant ce trait, vous pouvez vous connecter assez facilement (utilisez simplement le champ logger dans la classe héritant du trait).


la source
3

Formulaires rapides et faciles.

Scala 2.10 et plus ancien:

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

Et build.sbt:

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

Scala 2.11+ et plus récent:

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

Et build.sbt:

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

Après avoir utilisé slf4s et logula pendant un certain temps, j'ai écrit loglady, un trait de journalisation simple enveloppant slf4j.

Il propose une API similaire à celle de la bibliothèque de journalisation de Python, ce qui rend les cas courants (chaîne de base, formatage simple) triviaux et évite le formatage standard.

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

dln
la source
2

Je trouve très pratique d'utiliser une sorte de java logger, sl4j par exemple, avec un simple wrapper scala, ce qui m'apporte une telle syntaxe

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

object User with dsl.logging {

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

}

À mon avis, un mélange très utile de frameworks de journalisation éprouvés en Java et de la syntaxe sophistiquée de scala.

Alex Povar
la source
@sschaef, oh, oui. Désolé. J'ai presque oublié. Ce fut un grand combat avec ce problème pendant quelques jours, mais il semble que cela soit vraiment impossible. J'ai utilisé #! ! "message info" à la place. Je modifierai ma réponse.
Alex Povar