1、JUL 簡介
JUL 全稱 Java Util Logging,位於java.util.logging.Logger 包。
它是 java 原生的日志框架,使用時無需另外引用第三方的類庫,相對其他的框架使用方便,學習簡單,主要是使用在小型應用中。
2、JUL 組件介紹
Logger
- 記錄器,應用程序通過獲取 Logger 對象,調用其 API 來發布日志信息。Logger 通常被認為是訪問日志系統的入口程序
Handler
- 處理器,每個 Logger 都會關聯一個或者是一組 Handler,Logger 會將日志交給關聯的 Handler 去做處理,由 Handler 負責將日志做記錄 Handler 具體實現了日志的輸出位置,比如可以輸出到控制台或文件中等等
Filter
- 過濾器,根據需要定制哪些信息會被記錄,哪些信息會被略過
Formatter
- 格式化組件,它負責對日志中的數據和信息進行轉換和格式化,所以它決定了我們輸出日志最終的形式
Level
- 日志的輸出級別,每條日志消息都有一個關聯的級別。根據輸出級別的設置,用來展現最終所呈現的日志信息。根據不同的需求,去設置不同的級別
3、JUL 入門案例
① 新建普通 Java 項目
② 創建測試類,路徑:src / com / jul / test / JULTest.java
代碼示例
package com.jul.test;
import org.junit.Test;
import java.util.logging.Level;
import java.util.logging.Logger;
// 日志入口程序:java.util.logging.Logger
public class JULTest {
// JUL入門案例
@Test
public void test01() {
// 引入當前類的全路徑字符串獲取日志記錄器
Logger logger = Logger.getLogger("com.jul.test.JULTest");
// 對於日志的輸出有兩種方式
// 1、直接調用日志級別的相關方法,方法中傳遞日志輸出信息
logger.info("輸出info信息1");
// 2、調用 log 方法,通過 Level 類型定義日志級別參數,以及搭配日志輸出信息的參數
logger.log(Level.INFO, "輸出info信息2");
System.out.println("--------");
// 打印日志信息並傳參
// 輸出學生信息:姓名、年齡
String name = "張三";
int age = 23;
logger.log(Level.INFO, "方式一:學生姓名:" + name + ",學生年齡:" + age);
// 以上操作中,對於輸出消息用字符串拼接弊端很多。拼接麻煩、程序效率低、可讀性不強、維護成本高
// 應該使用動態生成數據的方式生產日志,就是占位符的方式來進行操作
logger.log(Level.INFO, "方式二:學生姓名:{0},學生年齡:{1}", new Object[]{name, age});
}
}
運行結果
六月 14, 2021 10:14:37 下午 com.jul.test.JULTest test01
信息: 輸出info信息1
六月 14, 2021 10:14:37 下午 com.jul.test.JULTest test01
--------
信息: 輸出info信息2
六月 14, 2021 10:14:37 下午 com.jul.test.JULTest test01
信息: 方式一:學生姓名:張三,學生年齡:23
六月 14, 2021 10:14:37 下午 com.jul.test.JULTest test01
信息: 方式二:學生姓名:張三,學生年齡:23
4、JUL 日志級別說明及展示
日志級別 | 數值 | 說明 |
---|---|---|
OFF | Integer.MAX_VALUE | 關閉所有消息的日志記錄 |
SEVERE | 1000 | 錯誤信息(最高級的日志級別) |
WARNING | 900 | 警告信息 |
INFO | 800 | 默認信息(默認級別) |
CONFIG | 700 | 配置信息 |
FINE | 500 | 詳細信息(少) |
FINER | 400 | 詳細信息(中) |
FINEST | 300 | 詳細信息(多)(最低級的日志級別) |
ALL | Integer.MIN_VALUE | 啟用所有消息的日志記錄 |
這個數值的意義在於,設置的日志級別是 INFO 級別 - 800 時,則最終展現的日志信息,必須是數值大於 800 的所有級別信息
代碼示例
// JUL日志級別說明及展示
@Test
public void test02() {
// 獲取日志記錄器
Logger logger = Logger.getLogger("com.jul.test.JULTest");
// 設置日志級別為配置級別
logger.setLevel(Level.CONFIG);
// 輸出日志信息
logger.severe("severe:錯誤信息");
logger.warning("warning:警告信息");
logger.info("info:默認信息");
logger.config("config:配置信息");
logger.fine("fine:詳細信息(少)");
logger.finer("finer:詳細信息(中)");
logger.finest("finest:詳細信息(多)");
}
運行結果
- 通過打印結果看到只輸出了 INFO 級別以及比 INFO 級別高的日志信息,而比 INFO 級別低的日志信息沒有打印,說明 INFO 級別的日志信息是系統默認的日志級別
- 僅通過以上形式來設置日志級別是不夠的,還需要搭配處理器 handler 共同設置才會生效
六月 14, 2021 10:17:53 下午 com.jul.test.JULTest test02
嚴重: severe:錯誤信息
六月 14, 2021 10:17:53 下午 com.jul.test.JULTest test02
警告: warning:警告信息
六月 14, 2021 10:17:53 下午 com.jul.test.JULTest test02
信息: info:默認信息
5、JUL 自定義日志級別
代碼示例
// JUL自定義日志級別
@Test
public void test03() {
// 獲取日志記錄器
Logger logger = Logger.getLogger("com.jul.test.JULTest");
// 將默認的日志打印方式關閉
// 參數設置為 false,打印日志的方式就不會按照父 logger 默認的方式去進行操作
logger.setUseParentHandlers(false);
// 控制台日志處理器
ConsoleHandler handler = new ConsoleHandler();
// 創建日志格式化組件對象
SimpleFormatter formatter = new SimpleFormatter();
// 在處理器中設置日志輸出格式
handler.setFormatter(formatter);
// 在記錄器中添加處理器
logger.addHandler(handler);
// 設置日志的打印級別
// 此處必須將日志記錄器和處理器的級別進行統一的設置,才會達到日志顯示相應級別的效果
logger.setLevel(Level.ALL);
handler.setLevel(Level.ALL);
// 輸出日志信息
logger.severe("severe:錯誤信息");
logger.warning("warning:警告信息");
logger.info("info:默認信息");
logger.config("config:配置信息");
logger.fine("fine:詳細信息(少)");
logger.finer("finer:詳細信息(中)");
logger.finest("finest:詳細信息(多)");
}
運行結果
六月 14, 2021 10:19:26 下午 com.jul.test.JULTest test04
嚴重: severe:錯誤信息
六月 14, 2021 10:19:26 下午 com.jul.test.JULTest test04
警告: warning:警告信息
六月 14, 2021 10:19:26 下午 com.jul.test.JULTest test04
信息: info:默認信息
六月 14, 2021 10:19:26 下午 com.jul.test.JULTest test04
配置: config:配置信息
六月 14, 2021 10:19:26 下午 com.jul.test.JULTest test04
詳細: fine:詳細信息(少)
六月 14, 2021 10:19:26 下午 com.jul.test.JULTest test04
較詳細: finer:詳細信息(中)
六月 14, 2021 10:19:26 下午 com.jul.test.JULTest test04
非常詳細: finest:詳細信息(多)
6、JUL 日志打印到文件中
日志文件后綴為 *.log,在 src 目錄下創建 JULTest.log 日志文件
代碼示例
// JUL日志打印到文件中,將日志輸出到具體的磁盤文件中相當於將日志做了持久化操作
@Test
public void test04() throws IOException {
// 獲取日志記錄器
Logger logger = Logger.getLogger("com.jul.test.JULTest");
// 關閉父記錄器打印方式
logger.setUseParentHandlers(false);
// 文件日志處理器
FileHandler handler = new FileHandler("src\\JULTest.log"); // 指定輸出的日志文件
SimpleFormatter formatter = new SimpleFormatter();
handler.setFormatter(formatter);
logger.addHandler(handler);
// 統一設置日志的打印級別
logger.setLevel(Level.ALL);
handler.setLevel(Level.ALL);
// 輸出日志信息
logger.severe("severe:錯誤信息");
logger.warning("warning:警告信息");
logger.info("info:默認信息");
logger.config("config:配置信息");
logger.fine("fine:詳細信息(少)");
logger.finer("finer:詳細信息(中)");
logger.finest("finest:詳細信息(多)");
}
運行結果:此時控制台中並沒有輸出日志信息,打開 JULTest.log 文件,日志信息打印到文件中了
六月 14, 2021 10:24:19 下午 com.jul.test.JULTest test05
嚴重: severe:錯誤信息
六月 14, 2021 10:24:19 下午 com.jul.test.JULTest test05
警告: warning:警告信息
六月 14, 2021 10:24:19 下午 com.jul.test.JULTest test05
信息: info:默認信息
六月 14, 2021 10:24:19 下午 com.jul.test.JULTest test05
配置: config:配置信息
六月 14, 2021 10:24:19 下午 com.jul.test.JULTest test05
詳細: fine:詳細信息(少)
六月 14, 2021 10:24:19 下午 com.jul.test.JULTest test05
較詳細: finer:詳細信息(中)
六月 14, 2021 10:24:19 下午 com.jul.test.JULTest test05
非常詳細: finest:詳細信息(多)
7、JUL 同時添加多個處理器
用戶使用 Logger 來進行日志的記錄,使用 Handler 來進行日志的輸出,Logger 可以持有多個處理器 Handler,
添加了哪些 Handler 對象,就相當於根據所添加的 Handler 將日志輸出到指定的位置上。例如控制台、文件中...
在 src 目錄下創建 JULTest2.log 日志文件
代碼示例
// JUL 同時添加多個處理器
@Test
public void test05() throws IOException {
Logger logger = Logger.getLogger("com.jul.test.JULTest");
logger.setUseParentHandlers(false);
SimpleFormatter formatter = new SimpleFormatter();
// 文件日志處理器
FileHandler handler1 = new FileHandler("src\\JULTest2.log"); // 指定輸出的日志文件
handler1.setFormatter(formatter);
logger.addHandler(handler1); // 記錄器中添加了一個文件日志處理器
// 控制台日志處理器
ConsoleHandler handler2 = new ConsoleHandler();
handler2.setFormatter(formatter);
logger.addHandler(handler2); // 記錄器中又添加了一個控制台日志處理器
// 統一設置日志的打印級別
logger.setLevel(Level.ALL);
handler1.setLevel(Level.ALL);
handler2.setLevel(Level.ALL);
// 輸出日志信息
logger.severe("severe:錯誤信息");
logger.warning("warning:警告信息");
logger.info("info:默認信息");
logger.config("config:配置信息");
logger.fine("fine:詳細信息(少)");
logger.finer("finer:詳細信息(中)");
logger.finest("finest:詳細信息(多)");
}
運行結果
-
控制台輸出了日志信息
六月 14, 2021 10:38:28 下午 com.jul.test.JULTest test05 嚴重: severe:錯誤信息 六月 14, 2021 10:38:28 下午 com.jul.test.JULTest test05 警告: warning:警告信息 六月 14, 2021 10:38:28 下午 com.jul.test.JULTest test05 信息: info:默認信息 六月 14, 2021 10:38:28 下午 com.jul.test.JULTest test05 配置: config:配置信息 六月 14, 2021 10:38:28 下午 com.jul.test.JULTest test05 詳細: fine:詳細信息(少) 六月 14, 2021 10:38:28 下午 com.jul.test.JULTest test05 較詳細: finer:詳細信息(中) 六月 14, 2021 10:38:28 下午 com.jul.test.JULTest test05 非常詳細: finest:詳細信息(多)
-
JULTest2.log 文件中也打印了日志信息
六月 14, 2021 10:38:28 下午 com.jul.test.JULTest test05 嚴重: severe:錯誤信息 六月 14, 2021 10:38:28 下午 com.jul.test.JULTest test05 警告: warning:警告信息 六月 14, 2021 10:38:28 下午 com.jul.test.JULTest test05 信息: info:默認信息 六月 14, 2021 10:38:28 下午 com.jul.test.JULTest test05 配置: config:配置信息 六月 14, 2021 10:38:28 下午 com.jul.test.JULTest test05 詳細: fine:詳細信息(少) 六月 14, 2021 10:38:28 下午 com.jul.test.JULTest test05 較詳細: finer:詳細信息(中) 六月 14, 2021 10:38:28 下午 com.jul.test.JULTest test05 非常詳細: finest:詳細信息(多)
8、JUL 記錄器的父子關系及作用
JUL 中 Logger 記錄器之間是存在"父子"關系的,這種父子關系不是我們普遍認為的類之間的繼承關系,關系是通過樹狀結構存儲的
JUL 在初始化時會創建一個頂層 RootLogger 作為所有 Logger 的父 Logger,RootLogger 是 LogManager 的內部類,默認的名稱為空串
以上的 RootLogger 對象作為樹狀結構的根節點存在的,將來自定義的父子關系通過路徑來進行關聯,父子關系同時也是節點之間的掛載關系
代碼示例
// JUL Logger(記錄器)的父子關系及作用
@Test
public void test06() {
// 創建兩個 logger 對象,可以認為 logger1 是 logger2 的父親
// RootLogger 是所有 logger 對象的頂層 logger,名稱默認是一個空的字符串
Logger logger1 = Logger.getLogger("com.jul.test");
Logger logger2 = Logger.getLogger("com.jul.test.JULTest");
System.out.println(logger2.getParent() == logger1);
System.out.println("----");
System.out.println("logger1名稱:" + logger1.getName() +
",\n父Logger名稱:" + logger1.getParent().getName() +
",\n父Logger引用:" + logger1.getParent());
System.out.println("----");
System.out.println("logger2名稱:" + logger2.getName() +
",\n父Logger名稱:" + logger2.getParent().getName() +
",\n父Logger引用:" + logger2.getParent());
System.out.println("----");
// 父親所做的設置,也能夠同時作用於兒子
// 對 logger1 做日志打印相關的設置,然后我們使用 logger2 進行日志的打印
logger1.setUseParentHandlers(false);
ConsoleHandler handler = new ConsoleHandler();
SimpleFormatter formatter = new SimpleFormatter();
handler.setFormatter(formatter);
logger1.addHandler(handler);
handler.setLevel(Level.ALL);
logger1.setLevel(Level.ALL);
//兒子做打印
logger2.severe("severe:錯誤信息");
logger2.warning("warning:警告信息");
logger2.info("info:默認信息");
logger2.config("config:配置信息");
logger2.fine("fine:詳細信息(少)");
logger2.finer("finer:詳細信息(中)");
logger2.finest("finest:詳細信息(多)");
}
運行結果
true
----
logger1名稱:com.jul.test,
父Logger名稱:,
父Logger引用:java.util.logging.LogManager$RootLogger@3b764bce
----
logger2名稱:com.jul.test.JULTest,
父Logger名稱:com.jul.test,
父Logger引用:java.util.logging.Logger@759ebb3d
----
六月 14, 2021 10:41:40 下午 com.jul.test.JULTest test06
嚴重: severe:錯誤信息
六月 14, 2021 10:41:40 下午 com.jul.test.JULTest test06
警告: warning:警告信息
六月 14, 2021 10:41:40 下午 com.jul.test.JULTest test06
信息: info:默認信息
六月 14, 2021 10:41:40 下午 com.jul.test.JULTest test06
配置: config:配置信息
六月 14, 2021 10:41:40 下午 com.jul.test.JULTest test06
詳細: fine:詳細信息(少)
六月 14, 2021 10:41:40 下午 com.jul.test.JULTest test06
較詳細: finer:詳細信息(中)
六月 14, 2021 10:41:40 下午 com.jul.test.JULTest test06
非常詳細: finest:詳細信息(多)
9、JUL 日志配置文件解析
以上所有的配置相關的操作,都是以 java 硬編碼的形式進行的,我們可以使用配置文件,若沒有指定自定義日志配置文件,則使用系統默認的日志配置文件
默認配置文件位置:jdk 安裝目錄下 \ jre \ lib \ logging.properties 文件
############################################################
# 默認日志記錄配置文件
#
# 您可以通過使用java.util.logging.config.file系統屬性指定文件名來使用不同的文件
# 例如 java -Djava.util.logging.config.file=myfile
############################################################
############################################################
# 全局性質
############################################################
# RootLogger使用的處理器,在獲取RootLogger對象時進行的設置
# 可在當前處理器類后,通過指定的英文逗號分隔,添加多個日志處理器
# 這些處理程序將在VM啟動期間安裝,請注意:這些類必須位於系統類路徑上
# 默認情況下,只配置控制台處理程序,默認打印INFO和高於INFO級別消息
handlers = java.util.logging.ConsoleHandler
# 要添加文件處理程序,請使用以下行(多個日志處理器)
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
# RootLogger 默認的全局日志記錄級別
# 對於這種全局層面的任何特定配置,可以通過配置特定的水平來覆蓋
# 如果不手動配置其它的日志級別,則默認輸出下述配置的級別以及更高的級別
.level = INFO
############################################################
# 處理器指定屬性,描述處理程序的特定配置信息
############################################################
# 文件處理器屬性設置
# 默認輸出的日志文件路徑,位於用戶的主目錄中
# %h:當前用戶系統的默認根路徑,C:\用戶\用戶名\java0.log
# %u:指向默認輸出的日志文件數量count,count=1,則:java0.log;count=2,則:java0.log,java1.log...
java.util.logging.FileHandler.pattern = %h/java%u.log
# 默認輸出的日志文件大小(單位字節)
java.util.logging.FileHandler.limit = 50000
# 默認輸出的日志文件數量
java.util.logging.FileHandler.count = 1
# 默認輸出的日志文件格式(XML)
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
# 控制台處理器屬性設置
# 默認輸出的日志級別
java.util.logging.ConsoleHandler.level = INFO
# 默認輸出的日志格式(Simple)
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 示例以自定義簡單的格式化器輸出格式,以打印這樣的單行日志消息:
# <level>: <log message> [<date/time>]
#
# java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n
############################################################
# 配置特定屬性,為每個記錄器提供額外的控制
############################################################
# 例如:將日志級別設定到具體的某個包下
com.xyz.foo.level = SEVERE
精簡后
############################################################
# 默認日志記錄配置文件
############################################################
# 全局性質
############################################################
# 默認配置控制台處理程序,默認打印INFO和高於INFO級別信息
handlers=java.util.logging.ConsoleHandler
# 如果不手動配置其它的日志級別,則默認輸出下述配置的級別以及更高的級別
.level = INFO
############################################################
# 處理器指定屬性,描述處理程序的特定配置信息
############################################################
# 文件處理器屬性設置
# 默認輸出的日志文件路徑,位於用戶的主目錄中
java.util.logging.FileHandler.pattern = %h/java%u.log
# 默認輸出的日志文件大小(單位字節)
java.util.logging.FileHandler.limit = 50000
# 默認輸出的日志文件數量
java.util.logging.FileHandler.count = 1
# 默認輸出的日志文件格式(XML)
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
# 控制台處理器屬性設置
# 默認輸出的日志級別
java.util.logging.ConsoleHandler.level = INFO
# 默認輸出的日志格式(Smiple)
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
############################################################
# 配置特定屬性,為每個記錄器提供額外的控制
############################################################
# 例如:將日志級別設定到具體的某個包下
com.xyz.foo.level = SEVERE
10、JUL 使用自定義配置文件
在 src 目錄下創建 logging.properties 日志配置文件,將 9 小節精簡后的默認配置文件內容復制到 logging.properties 文件中
將第 10 行:.level = INFO 值改為 ALL
將第 28 行:java.util.logging.ConsoleHandler.level = INFO 值改為 ALL
代碼示例
// JUL 使用自定義配置文件
@Test
public void test07() throws IOException {
// 讀取自定義日志配置文件
InputStream input = new FileInputStream("src/logging.properties");
// 獲取日志管理器
LogManager logManager = LogManager.getLogManager();
// 日志管理器讀取自定義配置文件
logManager.readConfiguration(input);
// 日志記錄器
Logger logger = Logger.getLogger("com.jul.test.JULTest");
// 輸出日志信息
logger.severe("severe:錯誤信息");
logger.warning("warning:警告信息");
logger.info("info:默認信息");
logger.config("config:配置信息");
logger.fine("fine:詳細信息(少)");
logger.finer("finer:詳細信息(中)");
logger.finest("finest:詳細信息(多)");
}
運行結果:此時控制台輸出的日志使用的是我們自定義的日志配置文件,打印了所有級別日志信息
六月 14, 2021 11:00:47 下午 com.jul.test.JULTest test07
嚴重: severe:錯誤信息
六月 14, 2021 11:00:47 下午 com.jul.test.JULTest test07
警告: warning:警告信息
六月 14, 2021 11:00:47 下午 com.jul.test.JULTest test07
信息: info:默認信息
六月 14, 2021 11:00:47 下午 com.jul.test.JULTest test07
配置: config:配置信息
六月 14, 2021 11:00:47 下午 com.jul.test.JULTest test07
詳細: fine:詳細信息(少)
六月 14, 2021 11:00:47 下午 com.jul.test.JULTest test07
較詳細: finer:詳細信息(中)
六月 14, 2021 11:00:47 下午 com.jul.test.JULTest test07
非常詳細: finest:詳細信息(多)
11、JUL 自定義配置文件中的文件輸出
修改 logging.properties 日志配置文件,在 11 行處添加自定義文件日志處理器的配置信息
# 自定義文件日志處理器
com.jul.test.handlers = java.util.logging.FileHandler
# 自定義輸出的日志級別
com.jul.test.level = WARNING
# 屏蔽父記錄器打印方式
com.jul.test.useParentHandlers = false
運行結果: C:\用戶\用戶名 目錄下會有一個 java0.log 文件,打開文件發現日志格式為 XML 格式,這是因為 java.util.logging.FileHandler.formatter 指定的格式為 XMLFormatter
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
<date>2021-06-14T23:24:06</date>
<millis>1623684246017</millis>
<sequence>0</sequence>
<logger>com.jul.test.JULTest</logger>
<level>SEVERE</level>
<class>com.jul.test.JULTest</class>
<method>test07</method>
<thread>1</thread>
<message>severe:錯誤信息</message>
</record>
<record>
<date>2021-06-14T23:24:06</date>
<millis>1623684246022</millis>
<sequence>1</sequence>
<logger>com.jul.test.JULTest</logger>
<level>WARNING</level>
<class>com.jul.test.JULTest</class>
<method>test07</method>
<thread>1</thread>
<message>warning:警告信息</message>
</record>
</log>
配置修改:在 src 目錄下創建 JULTest3.log 日志文件
# 指定輸出日志內容的日志文件
java.util.logging.FileHandler.pattern = src\\JULTest3.log
# 將XML格式更改為Simple格式
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
運行結果: JULTest3.log 文件中內容
六月 14, 2021 11:28:58 下午 com.jul.test.JULTest test07
嚴重: severe:錯誤信息
六月 14, 2021 11:28:58 下午 com.jul.test.JULTest test07
警告: warning:警告信息
12、JUL 追加日志信息
將 test07() 方法執行多次,會發現每次輸出的日志內容會將上次的內容覆蓋掉,這明顯是不太好
增加配置
# 默認輸出的日志內容會覆蓋上次輸出的內容, 設為true改為追加
java.util.logging.FileHandler.append=true
多次運行 test07() 方法:發現 JULTest3.log 文件中內容並沒有被覆蓋,而是一直往后追加
六月 14, 2021 11:47:17 下午 com.jul.test.JULTest test07
嚴重: severe:錯誤信息
六月 14, 2021 11:47:17 下午 com.jul.test.JULTest test07
警告: warning:警告信息
六月 14, 2021 11:47:22 下午 com.jul.test.JULTest test07
嚴重: severe:錯誤信息
六月 14, 2021 11:47:22 下午 com.jul.test.JULTest test07
警告: warning:警告信息
六月 14, 2021 11:47:33 下午 com.jul.test.JULTest test07
嚴重: severe:錯誤信息
六月 14, 2021 11:47:33 下午 com.jul.test.JULTest test07
警告: warning:警告信息
13、JUL 框架操作流程總結
① 初始化 LogManager,LogManager 加載 logging.properties 配置文件,添加 Logger 到 LogManager
② 從單例的 LogManager 獲取 Logger
③ Level 設置日志級別,在打印的過程中使用到了日志記錄的 LogRecord 類
④ Filter 作為過濾器提供了日志級別之外更細粒度的控制
⑤ Handler 日志處理器,決定日志的輸出格式,例如:XMLFormatter、SimpleFormatter;日志的輸出位置,例如:控制台、文件...
⑥ Formatter 是用來格式化輸出的日志內容