IGNITE-6946: Change Ignite Logger configuration on the fly. Fixes #3400. Signed-off-by: Valentin Kulichenko <valentin.kuliche...@gmail.com>
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/ebfab709 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/ebfab709 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/ebfab709 Branch: refs/heads/ignite-7485-2 Commit: ebfab709eafb78dbd920183e647eba442cc45667 Parents: 0088405 Author: Stanislav Lukyanov <stanlukya...@gmail.com> Authored: Wed Jan 31 13:02:45 2018 -0800 Committer: Valentin Kulichenko <valentin.kuliche...@gmail.com> Committed: Wed Jan 31 13:02:45 2018 -0800 ---------------------------------------------------------------------- config/ignite-log4j2.xml | 2 +- .../apache/ignite/logger/log4j/Log4JLogger.java | 100 +++++++++++- modules/log4j/src/test/config/log4j-debug.xml | 50 ++++++ modules/log4j/src/test/config/log4j-info.xml | 50 ++++++ .../logger/log4j/GridLog4jConfigUpdateTest.java | 163 +++++++++++++++++++ .../logger/log4j/GridLog4jWatchDelayTest.java | 49 ++++++ .../ignite/testsuites/IgniteLog4jTestSuite.java | 35 ++-- modules/log4j2/src/test/config/log4j2-debug.xml | 37 +++++ modules/log4j2/src/test/config/log4j2-info.xml | 37 +++++ .../logger/log4j2/Log4j2ConfigUpdateTest.java | 152 +++++++++++++++++ .../testsuites/IgniteLog4j2TestSuite.java | 2 + 11 files changed, 649 insertions(+), 28 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/ebfab709/config/ignite-log4j2.xml ---------------------------------------------------------------------- diff --git a/config/ignite-log4j2.xml b/config/ignite-log4j2.xml index 7154ae8..b55c563 100644 --- a/config/ignite-log4j2.xml +++ b/config/ignite-log4j2.xml @@ -17,7 +17,7 @@ limitations under the License. --> -<Configuration> +<Configuration monitorInterval="60"> <Appenders> <Console name="CONSOLE" target="SYSTEM_OUT"> <PatternLayout pattern="[%d{ISO8601}][%-5p][%t][%c{1}]%notEmpty{[%markerSimpleName]} %m%n"/> http://git-wip-us.apache.org/repos/asf/ignite/blob/ebfab709/modules/log4j/src/main/java/org/apache/ignite/logger/log4j/Log4JLogger.java ---------------------------------------------------------------------- diff --git a/modules/log4j/src/main/java/org/apache/ignite/logger/log4j/Log4JLogger.java b/modules/log4j/src/main/java/org/apache/ignite/logger/log4j/Log4JLogger.java index 02e7b35..ec0a5b3 100644 --- a/modules/log4j/src/main/java/org/apache/ignite/logger/log4j/Log4JLogger.java +++ b/modules/log4j/src/main/java/org/apache/ignite/logger/log4j/Log4JLogger.java @@ -39,8 +39,10 @@ import org.apache.log4j.Category; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.FileAppender; import org.apache.log4j.Level; +import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.apache.log4j.PatternLayout; +import org.apache.log4j.helpers.FileWatchdog; import org.apache.log4j.varia.LevelRangeFilter; import org.apache.log4j.xml.DOMConfigurator; import org.jetbrains.annotations.Nullable; @@ -184,25 +186,48 @@ public class Log4JLogger implements IgniteLogger, LoggerNodeIdAware, Log4jFileAw /** * Creates new logger with given configuration {@code path}. + * Calling this constructor is equivalent to calling {@code Log4JLogger(path, FileWatchdog.DEFAULT_DELAY}. * * @param path Path to log4j configuration XML file. * @throws IgniteCheckedException Thrown in case logger can't be created. */ public Log4JLogger(final String path) throws IgniteCheckedException { + this(path, FileWatchdog.DEFAULT_DELAY); + } + + /** + * Creates new logger with given configuration {@code path}. + * <p> + * If {@code watchDelay} is not zero, created logger will check the configuration file for changes once every + * {@code watchDelay} milliseconds, and update its configuration if the file was changed. + * See {@link DOMConfigurator#configureAndWatch(String, long)} for details. + * + * @param path Path to log4j configuration XML file. + * @param watchDelay delay in milliseconds used to check configuration file for changes. + * @throws IgniteCheckedException Thrown in case logger can't be created. + */ + public Log4JLogger(final String path, long watchDelay) throws IgniteCheckedException { if (path == null) throw new IgniteCheckedException("Configuration XML file for Log4j must be specified."); + if (watchDelay < 0) + throw new IgniteCheckedException("watchDelay can't be negative: " + watchDelay); + this.cfg = path; - final URL cfgUrl = U.resolveIgniteUrl(path); + final File cfgFile = U.resolveIgnitePath(path); - if (cfgUrl == null) + if (cfgFile == null) throw new IgniteCheckedException("Log4j configuration path was not found: " + path); addConsoleAppenderIfNeeded(null, new C1<Boolean, Logger>() { @Override public Logger apply(Boolean init) { - if (init) - DOMConfigurator.configure(cfgUrl); + if (init) { + if (watchDelay > 0) + DOMConfigurator.configureAndWatch(cfgFile.getPath(), watchDelay); + else + DOMConfigurator.configure(cfgFile.getPath()); + } return Logger.getRootLogger(); } @@ -213,23 +238,46 @@ public class Log4JLogger implements IgniteLogger, LoggerNodeIdAware, Log4jFileAw /** * Creates new logger with given configuration {@code cfgFile}. + * Calling this constructor is equivalent to calling {@code Log4JLogger(cfgFile, FileWatchdog.DEFAULT_DELAY}. * * @param cfgFile Log4j configuration XML file. * @throws IgniteCheckedException Thrown in case logger can't be created. */ public Log4JLogger(File cfgFile) throws IgniteCheckedException { + this(cfgFile, FileWatchdog.DEFAULT_DELAY); + } + + /** + * Creates new logger with given configuration {@code cfgFile}. + * <p> + * If {@code watchDelay} is not zero, created logger will check the configuration file for changes once every + * {@code watchDelay} milliseconds, and update its configuration if the file was changed. + * See {@link DOMConfigurator#configureAndWatch(String, long)} for details. + * + * @param cfgFile Log4j configuration XML file. + * @param watchDelay delay in milliseconds used to check configuration file for changes. + * @throws IgniteCheckedException Thrown in case logger can't be created. + */ + public Log4JLogger(final File cfgFile, final long watchDelay) throws IgniteCheckedException { if (cfgFile == null) throw new IgniteCheckedException("Configuration XML file for Log4j must be specified."); if (!cfgFile.exists() || cfgFile.isDirectory()) throw new IgniteCheckedException("Log4j configuration path was not found or is a directory: " + cfgFile); + if (watchDelay < 0) + throw new IgniteCheckedException("watchDelay can't be negative: " + watchDelay); + cfg = cfgFile.getAbsolutePath(); addConsoleAppenderIfNeeded(null, new C1<Boolean, Logger>() { @Override public Logger apply(Boolean init) { - if (init) - DOMConfigurator.configure(cfg); + if (init) { + if (watchDelay > 0) + DOMConfigurator.configureAndWatch(cfgFile.getPath(), watchDelay); + else + DOMConfigurator.configure(cfgFile.getPath()); + } return Logger.getRootLogger(); } @@ -240,20 +288,43 @@ public class Log4JLogger implements IgniteLogger, LoggerNodeIdAware, Log4jFileAw /** * Creates new logger with given configuration {@code cfgUrl}. + * Calling this constructor is equivalent to calling {@code Log4JLogger(cfgUrl, FileWatchdog.DEFAULT_DELAY}. * * @param cfgUrl URL for Log4j configuration XML file. * @throws IgniteCheckedException Thrown in case logger can't be created. */ public Log4JLogger(final URL cfgUrl) throws IgniteCheckedException { + this(cfgUrl, FileWatchdog.DEFAULT_DELAY); + } + + /** + * Creates new logger with given configuration {@code cfgUrl}. + * <p> + * If {@code watchDelay} is not zero, created logger will check the configuration file for changes once every + * {@code watchDelay} milliseconds, and update its configuration if the file was changed. + * See {@link DOMConfigurator#configureAndWatch(String, long)} for details. + * + * @param cfgUrl URL for Log4j configuration XML file. + * @param watchDelay delay in milliseconds used to check configuration file for changes. + * @throws IgniteCheckedException Thrown in case logger can't be created. + */ + public Log4JLogger(final URL cfgUrl, final long watchDelay) throws IgniteCheckedException { if (cfgUrl == null) throw new IgniteCheckedException("Configuration XML file for Log4j must be specified."); + if (watchDelay < 0) + throw new IgniteCheckedException("watchDelay can't be negative: " + watchDelay); + cfg = cfgUrl.getPath(); addConsoleAppenderIfNeeded(null, new C1<Boolean, Logger>() { @Override public Logger apply(Boolean init) { - if (init) - DOMConfigurator.configure(cfgUrl); + if (init) { + if (watchDelay > 0) + DOMConfigurator.configureAndWatch(cfg, watchDelay); + else + DOMConfigurator.configure(cfg); + } return Logger.getRootLogger(); } @@ -552,4 +623,17 @@ public class Log4JLogger implements IgniteLogger, LoggerNodeIdAware, Log4jFileAw } } } + + /** + * Cleans up the logger configuration. Should be used in unit tests only for sequential tests run with + * different configurations + */ + static void cleanup() { + synchronized (mux) { + if (inited) + LogManager.shutdown(); + + inited = false; + } + } } http://git-wip-us.apache.org/repos/asf/ignite/blob/ebfab709/modules/log4j/src/test/config/log4j-debug.xml ---------------------------------------------------------------------- diff --git a/modules/log4j/src/test/config/log4j-debug.xml b/modules/log4j/src/test/config/log4j-debug.xml new file mode 100644 index 0000000..2c36194 --- /dev/null +++ b/modules/log4j/src/test/config/log4j-debug.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" + "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd"> + +<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false"> + <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender"> + <param name="Target" value="System.out"/> + + <param name="Threshold" value="DEBUG"/> + + <layout class="org.apache.log4j.PatternLayout"> + <param name="ConversionPattern" value="[%p] %m%n"/> + </layout> + </appender> + + <appender name="FILE" class="org.apache.log4j.RollingFileAppender"> + <param name="File" value="${IGNITE_HOME}/work/log/GridLog4jConfigUpdateTest.log"/> + <param name="Append" value="true"/> + <param name="MaxFileSize" value="10MB"/> + <param name="MaxBackupIndex" value="10"/> + <layout class="org.apache.log4j.PatternLayout"> + <param name="ConversionPattern" value="[%p] %m%n"/> + </layout> + </appender> + + <root> + <level value="DEBUG"/> + + <appender-ref ref="CONSOLE"/> + <appender-ref ref="FILE"/> + </root> +</log4j:configuration> http://git-wip-us.apache.org/repos/asf/ignite/blob/ebfab709/modules/log4j/src/test/config/log4j-info.xml ---------------------------------------------------------------------- diff --git a/modules/log4j/src/test/config/log4j-info.xml b/modules/log4j/src/test/config/log4j-info.xml new file mode 100644 index 0000000..6e6d145 --- /dev/null +++ b/modules/log4j/src/test/config/log4j-info.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" + "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd"> + +<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false"> + <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender"> + <param name="Target" value="System.out"/> + + <param name="Threshold" value="DEBUG"/> + + <layout class="org.apache.log4j.PatternLayout"> + <param name="ConversionPattern" value="[%p] %m%n"/> + </layout> + </appender> + + <appender name="FILE" class="org.apache.log4j.RollingFileAppender"> + <param name="File" value="${IGNITE_HOME}/work/log/GridLog4jConfigUpdateTest.log"/> + <param name="Append" value="true"/> + <param name="MaxFileSize" value="10MB"/> + <param name="MaxBackupIndex" value="10"/> + <layout class="org.apache.log4j.PatternLayout"> + <param name="ConversionPattern" value="[%p] %m%n"/> + </layout> + </appender> + + <root> + <level value="INFO"/> + + <appender-ref ref="CONSOLE"/> + <appender-ref ref="FILE"/> + </root> +</log4j:configuration> http://git-wip-us.apache.org/repos/asf/ignite/blob/ebfab709/modules/log4j/src/test/java/org/apache/ignite/logger/log4j/GridLog4jConfigUpdateTest.java ---------------------------------------------------------------------- diff --git a/modules/log4j/src/test/java/org/apache/ignite/logger/log4j/GridLog4jConfigUpdateTest.java b/modules/log4j/src/test/java/org/apache/ignite/logger/log4j/GridLog4jConfigUpdateTest.java new file mode 100644 index 0000000..c0d9591 --- /dev/null +++ b/modules/log4j/src/test/java/org/apache/ignite/logger/log4j/GridLog4jConfigUpdateTest.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.logger.log4j; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.Date; +import junit.framework.TestCase; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.log4j.helpers.FileWatchdog; + +/** + * Checking that Log4j configuration is updated when its source file is changed. + */ +public class GridLog4jConfigUpdateTest extends TestCase { + /** Path to log4j configuration with INFO enabled. */ + private static final String LOG_CONFIG_INFO = "modules/log4j/src/test/config/log4j-info.xml"; + + /** Path to log4j configuration with DEBUG enabled. */ + private static final String LOG_CONFIG_DEBUG = "modules/log4j/src/test/config/log4j-debug.xml"; + + /** Path to log4j configuration with DEBUG enabled. */ + private static final String LOG_CONFIG_MAIN = "work/log/log4j-GridLog4jConfigUpdateTest.xml"; + + /** Path to log file. */ + private static final String LOG_DEST = "work/log/GridLog4jConfigUpdateTest.log"; + + /** + * Time to wait before updating the configuration file. + * Should be large enough to be recorded on different file systems. + */ + private static final int DELAY_BEFORE_MODIFICATION = 5000; + + /** + * Check that changing log4j config file causes the logger configuration to be updated. + * String-accepting constructor is used. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + public void testConfigChangeStringConstructor() throws Exception { + checkConfigUpdate(new Log4JLoggerSupplier() { + @Override public Log4JLogger get(File cfgFile) throws Exception { + return new Log4JLogger(cfgFile.getPath(), 10); + } + }, 5000); // use larger delay to avoid relying on exact timing + } + + /** + * Check that changing log4j config file causes the logger configuration to be updated. + * String-accepting constructor is used. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + public void testConfigChangeStringConstructorDefaultDelay() throws Exception { + checkConfigUpdate(new Log4JLoggerSupplier() { + @Override public Log4JLogger get(File cfgFile) throws Exception { + return new Log4JLogger(cfgFile.getPath()); + } + }, FileWatchdog.DEFAULT_DELAY + 5000); // use larger delay to avoid relying on exact timing + } + + /** + * Check that changing log4j config file causes the logger configuration to be updated. + * File-accepting constructor is used. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + public void testConfigChangeFileConstructor() throws Exception { + checkConfigUpdate(new Log4JLoggerSupplier() { + @Override public Log4JLogger get(File cfgFile) throws Exception { + return new Log4JLogger(cfgFile, 10); + } + }, 5000); // use larger delay to avoid relying on exact timing + } + + /** + * Check that changing log4j config file causes the logger configuration to be updated. + * File-accepting constructor is used. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + public void testConfigChangeUrlConstructor() throws Exception { + checkConfigUpdate(new Log4JLoggerSupplier() { + @Override public Log4JLogger get(File cfgFile) throws Exception { + return new Log4JLogger(cfgFile.toURI().toURL(), 10); + } + }, 5000); // use larger delay to avoid relying on exact timing + } + + /** + * Checks that Log4JLogger is updated after configuration file is changed. + * + * @param logSupplier Function returning a logger instance to be tested. + * @param delay time to wait after configuration file has been changed. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + private void checkConfigUpdate(Log4JLoggerSupplier logSupplier, long delay) throws Exception { + Log4JLogger.cleanup(); + + File infoCfgFile = new File(U.getIgniteHome(), LOG_CONFIG_INFO); + File debugCfgFile = new File(U.getIgniteHome(), LOG_CONFIG_DEBUG); + File mainCfgFile = new File(U.getIgniteHome(), LOG_CONFIG_MAIN); + File logFile = new File(U.getIgniteHome(), LOG_DEST); + + System.out.println("INFO config: " + infoCfgFile); + System.out.println("DEBUG config: " + debugCfgFile); + System.out.println("Main config: " + mainCfgFile); + System.out.println("Log file: " + logFile); + + if (logFile.delete()) + System.out.println("Old log file was deleted."); + + // Install INFO config. + mainCfgFile.getParentFile().mkdirs(); + Files.copy(infoCfgFile.toPath(), mainCfgFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + mainCfgFile.setLastModified(new Date().getTime()); + + Log4JLogger log = logSupplier.get(mainCfgFile); + + log.info("Accepted info"); + log.debug("Ignored debug"); + + // Wait a bit before copying the file so that new modification date is guaranteed to be different. + Thread.sleep(DELAY_BEFORE_MODIFICATION); + + // Replace current config with DEBUG config. + Files.copy(debugCfgFile.toPath(), mainCfgFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + mainCfgFile.setLastModified(new Date().getTime()); + + // Wait for the update to happen. + Thread.sleep(delay); + + log.debug("Accepted debug"); + + String logContent = U.readFileToString(logFile.getPath(), "UTF-8"); + + assertTrue(logContent.contains("[INFO] Accepted info")); + assertFalse(logContent.contains("[DEBUG] Ignored debug")); + assertTrue(logContent.contains("[DEBUG] Accepted debug")); + + mainCfgFile.delete(); + Log4JLogger.cleanup(); + } + + /** Creates Log4JLogger instance for testing. */ + private interface Log4JLoggerSupplier { + /** Creates Log4JLogger instance for testing. */ + Log4JLogger get(File cfgFile) throws Exception; + } + +} http://git-wip-us.apache.org/repos/asf/ignite/blob/ebfab709/modules/log4j/src/test/java/org/apache/ignite/logger/log4j/GridLog4jWatchDelayTest.java ---------------------------------------------------------------------- diff --git a/modules/log4j/src/test/java/org/apache/ignite/logger/log4j/GridLog4jWatchDelayTest.java b/modules/log4j/src/test/java/org/apache/ignite/logger/log4j/GridLog4jWatchDelayTest.java new file mode 100644 index 0000000..f9306fe --- /dev/null +++ b/modules/log4j/src/test/java/org/apache/ignite/logger/log4j/GridLog4jWatchDelayTest.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.logger.log4j; + +import java.io.File; +import java.net.MalformedURLException; +import org.apache.ignite.IgniteCheckedException; +import org.junit.Test; + +/** + * Checking Log4JLogger constructors accepting watchDelay parameter. + */ +public class GridLog4jWatchDelayTest { + /** Path to log4j configuration file. */ + private static final String LOG_CONFIG = "modules/log4j/src/test/config/log4j-info.xml"; + + /** Check negative watchDelay in String constructor. */ + @Test(expected = IgniteCheckedException.class) + public void testNegativeWatchDelayString() throws IgniteCheckedException { + new Log4JLogger(LOG_CONFIG, -1); + } + + /** Check negative watchDelay in String constructor. */ + @Test(expected = IgniteCheckedException.class) + public void testNegativeWatchDelayFile() throws IgniteCheckedException { + new Log4JLogger(new File(LOG_CONFIG), -1); + } + + /** Check negative watchDelay in String constructor. */ + @Test(expected = IgniteCheckedException.class) + public void testNegativeWatchDelayUrl() throws IgniteCheckedException, MalformedURLException { + new Log4JLogger(new File(LOG_CONFIG).toURI().toURL(), -1); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/ebfab709/modules/log4j/src/test/java/org/apache/ignite/testsuites/IgniteLog4jTestSuite.java ---------------------------------------------------------------------- diff --git a/modules/log4j/src/test/java/org/apache/ignite/testsuites/IgniteLog4jTestSuite.java b/modules/log4j/src/test/java/org/apache/ignite/testsuites/IgniteLog4jTestSuite.java index 2c0af79..c7a099c 100644 --- a/modules/log4j/src/test/java/org/apache/ignite/testsuites/IgniteLog4jTestSuite.java +++ b/modules/log4j/src/test/java/org/apache/ignite/testsuites/IgniteLog4jTestSuite.java @@ -17,32 +17,29 @@ package org.apache.ignite.testsuites; -import junit.framework.TestSuite; +import org.apache.ignite.logger.log4j.GridLog4jConfigUpdateTest; import org.apache.ignite.logger.log4j.GridLog4jCorrectFileNameTest; import org.apache.ignite.logger.log4j.GridLog4jInitializedTest; import org.apache.ignite.logger.log4j.GridLog4jLoggingFileTest; import org.apache.ignite.logger.log4j.GridLog4jLoggingPathTest; import org.apache.ignite.logger.log4j.GridLog4jLoggingUrlTest; import org.apache.ignite.logger.log4j.GridLog4jNotInitializedTest; +import org.apache.ignite.logger.log4j.GridLog4jWatchDelayTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; /** * Log4j logging tests. */ -public class IgniteLog4jTestSuite extends TestSuite { - /** - * @return Test suite. - * @throws Exception Thrown in case of the failure. - */ - public static TestSuite suite() throws Exception { - TestSuite suite = new TestSuite("Log4j Logging Test Suite"); - - suite.addTest(new TestSuite(GridLog4jInitializedTest.class)); - suite.addTest(new TestSuite(GridLog4jNotInitializedTest.class)); - suite.addTest(new TestSuite(GridLog4jCorrectFileNameTest.class)); - suite.addTest(new TestSuite(GridLog4jLoggingFileTest.class)); - suite.addTest(new TestSuite(GridLog4jLoggingPathTest.class)); - suite.addTest(new TestSuite(GridLog4jLoggingUrlTest.class)); - - return suite; - } -} \ No newline at end of file +@RunWith(Suite.class) +@Suite.SuiteClasses({ + GridLog4jInitializedTest.class, + GridLog4jNotInitializedTest.class, + GridLog4jCorrectFileNameTest.class, + GridLog4jLoggingFileTest.class, + GridLog4jLoggingPathTest.class, + GridLog4jLoggingUrlTest.class, + GridLog4jConfigUpdateTest.class, + GridLog4jWatchDelayTest.class, +}) +public class IgniteLog4jTestSuite { } http://git-wip-us.apache.org/repos/asf/ignite/blob/ebfab709/modules/log4j2/src/test/config/log4j2-debug.xml ---------------------------------------------------------------------- diff --git a/modules/log4j2/src/test/config/log4j2-debug.xml b/modules/log4j2/src/test/config/log4j2-debug.xml new file mode 100644 index 0000000..4c96491 --- /dev/null +++ b/modules/log4j2/src/test/config/log4j2-debug.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<Configuration monitorInterval="5"> + <Appenders> + <Console name="CONSOLE" target="SYSTEM_OUT"> + <PatternLayout pattern="[%p] %m%n"/> + </Console> + + <File name="FILE" fileName="${sys:IGNITE_HOME}/work/log/Log4j2ConfigUpdateTest.log" append="true"> + <PatternLayout pattern="[%p] %m%n"/> + </File> + </Appenders> + + <Loggers> + <Root level="DEBUG"> + <AppenderRef ref="CONSOLE"/> + <AppenderRef ref="FILE"/> + </Root> + </Loggers> +</Configuration> http://git-wip-us.apache.org/repos/asf/ignite/blob/ebfab709/modules/log4j2/src/test/config/log4j2-info.xml ---------------------------------------------------------------------- diff --git a/modules/log4j2/src/test/config/log4j2-info.xml b/modules/log4j2/src/test/config/log4j2-info.xml new file mode 100644 index 0000000..1036e45 --- /dev/null +++ b/modules/log4j2/src/test/config/log4j2-info.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<Configuration monitorInterval="5"> + <Appenders> + <Console name="CONSOLE" target="SYSTEM_OUT"> + <PatternLayout pattern="[%p] %m%n"/> + </Console> + + <File name="FILE" fileName="${sys:IGNITE_HOME}/work/log/Log4j2ConfigUpdateTest.log" append="true"> + <PatternLayout pattern="[%p] %m%n"/> + </File> + </Appenders> + + <Loggers> + <Root level="INFO"> + <AppenderRef ref="CONSOLE"/> + <AppenderRef ref="FILE"/> + </Root> + </Loggers> +</Configuration> http://git-wip-us.apache.org/repos/asf/ignite/blob/ebfab709/modules/log4j2/src/test/java/org/apache/ignite/logger/log4j2/Log4j2ConfigUpdateTest.java ---------------------------------------------------------------------- diff --git a/modules/log4j2/src/test/java/org/apache/ignite/logger/log4j2/Log4j2ConfigUpdateTest.java b/modules/log4j2/src/test/java/org/apache/ignite/logger/log4j2/Log4j2ConfigUpdateTest.java new file mode 100644 index 0000000..d9f7517 --- /dev/null +++ b/modules/log4j2/src/test/java/org/apache/ignite/logger/log4j2/Log4j2ConfigUpdateTest.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.logger.log4j2; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.Date; +import junit.framework.TestCase; +import org.apache.ignite.internal.util.typedef.internal.U; + +/** + * Checking that Log4j2 configuration is updated when its source file is changed. + */ +public class Log4j2ConfigUpdateTest extends TestCase { + /** Path to log4j2 configuration with INFO enabled. */ + private static final String LOG_CONFIG_INFO = "modules/log4j2/src/test/config/log4j2-info.xml"; + + /** Path to log4j2 configuration with DEBUG enabled. */ + private static final String LOG_CONFIG_DEBUG = "modules/log4j2/src/test/config/log4j2-debug.xml"; + + /** Path to log4j2 configuration with DEBUG enabled. */ + private static final String LOG_CONFIG_MAIN = "work/log/log4j2-Log4j2ConfigUpdateTest.xml"; + + /** Path to log file. */ + private static final String LOG_DEST = "work/log/Log4j2ConfigUpdateTest.log"; + + /** + * Time to wait before logger configuration changes. + * This value is made greater than the `monitorInterval` in the config files to avoid relying on exact timing. + */ + private static final long UPDATE_DELAY = 10000; + + /** + * Time to wait before updating the configuration file. + * Should be large enough to be recorded on different file systems. + */ + private static final int DELAY_BEFORE_MODIFICATION = 5000; + + /** + * Check that changing log4j2 config file causes the logger configuration to be updated. + * String-accepting constructor is used. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + public void testConfigChangeStringConstructor() throws Exception { + checkConfigUpdate(new Log4J2LoggerSupplier() { + @Override public Log4J2Logger get(File cfgFile) throws Exception { + return new Log4J2Logger(cfgFile.getPath()); + } + }); // use larger delay to avoid relying on exact timing + } + + /** + * Check that changing log4j config file causes the logger configuration to be updated. + * File-accepting constructor is used. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + public void testConfigChangeFileConstructor() throws Exception { + checkConfigUpdate(new Log4J2LoggerSupplier() { + @Override public Log4J2Logger get(File cfgFile) throws Exception { + return new Log4J2Logger(cfgFile); + } + }); // use larger delay to avoid relying on exact timing + } + + /** + * Check that changing log4j config file causes the logger configuration to be updated. + * File-accepting constructor is used. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + public void testConfigChangeUrlConstructor() throws Exception { + checkConfigUpdate(new Log4J2LoggerSupplier() { + @Override public Log4J2Logger get(File cfgFile) throws Exception { + return new Log4J2Logger(cfgFile.toURI().toURL()); + } + }); // use larger delay to avoid relying on exact timing + } + + /** + * Checks that Log4JLogger is updated after configuration file is changed. + * @param logSupplier Function returning a logger instance to be tested. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + private void checkConfigUpdate(Log4J2LoggerSupplier logSupplier) throws Exception { + Log4J2Logger.cleanup(); + + File infoCfgFile = new File(U.getIgniteHome(), LOG_CONFIG_INFO); + File debugCfgFile = new File(U.getIgniteHome(), LOG_CONFIG_DEBUG); + File mainCfgFile = new File(U.getIgniteHome(), LOG_CONFIG_MAIN); + File logFile = new File(U.getIgniteHome(), LOG_DEST); + + System.out.println("INFO config: " + infoCfgFile); + System.out.println("DEBUG config: " + debugCfgFile); + System.out.println("Main config: " + mainCfgFile); + System.out.println("Log file: " + logFile); + + if (logFile.delete()) + System.out.println("Old log file was deleted."); + + // Install INFO config. + mainCfgFile.getParentFile().mkdirs(); + Files.copy(infoCfgFile.toPath(), mainCfgFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + mainCfgFile.setLastModified(new Date().getTime()); + + Log4J2Logger log = logSupplier.get(mainCfgFile); + + log.info("Accepted info"); + log.debug("Ignored debug"); + + // Wait a bit before copying the file so that new modification date is guaranteed to be different. + Thread.sleep(DELAY_BEFORE_MODIFICATION); + + // Replace current config with DEBUG config. + Files.copy(debugCfgFile.toPath(), mainCfgFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + mainCfgFile.setLastModified(new Date().getTime()); + + // Wait for the update to happen. + Thread.sleep(UPDATE_DELAY); + + log.debug("Accepted debug"); + + String logContent = U.readFileToString(logFile.getPath(), "UTF-8"); + + assertTrue(logContent.contains("[INFO] Accepted info")); + assertFalse(logContent.contains("[DEBUG] Ignored debug")); + assertTrue(logContent.contains("[DEBUG] Accepted debug")); + + mainCfgFile.delete(); + Log4J2Logger.cleanup(); + } + + /** Creates Log4J2Logger instance for testing. */ + private interface Log4J2LoggerSupplier { + /** Creates Log4J2Logger instance for testing. */ + Log4J2Logger get(File cfgFile) throws Exception; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/ebfab709/modules/log4j2/src/test/java/org/apache/ignite/testsuites/IgniteLog4j2TestSuite.java ---------------------------------------------------------------------- diff --git a/modules/log4j2/src/test/java/org/apache/ignite/testsuites/IgniteLog4j2TestSuite.java b/modules/log4j2/src/test/java/org/apache/ignite/testsuites/IgniteLog4j2TestSuite.java index 66270b2..0b8cefa 100644 --- a/modules/log4j2/src/test/java/org/apache/ignite/testsuites/IgniteLog4j2TestSuite.java +++ b/modules/log4j2/src/test/java/org/apache/ignite/testsuites/IgniteLog4j2TestSuite.java @@ -18,6 +18,7 @@ package org.apache.ignite.testsuites; import junit.framework.TestSuite; +import org.apache.ignite.logger.log4j2.Log4j2ConfigUpdateTest; import org.apache.ignite.logger.log4j2.Log4j2LoggerMarkerTest; import org.apache.ignite.logger.log4j2.Log4j2LoggerSelfTest; import org.apache.ignite.logger.log4j2.Log4j2LoggerVerboseModeSelfTest; @@ -36,6 +37,7 @@ public class IgniteLog4j2TestSuite extends TestSuite { suite.addTestSuite(Log4j2LoggerSelfTest.class); suite.addTestSuite(Log4j2LoggerVerboseModeSelfTest.class); suite.addTestSuite(Log4j2LoggerMarkerTest.class); + suite.addTestSuite(Log4j2ConfigUpdateTest.class); return suite; }