Accesso a Scala


169

Qual è un buon modo per fare il login in un'applicazione Scala? Qualcosa che è coerente con la filosofia del linguaggio, non ingombra il codice ed è a bassa manutenzione e discreto. Ecco un elenco di requisiti di base:

  • semplice
  • non ingombra il codice. Scala è eccezionale per la sua brevità. Non voglio che metà del mio codice contenga dichiarazioni di registrazione
  • il formato del registro può essere modificato per adattarsi al resto dei registri aziendali e al software di monitoraggio
  • supporta livelli di registrazione (ad es. debug, traccia, errore)
  • può accedere al disco e ad altre destinazioni (es. socket, console, ecc.)
  • configurazione minima, se presente
  • funziona in container (ad es. web server)
  • (facoltativo, ma bello da avere) viene fornito come parte del linguaggio o come artefatto maven, quindi non devo hackerare le mie build per usarlo

So di poter utilizzare le soluzioni di registrazione Java esistenti, ma non riescono su almeno due dei precedenti, vale a dire disordine e configurazione.

Grazie per le tue risposte

Risposte:


119

involucri slf4j

La maggior parte delle librerie di registrazione di Scala sono state avvolte in un framework di registrazione Java (slf4j, log4j ecc.), Ma da marzo 2015 le librerie di log sopravvissute sono tutte slf4j. Queste librerie di log forniscono una sorta di logoggetto a cui è possibile chiamare info(...), debug(...)ecc. Non sono un grande fan di slf4j, ma ora sembra essere il framework di registrazione predominante. Ecco la descrizione di SLF4J :

Simple Logging Facade per Java o (SLF4J) funge da semplice facciata o astrazione per vari framework di registrazione, ad esempio java.util.logging, log4j e logback, consentendo all'utente finale di collegare il framework di registrazione desiderato al momento della distribuzione.

La possibilità di modificare la libreria di log sottostante al momento della distribuzione offre caratteristiche uniche all'intera famiglia di logger slf4j, di cui è necessario essere a conoscenza:

  1. percorso di classe come approccio di configurazione . Il modo in cui slf4j sa quale libreria di log sottostante stai usando caricando una classe con un nome. Ho avuto problemi in cui slf4j non riconosce il mio logger quando il classloader è stato personalizzato.
  2. Poiché la semplice facciata cerca di essere il comune denominatore, è limitata solo alle chiamate di registro effettive. In altre parole, la configurazione non può essere effettuata tramite il codice.

In un grande progetto, potrebbe essere conveniente essere in grado di controllare il comportamento di registrazione delle dipendenze transitive se tutti usassero slf4j.

Scala Logging

Scala Logging è scritto da Heiko Seeberger come successore dei suoi slf4s . Utilizza la macro per espandere le chiamate in if espressione per evitare chiamate di registro potenzialmente costose.

Scala Logging è una libreria di registrazione comoda e performante che avvolge le librerie di registrazione come SLF4J e potenzialmente altre.

Logger storici

  • Logula , un wrapper Log4J scritto da Coda Hale. Mi piaceva questo, ma ora è abbandonato.
  • configgy , un wrapper java.util.logging che era popolare nei primi giorni di Scala. Ora abbandonato.

1
Devo dire che adoro Logula ... finalmente un registratore che non mi fa venir voglia di pugnalare un cacciavite all'orecchio. No xml e no BS, solo una scala config all'avvio
Arg

La SLF4S di Heiko Seeberger non sembra più essere mantenuta. Ho realizzato il mio targeting per Scala 2.10 e l'ultimo SLF4J. http://slf4s.org/
Matt Roberts,

3
Logula viene abbandonata secondo il suo README su Github.
Erik Kaplun,

Oncue Journal ha un concetto simile a Scala Logging, ma i messaggi di registro vengono inviati a un attore che chiama SLF4J da un pool di thread dedicato. Scambia il tempo della CPU per una latenza (molto migliore). github.com/oncue/journal
Gary Coady,

