首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 程式動態修改 Log4j 輸出等級實作

     編輯:Java 綜合教學

應用場景

  • 生產環境問題排查,需臨時調整特定 Appender 日誌等級
  • 避免重啟應用、修改配置檔案
  • 管理後台整合動態日誌控制功能

核心實作程式碼

import org.apache.log4j.Appender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.varia.LevelRangeFilter;

public class LogLevelManager {
    
    /**
     * 動態設定指定 Appender 的最小日誌等級
     * @param appenderName Appender 名稱 (對應 log4j.xml/appender name)
     * @param levelStr 等級字串: DEBUG, INFO, WARN, ERROR, FATAL, OFF
     */
    public static void setLogLevel(String appenderName, String levelStr) {
        if (levelStr == null || appenderName == null) return;
        
        // 1. 獲取根 Logger 的指定 Appender
        Appender appender = Logger.getRootLogger().getAppender(appenderName);
        if (appender == null) {
            System.err.println("Appender not found: " + appenderName);
            return;
        }
        
        // 2. 解析等級
        Level level = Level.toLevel(levelStr);
        if (level == null) {
            System.err.println("Invalid level: " + levelStr);
            return;
        }
        
        // 3. 遍歷 Filter 鏈,尋找 LevelRangeFilter
        org.apache.log4j.spi.Filter filter = appender.getFilter();
        while (filter != null) {
            if (filter instanceof LevelRangeFilter) {
                LevelRangeFilter lrf = (LevelRangeFilter) filter;
                lrf.setLevelMin(level); // 設定最小等級
                System.out.println("Updated " + appenderName + " LevelRangeFilter LevelMin to " + level);
                break;
            }
            filter = filter.getNext();
        }
        
        // 4. 同步更新 Logger 等級 (確保生效)
        Logger.getRootLogger().setLevel(level);
    }
    
    /**
     * 獲取當前 Appender 實際等級
     */
    public static Level getLogLevel(String appenderName) {
        Appender appender = Logger.getRootLogger().getAppender(appenderName);
        if (appender != null) {
            org.apache.log4j.spi.Filter filter = appender.getFilter();
            while (filter != null) {
                if (filter instanceof LevelRangeFilter) {
                    return ((LevelRangeFilter) filter).getLevelMin();
                }
                filter = filter.getNext();
            }
        }
        return Logger.getRootLogger().getLevel();
    }
}

Log4j 配置檔對應 (log4j.xml)

<appender name="FILE" class="org.apache.log4j.RollingFileAppender">
    <param name="File" value="logs/app.log"/>
    <param name="MaxFileSize" value="10MB"/>
    <param name="MaxBackupIndex" value="10"/>
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%p] %c - %m%n"/>
    </layout>
    <!-- 關鍵:必須配置 LevelRangeFilter 才能動態調整 -->
    <filter class="org.apache.log4j.varia.LevelRangeFilter">
        <param name="LevelMin" value="INFO"/>
        <param name="LevelMax" value="FATAL"/>
    </filter>
</appender>

<root>
    <level value="DEBUG"/>
    <appender-ref ref="FILE"/>
    <appender-ref ref="CONSOLE"/>
</root>

使用範例

public class TestLogLevel {
    private static final Logger log = Logger.getLogger(TestLogLevel.class);
    
    public static void main(String[] args) {
        log.info("初始 INFO 級別");
        log.debug("初始 DEBUG 級別 (不會輸出)");
        
        // 動態開啟 DEBUG
        LogLevelManager.setLogLevel("FILE", "DEBUG");
        log.debug("動態調整後 DEBUG 級別 (會輸出)");
        
        // 動態關閉 DEBUG
        LogLevelManager.setLogLevel("FILE", "INFO");
        log.debug("再次調整後 DEBUG 級別 (不會輸出)");
    }
}

進階:JMX 管理介面整合

@Component
@ManagedResource(objectName = "com.example:type=LogManager", description = "動態日誌管理")
public class LogManagerMBean {
    
    @ManagedAttribute(description = "檔案 Appender 日誌等級")
    public String getFileAppenderLevel() {
        return LogLevelManager.getLogLevel("FILE").toString();
    }
    
    @ManagedAttribute
    public void setFileAppenderLevel(String level) {
        LogLevelManager.setLogLevel("FILE", level);
    }
    
    @ManagedOperation(description = "設定所有 Appender 等級")
    public void setAllAppendersLevel(String level) {
        Logger root = Logger.getRootLogger();
        for (Enumeration<Appender> e = root.getAllAppenders(); e.hasMoreElements();) {
            Appender appender = e.nextElement();
            LogLevelManager.setLogLevel(appender.getName(), level);
        }
    }
}

注意事項

  1. 必須在 log4j.xml 配置 LevelRangeFilter,否則遍歷 Filter 鏈找不到目標
  2. Logger 等級與 Appender Filter 邏輯與關係:訊息需同時通過 Logger 等級檢查 AND Appender Filter 檢查才會輸出
  3. 非根 Logger:使用 Logger.getLogger("com.xxx") 獲取特定 Logger
  4. Log4j 2.x 差異:API 完全不同,使用 Configurator.setLevel()、LoggerConfig

Log4j 2.x 版本實作

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;

public class Log4j2LevelManager {
    public static void setLevel(String loggerName, String levelStr) {
        LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
        Configuration config = ctx.getConfiguration();
        LoggerConfig loggerConfig = config.getLoggerConfig(loggerName);
        loggerConfig.setLevel(Level.toLevel(levelStr));
        ctx.updateLoggers(); // 關鍵:通知所有 Logger 更新
    }
}

總結

透過操作 Appender 的 LevelRangeFilter 實現 Log4j 1.x 動態調整日誌等級,核心步驟:獲取 Appender → 遍歷 Filter → 設定 LevelMin → 同步 Logger 等級。生產環境建議整合 JMX 或 Actuator 端點,提供安全的管理介面。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved