0、前言 本篇博客的初衷是為了特定的人弄的,這些內容在網上都可以找到原文,因此:這篇博客只是保證能夠讓特定的人看懂,其他人看得懂就看,看不懂拉倒,同時,這篇博客中細節說明沒有、運行截圖沒有、特別備註沒有...... 1、JUL 指的是Java Util Logging包,它是java原生的日誌框架, ...
0、前言
- 本篇博客的初衷是為了特定的人弄的,這些內容在網上都可以找到原文,因此:這篇博客只是保證能夠讓特定的人看懂,其他人看得懂就看,看不懂拉倒,同時,這篇博客中細節說明沒有、運行截圖沒有、特別備註沒有......
1、JUL
- 指的是Java Util Logging包,它是java原生的日誌框架,使用時不需要另外引用第三方的類庫,相對其他的框架使用方便,學習簡單,主要是使用在小型應用中
1.1、JUL的組成結構
-
Logger:被稱為記錄器,應用程式通過獲取Logger對象,抵用其API來發佈日誌信息。Logger通常被認為是訪問日誌系統的入口程式。
-
Handler:處理器,每個Logger都會關聯一個或者是一組Handler,Logger會將日誌交給關聯的Handler去做處理,由Handler負責將日誌做記錄。Handler具體實現了日誌的輸出位置,比如可以輸出到控制台或者是文件中等等。
-
Filter:過濾器,根據需要定製哪些信息會被記錄,哪些信息會被略過。
-
Formatter:格式化組件,它負責對日誌中的數據和信息進行轉換和格式化,所以它決定了我們輸出日誌最終的形式。
-
Level:日誌的輸出級別,每條日誌消息都有一個關聯的級別。我們根據輸出級別的設置,用來展現最終所呈現的日誌信息。根據不同的需求,去設置不同的級別。
1.2、入門
- 可以直接去JDK文檔中查看java.util.logging這個包
public class JULTest {
@Test
public void test01(){
/*
日誌入口程式 java.util.logging.Logger
*/
// Logger對象的創建方式,不能直接new對象
// 取得對象的方法參數,需要引入當前類的全路徑字元串(當前我們先這麼用,以後根據包結構有Logger父子關係,以後詳細介紹)
Logger logger = Logger.getLogger("cn.zixieqing.jul.test.JULTest");
/*
對於日誌的輸出,有兩種方式
第一種方式:
直接調用日誌級別相關的方法,方法中傳遞日誌輸出信息
假設現在我們要輸出info級別的日誌信息
*/
//logger.info("輸出info信息1");
/*
第二種方式:
調用通用的log方法,然後在裡面通過Level類型來定義日誌的級別參數,以及搭配日誌輸出信息的參數
*/
//logger.log(Level.INFO,"輸出info信息2");
/*
輸出學生信息
姓名
年齡
*/
/*String name = "zs";
int age = 23;
logger.log(Level.INFO,"學生的姓名為:"+name+";年齡為:"+age);*/
/*
對於輸出消息中,字元串的拼接弊端很多
1.麻煩
2.程式效率低
3.可讀性不強
4.維護成本高
我們應該使用動態生成數據的方式,生產日誌
我們使用的就是占位符的方式來進行操作
*/
String name = "zs";
int age = 23;
logger.log(Level.INFO,"學生的姓名:{0},年齡:{1}",new Object[]{name,age});
}
}
1.3、日誌級別
@Test
public void test02(){
/*
日誌的級別(通過源碼查看,非常簡單)
SEVERE : 錯誤 --- 最高級的日誌級別
WARNING : 警告
INFO : (預設級別)消息
源碼:Logger.getLogger() --->demandLogger -----> getLogManager() -----> ensureLogManagerInitialized()--->350行左右有一個 defaultlevel屬性
CONFIG : 配置
FINE : 詳細信息(少)
FINER : 詳細信息(中)
FINEST : 詳細信息 (多) --- 最低級的日誌級別
兩個特殊的級別
OFF 可用來關閉日誌記錄
ALL 啟用所有消息的日誌記錄
對於日誌的級別,重點關註的是new對象的時候的第二個參數,是一個數值(源碼中有)
OFF Integer.MAX_VALUE 整型最大值
SEVERE 1000
WARNING 900
...
...
FINEST 300
ALL Integer.MIN_VALUE 整型最小值
這個數值的意義在於,如果設置的日誌的級別是INFO -- 800
那麼最終展現的日誌信息,必須是數值大於800的所有的日誌信息
最終展現的就是
SEVERE
WARNING
INFO
*/
Logger logger = Logger.getLogger("cn.zixieqing.jul.test.JULTest");
/*
通過列印結果,我們看到了僅僅只是輸出了info級別以及比info級別高的日誌信息
比info級別低的日誌信息沒有輸出出來
證明瞭info級別的日誌信息,它是系統預設的日誌級別
在預設日誌級別info的基礎上,列印比它級別高的信息
*/
/*
如果僅僅只是通過以下形式來設置日誌級別
那麼不能夠起到效果
將來需要搭配處理器handler共同設置才會生效
*/
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信息");
}
1.4、自定義日誌級別
1.4.1、輸出信息在console上
@Test
public void test03(){
/*
自定義日誌的級別
*/
// 日誌記錄器
Logger logger = Logger.getLogger("cn.zixieqing.jul.test.JULTest");
/*
將預設的日誌列印方式關閉掉
參數設置為false,列印日誌的方式就不會按照父logger預設的方式去進行操作
*/
logger.setUseParentHandlers(false);
/*
處理器Handler
這裡使用的是控制台日誌處理器,取得處理器對象
*/
ConsoleHandler handler = new ConsoleHandler();
// 創建日誌格式化組件對象
SimpleFormatter formatter = new SimpleFormatter();
// 在處理器中設置輸出格式
handler.setFormatter(formatter);
// 在記錄器中添加處理器
logger.addHandler(handler);
/*
設置日誌的列印級別
此處必須將日誌記錄器和處理器的級別進行統一的設置,才會達到日誌顯示相應級別的效果
logger.setLevel(Level.CONFIG);
handler.setLevel(Level.CONFIG);
*/
// 設置如下的all級別之後,那麼下麵所有的輸出信息都會列印到控制台,在需要的時候改成對應的日誌級別即可
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信息");
}
1.4.2、輸出信息在磁碟上
@Test
public void test04() throws IOException {
/*
將日誌輸出到具體的磁碟文件中
這樣做相當於是做了日誌的持久化操作
*/
Logger logger = Logger.getLogger("cn.zixieqing.jul.test.JULTest");
logger.setUseParentHandlers(false);
// 文件日誌處理器
FileHandler handler = new FileHandler("D:\\test\\" + this.getClass().getSimpleName() + ".log");
SimpleFormatter formatter = new SimpleFormatter();
handler.setFormatter(formatter);
logger.addHandler(handler);
// 也可以同時在控制台和文件中進行列印
ConsoleHandler handler2 = new ConsoleHandler();
handler2.setFormatter(formatter);
// 可以在記錄器中同時添加多個處理器,這樣磁碟和控制臺中都可以輸出對應級別的日誌信息了
logger.addHandler(handler2);
logger.setLevel(Level.ALL);
handler.setLevel(Level.ALL);
handler2.setLevel(Level.CONFIG);
logger.severe("severe信息");
logger.warning("warning信息");
logger.info("info信息");
logger.config("config信息");
logger.fine("fine信息");
logger.finer("finer信息");
logger.finest("finest信息");
/*
總結:
用戶使用Logger來進行日誌的記錄,Logger可以持有多個處理器Handler
(日誌的記錄使用的是Logger,日誌的輸出使用的是Handler)
添加了哪些handler對象,就相當於需要根據所添加的handler
將日誌輸出到指定的位置上,例如控制台、文件..
*/
}
1.5、logger的父子關係
@Test
public void test05(){
/*
Logger之間的父子關係
JUL中Logger之間是存在"父子"關係的
值得註意的是,這種父子關係不是我們普遍認為的類之間的繼承關係
關係是通過樹狀結構存儲的
*/
/*
從下麵創建的兩個logger對象看來
我們可以認為logger1是logger2的父親
*/
/*
父親是RootLogger,名稱預設是一個空的字元串
RootLogger可以被稱之為所有logger對象的頂層logger
*/
// 這就是父
Logger logger1 = Logger.getLogger("cn.zixieqing.jul.test");
// 這是子,甚至cn.zixieqing.jul也是這個logger2的父
Logger logger2 = Logger.getLogger("cn.zixieqing.jul.test.JULTest");
//System.out.println(logger2.getParent()==logger1); //true
System.out.println("logger1的父Logger引用為:"
+logger1.getParent()+"; 名稱為"+logger1.getName()+"; 父親的名稱為"+logger1.getParent().getName());
System.out.println("logger2的父Logger引用為:"
+logger2.getParent()+"; 名稱為"+logger2.getName()+"; 父親的名稱為"+logger2.getParent().getName());
/*
父親所做的設置,也能夠同時作用於兒子
對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沒做配置,但是父親logger1做了,所以兒子logger2的輸出級別就是父親的級別
logger2.severe("severe信息");
logger2.warning("warning信息");
logger2.info("info信息");
logger2.config("config信息");
logger2.fine("fine信息");
logger2.finer("finer信息");
logger2.finest("finest信息");
}
小結:看源碼的結果
// JUL在初始化時會創建一個頂層RootLogger作為所有Logger的父Logger,java.util.logging.LogManager$RootLogger,預設的名稱為空串
// 查看源碼Logger.getLogger() --->demandLogger -----> getLogManager() -----> ensureLogManagerInitialized()--->350行左右:
owner.rootLogger = owner.new RootLogger();
RootLogger是LogManager的內部類
/*
以上的RootLogger對象作為樹狀結構的根節點存在的
將來自定義的父子關係通過路徑來進行關聯
父子關係,同時也是節點之間的掛載關係
*/
// 350行左右
owner.addLogger(owner.rootLogger);
addLogger ----> LoggerContext cx = getUserContext();
/*
LoggerContext一種用來保存節點的Map關係,WeakHashMap<Object, LoggerContext> contextsMap
點進WeakHashMap<Object, LoggerContext>的LoggerContext,裡面的結構為:Hashtable<String,LoggerWeakRef> namedLoggers
*/
// 再點開Hashtable<String,LoggerWeakRef> namedLoggers 的LoggerWeakRef,得到的信息如下:
private String name; // for namedLoggers cleanup,這玩意就是節點,也就是說父子關係也就是節點掛載關係
private LogNode node; // for loggerRef cleanup
private WeakReference<Logger> parentRef; // for kids cleanup
1.6、使用配置文件
1.6.1、預設配置文件所在地
- 查看源碼
Logger.getLogger() --->demandLogger -----> getLogManager() -----> ensureLogManagerInitialized()--->345行左右:
有這麼一行代碼:owner.readPrimordialConfiguration();
點擊readPrimordialConfiguration(),在340行左右有一句readConfiguration();
點擊readConfiguration();在1290行左右,有如下的代碼
String fname = System.getProperty("java.util.logging.config.file");
if (fname == null) {
fname = System.getProperty("java.home");
if (fname == null) {
throw new Error("Can't find java.home ??");
}
File f = new File(fname, "lib");
f = new File(f, "logging.properties");
fname = f.getCanonicalPath();
}
結論:預設配置文件所在地
- java.home( 即:安裝JDK的目錄 ) --> 找到jre文件夾 --> lib --> logging.properties
分析一下上述目錄中的logging.properties文件,找打這個目錄下的此文件打開
# RootManager預設使用的處理器
# 若是想要配置多個處理器,可以使用逗號進行分開,如:java.util.logging.ConsoleHandler,java.util.logging.FileHandler
handlers= java.util.logging.ConsoleHandler
# RootManager的預設日誌級別,這是全局的日誌級別
# 若不手動進行級別配置,那麼預設使用INFO及更高的級別進行輸出
.level= INFO
# 文件處理器設置
# 日誌文件的路徑
# %h/java%u.log h指的是用戶目錄 - 不分window還是linux
# java%u.log是生成的文件名,其中:u相當於是自增,從0開始的
# 如:java0.log、java1.log、java2.log......這裡生成多少份由java.util.logging.FileHandler.count = 1這個配置決定
java.util.logging.FileHandler.pattern = %h/java%u.log
# 日誌文件的限制 - 預設50000位元組
java.util.logging.FileHandler.limit = 50000
# 日誌文件的數量 - 預設1份
java.util.logging.FileHandler.count = 1
# 日誌文件的格式,預設XML格式,也可以採用SimpleFormatter
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
# 控制台處理器設置
# 控制台預設級別
java.util.logging.ConsoleHandler.level = INFO
# 控制台預設輸出格式
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 這是這個源文檔中舉的一個例子,意思是像下麵這樣,可將日誌級別設定到具體的某個包下
# com.xyz.foo.level = SEVERE
1.6.2、修改成更友好點的配置文件
# 自定義Logger
cn.zixieqing.handlers=java.util.logging.FileHandler
# 自定義Logger日誌級別
cn.zixieqing.level=SERVER
# 屏蔽父logger的日誌設置,相當於前面玩的logger.setUseParentHandlers(false);
cn.zixieqing.useParentHandlers=false
# RootManager預設使用的處理器
# 若是想要配置多個處理器,可以使用逗號進行分開,如:java.util.logging.ConsoleHandler,java.util.logging.FileHandler
handlers= java.util.logging.ConsoleHandler
# RootManager的預設日誌級別,這是全局的日誌級別
.level= SERVER
# 文件處理器設置
# 日誌文件的路徑
# %h/java%u.log h指的是用戶目錄 - 不分window還是linux
# java%u.log是生成的文件名,其中:u相當於是自增,從0開始的,如:java0.log、java1.log、java2.log......這裡生成多少份由java.util.logging.FileHandler.count = 1這個配置決定
java.util.logging.FileHandler.pattern = %h/java%u.log
# 日誌文件的限制 - 50000位元組
java.util.logging.FileHandler.limit = 50000
# 日誌文件的數量
java.util.logging.FileHandler.count = 1
# 日誌文件的格式
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
# 控制台處理器設置
# 控制台預設級別
java.util.logging.ConsoleHandler.level = SERVER
# 控制台預設輸出格式
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 下一次生成的日誌文件預設是覆蓋了上一此的文件 - 改為在原日誌上進行追加
java.util.logging.FileHandler.append=true
1.6.3、使用自定義的配置文件
@Test
public void test06() throws Exception {
// 載入自定義的配置文件
InputStream input = new FileInputStream("D:\\test\\logging.properties");
// 取得日誌管理器對象
LogManager logManager = LogManager.getLogManager();
// 讀取自定義的配置文件
logManager.readConfiguration(input);
Logger logger = Logger.getLogger("cn.zixieqing.jul.test.JULTest");
logger.severe("severe信息");
logger.warning("warning信息");
logger.info("info信息");
logger.config("config信息");
logger.fine("fine信息");
logger.finer("finer信息");
logger.finest("finest信息");
}
1.7、總結:JUL原理
-
1、初始化LogManager
- LogManager載入logging.properties配置文件
- 添加Logger到LogManager
-
2、從單例的LogManager獲取Logger,即:LogManager.getLogManager();
-
3、設置日誌級別Level,在列印的過程中使用到了日誌記錄的LogRecord類,源碼找尋如下:
-
1、點擊logger.severe("severe信息");中的severe,當然點擊其他warning、info、config也是可以進去的,之後會看到如下的代碼 public void severe(String msg) { log(Level.SEVERE, msg); } 2、點擊上述的log(),看到如下的代碼 public void log(Level level, String msg) { if (!isLoggable(level)) { return; } // 這裡就是目的地 LogRecord lr = new LogRecord(level, msg); doLog(lr); }
-
-
4、Filter作為過濾器提供了日誌級別之外更細粒度的控制
-
5、Handler日誌處理器,決定日誌的輸出位置,例如控制台、文件...
-
6、Formatter是用來格式化輸出的
2、LOG4J
- 全稱:log for java
2.1、LOG4J的組成
- Loggers (日誌記錄器):控制日誌的輸出以及輸出級別(JUL做日誌級別Level)
- Appenders(輸出控制器):指定日誌的輸出方式(輸出到控制台、文件等)
- Layout(日誌格式化器):控制日誌信息的輸出格式
2.1.1、Loggers日誌記錄器
- 負責收集處理日誌記錄,實例的命名就是類的全限定名,如:
cn.zixieqing.test
,同時:Logger的名字大小寫敏感,其命名有繼承機制( 和JUL中的父子關係一樣,以包路徑來區分的 );另外:root logger是所有logger的根 - 上輩所做的日誌屬性設置,會直接的影響到子輩
// root logger的獲取
Logger.getRootLogger();
日誌級別
- DEBUG
- INFO
- WARN
- ERROR
- ........
- 大小關係:ERROR > WARN > INFO > DEBUG
- 輸出規則:輸出日誌的規則是:只輸出級別不低於設定級別的日誌信息
- 如:假設Loggers級別設定為INFO,則INFO、WARN、ERROR級別的日誌信息都會輸出,而級別比INFO低的DEBUG則不會輸出
2.1.2、Appenders輸出控制器
- 允許把日誌輸出到不同的地方,如控制台(Console)、文件(Files)等,可以根據時間或者文件大小產生新的文件,可以以流的形式發送到其它地方等等
- 常用Appenders輸出控制器類型:
- ConsoleAppender 將日誌輸出到控制台
- FileAppender 將日誌輸出到文件中
- DailyRollingFileAppender 根據指定時間輸出到一個新的文件, 將日誌輸出到一個日誌文件
- RollingFileAppender 根據指定文件大小,當文件大小達到指定大小時,會自動把文件改名,產生一個新的文件,將日誌信息輸出到一個日誌文件
- JDBCAppender 把日誌信息保存到資料庫中
2.2、玩LOG4J
2.2.1、入門
- 依賴
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
public class Log4jTest01 {
@Test
public void test01(){
// 載入初始化配置
BasicConfigurator.configure();
// 註意:這個logger是apache下的,前面玩的JUL中的是java。util.logging包下的
Logger logger = Logger.getLogger(Log4jTest01.class);
logger.fatal("fatal信息");
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
}
- 註意載入初始化信息:
BasicConfigurator.configure();
,不加這一句代碼就報錯 - 沒有添加Appenders輸出控制器,加上不報錯是因為:源碼中有這樣一句代碼rootManager.addAppenders( XxxxAppender( PatternLayout layout ) )
,configure源碼如下:
public static void configure() {
Logger root = Logger.getRootLogger();
root.addAppender(new ConsoleAppender(new PatternLayout("%r [%t] %p %c %x - %m%n")));
}
LOG4J日誌級別
- Log4j提供了8個級別的日誌輸出,分別為如下級別:
- ALL 最低等級 用於打開所有級別的日誌記錄
- TRACE 程式推進下的追蹤信息,這個追蹤信息的日誌級別非常低,一般情況下是不會使用的
- DEBUG 指出細粒度信息事件對調試應用程式是非常有幫助的,主要是配合開發,在開發過程中列印一些重要的運行信息,在沒有進行設置的情況下,預設的日誌輸出級別
- INFO 消息的粗粒度級別運行信息
- WARN 表示警告,程式在運行過程中會出現的有可能會發生的隱形的錯誤
- 註意,有些信息不是錯誤,但是這個級別的輸出目的就是為了給程式員以提示
- ERROR 系統的錯誤信息,發生的錯誤不影響系統的運行
- 一般情況下,如果不想輸出太多的日誌,則使用該級別即可
- FATAL 表示嚴重錯誤,它是那種一旦發生系統就不可能繼續運行的嚴重錯誤
- 如果這種級別的錯誤出現了,表示程式可以停止運行了
- OFF 最高等級的級別,用戶關閉所有的日誌記錄
2.2.2、分析源碼
BasicConfigurator.configure();
Logger logger = Logger.getLogger(Log4jTest01.class);
logger.fatal("fatal信息");
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
分析 BasicConfigurator.configure();點擊configure()
- 得到的代碼如下
public static void configure() {
Logger root = Logger.getRootLogger();
root.addAppender(new ConsoleAppender(new PatternLayout("%r [%t] %p %c %x - %m%n")));
}
-
從中可以得到幾個信息:
- 1、創建根節點的對象
Logger root = Logger.getRootLogger();
- 2、根節點添加了
ConsoleAppender對象(表示預設列印到控制台,自定義的格式化輸出PatternLayout)
- 1、創建根節點的對象
-
那麼想要自定義配置文件來實現上述源代碼的功能呢?通過上面這個源代碼分析,我們需要具備如下的條件:
- 們的配置文件需要提供Logger、Appender、Layout這3個組件信息
分析Logger logger = Logger.getLogger(Log4jTest01.class);點擊getLogger()
public static Logger getLogger(Class clazz) {
return LogManager.getLogger(clazz.getName());
}
- 發現:
LogManager.getLogger(clazz.getName());
,其中:LogManager
j就是日誌管理器
查看LogManager
- 首先看到如下信息,一堆常量
public static final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";
static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";
/** @deprecated */
public static final String DEFAULT_CONFIGURATION_KEY = "log4j.configuration";
/** @deprecated */
public static final String CONFIGURATOR_CLASS_KEY = "log4j.configuratorClass";
/** @deprecated */
public static final String DEFAULT_INIT_OVERRIDE_KEY = "log4j.defaultInitOverride";
private static Object guard = null;
private static RepositorySelector repositorySelector;
- 這些東西代表的就是不同形式(不同尾碼名)的配置文件,其中
log4j.properties屬性
使我們最常使用的,因為它語法簡單、使用方便
log4j.properties的載入時機
載入 - 那就是static
觀察LogManager中的代碼,找到其中的靜態代碼塊static
- 發現如下的一堆源碼
static {
Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));
repositorySelector = new DefaultRepositorySelector(h);
String override = OptionConverter.getSystemProperty("log4j.defaultInitOverride", (String)null);
if (override != null && !"false".equalsIgnoreCase(override)) {
LogLog.debug("Default initialization of overridden by log4j.defaultInitOverrideproperty.");
} else {
String configurationOptionStr = OptionConverter.getSystemProperty("log4j.configuration", (String)null);
String configuratorClassName = OptionConverter.getSystemProperty("log4j.configuratorClass", (String)null);
URL url = null;
if (configurationOptionStr == null) {
url = Loader.getResource("log4j.xml");
if (url == null) {
// 前面就是文件格式的一堆判斷,這裡才是log4j.propertie格式做的事情
url = Loader.getResource("log4j.properties");
}
} else {
try {
url = new URL(configurationOptionStr);
} catch (MalformedURLException var7) {
url = Loader.getResource(configurationOptionStr);
}
}
if (url != null) {
LogLog.debug("Using URL [" + url + "] for automatic log4j configuration.");
try {
// 這裡又是一個信息:selectAndConfigure()翻譯就是選擇配置文件
OptionConverter.selectAndConfigure(url, configuratorClassName, getLoggerRepository());
} catch (NoClassDefFoundError var6) {
LogLog.warn("Error during default initialization", var6);
}
} else {
LogLog.debug("Could not find resource: [" + configurationOptionStr + "].");
}
}
}
-
從源碼中,發現
url = Loader.getResource("log4j.properties");
- 從這句代碼得到的信息:系統預設是從當前的類路徑下找到
log4j.properties
,而若是maven工程,那麼:就應該在resources路徑下去找
- 從這句代碼得到的信息:系統預設是從當前的類路徑下找到
-
同時在上面的源碼中發現
OptionConverter.selectAndConfigure(url, configuratorClassName, getLoggerRepository());
查看selectAndConfigure()
- 發現如下源碼
public static void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) {
Configurator configurator = null;
String filename = url.getFile();
if (clazz == null && filename != null && filename.endsWith(".xml")) {
clazz = "org.apache.log4j.xml.DOMConfigurator";
}
if (clazz != null) {
LogLog.debug("Preferred configurator class: " + clazz);
configurator = (Configurator)instantiateByClassName(clazz, Configurator.class, (Object)null);
if (configurator == null) {
LogLog.error("Could not instantiate configurator [" + clazz + "].");
return;
}
} else {
// 有用信息在這裡,即 new PropertyConfigurator();創建了一個properties配置對象
configurator = new PropertyConfigurator();
}
((Configurator)configurator).doConfigure(url, hierarchy);
}
查看PropertyConfigurator類
- 首先看到的就是如下的常量信息
static final String CATEGORY_PREFIX = "log4j.category.";
static final String LOGGER_PREFIX = "log4j.logger.";
static final String FACTORY_PREFIX = "log4j.factory";
static final String ADDITIVITY_PREFIX = "log4j.additivity.";
static final String ROOT_CATEGORY_PREFIX = "log4j.rootCategory";
// 這是一個重要信息
static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
// 這也是一個重要信息
static final String APPENDER_PREFIX = "log4j.appender.";
static final String RENDERER_PREFIX = "log4j.renderer.";
static final String THRESHOLD_PREFIX = "log4j.threshold";
private static final String THROWABLE_RENDERER_PREFIX = "log4j.throwableRenderer";
private static final String LOGGER_REF = "logger-ref";
private static final String ROOT_REF = "root-ref";
private static final String APPENDER_REF_TAG = "appender-ref";
public static final String LOGGER_FACTORY_KEY = "log4j.loggerFactory";
private static final String RESET_KEY = "log4j.reset";
private static final String INTERNAL_ROOT_NAME = "root";
- 通過前面的基礎,從這源碼中,發現有兩個信息是要進行配置的
static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
static final String APPENDER_PREFIX = "log4j.appender.";
- 那麼這二者是怎麼進行配置的?
找尋static final String APPENDER_PREFIX = "log4j.appender."中的appender配置方式
直接在當前源碼頁面搜索appender
- 發現如下的源代碼
Appender parseAppender(Properties props, String appenderName) {
Appender appender = this.registryGet(appenderName);
if (appender != null) {
LogLog.debug("Appender \"" + appenderName + "\" was already parsed.");
return appender;
} else {
// 重要信息就在這裡,這裡告知了一件事:上面找到的log4j.appender.配置方式為如下的方式
String prefix = "log4j.appender." + appenderName;
// 這也是重要信息,layout日誌格式化的配置方式
String layoutPrefix = prefix + ".layout";
appender = (Appender)OptionConverter.instantiateByKey(props, prefix, Appender.class, (Object)null);
if (appender == null) {
LogLog.error("Could not instantiate appender named \"" + appenderName + "\".");
return null;
} else {
appender.setName(appenderName);
if (appender instanceof OptionHandler) {
if (appender.requiresLayout()) {
Layout layout = (Layout)OptionConverter.instantiateByKey(props, layoutPrefix, Layout.class, (Object)null);
if (layout != null) {
appender.setLayout(layout);
LogLog.debug("Parsing layout options for \"" + appenderName + "\".");
PropertySetter.setProperties(layout, props, layoutPrefix + ".");
LogLog.debug("End of parsing for \"" + appenderName + "\".");
}
}
String errorHandlerPrefix = prefix + ".errorhandler";
String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props);
if (errorHandlerClass != null) {
ErrorHandler eh = (ErrorHandler)OptionConverter.instantiateByKey(props, errorHandlerPrefix, ErrorHandler.class, (Object)null);
if (eh != null) {
appender.setErrorHandler(eh);
LogLog.debug("Parsing errorhandler options for \"" + appenderName + "\".");
this.parseErrorHandler(eh, errorHandlerPrefix, props, this.repository);
Properties edited = new Properties();
String[] keys = new String[]{errorHandlerPrefix + "." + "root-ref", errorHandlerPrefix + "." + "logger-ref", errorHandlerPrefix + "." + "appender-ref"};
Iterator iter = props.entrySet().iterator();
while(true) {
if (!iter.hasNext()) {
PropertySetter.setProperties(eh, edited, errorHandlerPrefix + ".");
LogLog.debug("End of errorhandler parsing for \"" + appenderName + "\".");
break;
}
Entry entry = (Entry)iter.next();
int i;
for(i = 0; i < keys.length && !keys[i].equals(entry.getKey()); ++i) {
}
if (i == keys.length) {
edited.put(entry.getKey(), entry.getValue());
}
}
}
}
PropertySetter.setProperties(appender, props, prefix + ".");
LogLog.debug("Parsed \"" + appenderName + "\" options.");
}
this.parseAppenderFilters(props, appenderName, appender);
this.registryPut(appender);
return appender;
}
}
}
-
通過上述的源碼,發現配置
log4j.appender.
的方式:log4j.appender.+appenderName
- 其中:appenderName就是輸出控制器名字
-
繼而:推導出
log4j.properties
配置文件中的一個配置項appender輸出方式為:log4j.appender.+appenderName=某一種輸出控制器名字
- 其中:輸出控制器名字在前面一開始就接觸過了
- 因此
Log4j.properties
的appender輸出方式配置方式舉例就是log4j.appender.console=org.apache.log4j.ConsoleAppender
- 同樣道理,通過第二句代碼
String layoutPrefix = prefix + ".layout";
,也就知道了layout輸出格式的配置方式
- layout日誌輸出格式配置舉例:
log4j.appender.console.layout=org.log4j.SimpleLayout
小小總結一波:log4j.properties配置文件中的appender輸出控制器 和 layout日誌輸出格式的配置方式
log4j.appender,console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.log4j.SimpleLayout
繼續找第二個配置static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger"中的rootLogger配置方式
通過log4j.rootLogge進行搜索
- 發現如下的方法
void configureRootCategory(Properties props, LoggerRepository hierarchy) {
String effectiveFrefix = "log4j.rootLogger";
String value = OptionConverter.findAndSubst("log4j.rootLogger", props);
if (value == null) {
value = OptionConverter.findAndSubst("log4j.rootCategory", props);
effectiveFrefix = "log4j.rootCategory";
}
if (value == null) {
LogLog.debug("Could not find root logger information. Is this OK?");
} else {
Logger root = hierarchy.getRootLogger();
synchronized(root) {
// 這裡面執行了這個方式
this.parseCategory(props, root, effectiveFrefix, "root", value);
}
}
}
查看parseCategory()
- 在這裡找到了想要的配置方式
void parseCategory(Properties props, Logger logger, String optionKey, String loggerName, String value) {
LogLog.debug("Parsing for [" + loggerName + "] with value=[" + value + "].");
// 配置方式就在這裡,這個操作的意思就是:表示要以逗號的方式來切割字元串,證明瞭log4j.rootLogger的取值,可以有多個值,使用逗號進行分隔
StringTokenizer st = new StringTokenizer(value, ",");
if (!value.startsWith(",") && !value.equals("")) {
if (!st.hasMoreTokens()) {
return;
}
// 把字元串通過逗號切割之後,第一個值的用途就在這裡 - levelStr、level,即:切割後的第一個值是日誌的級別
String levelStr = st.nextToken();
LogLog.debug("Level token is [" + levelStr + "].");
if (!"inherited".equalsIgnoreCase(levelStr) && !"null".equalsIgnoreCase(levelStr)) {
logger.setLevel(OptionConverter.toLevel(levelStr, Level.DEBUG));
} else if (loggerName.equals("root")) {
LogLog.warn("The root logger cannot be set to null.");
} else {
logger.setLevel((Level)null);
}
LogLog.debug("Category " + loggerName + " set to " + logger.getLevel());
}
logger.removeAllAppenders();
// 字元串切割之後的第一個值是level日誌級別,而剩下的值的用途就在這裡
while(st.hasMoreTokens()) {
// 通過這句代碼得知:第2 - 第n個值,就是我們配置的其他信息,這個信息就是appenderName
String appenderName = st.nextToken().trim();
if (appenderName != null && !appenderName.equals(",")) {
LogLog.debug("Parsing appender named \"" + appenderName + "\".");
Appender appender = this.parseAppender(props, appenderName);
if (appender != null) {
logger.addAppender(appender);
}
}
}
}
- 通過上述的代碼分析,得知
log4j.rootLogger
的配置方式為:log4j.rootLogger=日誌級別,appenderName1,appenderName2,appenderName3....
- 表示可以同時在根節點上配置多個日誌輸出的途徑
2,2,3、最基本的log4j.properties配置
- 通過前面的源碼分析之後,得出
BasicConfigurator.configure();
替代品的properties配置如下:
# rootLogger所有logger的根配置 - log4j.rootLogger=日誌級別,appenderName1,appenderName2,appenderName3....
# 這裡的例子沒用日誌輸出路徑,這個日誌輸出路徑後續再加
log4j.rootLogger=debug,console
# appender輸出控制器配置 log4j.appender.+appenderName=某一種輸出類型 - 採用Console控制台的方式舉例
log4j.appender.console=org.apache.log4j.ConsoleAppender
# 輸出的格式配置log4j.appender.+appenderName+layout=某種layout格式類型
log4j.appender.console.layout=org.apache.log4j.SimpleLayout
測試
// 註掉這一句就可以了,這句代碼的配置由上面的自定義配置進行代替了
//BasicConfigurator.configure();
Logger logger = Logger.getLogger(Log4jTest01.class);
logger.fatal("fatal信息");
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
2.2.3、打開日誌輸出的詳細信息
@Test
public void test03(){
/*
通過Logger中的開關
打開日誌輸出的詳細信息
查看LogManager類中的方法getLoggerRepository()
找到代碼LogLog.debug(msg, ex);
LogLog會使用debug級別的輸出為我們展現日誌輸出詳細信息
Logger是記錄系統的日誌,那麼LogLog就是用來記錄Logger的日誌
進入到LogLog.debug(msg, ex);方法中
通過代碼:if (debugEnabled && !quietMode)
觀察到if判斷中的這兩個開關都必須開啟才行
!quietMode是已經啟動的狀態,不需要我們去管
debugEnabled預設是關閉的
所以我們只需要設置debugEnabled為true就可以了
*/
// 開啟 debugEnabled
LogLog.setInternalDebugging(true);
Logger logger = Logger.getLogger(Log4jTest01.class);
logger.fatal("fatal信息");
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
- 若開啟debugEnabled,那麼輸出信息就是如下的樣子( 如下是舉的一個例子而已)
0 [main] FATAL cn.zixieqing.HotelJavaApplicationTests - fatal信息
1 [main] ERROR cn.zixieqing.HotelJavaApplicationTests - error信息
1 [main] WARN cn.zixieqing.HotelJavaApplicationTests - warn信息
1 [main] INFO cn.zixieqing.HotelJavaApplicationTests - info信息
1 [main] DEBUG cn.zixieqing.HotelJavaApplicationTests - debug信息
- 開啟之後,信息就會更全
log4j: Trying to find [log4j.xml] using context classloader sun.misc.Launcher$AppClassLoader@18b4aac2.
log4j: Trying to find [log4j.xml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
log4j: Trying to find [log4j.xml] using ClassLoader.getSystemResource().
log4j: Trying to find [log4j.properties] using context classloader sun.misc.Launcher$AppClassLoader@18b4aac2.
log4j: Trying to find [log4j.properties] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
log4j: Trying to find [log4j.properties] using ClassLoader.getSystemResource().
log4j: Could not find resource: [null].
0 [main] FATAL cn.zixieqing.HotelJavaApplicationTests - fatal信息
0 [main] ERROR cn.zixieqing.HotelJavaApplicationTests - error信息
1 [main] WARN cn.zixieqing.HotelJavaApplicationTests - warn信息
1 [main] INFO cn.zixieqing.HotelJavaApplicationTests - info信息
1 [main] DEBUG cn.zixieqing.HotelJavaApplicationTests - debug信息
2.2.4、自定義輸出格式 patternLayout
- 自定義配置的也就是
Layout
而已,而自定義就是玩的PatternLayout
,這個類有一個setConversionPattern()
方法,查看這個方法的源碼
public void setConversionPattern(String conversionPattern) {
this.pattern = conversionPattern;
this.head = this.createPatternParser(conversionPattern).parse();
}
- 從中發現,需要配置的就是
String conversionPattern
,因此:在log4j.properties
配置文件中添加上conversionPattern
屬性配置即可,當然:這個屬性配置遵循一定的寫法,寫法如下:
%m 輸出代碼中指定的日誌信息
%p 輸出優先順序,及 DEBUG、INFO 等
%n 換行符(Windows平臺的換行符為 "\n",Unix 平臺為 "\n")
%r 輸出自應用啟動到輸出該 log 信息耗費的毫秒數
%c 輸出列印語句所屬的類的全名
%t 輸出產生該日誌的線程全名
%d 輸出伺服器當前時間,預設為 ISO8601,也可以指定格式,如:%d{yyyy年MM月dd日 HH:mm:ss}
%l 輸出日誌時間發生的位置,包括類名、線程、及在代碼中的行數。如:Test.main(Test.java:10)
%F 輸出日誌消息產生時所在的文件名稱
%L 輸出代碼中的行號
%% 輸出一個 "%" 字元
可以在 % 與字元之間加上修飾符來控制最小寬度、最大寬度和文本的對其方式
[%10p]:[]中必須有10個字元,由空格來進行補齊,信息右對齊
[%-10p]:[]中必須有10個字元,由空格來進行補齊,信息左對齊,應用較廣泛
上述舉例:[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
- 修改了
log4j.properties
的配置如下( 做的修改就是最後兩個配置項 )
# rootLogger所有logger的根配置 - log4j.rootLogger=日誌級別,appenderName1,appenderName2,appenderName3....
log4j.rootLogger=debug,console
# appender輸出控制器配置 log4j.appender.+appenderName=某一種輸出類型 - 採用Console控制台的方式舉例
log4j.appender.console=org.apache.log4j.ConsoleAppender
# 輸出的格式配置log4j.appender.+appenderName+layout=某種layout格式類型 - 註意:這裡類型改了,是PatternLayout,即自定義
log4j.appender.console.layout=org.apache.log4j.PatternLayout
# 編寫自定義輸出格式( 直接把上面舉例的拿過來 ) - 註意:加上了剛剛說的conversionPattern屬性
log4j.appender.console.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
- 測試代碼
public void test04(){
LogLog.setInternalDebugging(true);
Logger logger = Logger.getLogger(Log4jTest01.class);
logger.fatal("fatal信息");
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
2.2.5、將日誌輸出到文件中
2.2.5.1、改造log4j.properties文件
# rootLogger所有logger的根配置 - 這裡再加一個file
log4j.rootLogger=debug,console,file
# 控制台的appender輸出控制器配置
log4j.appender.console=org.apache.log4j.ConsoleAppender
# 採用自定義控制台輸出的格式
log4j.appender.console.layout=org.apache.log4j.PatternLayout
# 編寫控制台自定義輸出格式
log4j.appender.console.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
# 再來一份,變為file的配置 - 把console改為file即可
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
2.2.5.2、指定文件輸出位置 及 字元編碼設置
- 查看
FileAppender
源碼,首先看到的是四個屬性
// 表示日誌文件是否採用內容追加的方式 - 源碼中有一個構造方法,這個的預設值是true
protected boolean fileAppend;
protected String fileName;
protected boolean bufferedIO;
// 日誌文件的大小 - 源碼的構造方法中預設值是8192
protected int bufferSize;
// 構造方法源碼
public FileAppender() {
this.fileAppend = true;
this.fileName = null;
this.bufferedIO = false;
this.bufferSize = 8192;
}
- 在
FlieAppender
中還有一個setFile()
的方法,得知這個就是設置文件的方法 ,也就是文件日誌文件存放路徑位置
public void setFile(String file) {
String val = file.trim();
this.fileName = val;
}
- 這裡面需要傳一個file參數進去,因此:通過
MyBatis
的知識就知道log4j.properties
配置中的這個對應屬性就是file了( 截掉set,得到屬性名 )
# rootLogger所有logger的根配置 - 這裡再加一個file
log4j.rootLogger=debug,console,file
# 控制台的appender輸出控制器配置
log4j.appender.console=org.apache.log4j.ConsoleAppender
# 採用自定義控制台輸出的格式
log4j.appender.console.layout=org.apache.log4j.PatternLayout
# 編寫控制台自定義輸出格式
log4j.appender.console.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
# 再來一份,變為file的配置 - 把console改為file即可
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
# 日誌文件保存路徑 - 註:前一個file為自定義的appenderName;後一個file是日誌文件輸出路徑的屬性,要是怕看錯眼,可以把後者換為File大寫也沒錯
log4j.appender.file.file=D:\log4j\zixieqing\log4j.log
- 現在看
FileAppender
的父類WriterAppender
,去看字元編碼設置
protected boolean immediateFlush;
// 這個屬性就是編碼設置
protected String encoding;
protected QuietWriter qw;
- 因此:加上日誌文件輸出路徑和字元編碼之後的
log4.properties
配置為:
# 控制台日誌輸出設置
log4j.rootLogger=debug,console,file
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
# 日誌文件輸出設置
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
log4j.appender.file.file=D:\log4j\zixieqing\log4j.log
log4j.appender.file.encoding=UTF-8
2.2.5.3、拆分日誌文件
-
一份日誌不可能用於一直記錄下去,那樣的話,日誌文件體積就太大了
-
繼續查看
FileAppender
源碼,看實現類
2.2.5.3.1、RollingFileAppender實現類
- 這個玩意兒就是利用文件大小來進行日誌拆分
- 源碼中有兩個屬性需要關註
// 達到多大文件時進行日誌拆分
protected long maxFileSize = 10485760L;
// 一共能夠拆分出多少份日誌文件
protected int maxBackupIndex = 1;
- 因此:現在將
log4j.properties
配置文件修改一下即可實現日誌根據文件大小進行拆分
# 註意:記得在log4j.rootLoger=debug,console,file這一句中加上下麵的這個文件大小進行拆分的配置
# 如:log4j.rootLoger=debug,console,rollingFile
log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
log4j.appender.rollingFile.file=D:\log4j\zixieqing\log4j.log
# 文件多大時進行日誌拆分
log4j.appender.rollingFile.maxFileSize=10MB
# 最多可以拆分多少份
log4j.appender.rollingFile.maxBackupIndex=50
2.2.5.3.2、DailyRollingFileAppender實現類 - 建議用
- 這個東西就是根據時間來進行日誌拆分,看源碼,有這麼一個屬性
// 時間格式,預設值就是如下的天
private String datePattern = "'.'yyyy-MM-dd";
- 在
log4j.properties
配置文件中修改成如下的配置即可使用
# 一樣的,要使用這種方式:記得在log4j.rootLogger=debug,console,file這一句中加上下麵的這個文件大小進行拆分的配置
# 如:log4j.rootLogger=debug,console,dateRollingFile
log4j.appender.dateRollingFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.dateRollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.dateRollingFile.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
log4j.appender.dateRollingFile.file=D:\log4j\zixieqing\log4j.log
# 加上時間格式 - 下麵的值根據實際情況即可
log4j.appender.dateRollingFile.datePattern='.'yyyy-MM-dd
2.2.6、日誌持久化到資料庫
表基礎欄位
CREATE TABLE tbl_log(
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(100) DEFAULT NULL COMMENT '項目名稱',
createTime varchar(100) DEFAULT NULL COMMENT '創建時間',
level varchar(10) DEFAULT NULL COMMENT '日誌級別',
category varchar(100) DEFAULT NULL COMMENT '所在類的全路徑',
fileName varchar(100) DEFAULT NULL COMMENT '文件名稱',
message varchar(255) DEFAULT NULL COMMENT '日誌消息',
PRIMARY KEY(id)
)
項目MySQL驅動
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
<scope>runtime</scope>
</dependency>
log4.properties配置
# 配置appender輸出方式 輸出到資料庫表 - 註意:在rootManager中加上logDB,如:log4j.rootLogger=debug,console,logDB
log4j.appender.logDB=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.logDB.layout=org.apache.log4j.PatternLayout
log4j.appender.logDB.Driver=com.mysql.jdbc.Driver
log4j.appender.logDB.URL=jdbc:mysql://localhost:3306/test
log4j.appender.logDB.User=root
log4j.appender.logDB.Password=072413
log4j.appender.logDB.Sql=INSERT INTO tbl_log(name,createTime,level,category,fileName,message) values('project_log','%d{yyyy-MM-dd HH:mm:ss}','%p','%c','%F','%m')
測試代碼
@Test
public void test07(){
Logger logger = Logger.getLogger(Log4jTest01.class);
logger.fatal("fatal信息");
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
2.2.7、自定義logger
- 前面配置文件中使用的都是
rootManager
的logger,接下來就自定義一個logger,看PropertyConfigurator
類的源碼,它裡面有一個屬性
// 自定義logger配置的寫法 - 這後面拼接的就是自定義的logger名字
static final String LOGGER_PREFIX = "log4j.logger.";
- 其中:上述說的自定義logger名字遵循父子關係,也就是包關係
如:cn.zixieqing.log4j.test.Log4jTest01
它的父logger就是上層的路徑或者是更上層的路徑
例如:
cn.zixieqing.log4j.test
cn.zixieqing.log4j
...
cn
修改log4j.properties
# 根logger,輸出級別是trace,在console控制台進行輸出
log4j.rootLogger=trace,console
# 自定義logger,級別為info,在file文件中輸出
log4j.logger.cn.zixieqing.log4j.test=info,file
# 自定義logger,是apache的,級別為error
log4j.logger.org.apache=error
自定義logger的註意點
- 如果根節點的logge( 即:rootManager ) 和 自定義logger配置的輸出位置是不同的,則取二者的並集,配置的位置都會進行輸出操作
- 如果二者配置的日誌級別不同,以我們自定義logger的輸出級別為主
2.2.8、一份簡單的log4j.properties配置
log4j.rootLogger=DEBUG,console,file
#控制台輸出的相關設置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件輸出的相關設置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./1og/zixieqing.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]‰m%n
#日誌輸出級別
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sq1=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
3、JCL
-
全稱為Jakarta Commons Logging,是Apache提供的一個通用日誌AP
-
註意:這個玩意兒本身沒有記錄日誌的實現功能,而是相當於一個門面。我們可以自由選擇第三方的日誌組件( log4j、JUL )作為具體實現,common-logging會通過動態查找的機制,在程式運行時自動找出真正使用的日誌庫
JCL的組成
- JCL 有兩個基本的抽象類
- Log:日誌記錄器
- LogFactory:日誌工廠(負責創建Log實例)
3.1、玩JCL
依賴
<dependency>