64

Con Scala 2.10+ Valuta ScalaLogging di Typesafe. Utilizza le macro per fornire un'API molto pulita

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

Citando dal loro wiki:

Fortunatamente le macro Scala possono essere utilizzate per semplificarci la vita: ScalaLogging offre alla classe Loggermetodi di registrazione leggeri che verranno estesi al linguaggio di cui sopra. Quindi tutto ciò che dobbiamo scrivere è:

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

Dopo l'applicazione della macro, il codice sarà stato trasformato nel linguaggio descritto sopra.

Inoltre ScalaLogging offre la caratteristica Loggingche fornisce convenientemente Loggerun'istanza inizializzata con il nome della classe mescolata in:

import com.typesafe.scalalogging.slf4j.LazyLogging

class MyClass extends LazyLogging {
  logger.debug("This is very convenient ;-)")
}

12
Usa class MyClass with Logging.
Andrzej Jozwik,

1
Si noti che l'utilizzo dei tratti di accesso comporta che lo stesso tratto abbia lo stesso nome per il logger della classe in cui è miscelato. Penso che sia possibile ottenere manualmente il logger invece della comodità macro per fornire manualmente il nome del logger, se necessario. Ma per favore, correggimi se sbaglio.
mkko,

1
Questo significa che MyClass ha metodi pubblici per il debug, informazioni avvisano ecc? In tal caso, preferirei fare il lavoro manuale aggiuntivo di istanziare un logger privato per la classe. I metodi di registrazione non devono entrare nell'interfaccia delle classi.
ChucK,

2
ottieni un campo logger, che puoi usare a tuo piacimento. Per i dettagli, consultare github.com/typesafehub/scalalogging/blob/master/…
fracca,

2
2.x richiede Scala 2.11; speriamo non ci sia molta differenza pratica; per Scala 2.10.x, c'è "com.typesafe" %% "scalalogging-slf4j" % "1.1.0".
Erik Kaplun,

14

L'uso di slf4j e un wrapper è utile, ma l'uso della sua interpolazione integrata si interrompe quando si hanno più di due valori da interpolare, da allora è necessario creare una matrice di valori da interpolare.

Una soluzione più simile a Scala consiste nell'utilizzare un thunk o un cluster per ritardare la concatenazione del messaggio di errore. Un buon esempio di ciò è il logger di Lift

Log.scala Slf4jLog.scala

Che assomiglia a questo:

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

Si noti che msg è un call-by-name e non verrà valutato a meno che isTraceEnabled sia vero, quindi non ci sono costi nel generare una bella stringa di messaggi. Questo funziona attorno al meccanismo di interpolazione di slf4j che richiede l'analisi del messaggio di errore. Con questo modello, è possibile interpolare qualsiasi numero di valori nel messaggio di errore.

Se hai un tratto separato che mescola questo Log4JLogger nella tua classe, allora puoi farlo

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

invece di

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
è possibile ottenere numeri di riga precisi per la registrazione delle istruzioni nelle classi scala ?????
jpswain,

13

Non usare Logula

In realtà ho seguito la raccomandazione di Eugene e l'ho provato e ho scoperto che ha una configurazione goffa ed è soggetto a bug, che non vengono corretti (come questo ). Non sembra essere ben mantenuto e non supporta Scala 2.10 .

Usa slf4s + slf4j-simple

Vantaggi chiave:

  • Supporta l'ultima Scala 2.10 (ad oggi è M7)
  • La configurazione è versatile ma non potrebbe essere più semplice. E 'fatto con le proprietà del sistema , che è possibile impostare sia aggiungendo qualcosa di simile -Dorg.slf4j.simplelogger.defaultlog=traceal comando di esecuzione o hardcode nello script: System.setProperty("org.slf4j.simplelogger.defaultlog", "trace"). Non c'è bisogno di gestire i file di configurazione del cestino!
  • Si adatta perfettamente agli IDE. Ad esempio, per impostare il livello di registrazione su "traccia" in una configurazione di esecuzione specifica in IDEA basta andare su Run/Debug Configurationse aggiungere -Dorg.slf4j.simplelogger.defaultlog=tracea VM options.
  • Installazione semplice: basta inserire le dipendenze dal fondo di questa risposta

