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

Java 內置日誌框架 java.util.logging 詳細指南:Logger、Handler 與 Formatter 實戰

     編輯:Java 綜合教學

前言:為什麼要了解 JUL (java.util.logging)?

作為 JDK 1.4 就內置的日誌框架,java.util.logging (簡稱 JUL) 無需引入第三方依賴即可使用,雖然功能不如 Log4j2 或 Logback 豐富,但在中小型項目、Android 開發、或受限於無法引入外部 JAR 的環境中,它仍是輕量級日誌記錄的首選。本文將系統講解 JUL 的核心組件:Logger 級別體系、Handler 處理器、Formatter 格式化器,並通過完整代碼示例演示控制台輸出、文件寫入及自定義格式化的實戰技巧。

一、Logger 級別體系詳解

JUL 定義了比 Log4j 更精細的 7 個標準級別(由高到低):

級別常量數值用途場景
SEVERE1000嚴重錯誤,導致程序無法繼續運行
WARNING900潛在風險,但程序可繼續運行
INFO800關鍵業務流程節點信息(默認級別)
CONFIG700配置信息,如啟動參數、環境變量
FINE500一般調試信息
FINER400詳細調試信息
FINEST300極度詳細的追蹤信息

另有兩個特殊級別:OFF (關閉所有日誌) 和 ALL (開啟所有日誌)。

核心級別規則:

  • 高級別 Logger 不會記錄低級別消息:設置為 WARNING 的 Logger 不會輸出 INFO/FINE 等級別日誌
  • 低級別 Logger 會記錄高級別消息:設置為 FINE 的 Logger 會輸出 WARNING/SEVERE 等高級別日誌
  • 默認級別為 INFO:低於 INFO 的級別 (CONFIG/FINE/FINER/FINEST) 默認不會在控制台顯示,但會寫入文件(若配置了文件 Handler)

二、創建與獲取 Logger 實例

使用工廠方法獲取 Logger,採用層次化命名空間(建議使用包名風格):

// 獲取/創建名為 "com.example.module" 的 Logger
Logger logger = Logger.getLogger("com.example.module");

// 帶資源包名稱的獲取方式(用於國際化)
Logger logger2 = Logger.getLogger("com.example.module", "com.example.Messages");

重要特性: 同名 Logger 全局單例,不同引用指向同一實例,配置修改將全局生效。

三、實戰示例 1:控制台多級別日誌演示

public class LoggerLevelDemo {
    public static void main(String[] args) {
        // 創建不同級別的 Logger
        Logger severeLogger = Logger.getLogger("demo.severe");
        severeLogger.setLevel(Level.SEVERE);

        Logger warningLogger = Logger.getLogger("demo.warning");
        warningLogger.setLevel(Level.WARNING);

        Logger infoLogger = Logger.getLogger("demo.info");
        infoLogger.setLevel(Level.INFO);

        Logger configLogger = Logger.getLogger("demo.config");
        configLogger.setLevel(Level.CONFIG);

        // 測試日誌記錄行為
        infoLogger.info("這是 INFO 級別記錄");
        infoLogger.warning("低級別 Logger 能記錄高級別信息嗎?");  // 能輸出

        warningLogger.warning("這是 WARNING 級別記錄");
        warningLogger.severe("低級別 Logger 能記錄高級別信息嗎?");  // 能輸出
        warningLogger.info("高級別 Logger 能記錄低級別信息嗎?");  // 無輸出

        severeLogger.severe("這是 SEVERE 級別記錄");
        severeLogger.warning("高級別 Logger 能記錄低級別信息嗎?");  // 無輸出

        configLogger.config("這是 CONFIG 級別記錄");  // 控制台無輸出(低於默認 INFO)

        // 驗證同名 Logger 單例特性
        Logger sameLogger = Logger.getLogger("demo.info");
        System.out.println("同名 Logger 是否同一實例: " + (infoLogger == sameLogger));  // true
        
        // 動態調整級別驗證
        sameLogger.setLevel(Level.SEVERE);
        infoLogger.info("調整為 SEVERE 後,INFO 應不輸出");  // 無輸出
        sameLogger.setLevel(Level.INFO);
        infoLogger.info("恢復 INFO 級別後,INFO 應輸出");  // 有輸出
    }
}

四、Handler 處理器:日誌的輸出目的地

Handler 負責將日誌記錄導出到不同目標,JUL 內置以下核心 Handler:

  • ConsoleHandler:輸出到控制台 (System.err)
  • FileHandler:寫入文件,支持輪轉、大小限制、追加模式
  • SocketHandler:發送到網絡日誌服務
  • MemoryHandler:緩存到內存,滿足條件再刷新
  • StreamHandler:抽象基類,輸出到任意 OutputStream

FileHandler 關鍵構造參數:

FileHandler(String pattern, int limit, int count, boolean append)
// pattern: 文件名模式,支持 %h(用戶目錄)、%t(臨時目錄)、%g(輪轉索引)、%u(唯一ID)
// limit: 單個文件最大字節數,超過則輪轉
// count: 輪轉文件數量
// append: 是否追加模式

