差異處
這裏顯示兩個版本的差異處。
java:log4j2:changeloglevelatruntime [2022/02/04 22:01] tony |
java:log4j2:changeloglevelatruntime [2023/06/25 09:48] |
||
---|---|---|---|
行 1: | 行 1: | ||
- | {{tag>log4j2}} | ||
- | ====== Log4j2 - Change log level at runtime ====== | ||
- | ===== Problem ===== | ||
- | 為了能夠在系統執行期間蒐集發生問題的資訊,有時我們會在執行功能之前,去動態調整log level,儘可能最大化地去蒐集必要資訊。在Log4j 1.x時,我們採用了以下寫法去做到動態切換log level: | ||
- | <code java> | ||
- | Logger logger = LogManager.getLogger(packName); | ||
- | logger.setLevel(Level.TRACE); | ||
- | </code> | ||
- | 本篇文章主要分享在Log4j2可行的做法。範例程式可參考: [[https://github.com/frank007love/Log4j2Practice/tree/master/src/test/java/org/tonylin/practice/log4j2/example1|link]]。 | ||
- | ===== How to? ===== | ||
- | ==== SUT ==== | ||
- | <code java> | ||
- | public class TestLogger { | ||
- | private static final Logger LOGGER = LoggerFactory.getLogger(TestLogger.class); | ||
- | | ||
- | public static void debug(String message) { | ||
- | LOGGER.debug(message); | ||
- | } | ||
- | | ||
- | public static void error(String message) { | ||
- | LOGGER.error(message); | ||
- | } | ||
- | } | ||
- | </code> | ||
- | ==== Configuration properties ==== | ||
- | 預設啟用了ConsoleAppender,而SUT的level是設為error;另外appender.console.follow設為true,讓Log4j可以反應system.out或system.err的變更,這主要和我們測試時會去攔截console有關: | ||
- | <code> | ||
- | rootLogger.level = info | ||
- | rootLogger.appenderRef.stdout.ref = STDOUT | ||
- | status = error | ||
- | dest = err | ||
- | name = PropertiesConfig | ||
- | |||
- | filter.threshold.type = ThresholdFilter | ||
- | filter.threshold.level = debug | ||
- | |||
- | appender.console.type = Console | ||
- | appender.console.name = STDOUT | ||
- | appender.console.follow = true | ||
- | appender.console.layout.type = PatternLayout | ||
- | appender.console.layout.pattern = %d %p %C{1.} [%t] %m%n | ||
- | appender.console.filter.threshold.type = ThresholdFilter | ||
- | appender.console.filter.threshold.level = trace | ||
- | |||
- | logger.rolling.name = org.tonylin.practice.log4j2.example1 | ||
- | logger.rolling.level = error | ||
- | logger.rolling.additivity = false | ||
- | logger.rolling.appenderRef.rolling.ref = STDOUT | ||
- | </code> | ||
- | ==== Unit Test ==== | ||
- | 測試程式我使用了[[https://github.com/Hakky54/console-captor|console-captor]] library讓我可以很容易去捕捉console內容;測試後我會透過Configurator.reconfigure去重置設置內容: | ||
- | <code java> | ||
- | private ConsoleCaptor captor; | ||
- | |||
- | @Before | ||
- | public void setup() { | ||
- | captor = new ConsoleCaptor(); | ||
- | Configurator.reconfigure(); | ||
- | } | ||
- | |||
- | @After | ||
- | public void teardown() { | ||
- | captor.close(); | ||
- | } | ||
- | </code> | ||
- | 首先讓我們先測試debug level不會有console,而error level會有console且內容符合預期: | ||
- | <code java> | ||
- | @Test | ||
- | public void Should_NotSystemOutToConsole_When_LogDebugWithDefaultConfiguration() { | ||
- | TestLogger.debug("test debug"); | ||
- | assertEquals(0, captor.getStandardOutput().size()); | ||
- | } | ||
- | |||
- | @Test | ||
- | public void Should_SystemOutToConsole_When_LogErrorWithDefaultConfiguration() { | ||
- | TestLogger.error("test error"); | ||
- | assertEquals(1, captor.getStandardOutput().size()); | ||
- | assertTrue(captor.getStandardOutput().get(0).contains("test error")); | ||
- | } | ||
- | </code> | ||
- | 接著就是本篇主角了,第一個方法是直接用Configurator去設定,雖然簡單,但這不是一個[[https://logging.apache.org/log4j/2.x/faq.html#reconfig_level_from_code|public的API]],不建議在production code使用: | ||
- | <code java> | ||
- | @Test | ||
- | public void Should_SystemOutToConsole_When_LogDebugAfterChangingLogLevelWithConfigurator() { | ||
- | Configurator.setLevel(TestLogger.class.getName(), Level.DEBUG); | ||
- | | ||
- | TestLogger.debug("test debug"); | ||
- | assertEquals(1, captor.getStandardOutput().size()); | ||
- | assertTrue(captor.getStandardOutput().get(0).contains("test debug")); | ||
- | } | ||
- | </code> | ||
- | 另外一個方法參考自[[https://stackoverflow.com/questions/23434252/programmatically-change-log-level-in-log4j2|這裡]],其實就是Configurator實作的方法(Configurator像是一個Utility的Class): | ||
- | <code java> | ||
- | @Test | ||
- | public void Should_SystemOutToConsole_When_LogDebugAfterChangingLogLevel() { | ||
- | LoggerContext ctx = (LoggerContext) LogManager.getContext(false); | ||
- | Configuration config = ctx.getConfiguration(); | ||
- | LoggerConfig loggerConfig = config.getLoggerConfig(TestLogger.class.getName()); | ||
- | loggerConfig.setLevel(Level.DEBUG); | ||
- | ctx.updateLoggers(); | ||
- | | ||
- | TestLogger.debug("test debug"); | ||
- | assertEquals(1, captor.getStandardOutput().size()); | ||
- | assertTrue(captor.getStandardOutput().get(0).contains("test debug")); | ||
- | } | ||
- | </code> | ||
- | 註: LogManager.getContext(true)的真正作用情境還不曉得為何,有弄清楚後再分享。 | ||
- | ===== Reference ===== | ||
- | * [[https://stackoverflow.com/questions/23434252/programmatically-change-log-level-in-log4j2|Programmatically change log level in Log4j2]] | ||
- | |||
- | ===== ===== | ||
- | ---- | ||
- | \\ | ||
- | ~~DISQUS~~ |