Ecco di cosa hai bisogno per eseguirlo con 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>

dove trovi slf4s che supporta 2.10? Sto guardando github.com/weiglewilczek/slf4s e vedere "Le versioni Scala supportate sono 2.8.0, 2.8.1, 2.9.0-1 e 2.9.1." .. sto guardando nel posto sbagliato?
nairbv,

@Brian Usa 2.9.1 come suggerito nella risposta. L'ho provato per funzionare perfettamente con 2.10.0-M7
Nikita Volkov,

Sto usando 2.9.1, ero solo curioso del "vantaggio chiave" evidenziato e di ciò che significa.
nairbv,

Dopo aver cercato attentamente le risposte ai pro e ai contro delle librerie di log, scelgo quello che mi dice quale dipendenza importare. Grazie Signore.
Teo,

11

Ecco come ho fatto funzionare Scala Logging per me:

Metti questo nel tuo build.sbt:

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

Quindi, dopo aver fatto un sbt update, questo stampa un messaggio di registro amichevole:

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

Se si utilizza Play, potete naturalmente semplicemente import play.api.Loggerper la scrittura di messaggi di log: Logger.debug("Hi").

Vedi i documenti per maggiori informazioni.


Ha funzionato solo per me dopo averlo creato log4j.propertiesnella cartella delle risorse del progetto.
Nadjib Mami,

7

Ho tratto un po 'di lavoro dal Loggingtratto scalaxe ho creato un tratto che integrava anche una MessageFormat-basedlibreria.

Quindi le cose sembrano in questo modo:

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

Finora ci piace l'approccio.

Implementazione:

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

Carino, anche se sto avendo alcuni problemi a farlo funzionare - in particolare, l'output (sulla configurazione predefinita di slf4j) è sempre simile al seguente, invece della classe che si estende effettivamente Loggable: 22 lug 2011 3:02:17 redacted.util.Loggable $ class info INFO: qualche messaggio ... Qualche possibilità ti sei imbattuto in questo?
Tomer Gabel,

6

Uso SLF4J + Logback classic e lo applico in questo modo:

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

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

Quindi puoi usarlo come meglio si adatta al tuo stile:

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

ma questo approccio usa ovviamente un'istanza logger per istanza di classe.


4

Dovresti dare un'occhiata alla libreria scalax: http://scalax.scalaforge.org/ In questa libreria, c'è una caratteristica di registrazione, usando sl4j come backend. Usando questa caratteristica, puoi accedere abbastanza facilmente (basta usare il campo logger nella classe che eredita la caratteristica).




3

Forme facili e veloci.

Scala 2.10 e precedenti:

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

E build.sbt:

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

Scala 2.11+ e successive:

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

E build.sbt:

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

2

Dopo aver usato slf4s e logula per un po ', ho scritto loglady, un semplice tratto di registrazione che avvolge slf4j.

Offre un'API simile a quella della libreria di logging di Python, che rende banali i casi comuni (stringa di base, formattazione semplice) ed evita la formattazione del boilerplate.

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


2

Trovo molto conveniente usare una sorta di java logger, ad esempio sl4j, con un semplice scala wrapper, che mi porta tale sintassi

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

object User with dsl.logging {

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

}

A mio avviso, un mix molto utile di framework di registrazione comprovati da Java e sintassi elaborata di Scala.


@sschaef, oh, sì. Scusate. Ho quasi dimenticato. È stata una grande lotta con questo problema per alcuni giorni, ma sembra che sia davvero impossibile. Ho usato #! ! "messaggio informativo" invece. Modifica modifica la mia risposta.
Alex Povar,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.