五、實戰示例 2:文件日誌記錄與級別控制

public class FileLoggingDemo {
    public static void main(String[] args) throws IOException {
        // 創建日誌目錄
        File logDir = new File("f:/logTest", "logDir");
        logDir.mkdirs();
        File logFile = new File(logDir, "logTest.log");

        // 配置 FileHandler:10KB 輪轉,保留 1 個備份,追加模式
        FileHandler fileHandler = new FileHandler(logFile.getAbsolutePath(), 10240, 1, true);
        fileHandler.setLevel(Level.ALL);  // 記錄所有級別

        // 創建不同級別 Logger 並添加 Handler
        Logger infoLogger = Logger.getLogger("file.info");
        infoLogger.setLevel(Level.INFO);
        infoLogger.addHandler(fileHandler);

        Logger configLogger = Logger.getLogger("file.config");
        configLogger.setLevel(Level.CONFIG);
        configLogger.addHandler(fileHandler);

        // 測試記錄
        infoLogger.info("infoLogger 的 INFO 記錄 → 會寫入文件");
        infoLogger.config("infoLogger 的 CONFIG 記錄 → 不會寫入(低於 Logger 級別)");
        
        configLogger.info("configLogger 的 INFO 記錄 → 會寫入文件");
        configLogger.config("configLogger 的 CONFIG 記錄 → 會寫入文件(Logger 級別允許)");
        
        // 注意:Handler 級別 (ALL) 低於 Logger 級別時,以 Logger 級別為準
        // 只有同時滿足 Logger 級別 和 Handler 級別 才會最終輸出
    }
}

六、Formatter 格式化器:定義日誌輸出格式

JUL 默認使用 XMLFormatter 輸出 XML 格式,常用格式化器包括:

  • SimpleFormatter:簡單文本格式(類似傳統日誌)
  • XMLFormatter:結構化 XML 格式(默認)
  • Formatter:抽象基類,用於自定義格式

注意區分: java.util.logging.Formatter (日誌格式化) vs java.util.Formatter (字符串格式化,類似 printf)。

七、實戰示例 3:自定義 Formatter 實現緊湊文本格式

// 自定義格式化器
class CompactLogFormatter extends Formatter {
    @Override
    public String format(LogRecord record) {
        // 格式:類名: 方法名: 級別: 消息\n
        return record.getSourceClassName() + ": " 
             + record.getSourceMethodName() + ": " 
             + record.getLevel() + ": " 
             + record.getMessage() + "\n";
    }
}

// 使用示例
public class CustomFormatterDemo {
    public static void main(String[] args) throws IOException {
        Logger logger = Logger.getLogger("custom.format");
        logger.setLevel(Level.ALL);
        
        FileHandler fh = new FileHandler("custom.log");
        fh.setFormatter(new CompactLogFormatter());  // 設置自定義格式
        logger.addHandler(fh);
        
        logger.info("這是一條自定義格式的 INFO 日誌");
        logger.warning("這是一條 WARNING 日誌");
    }
}

輸出效果:

CustomFormatterDemo: main: INFO: 這是一條自定義格式的 INFO 日誌
CustomFormatterDemo: main: WARNING: 這是一條 WARNING 日誌

八、進階技巧與最佳實踐

1. 全局配置文件 (logging.properties)

在 JRE 目錄 lib/logging.properties 或項目 classpath 下放置配置文件,實現零代碼配置:

# 默認級別
.level=INFO

# ConsoleHandler 配置
handlers=java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level=INFO
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter

# FileHandler 配置
# java.util.logging.FileHandler.pattern=%h/myapp%u.log
# java.util.logging.FileHandler.limit=50000
# java.util.logging.FileHandler.count=1
# java.util.logging.FileHandler.formatter=java.util.logging.XMLFormatter

# 特定包級別控制
com.example.dao.level=FINE
com.example.web.level=WARNING

2. 避免重複日誌輸出

默認情況下 Logger 會向父 Logger 傳遞日誌(useParentHandlers=true),導致控制台重複輸出。解決方法:

logger.setUseParentHandlers(false);  // 禁用父 Handler 傳遞
logger.addHandler(myCustomHandler);   // 僅使用自定義 Handler

3. 性能優化:延遲字符串構造

對於高頻調用的低級別日誌,使用 Supplier<String> 延遲消息構造(Java 8+):

// 只有當級別滿足時才會調用 lambda 構造消息
logger.fine(() -> "昂貴的調試信息構造: " + expensiveComputation());

九、總結:JUL 選型建議

場景推薦方案
中小型項目、無第三方依賴限制直接使用 JUL,配合 logging.properties
高性能、異步日誌、複雜路由需求Log4j2 / Logback + SLF4J
Android 應用開發JUL (Android 封裝版) 或 Timber
微服務分布式追蹤Logback + MDC + ELK/EFK 堆棧

掌握 JUL 核心機制不僅能應對受限環境,更能深入理解現代日誌框架的設計思想(Logger/Handler/Formatter 分離架構源於此)。合理配置級別、Handler 與 Formatter,即可構建清晰可維護的日誌體系。

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