Log4j 設定ファイルの動的リロード機能の実装

Apache Log4j では、設定ファイル(properties 形式または XML 形式)を変更した際に、 application を再起動せずに即座に反映させるための仕組みが提供されています。

1. properties 形式の設定と動的リロード

LogLevel や出力先の変更を反映させるために、`PropertyConfigurator.configureAndWatch()` メソッドを使用します。このメソッドは指定された設定ファイルを監視し、指定した間隔(ミリ秒単位)ごとに変更を検知して自動的に再読み込みを行います。

public class ConfigLoader {
    private static boolean autoReloadEnabled = true;

    public static void initialize() {
        String resourcePath = ConfigLoader.class
                .getClassLoader()
                .getResource("log4j.properties")
                .getPath();
        
        System.out.println("Loading log4j configuration from: " + resourcePath);
        PropertyConfigurator.configureAndWatch(resourcePath, 2000); // 2秒ごとに監視
    }

    public static void enableAutoReload(boolean flag) {
        autoReloadEnabled = flag;
    }

    public static void triggerReload() {
        if (autoReloadEnabled) {
            initialize();
        }
    }
}

ログ出力ラッパークラスの実装例:

public class ApplicationLogger {
    private static final Log logger = LogFactory.getLog(ApplicationLogger.class);

    static {
        ConfigLoader.initialize();
    }

    private ApplicationLogger() {}

    public static ApplicationLogger getInstance() {
        return new ApplicationLogger();
    }

    public void logTrace(String msg) { if (logger.isTraceEnabled()) logger.trace(msg); }
    public void logDebug(String msg) { if (logger.isDebugEnabled()) logger.debug(msg); }
    public void logInfo(String msg) { if (logger.isInfoEnabled()) logger.info(msg); }
    public void logWarn(String msg) { if (logger.isWarnEnabled()) logger.warn(msg); }
    public void logError(String msg) { if (logger.isErrorEnabled()) logger.error(msg); }
    public void logFatal(String msg) { if (logger.isFatalEnabled()) logger.fatal(msg); }
}

テストコード:

public class LogTestRunner {
    private static final ApplicationLogger appLogger = ApplicationLogger.getInstance();

    public static void main(String[] args) {
        int count = 0;
        while (count < 1000) {
            appLogger.logInfo("Routine info message " + count);
            appLogger.logDebug("Debug trace: iteration " + count);
            appLogger.logError("Expected error condition at " + count);

            System.out.println("[Main Loop] Iteration " + count++);
            try { Thread.sleep(2000); } 
            catch (InterruptedException e) { e.printStackTrace(); }
        }
    }
}

2. XML 形式の設定と明示的リロード

XML 形式では更に細かい制御が可能で、`DOMConfigurator` を用いてプログラム上で明示的に設定を再読み込みできます。

<?xml version="1.0" encoding="UTF-8"?>
<log4j:configuration>
    <appender name="Console" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5p] %m%n"/>
        </layout>
    </appender>

    <appender name="DailyRollingFile" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="File" value="/var/log/app/app.log"/>
        <param name="DatePattern" value="'.'yyyy-MM-dd-HH-mm'.log'"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d [%t] %-5p %c{1} - %m%n"/>
        </layout>
    </appender>

    <root>
        <level value="INFO"/>
        <appender-ref ref="Console"/>
        <appender-ref ref="DailyRollingFile"/>
    </root>
</log4j:configuration>

加载与手動.Refresh(XML専用)の例:

public class XmlConfigReloader {
    private static final Logger log = Logger.getLogger(XmlConfigReloader.class);

    static {
        // 初回読み込み
        String configPath = System.getProperty("user.dir") + "/config/log4j.xml";
        DOMConfigurator.configure(configPath);
    }

    // 手動で再読み込みするメソッド
    public void reloadConfiguration(String configFilePath) {
        DOMConfigurator.configure(configFilePath);
        log.info("Log4j XML config has been reloaded from: " + configFilePath);
    }

    public static void startMonitoringLoop() {
        XmlConfigReloader reloader = new XmlConfigReloader();
        String path = "config/log4j.xml";

        RuntimeExceptionLoop: while (true) {
            reloader.reloadConfiguration(path);
            log.info("Running after reload...");
            
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                break RuntimeExceptionLoop;
            }
        }
    }
}

※ 注意点として、Reloader ループ中は無限ループのため、実際のアプリケーションではスレッドプールや Spring の Lifecycle コントロールなど、適切なタイミングで制御する必要があります。

タグ: Log4j properties XML dynamic-reload domconfigurator

6月22日 21:27 投稿