From 77cc634b5198d8520cefe894d722e7dca3212fd8 Mon Sep 17 00:00:00 2001 From: "Taro L. Saito" Date: Thu, 6 Jun 2024 19:09:27 -0700 Subject: [PATCH] airspec (feature): Support -L(wildcard pattern)=(log level) (#3559) --- .../src/main/scala/wvlet/log/Logger.scala | 22 +++++++++++++++++++ .../test/scala/wvlet/log/LogLevelTest.scala | 8 +++++++ .../airspec/runner/AirSpecTaskRunner.scala | 15 ++++++------- docs/airspec.md | 5 +++++ 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/airframe-log/src/main/scala/wvlet/log/Logger.scala b/airframe-log/src/main/scala/wvlet/log/Logger.scala index 1a2c858f2..c1a5dab7d 100644 --- a/airframe-log/src/main/scala/wvlet/log/Logger.scala +++ b/airframe-log/src/main/scala/wvlet/log/Logger.scala @@ -296,6 +296,28 @@ object Logger { } } + /** + * Reset log level set by the given pattern + * @param pattern + */ + def resetLogLevel(pattern: String): Unit = { + if (pattern.contains("*")) { + val regexPattern = pattern.replaceAll("\\.", "\\\\.").replaceAll("\\*", ".*").r + val regexPatternStr = regexPattern.regex + synchronized { + logLevelPatterns = logLevelPatterns.filter(x => x._1.regex != regexPatternStr) + } + // Reset the log level of already created loggers matching the removed pattern + loggerCache.values.foreach { l => + if (regexPattern.findFirstIn(l.getName).isDefined) { + l.resetLogLevel + } + } + } else { + Logger(pattern).resetLogLevel + } + } + def setDefaultFormatter(formatter: LogFormatter): Unit = { synchronized { rootLogger.resetHandler(new ConsoleLogHandler(formatter)) diff --git a/airframe-log/src/test/scala/wvlet/log/LogLevelTest.scala b/airframe-log/src/test/scala/wvlet/log/LogLevelTest.scala index 2a1c25447..2477287fe 100644 --- a/airframe-log/src/test/scala/wvlet/log/LogLevelTest.scala +++ b/airframe-log/src/test/scala/wvlet/log/LogLevelTest.scala @@ -39,4 +39,12 @@ class LogLevelTest extends Spec { Logger.setLogLevel("example.app", LogLevel.WARN) l.getLogLevel shouldBe LogLevel.WARN } + + test("Reset log level set by a pattern") { + val l = Logger.setLogLevel("example.*", LogLevel.WARN) + Logger("example.app").getLogLevel shouldBe LogLevel.WARN + + Logger.resetLogLevel("example.*") + Logger("example.app").getLogLevel shouldBe LogLevel.INFO + } } diff --git a/airspec/src/main/scala/wvlet/airspec/runner/AirSpecTaskRunner.scala b/airspec/src/main/scala/wvlet/airspec/runner/AirSpecTaskRunner.scala index 92df5135c..251448815 100644 --- a/airspec/src/main/scala/wvlet/airspec/runner/AirSpecTaskRunner.scala +++ b/airspec/src/main/scala/wvlet/airspec/runner/AirSpecTaskRunner.scala @@ -87,9 +87,8 @@ private[airspec] class AirSpecTaskRunner( * Run this AirSpec task */ def runTask: Future[Unit] = { - val startTimeNanos = System.nanoTime() - val prevLogLevel = Map.newBuilder[String, LogLevel] - prevLogLevel += testClassName -> Logger(testClassName).getLogLevel + val startTimeNanos = System.nanoTime() + var loggerNamePatterns: List[String] = List(testClassName) Future .apply { @@ -97,9 +96,9 @@ private[airspec] class AirSpecTaskRunner( Logger(testClassName).setLogLevel(config.defaultLogLevel) // Set log level for other classes - config.additionalLogLevels.foreach { case (pkg, level) => - prevLogLevel += pkg -> Logger(pkg).getLogLevel - Logger(pkg).setLogLevel(level) + config.additionalLogLevels.foreach { case (pattern, level) => + loggerNamePatterns = pattern :: loggerNamePatterns + Logger.setLogLevel(pattern, level) } // Start a background log level scanner thread. If a thread is already running, reuse it. @@ -121,8 +120,8 @@ private[airspec] class AirSpecTaskRunner( compat.stopLogScanner // Reset log levels - prevLogLevel.result().foreach { case (pkg, level) => - Logger(pkg).setLogLevel(level) + loggerNamePatterns.foreach { pattern => + Logger.resetLogLevel(pattern) } ret } diff --git a/docs/airspec.md b/docs/airspec.md index a74916a1f..5e50e7d2a 100644 --- a/docs/airspec.md +++ b/docs/airspec.md @@ -264,6 +264,11 @@ To change the log level only for a specific package or a class, use `-L(package ``` You can use multiple `-L` options to set different log levels for multiple packages. +You can also use wildcard `*` with `-L` option for the ease of setting log levels for specific classes: +```scala +> testOnly -- -Lmyapp.*=debug +> testOnly -- -L*.MyClass=debug +``` ### Configure Log Levels in log-test.properties