Konfiguracja fabryki loggerów z biblioteki slogging w Scali
W poście Biblioteki do logowania dla języka Scala skonfigurowałem logger slogging. Zapomniałem tylko wybrać fabrykę loggerów. Bez tego logger w ogóle nie działa.
Przy okazji, tu rodzi się pytanie-zagadka “jak przetestować loggera w testach jednostkowych”?
Rozwiązanie uniwersalne
Uniwersalną fabryką loggerów jest PrintLoggerFactory
. Działa ona dla wszystkich platform docelowych. Jej działanie jest oparte na metodzie/funkcji/procedurerze println
.
Do pliku build.sbt
dodajemy zależność i ustawiamy wersję biblioteki :
val SloggingVersion = "0.6.1"
// ...
val SharedSettings = Seq(
// ...
libraryDependencies ++= Seq(
"biz.enef" %%% "slogging" % SloggingVersion,
),
)
A w pliku Main.scala
ustawiamy PrintLoggerFactory
jako implementację:
package pl.writeonly.re.main
import pl.writeonly.re.shared.core.Core
import slogging._
object Main extends App {
LoggerConfig.factory = PrintLoggerFactory()
LoggerConfig.level = LogLevel.TRACE
Core.apply("Goodbye to the World")
}
Wszystko działa i można by zakończyć na tym artykuł. Zwłaszcza jeśli dockeryzujemy swoją aplikację i kto inny zajmuje się zapisywaniem logów do plików i baz danych. Jeśli jednak nasza aplikacja ma działać poza dockerem przydatne mogą być bardziej zaawanasowane funkcjonalności loggera.
Rozwiązania dedykowane dla platformy
Aktualnie język Scala można kompilować skrośnie (ang. cross compiler) na trzy platformy JVM, JS i Native Każda z platform posiada inne możliwości konfiguracyjne loggera.
Scala JVM
Dla platformy JVM nie ma dużego wyboru. Jest jedna dedykowana fabryka SLF4JLoggerFactory
. Jest ona opakowaniem slf4j, dzięki czemu możemy wybrać dowolny logger implementujący tą fasadę. Ja wybrałem logback.
Do pliku build.sbt
dodajemy zależności:
val jvmSettings = Seq(
// ...
libraryDependencies ++= Seq(
"biz.enef" %% "slogging-slf4j" % SloggingVersion,
"ch.qos.logback" % "logback-classic" % "1.2.3",
),
)
A w pliku Main.scala
ustawiamy PrintLoggerFactory
jako implementację:
package pl.writeonly.re.main
import pl.writeonly.re.shared.core.Core
import slogging._
object Main extends App {
LoggerConfig.factory = SLF4JLoggerFactory()
LoggerConfig.level = LogLevel.TRACE
Core.apply("JVM")
}
I uruchamiamy projekt w Scala JVM za pomocą polecenia:
sbt clean reJVM/run
Warto przeczytać jeszcze sposób konfiguracji loggera logback.
Scala.js
Jeśli używamy transpilatora Scala.js to mamy do wyboru trzy możliwości :
ConsoleLoggerFactory
- podobny doPrintLoggerFactory
, ale używaconsole.log()
zamiastprintln()
.WinstonLoggerFactory
- opakowanie wokół biblioteki do logowaniawinston
dla Node.js.HttpLoggerFactory
- ten backend wysyła komunikaty dziennika do serwera HTTP za pośrednictwem żądań POST Ajax. Należy zauważyć, że ta sama zasada pochodzenia zazwyczaj wymaga, aby serwer, do którego wysyłane są komunikaty, był tym samym serwerem, z którego załadowano źródło javascript.
Ja chcę zbudować aplikację konsolową CLI, więc wybierałem ConsoleLoggerFactory
.
Do pliku build.sbt
dodajemy… nic:
val jsSettings = Seq(
// ...
// scalaJSModuleKind := ModuleKind.CommonJSModule,
libraryDependencies ++= Seq(
// "biz.enef" %%% "slogging-winston" % SloggingVersion,
// "biz.enef" %%% "slogging-http" % SloggingVersion,
),
)
Gdybyśmy wybrali WinstonLoggerFactory
musielibyśmy:
- dodać
"biz.enef" %%% "slogging-winston" % SloggingVersion
do zależności - ustawić
scalaJSModuleKind := ModuleKind.CommonJSModule
Gdybyśmy wybrali HttpLoggerFactory
musielibyśmy dodać "biz.enef" %%% "slogging-http" % SloggingVersion
do zależności.
A w pliku Main.scala
ustawiamy ConsoleLoggerFactory
jako implementację:
package pl.writeonly.re.main
import pl.writeonly.re.shared.core.Core
import slogging._
object Main extends JSApp {
override def main(): Unit = {
LoggerConfig.factory = ConsoleLoggerFactory()
LoggerConfig.level = LogLevel.TRACE
Core.apply("JS")
}
}
I uruchamiamy projekt w Scala.js za pomocą polecenia:
sbt clean reJS/run
Scala Native
Jeśli używamy kompilatora Scala Native to mamy do wyboru trzy możliwości :
TerminalLoggerFactory
- rejestruje wszystkie komunikaty nastderr
za pomocąfprintf()
. Komunikaty są zawarte w kodach kontrolnych terminala ANSI, które można skonfigurować oddzielnie dla każdego poziomu. Dzięki czemu możemy mieć tęczowe logi.GLibLoggerFactory
- ten backend używa GLib’s g_log() do rejestrowania wiadomości, np. do użytku z skalan-gtk.SyslogLoggerFactory
- ten backend używa standardowego narzędziasyslog
.
Ja chcę zbudować aplikację konsolową CLI, więc wybierałem TerminalLoggerFactory
.
W pliku build.sbt
dodajemy… nic:
val nativeSettings = Seq(
// ...
// nativeLinkingOptions += "-lglib-2.0",
libraryDependencies ++= Seq(
// "biz.enef" %%% "slogging-glib" % SloggingVersion,
// "biz.enef" %%% "slogging-syslog" % SloggingVersion,
),
)
Gdybyśmy wybrali GLibLoggerFactory
to musielibyśmy:
- dodać do zależności
"biz.enef" %%% "slogging-glib" % SloggingVersion,
- ustawiamu opcję kompilatora
nativeLinkStubs := true
Gdybyśmy wybrali SyslogLoggerFactory
musielibyśmy dodać "biz.enef" %%% "slogging-syslog" % SloggingVersion
do zależności.
A w pliku Main.scala
ustawiamy TerminalLoggerFactory
jako implementację:
package pl.writeonly.re.main
import pl.writeonly.re.shared.core._
import slogging._
import slogging.TerminalLoggerFactory.TerminalControlCode
object Main extends App {
LoggerConfig.factory = TerminalLoggerFactory()
TerminalLoggerFactory.infoCode = TerminalControlCode.green
TerminalLoggerFactory.debugCode = TerminalControlCode.cyan
TerminalLoggerFactory.traceCode = TerminalControlCode.blue
LoggerConfig.level = LogLevel.TRACE
Core.apply("Native")
StrictLoggingCore.rainbow()
}
Implementacja metody StrictLoggingCore.rainbow()
to:
def rainbow(): Unit = {
logger.error("rainbow error")
logger.warn("rainbow warn")
logger.info("rainbow info")
logger.debug("rainbow debug")
logger.trace("rainbow trace")
}
Uruchamiamy projekt w Scala Native za pomocą polecenia:
sbt clean re/run
I oglądamy tęczę XD
Podsumowanie i wnioski
Kod jest dostępny pod adresem resentiment. Co do wniosków to:
- warto czytać dokumentację
- trudno testować logowanie
Zostaw komentarz