-
-
Save Danielfenghk/c3869a020fb5e849cc2336b91215c6ca to your computer and use it in GitHub Desktop.
Playing/testing with logback configuration. / published by https://github.com/dacr/code-examples-manager #b45b0038-e0fc-4dd5-810f-5ad9b420f41e/160f7f5e1ce02df1e29f81405c8d10a742acc219
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// summary : Playing/testing with logback configuration. | |
// keywords : scala, scalatest, logs, logback, @testable | |
// publish : gist | |
// authors : David Crosson | |
// license : Apache2 | |
// id : b45b0038-e0fc-4dd5-810f-5ad9b420f41e | |
// created-on : 2020-05-31T19:54:52Z | |
// managed-by : https://github.com/dacr/code-examples-manager | |
// run-with : scala-cli $file | |
// --------------------- | |
//> using scala "3.1.1" | |
//> using lib "org.scalatest::scalatest:3.2.10" | |
//> using lib "ch.qos.logback:logback-classic:1.2.3" | |
//> using lib "net.logstash.logback:logstash-logback-encoder:5.2" | |
// --------------------- | |
import org.scalatest._, flatspec._, matchers._ | |
import org.slf4j._ | |
trait LogBackHelpers { | |
def configureLogBack(xmlConfig:String):Unit = { | |
val loggerContext = LoggerFactory.getILoggerFactory.asInstanceOf[ch.qos.logback.classic.LoggerContext] | |
loggerContext.reset() | |
val configurator = new ch.qos.logback.classic.joran.JoranConfigurator() | |
val configStream = new java.io.ByteArrayInputStream(xmlConfig.getBytes) | |
configurator.setContext(loggerContext) | |
configurator.doConfigure(configStream) // loads logback file | |
} | |
def getRootLogger():ch.qos.logback.classic.Logger = { | |
LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME).asInstanceOf[ch.qos.logback.classic.Logger] | |
} | |
def configureDebugRootLogger():java.io.InputStream = { | |
val loggerContext = LoggerFactory.getILoggerFactory.asInstanceOf[ch.qos.logback.classic.LoggerContext] | |
loggerContext.reset() | |
val rootLogger = getRootLogger() | |
val output = new java.io.PipedOutputStream() | |
val encoder = new ch.qos.logback.classic.encoder.PatternLayoutEncoder() | |
encoder.setPattern("%date %level [%thread] %logger{10} [%file:%line] - %msg%n") | |
encoder.setContext(loggerContext) | |
val outputStreamAppender = new ch.qos.logback.core.OutputStreamAppender[ch.qos.logback.classic.spi.ILoggingEvent]() | |
outputStreamAppender.setContext(loggerContext) | |
outputStreamAppender.setOutputStream(output) | |
outputStreamAppender.setEncoder(encoder) | |
rootLogger.addAppender(outputStreamAppender) | |
new java.io.PipedInputStream(output) | |
} | |
} | |
class LoggingTesting extends AnyFlatSpec with should.Matchers with LogBackHelpers { | |
override val suiteName = "LoggingTesting" | |
// ------------------------------------------------------------------------------------------------- | |
"logback" should "allow to programmically change default ROOT logger configuration" in { | |
val rootLogger = getRootLogger() | |
rootLogger.setLevel(ch.qos.logback.classic.Level.ERROR) | |
rootLogger.getLevel shouldBe ch.qos.logback.classic.Level.ERROR | |
} | |
// ------------------------------------------------------------------------------------------------- | |
it should "be configurable through a xml configuration" in { | |
val config= | |
"""<configuration scan="false" scanPeriod="10 seconds"> | |
| <root level="INFO"> | |
| <appender-ref ref="FILE"/> | |
| </root> | |
|</configuration>""".stripMargin | |
configureLogBack(config) | |
getRootLogger().getLevel shouldBe ch.qos.logback.classic.Level.INFO | |
} | |
// ------------------------------------------------------------------------------------------------- | |
it should "raise an exception with an invalid configuration file" in { | |
val config= | |
"""<configuration scan="false" scanPeriod="10 seconds"> | |
| <rootx level="INFO"> | |
| <appender-ref ref="FILE"/> | |
| </root> | |
|</configuration>""".stripMargin | |
intercept[Exception] { | |
configureLogBack(config) | |
} | |
} | |
// ------------------------------------------------------------------------------------------------- | |
ignore should "be possible to use a memory appender" in { | |
val logInputStream = configureDebugRootLogger() | |
val logEntries = scala.io.Source.fromInputStream(logInputStream) | |
val logger = LoggerFactory.getLogger("Test") | |
logger.info("HELLO") | |
logEntries.getLines.next should include regex "(?i)hello" | |
} | |
// ------------------------------------------------------------------------------------------------- | |
it should "insert exception stack trace digest" in { | |
val logFileName="testFile.log" | |
val config = | |
"""<?xml version="1.0" encoding="UTF-8"?> | |
|<!-- application logging configuration to ship logs directly to Logstash --> | |
|<configuration> | |
| <!-- define stack trace element exclusion patterns as a global property --> | |
| <property name="STE_EXCLUSIONS" value="^ammonite\.,\$\$FastClassByCGLIB\$\$,\$\$EnhancerBySpringCGLIB\$\$,^sun\.reflect\..*\.invoke,^com\.sun\.,^sun\.net\.,^net\.sf\.cglib\.proxy\.MethodProxy\.invoke,^org\.junit\.,^org\.apache\.maven\.surefire\.,^java\.lang\.reflect\.Method\.invoke,^java\.util\.concurrent\.ThreadPoolExecutor\.runWorker,^java\.lang\.Thread\.run"/> | |
| | |
| <conversionRule conversionWord="sEx" converterClass="net.logstash.logback.stacktrace.ShortenedThrowableConverter" /> | |
| | |
|<appender name="FILE" class="ch.qos.logback.core.FileAppender"> | |
| <file>{{LOG_FILE_NAME}}</file> | |
| <append>false</append> | |
| <immediateFlush>true</immediateFlush> | |
| <encoder> | |
| <conversionRule conversionWord="exId" | |
| converterClass="net.logstash.logback.stacktrace.ShortenedThrowableConverter" /> | |
| <pattern> | |
| %-4relative [%thread] %-5level %logger{35} - %msg - %sEx{3,full,full,inlineHash,${STE_EXCLUSIONS}}%n | |
| </pattern> | |
| <!-- computes and adds a 'stack_hash' field on errors --> | |
| <provider class="net.logstash.logback.composite.loggingevent.StackHashJsonProvider"> | |
| <!-- use global property for exclusion patterns --> | |
| <exclusions>${STE_EXCLUSIONS}</exclusions> | |
| </provider> | |
| <!-- enriches the stack trace with unique hash --> | |
| <throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter"> | |
| <!-- compute and inline hash in stack trace --> | |
| <inlineHash>true</inlineHash> | |
| <!-- use global property for exclusion patterns --> | |
| <exclusions>${STE_EXCLUSIONS}</exclusions> | |
| </throwableConverter> | |
| </encoder> | |
| </appender> | |
| | |
| <logger name="Test" level="DEBUG" /> | |
| | |
| <root level="INFO"> | |
| <appender-ref ref="FILE" /> | |
| </root> | |
| | |
|</configuration>""".stripMargin.replaceAll("""\{\{LOG_FILE_NAME\}\}""", logFileName) | |
configureLogBack(config) | |
val logger = LoggerFactory.getLogger("Test") | |
logger.info("started") | |
logger.error("something wrong", new Exception("bad")) | |
logger.error("something wrong", new Exception("bad")) | |
logger.debug("debug message") | |
val logFileContent = scala.io.Source.fromFile(logFileName).getLines.toList.mkString("\n") | |
logFileContent should include regex """(?i)<#[A-F0-9]+>""" | |
} | |
} | |
org.scalatest.tools.Runner.main(Array("-oDF", "-s", classOf[LoggingTesting].getName)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment