歡迎來到Apache Log4j2範例教程。如果你問一位專業的開發人員關於應用程序最煩人的事情,答案可能與日誌記錄有關。如果應用程序中沒有合適的日誌記錄,維護將會是一場噩夢。大多數應用程序都會經歷開發測試、單元測試、集成測試。但是當進入生產環境時,您總會面臨獨特的情況和異常。因此,在特定情況下弄清楚發生了什麼的唯一方法就是通過日誌進行調試。許多框架提供了一些默認的日誌記錄方式,但最好還是遵循行業標準的日誌記錄機制。Apache Log4j是最廣泛使用的日誌記錄框架之一。Apache Log4j 2是下一個版本,比Log4j好得多。
Log4j範例教程
在這個Log4j2範例教程中,您將學習如何開始使用Apache Log4j2。我們還將探索Log4j2的架構、log4j2配置、log4j2日誌記錄級別、附加器、過濾器等等。
- Log4j2概述
- Log4j2架構
- Log4j2配置
- Log4j2級別
- Log4j2查找
- Log4j2附加器
- Log4j2過濾器
- Log4j2 布局
- 您应该使用哪个 Log4j2 级别
- Log4j2 教程摘要
Log4j2 概述
在应用程序中使用日志记录 API 不是奢侈,而是必不可少的。Log4j 是一个开源库,发布和授权协议均在 Apache Software 下。您可以使用 Eclipse 调试或其他一些工具来调试应用程序,但这在生产环境中是不够的且不可行的。日志记录机制将为您提供一些在普通调试中找不到的好处。
Category / Operation (Debugging, Logging) | Debugging | Logging |
---|---|---|
Human Intervention | There’s a need for human intervention | No need for human intervention |
Persistent Medium | Can’t be integrated with persistent storage | Can be integrated with persistent storage (Files, Database, NoSQL database, etc.) |
May used for Auditing | Can’t be used for achieving auditing | Can be used for achieving auditing if it’s used efficiently |
Sufficient for complicated structure and flow | Not sufficient; you may get lost with flow. | Sufficient |
Productivity | Less productive | More productive |
正如您所看到的,使用日志记录机制将更有效率且维护成本更低。Apache Log4j 是 Java 应用程序中日志记录的领先工具,因此您应该使用它。
Log4j2 架构
在我们继续进行Log4j示例教程之前,了解Log4j2架构是很重要的。下面的图片显示了Log4j2 API中的重要类。 以下是上述架构的详细解释:
-
应用程序将向
LogManager
请求具有特定名称的Logger
。 -
LogManager
将定位适当的LoggerContext
,然后从中获取Logger
。 -
如果尚未创建Logger,则将根据以下三个选择创建Logger并与LoggerConfig关联:
- 将创建Logger实例并与具有相同名称的LoggerConfig关联。例如,在
getLogger(App.class)
中,App.class将被评估为字符串com.journaldev.App
。 LoggerConfig名称与完全限定类名(软件组件)相同。 - 将创建Logger实例并与具有相同记录器父包的LoggerConfig关联。例如,在
getLogger("com.journaldev")
中的com.journaldev
- 将创建Logger实例并与根LoggerConfig关联。当没有配置文件或当您获取的记录器的名称未在记录器声明中定义时,将使用根LoggerConfig。
- 将创建Logger实例并与具有相同名称的LoggerConfig关联。例如,在
-
LoggerConfig
對象是從配置文件中的Logger聲明創建的。 LoggerConfig也用於處理LogEvents
並將它們委派給其定義的Log4j2 Appenders。 -
根日誌記錄器在其存在方面是一個特殊情況。 它總是存在並位於任何日誌記錄器層次結構的頂部。
-
您可以使用以下語句獲取根日誌記錄器:
Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME); Logger logger = LogManager.getRootLogger();
-
log4j2日誌記錄器的名稱區分大小寫。
-
除根日誌記錄器外,所有日誌記錄器都可以通過將其名稱傳遞給
LogManager.getLogger()
來獲取。 -
LoggerContext是日志系统的一个重要组成部分,因为您的应用程序中可能有多个LoggerContexts。对于每个LoggerContext,应设置一个活动配置。
-
Log4j2配置包含所有日志系统资产;LoggerConfig(s)、Appender(s)、Filter(s)和许多其他内容。
-
通过传递相同的名称调用LogManager.getLogger()将始终返回完全相同的记录器实例的引用。
-
通常在应用程序初始化时配置日志系统。这可以采用不同的形式进行;以编程方式或通过读取log4j2配置文件。
每個記錄器都與一個 LoggerConfig 物件相關聯,一組 LoggerConfig 物件構成了一個記錄器的層次結構。這個概念被稱為記錄器層次結構。記錄器層次結構由具有父子關係的一組 LoggerConfig 物件組成。每個 Logger 層次結構中最頂層的元素是根記錄器。如果 Log4j2 找不到配置文件,則只會使用根記錄器進行日誌記錄,日誌級別為 ERROR。下面的圖像顯示了在這種情況下您將收到的警告消息。錯誤 StatusLogger 未找到 log4j2 配置文件。使用默認配置:僅將錯誤記錄到控制台。 下表顯示了 Logger 層次結構中的父子關係。
LoggerConfig (Is A) | Root | com | com.journaldev | com.journaldev.logging |
---|---|---|---|---|
Root | X | Child | descendant | descendant |
com | Parent | X | Child | descendant |
com.journaldev | Ancestor | Parent | X | Child |
com.journaldev.logging | Ancestor | Ancestor | Parent | X |
為了澄清父子關係,上表應該讀作:
- Root 是 com 的父節點。
- Root 是 com.journaldev 的祖先。
- Root 是 com.journaldev.logging 的祖先。
- com 是 Root 的子節點。
- com 是 com.journaldev 的父節點。
- com 是 com.journaldev.logging 的祖先。
- com.journaldev.logging 是 com.journaldev 的子節點,依此類推。
一個 LoggerConfig 的實例被稱為另一個 LoggerConfig 的祖先,如果其名稱後跟一個點是後者名稱的前綴。如果兩者之間沒有交錯的名稱,則 LoggerConfig 的一個實例被稱為另一個 LoggerConfig 的父節點。
Log4j2 配置
有很多種方式可以在您的應用程序中使用 Log4j2 配置。
- 使用以 XML、JSON、YAML 或屬性文件編寫的配置文件。
- 通過創建配置工廠和配置實現來以程序方式使用。
- 通過調用配置接口中公開的 API 來以程序方式使用。
- 通過在內部日誌記錄器類上調用方法來以程序方式使用。
我們主要將重點放在配置文件上。然而,瞭解編程方法也是很好的,以防您想為某些特定的 Logger 配置特定的日誌記錄策略。首先,讓我們考慮一下您未提供配置文件的情況。Log4j2 實現假定存在一個名為 log4j.configurationFile 的系統變量,指向 log4j2 配置文件的位置。
package com.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class App
{
public static void main( String[] args ) {
Logger logger = LogManager.getRootLogger();
logger.trace("Configuration File Defined To Be :: "+System.getProperty("log4j.configurationFile"));
}
}
A simple log4j2 configuration file will look like below. configuration.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
這裡是上面列出的代碼的詳細解釋:
- 應用程序通過調用 LogManager 的
getRootLogger
方法引用了根日誌記錄器。 - 從 LogManager 引用日誌記錄器已啟動 Log4j 系統。
- Log4j 會檢查 log4j.configurationFile 系統屬性以確定 log4j2 配置文件。 Log4j 配置可以以 JSON、YAML 和 XML 格式編寫。
- 我們可以通過
System.setProperties("log4j.configurationFile","FILE_PATH")
來設置 log4j.configurationFile 系統屬性,或者通過將其作為 JVM 參數傳遞,就像你在下面的圖中看到的那樣。同時注意文件協議前綴。
- 如果未定义系统属性,则配置顺序如下优先级:
- Property ConfigurationFactory 将在类路径中查找
log4j2-test.properties
。 - YAML ConfigurationFactory 将在类路径中查找 log4j2-test.yaml 或 log4j2-test.yml。
- JSON ConfigurationFactory 将在类路径中查找 log4j2-test.jsn 或 log4j2-test.json。
- XML ConfigurationFactory 将在类路径中查找 log4j2-test.xml。
- Property ConfigurationFactory 将在类路径中查找
log4j2.properties
。 - YAML ConfigurationFactory 将在类路径中查找 log4j2.yml 或 log4j2.yaml。
- JSON ConfigurationFactory 将在类路径中查找 log4j2.jsn 或 log4j2.json。
- XML ConfigurationFactory 将在类路径中查找 log4j2.xml。
- 如果未提供配置文件,则将使用 DefaultConfiguration,这将导致一组默认行为:
- 将使用根记录器。
- 根记录器级别将设置为 ERROR。
- 根记录器将将日志消息传播到控制台。
- PatternLayout 被设置为
%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
- Property ConfigurationFactory 将在类路径中查找
使用 log4j2 配置文件使 log4j2 配置變得如此簡單,但讓我們看看如何以編程方式配置它。這一切都與使用 ConfigurationFactory 有關。
package com.journaldev;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
public class App
{
public static void main( String[] args ) throws FileNotFoundException, IOException {
// 獲取配置工廠的實例;您可以選擇使用默認的ConfigurationFactory、XMLConfigurationFactory、
// YamlConfigurationFactory和JsonConfigurationFactory
ConfigurationFactory factory = XmlConfigurationFactory.getInstance();
// 定位配置的來源,這個位於的文件是一個虛擬文件,僅包含一個空的配置標籤
ConfigurationSource configurationSource = new ConfigurationSource(new FileInputStream(new File("C:/dummyConfiguration.xml")));
// 獲取配置的引用
Configuration configuration = factory.getConfiguration(configurationSource);
// 創建默認的控制台 appender
ConsoleAppender appender = ConsoleAppender.createDefaultAppenderForLayout(PatternLayout.createDefaultLayout());
// 將控制台 appender 添加到配置中
configuration.addAppender(appender);
// 創建 loggerConfig
LoggerConfig loggerConfig = new LoggerConfig("com",Level.FATAL,false);
// 添加 appender
loggerConfig.addAppender(appender,null,null);
// 添加 logger 並將其與 loggerConfig 實例關聯
configuration.addLogger("com", loggerConfig);
// 獲取上下文實例
LoggerContext context = new LoggerContext("JournalDevLoggerContext");
// 啟動日誌系統
context.start(configuration);
// 獲取 logger 的引用
Logger logger = context.getLogger("com");
// DEBUG 消息的 LogEvent
logger.log(Level.FATAL, "Logger Name :: "+logger.getName()+" :: Passed Message ::");
// 配置為 FATAL 的 Logger 的 Error 消息的 LogEvent
logger.log(Level.ERROR, "Logger Name :: "+logger.getName()+" :: Not Passed Message ::");
// 將由 Root 處理的 ERROR 消息的 LogEvent
logger.getParent().log(Level.ERROR, "Root Logger :: Passed Message As Root Is Configured For ERROR Level messages");
}
}
- 您可以使用Log4j2提供的任何ConfigurationFactory,或者使用默認的配置工廠。我們使用了
XMLConfigurationFactory
來獲取 ConfigurationFactory 的實例。 - 工廠將通過傳遞相應的配置文件來提供所需的 Configuration 引用。
- 配置實例將與 LoggerContext 一起用於啟動日誌系統。
- A console Appender has been configured and added into configuration instance with default layout. This Appender would print out messages into your console.
- 已使用提供的名稱、LEVEL和無過濾器創建了 LoggerConfig 實例。創建的 appender 將分配給此 LoggerConfig 實例。
- 將 LoggerConfig 實例添加到配置實例中。
- A new instance of LoggerContext is created with defined name.
- 配置實例已傳遞給LoggerContext實例並對後者調用了start方法。
- A logger instance has been acquired from LoggerContext. This logger instance will be used to fire set of Log events.
- Logger實例觸發了三個事件,這些事件將在Log4j2 Levels部分中進行解釋。
- com logger已配置為打印出級別為FATAL的消息。
- 默認情況下,根logger已配置為打印出級別為ERROR的消息。
- ERROR消息將不會被“com”logger記錄,因為它的級別是FATAL。
可以通過使用YAML、JSON或屬性文件來進行相同的配置。但是,log4j2屬性文件配置與log4j屬性文件不同,因此請確保您不嘗試將log4j屬性文件配置與log4j2一起使用。它將拋出以下錯誤;
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
處理上述代碼將給出以下輸出:
Logger Name :: com :: Passed Message ::
00:01:27.705 [main] ERROR - Root Logger:: Passed Message As Root Is Configured For ERROR Level messages
日誌的第一行來自comlogger,第二行來自根Logger。 com logger的錯誤消息未被打印,因為其級別為Fatal。
Log4j2級別
在上述的程式碼範例中,我們可以看到每次定義 LoggerConfig 時,都會提供日誌級別。預設情況下,log4j2 的日誌是累加的。這意味著當使用特定的日誌記錄器時,所有父日誌記錄器也將被使用。下圖澄清了這種情況。 以下是針對此的澄清要點:
- 正如我們之前所述,每個日誌記錄器都與 LoggerConfig 實例相關聯。這個 loggerConfig 已在配置範圍內定義。
- 日誌記錄的層級可以在 LoggerConfig 範圍內確定。
- 您可以通過其名稱、父包或指向根 Logger 本身來獲取日誌記錄器。
- 根 Logger 是每個 LoggerConfig 層次結構的最高級節點。
- 一旦您獲取了
com.journaldev
日誌記錄器並初始化了一個 logEvent 進行記錄,loggerConfig (net.journaldev) 將記錄消息,消息也將被傳播到層次結構中的更上層,而不考慮父級日誌記錄器的日誌級別。因此,log 事件將被傳播到 com 和 Root 日誌記錄器,它們將根據定義的級別分別記錄消息。 - 一旦您獲取了 com 日誌記錄器並初始化了一個 logEvent 進行記錄,loggerConfig(com) 將記錄消息,消息也將被傳播到層次結構中的更上層,而不考慮父級日誌記錄器的日誌級別。也就是說,Root 日誌記錄器將傳播 log 事件並記錄消息。
- net.journaldev 層次結構也是相同情況。
- 接下来的章节,将对添加概念进行更多的说明。
- 父级有机会通过使用过滤器概念或将添加指示器设置为false来忽略消息,因此日志事件将不会传播到父级。
- 如果相应的loggerConfig的级别大于日志事件的级别,则记录器有可能忽略消息。
现在,让我们看看与上述解释的添加性概念相关的示例:
import net.NetApp;
import net.journaldev.NetJournalDevApp;
import com.ComApp;
import com.journaldev.ComJournalDevApp;
public class Main {
public static void main(String [] args){
new ComApp();
new ComJournalDevApp();
new NetApp();
new NetJournalDevApp();
}
}
package com.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class ComJournalDevApp {
public ComJournalDevApp(){
Logger logger = LogManager.getLogger(ComJournalDevApp.class);
logger.trace("COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::");
}
}
package net;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class NetApp {
public NetApp(){
Logger logger = LogManager.getLogger(NetApp.class);
logger.error("NET :: LEVEL :: NetApp ERROR Message ::");
}
}
package net.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class NetJournalDevApp {
public NetJournalDevApp(){
Logger logger = LogManager.getLogger(NetJournalDevApp.class);
logger.error("NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::");
}
}
log4j2配置文件如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console"/>
</Root>
<logger name="com" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="net" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
<logger name="net.journaldev" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
</Loggers>
</Configuration>
如果你执行Main类,你会发现以下结果:
10:34:47.168 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
10:34:47.168 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.171 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
10:34:47.171 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
以下是上面列出的代码的详细解释:
- 配置文件包含了五个loggerConfig实例的定义,它们分别是Root、com、com.journaldev、net和net.journaldev。就像上面显示的Logger层次结构一样。
- Root的级别被配置为ERROR,这实际上是默认值。
- com和com.journaldev的级别被配置为TRACE。
- net和net.journaldev的级别被配置为ERROR。
- 你可能注意到ComAPP和ComJournalDevApp记录器的消息分别显示了两次和三次。这些消息根据ComApp和ComJournalDevApp的Logger层次结构显示,在那里它们分别位于com和com.journalDev包中。我们在NetApp和NetJournalDevApp类中也有类似的情况。
- 父级会根据默认情况下设置的添加指示器进行传播。
記錄空間考慮了日誌事件的級別、loggerConfig 的級別,以及 Logger 階層。
那麼,如果我們將 com 的 LoggerConfig 更改為 INFO,並保持整個程式不變:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console"/>
</Root>
<logger name="com" level="INFO">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="net" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
<logger name="net.journaldev" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
</Loggers>
</Configuration>
結果如下:
11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.307 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
11:08:10.307 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
- 當然,您可能會注意到 ComAPP 的日誌事件被忽略了,這是因為 com 包的 loggerConfig 定義了 INFO (400) 級別,低於此處的 TRACE (600) 級別日誌事件。因此,ComApp 的消息將不再顯示,要使其顯示,您需要將 com 的 LoggerConfig 級別修改為 TRACE (600) 或 ALL (Integer.MAX_VALUE)。
為確保日誌事件已顯示,LoggerConfig 的級別應大於或等於日誌事件的級別。
下表顯示了 log4j2 級別及其權重:
LEVEL | Weight |
---|---|
OFF | 0 |
FATAL | 100 |
ERROR | 200 |
WARN | 300 |
INFO | 400 |
DEBUG | 500 |
TRACE | 600 |
ALL | Integer.MAX_VALUE |
當然,上面的表格比文字更能清楚地說明,它為日誌事件 TRACE 不顯示的主要原因提供了解釋,即使 LoggerConfig 的級別是 INFO。
請注意,日誌事件在 logger 階層中的傳播超出此計算並且忽略了級別。
但是,如果我們從配置中刪除了 com.journaldev 的 LoggerConfig,並添加了一個新的配置來構建 com.journaldev.logging 的配置文件,會發生什麼?
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console"/>
</Root>
<logger name="com" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev.logging" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="net" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
<logger name="net.journaldev" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
</Loggers>
</Configuration>
您可能会发现下面的图更方便您理解上述log4j2配置所发生的情况。以下是对上面显示的图的一些说明,以及它如何影响日志事件的行为:
- 当由名为com.journaldev.logging的Logger抛出日志事件时,将使用与该名称(即com.journaldev.logging)关联的LoggerConfig来处理并打印出消息。
- 由于com.journaldev.logging LoggerConfig的additive属性默认设置为true,因此该日志事件已被传播到父级,这在本例中是指com.journaldev。
- 由于在配置中未定义com.journaldev LoggerConfig,因此不会发生任何操作,并且日志事件将被传播到com,然后传播到Root LoggerConfig实例。
- Com和Root将接收日志事件并打印输出,而不管它以哪个级别发送。
由于提到的几点,您将看到以下输出:
14:08:37.634 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:08:37.634 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:08:37.636 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:08:37.636 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.638 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:08:37.638 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
您可能会注意到以下内容:
- com包中的日志事件显示了两次。一次是为com,第二次是为Root。
- 日志事件在 com.journaldev 被显示了两次。一次是为 com,第二次是为 Root。即使之前它出现了三次,但现在 com.journaldev 的 LoggerConfig 缺失了,所以在 com.journaldev 包中可能没有发生日志记录,该事件会传播给 com 和 Root。
- 日志事件在 com.journaldev.logging 被显示了三次,一次是为 com.journaldev.logging 包,第二次是为 com,第三次是为 Root。根据 Logger 层次传播,应该显示第四次,但由于 com.jounraldev 的 LoggerConfig 缺失,它只显示了三次。
如果您在配置文件中定义了一个 com.journaldev 的 LoggerConfig 实例,但没有指定 Level,它将继承其父级的 Level。
但是,如果您在配置文件中定义了 com.journaldev 的 LoggerConfig,并且忘记指定 LoggerConfig 的级别,那么幸运的是,Logger 层次的概念会在这里为您节省麻烦,com.journaldev 将从其父级继承级别值。以下是一个示例配置文件,后面是每个日志记录器配置的日志级别表。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console"/>
</Root>
<logger name="com" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev.logging" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="net" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
<logger name="net.journaldev" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
</Loggers>
</Configuration>
Logger Name | Assigned LoggerConfig | LoggerConfig Level | Logger Level |
---|---|---|---|
Root | Root | ERROR | ERROR |
com | com | TRACE | TRACE |
com.journaldev | com | TRACE | TRACE |
com.journaldev.logging | com.journaldev.logging | TRACE | TRACE |
- com.journaldev.logging 包已经关联了一个日志级别为 TRACE 的 LoggerConfig。
- com.journaldev 包已经关联了一个没有指定日志级别的 LoggerConfig,所以它将继承其父级的日志级别,并且对于 com 包来说,该值肯定是 TRACE。
- com 包已经关联了一个日志级别为 TRACE 的 LoggerConfig。
- 默认情况下,Root 的日志级别为 ERROR。
- 如果未声明 com 包,则 com.journaldev 的 LoggerConfig 将继承 Root 的日志级别。
以下是當 com.journaldev 繼承 com 日誌級別時執行的結果:
14:41:37.419 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:41:37.419 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.423 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:41:37.423 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
如果刪除 com 包的 LoggerConfig 声明,則以下結果將會是:
14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.811 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:43:28.811 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
您可能會注意到 com 和 com.journaldev 沒有記錄任何消息,原因如下:
- 刪除與 com 包關聯的 LoggerConfig 將使該包中提到的所有日誌事件被忽略。
- 由於配置中未為 com 包定義 LoggerConfig,因此與 com.journaldev 相關聯的 LoggerConfig 將繼承其父級的日誌級別。Com 未定義,且 Logger 層次結構已達到頂層,現在它參考的是 Root。根日誌級別為 ERROR(200),而 com.journaldev 中的日誌事件級別為 TRACE(600) – 請參見 ComJournalDevApp – 根據先前定義的等式,LoggerConfig 級別應大於或等於日誌事件,但這是錯誤的,因此對於 com.journaldev 不會顯示任何消息。
最後但並非最不重要的,以下表格顯示了您在使用日誌記錄系統時可能遇到的所有可能的日誌記錄方案:
X (N/A) | LoggerConfig Level | OFF(0) | FATAL(100) | ERROR(200) | WARN(300) | INFO(400) | DEBUG(500) | TRACE(600) | ALL(MAX) |
---|---|---|---|---|---|---|---|---|---|
Event Level | X | X | X | X | X | X | X | X | X |
OFF(0) | X | YES | NO | NO | NO | NO | NO | NO | NO |
FATAL(100) | X | NO | YES | YES | YES | YES | YES | YES | YES |
ERROR(200) | X | NO | NO | YES | YES | YES | YES | YES | YES |
WARN(300) | X | NO | NO | NO | YES | YES | YES | YES | YES |
INFO(400) | X | NO | NO | NO | NO | YES | YES | YES | YES |
DEBUG(500) | X | NO | NO | NO | NO | NO | YES | YES | YES |
TRACE(600) | X | NO | NO | NO | NO | NO | NO | YES | YES |
ALL(MAX) | X | NO | NO | NO | NO | NO | NO | NO | YES |
- 沒有可以用於拋出 OFF/ALL 日誌事件的直接方法。
- 主要地,用於拋出 OFF/ALL 日誌事件,您可以分別使用 logger.log(Level.OFF, “Msg”) 或 logger.log(LEVEL.ALL,“Msg”)。
- log 方法負責根據上述等式處理日誌事件。
處理方程式說明:如果 LoggerConfig 級別大於或等於日誌事件的級別,則該事件將被接受進行進一步處理。
日志事件將被接受進行進一步處理 – 這很重要,因為您可以使用Log4j2過濾器阻止某些事件被處理,即使它被接受。您可以將additive屬性設置為false,以避免將日志事件傳播到父記錄器。以下是您之前看到的相同示例,但這次帶有一個additivity屬性,因此您可能會注意到差異。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console"/>
</Root>
<logger name="com" level="TRACE" additivity="false">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev" additivity="false">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev.logging" level="TRACE" additivity="false">
<AppenderRef ref="Console"/>
</logger>
<logger name="net" level="ERROR" additivity="false">
<AppenderRef ref="Console"/>
</logger>
<logger name="net.journaldev" level="ERROR" additivity="false">
<AppenderRef ref="Console"/>
</logger>
</Loggers>
</Configuration>
執行結果如下:
17:55:30.558 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
17:55:30.560 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
17:55:30.561 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
17:55:30.561 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
17:55:30.562 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
您可能會注意到,日志事件不會傳播到其父記錄器。
Log4j2查找
理想情況下,您可以將查找定義為在日誌配置文件中傳遞值的一種方式。Log4j2為您提供了一組不同的查找,可以獨立使用以從不同的上下文中設置值:
- 上下文地圖查找
- 日期查找
- 環境查找
- Java查找
- JNDI查找
- JVM輸入引數查找(JMX)
- 主要引數查找
- 地圖查找
- 結構化數據查找
- 系統屬性查找
- Web查找
您可以參考 Log4j2 文件 以獲取有關各種查找的進一步詳細信息,但讓我們在這裡看一些示例,以涵蓋 Log4j2 查找的基礎知識。環境查找 代表您可以通過 Linux etc/profile、Windows 系統環境或應用程序的啟動腳本來傳遞環境值的方式。正如大多數人所知,我們有能力為應用程序定義一組環境值供使用。讓我們來看看定義環境變量的最常用方法。
- 使用 Windows 環境設施定義環境變量:
- 右鍵點擊計算機圖標,然後選擇“內容”。控制面板主頁應該顯示出來。
- 點擊“高級系統設置”,然後打開“環境變量”窗口。
- 在 系統變量 部分,使用 JournalDevVar 定義 JournalDev 值。
- 更新 log4j2.xml 內的 PatternLayout,以包含您新增的變量。
- 利用啟動腳本功能定義您的環境變量。
- 而不是使用正常的默認腳本,您可以使用Eclipse IDE運行腳本功能,單擊運行菜單,選擇運行配置。
- 進入環境選項卡,並在其中定義您的變量。
現在查看修改後的log4j2.xml文件,注意環境變量的使用。
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} $${env:JournalDevVar} $${env:JournalDevSecondVar} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
執行的結果將如下所示:
23:57:02.511 JournalDev www.journaldev.com [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
23:57:02.517 JournalDev www.journaldev.com [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
23:57:02.520 JournalDev www.journaldev.com [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
23:57:02.523 JournalDev www.journaldev.com [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
23:57:02.527 JournalDev www.journaldev.com [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
但在這裡可能會遇到一些問題,特別是當您在定義操作系統的環境變數時,尤其是Eclipse快取。理想情況下,當Eclipse運行時,它已經緩存了所有系統變數,您可以在運行 – 運行配置 – 環境選項卡 – 點擊選擇按鈕下找到它們。因此,當您已經定義了它,但應用程序卻無法識別它時,您可能會感到困惑。即使您重新啟動Eclipse,也無法解決問題,您必須在Eclipse安裝目錄下執行eclipse.exe -clean。為了確保您的環境變數被正確定義並且系統能夠自願找到它們,您可以使用Log4j2 API提供的對應插件類型。創建一個 EnvironmentLookup 的實例,並要求它查找特定變數,如果已定義,則您可以輕鬆找到它們。
EnvironmentLookup lookup = new EnvironmentLookup();
LogManager.getRootLogger().error(lookup.lookup("JournalDevSecondVar"));
Log4j2 Appenders
您之前看到了如何使用Lookups将变量注入到配置文件中。然而,您可能希望修改消息传递的媒介。而不是直接使用控制台,您可能希望使用文件或数据库存储库来确保您的消息被永久保留。Log4j2提供了许多Appenders,您可以参考log4j2文档以获取Appender的更多详细信息。简要列出所有Log4j2 Appenders如下:
- ConsoleAppender
- AsyncAppender
- FailoverAppender
- FileAppender
- FlumeAppender
- JDBCAppender
- JMSAppender
- JPAAppender
- MemoryMappedFileAppender
- NoSQLAppender
- OutputStreamAppender
- RandomAccessFileAppender
- RewriteAppender
- RollingFileAppender
- RollingRandomAccessFileAppender
- RoutingAppender
- SMTPAppender
- SocketAppender
- SyslogAppender
用于记录事件的最常用媒介是控制台、文件和数据库。由于文件可以保存您的消息,数据库可能被用于对其进行审计。为此,本节将重点介绍如何高效使用JDBCAppender。
JDBCAppender
JDBCAppender 的主要目標是通過 JDBC 連接將日誌事件寫入關聯表。我們不會花太多時間解釋如何優化連接池,因為這個教程不是為此目的而設計的。但是,您肯定會得到一個完整的功能示例,可以幫助將您的日誌事件寫入數據庫。在我們繼續之前,讓我們看看所有需要的參數及其描述,以便正確配置 JDBCAppender。
Parameter Name | Type | Description |
---|---|---|
Name | String | Required, The name of the Appender |
ignoreExceptions | boolean | Default value is set to true, making exceptions thrown to be logged also and then ignored. False value means the exception will be propagated for the caller. |
filter | Filter | The filter that should be used to make a decision whether the log events are going to be handled by this Appender or not. |
bufferSize | int | Default value is zero, indicating there’s no buffering have been done upon log events. Value greater than 0 would lead the Appender to buffer log events and then flush them once the buffer reaches the limit specified. |
connectionSource | ConnectionSource | Required, the connections source from which the database connections should be retrieved. |
tableName | String | Required, the name of the Table on which your log events should be persisted. |
columnConfigs | ColumnConfig[] | Required, additional information may be set upon those used columns and how the data should be persisted on each of them. This can be handled with multiple <Column> elements. |
Parameter Name | Type | Description |
---|---|---|
jndiName | String | Required, full prefixed JNDI name that the javax.sql.Datasource is bound to. |
Parameter Name | Type | Description |
---|---|---|
class | String | Requird, The fully qualified name for a class containg a static factory method for obtaining JDBC connections. |
method | boolean | Required, The name of the static factory method for obtaining JDBC connections. |
Parameter Name | Type | Description |
---|---|---|
name | String | Required, the name of the database column |
pattern | String | Ability to specify any legal pattern that Log event would be formatted with |
literal | String | Ability to specify literal value in this column (i.e. SEQ.NEXTVAL) |
isEventTimestamp | boolean | Indicating whether the event would consider Timestamp |
isUnicode | boolean | For unicode purpose as you may refer for Log4j2 documentation for further details |
isClob | boolean | For storing character large object, you may refer for Log4j2 documentation for further details. |
由於您被強制使用 JNDI,我們的示例將為 Oracle 數據庫和 Apache Tomcat 7 配置連接數據源。
- 如果您尚未在您的環境中安裝 Oracle 數據庫,我們建議您這樣做。如果您對 Oracle 不太熟悉,我建議您安裝其 Express Edition。
- 將 Apache Tomcat 7 安裝到您的環境中。
- 在 Eclipse 中創建一個 Maven WebApp 項目。
- 確保您的項目成功創建,如果在 pom 中注意到任何錯誤,請確保您修復它們。
- 添加 Log4j2 依賴項。
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.2</version>
</dependency>
- 配置您的上下文以包含一個 MySQL 數據源聲明。根據 Apache 文檔,此文件應位於您的 Web Application META-INF 文件夾中。
<Context path="/JournalDevWebLogging"
privileged="true" antiResourceLocking="false" antiJARLocking="false">
<Resource name="jdbc/JournalDevDB" auth="Container"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000"
username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/journaldev" />
</Context>
- 配置您的數據庫並創建您的日誌表。
CREATE TABLE `logging` (
`EVENT_ID` int(11) NOT NULL AUTO_INCREMENT,
`EVENT_DATE` datetime DEFAULT NULL,
`LEVEL` varchar(45) DEFAULT NULL,
`LOGGER` varchar(45) DEFAULT NULL,
`MSG` varchar(45) DEFAULT NULL,
`THROWABLE` varchar(45) DEFAULT NULL,
PRIMARY KEY (`EVENT_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
- 將您的log4j2.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout
pattern="%d{HH:mm:ss.SSS} $${env:JournalDevVar} $${env:JournalDevSecondVar} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<JDBC name="databaseAppender" tableName="journaldev.logging">
<DataSource jndiName="java:/comp/env/jdbc/JournalDevDB" />
<Column name="EVENT_DATE" isEventTimestamp="true" />
<Column name="LEVEL" pattern="%level" />
<Column name="LOGGER" pattern="%logger" />
<Column name="MSG" pattern="%message" />
<Column name="THROWABLE" pattern="%ex{full}" />
</JDBC>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console" />
</Root>
<logger name="com" level="TRACE" additivity="false">
<AppenderRef ref="databaseAppender" />
</logger>
<logger name="com.journaldev" additivity="false">
<AppenderRef ref="databaseAppender" />
</logger>
</Loggers>
</Configuration>
- 創建任何Web資源,使您可以獲取日誌記錄器的引用,然後記錄事件。
package com.journaldev;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class JournalDevServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
Logger logger = LogManager.getLogger(JournalDevServlet.class);
logger.trace("JournalDev Database Logging Message !");
}
}
- 您還可以選擇配置一個ServletContextListener,以確保數據源的初始化正確進行。
package com.journaldev;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.logging.log4j.LogManager;
public class JournalDevServletContextListener implements ServletContextListener{
private InitialContext context = null;
public void contextDestroyed(ServletContextEvent event) {
}
public void contextInitialized(ServletContextEvent event) {
try {
// 獲取初始上下文
context = new InitialContext();
// 獲取子上下文env的引用
Context envContext = (Context)context.lookup("java:comp/env");
// 獲取子上下文jdbc的引用,然後找到所定義的數據源
LogManager.getRootLogger().error(((Context)envContext.lookup("jdbc")).lookup("JournalDevDB"));
} catch (NamingException e) {
LogManager.getRootLogger().error(e);
}
}
}
- 在web.xml文件中定義您的Servlet。
- 運行應用程序並訪問上述定義的Servlet。 您將看到以下日誌:
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.AprLifecycleListener init
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: C:\Program Files\Java\jdk1.6.0_26\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:\Program Files\Java\jdk1.6.0_26\jre\bin;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/bin/server;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/bin;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/lib/amd64;D:\OracleWebCenter\OracleWC\Oracle11g\app\oracle\product\11.2.0\server\bin;;C:\Program Files\Common Files\Microsoft Shared\Windows Live;C:\Program Files (x86)\Common Files\Microsoft Shared\Windows Live;D:\OracleDB\app\product\11.2.0\dbhome_1\bin;org.C:\Program Files (x86)\Common Files\NetSarang;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x86;C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x64;D:\SpringRoo\spring-roo-1.2.5.RELEASE\bin;D:\Ant\apache-ant-1.9.2\bin;C:\Python27;C:\Program Files\Java\jdk1.6.0_26\bin;D:\Maven\apache-maven-3.2.1/bin;D:\bower-master\bin;C:\Program Files (x86)\Git\cmd;C:\Program Files\nodejs\;C:\Program Files\Microsoft Windows Performance Toolkit\;D:\Grails\grails-2.4.0\bin;D:\Gradle\gradle-2.0\bin;C:\Program Files (x86)\Windows Live\Shared;C:\Program Files\TortoiseSVN\bin;D:\Strawberry\perl\bin;D:\Strawberry\perl\site\bin;D:\Strawberry\c\bin;C:\Users\mohammad.amr\AppData\Roaming\npm;D:\JournalDev\eclipse;;.
Mar 15, 2015 2:31:41 PM org.apache.tomcat.util.digester.SetPropertiesRule begin
WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.j2ee.server:JournalDevWebLogging' did not find a matching property.
Mar 15, 2015 2:31:41 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
Mar 15, 2015 2:31:41 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["ajp-bio-8009"]
Mar 15, 2015 2:31:41 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 1020 ms
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Catalina
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.35
14:31:43.847 [localhost-startStop-1] ERROR - org.apache.tomcat.jdbc.pool.DataSource@10fd0a62{ConnectionPool[defaultAutoCommit=null; defaultReadOnly=null; defaultTransactionIsolation=-1; defaultCatalog=null; driverClassName=com.mysql.jdbc.Driver; maxActive=100; maxIdle=30; minIdle=10; initialSize=10; maxWait=10000; testOnBorrow=false; testOnReturn=false; timeBetweenEvictionRunsMillis=5000; numTestsPerEvictionRun=0; minEvictableIdleTimeMillis=60000; testWhileIdle=false; testOnConnect=false; password=root; url=jdbc:mysql://localhost:3306/journaldev; username=root; validationQuery=null; validatorClassName=null; validationInterval=30000; accessToUnderlyingConnectionAllowed=true; removeAbandoned=false; removeAbandonedTimeout=60; logAbandoned=false; connectionProperties=null; initSQL=null; jdbcInterceptors=null; jmxEnabled=true; fairQueue=true; useEquals=true; abandonWhenPercentageFull=0; maxAge=0; useLock=false; dataSource=null; dataSourceJNDI=null; suspectTimeout=0; alternateUsernameAllowed=false; commitOnReturn=false; rollbackOnReturn=false; useDisposableConnectionFacade=true; logValidationErrors=false; propagateInterruptState=false; }
Mar 15, 2015 2:31:43 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]
Mar 15, 2015 2:31:43 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-bio-8009"]
Mar 15, 2015 2:31:43 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 1909 ms
Log4j2過濾器
即使存在一個用於處理日誌事件的LoggerConfig候選者,您也可以配置它以拒絕將日誌事件傳遞給後端Appenders。 這可以通過log4j2過濾器來完成。 本部分不旨在提供有關在Log4j2中使用過濾器的入侵性、大規模和大量的教程,因為它們需要大量的文章來覆蓋每一個。 但在這裡,您將看到如何使用最簡單的過濾器來學習該概念。 您可以使用的最簡單的過濾器之一是BurstFilter,它提供了一種機制,可以在達到最大限制後默默地丟棄事件,以控制LogEvents的處理速率。 現在,您可以看到下面列出的使用BurstFilter所需的所有細節。
Parameter Name | Type | Description |
---|---|---|
level | String | Level of messages to be filtered |
rate | float | The average number of events per second to allow |
maxBurst | integer | The maximum number of events that can occur before events are filtered for exceeding the average rate. The default is 10 times the rate. |
onMatch | String | Action to take when filter matches. May be Accept, DENY or NEUTRAL. The default is NEUTRAL |
onMismatch | String | Action to tale when filter doesn’t match. May be Accept, DENY or NEUTRAL. The default is NEUTRAL |
現在,請查看BurstFilter在數據庫Appender中的位置。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout
pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<JDBC name="databaseAppender" tableName="journaldev.logging">
<DataSource jndiName="java:/comp/env/jdbc/JournalDevDB" />
<BurstFilter level="TRACE" rate="20" maxBurst="2"/>
<Column name="EVENT_DATE" isEventTimestamp="true" />
<Column name="LEVEL" pattern="%level" />
<Column name="LOGGER" pattern="%logger" />
<Column name="MSG" pattern="%message" />
<Column name="THROWABLE" pattern="%ex{full}" />
</JDBC>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console" />
</Root>
<logger name="com" level="TRACE" additivity="false">
<AppenderRef ref="databaseAppender" />
</logger>
<logger name="com.journaldev" additivity="false">
<AppenderRef ref="databaseAppender" />
</logger>
</Loggers>
</Configuration>
- Database Appender 在考虑 BurstFilter 时,控制台 Appender 不考虑。
- 使用控制台记录器将导致完整的日志事件被记录,而数据库 Appender 不会这样做,因为 BurstFilter 将阻止其中一些事件继续进行。
- 即使使用的记录器是处理日志事件的候选项,也会实现这种日志事件拒绝,如下所示 JournalDevServlet。
package com.journaldev;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class JournalDevServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
Logger logger = LogManager.getLogger(JournalDevServlet.class);
for(int i = 0 ; i < 1000 ; i++){
logger.trace("Index :: "+i+" :: JournalDev Database Logging Message !");
LogManager.getRootLogger().error("Index :: "+i+" :: JournalDev Database Logging Message !");
}
}
}
即使这些 LoggerConfigs 是处理在那里抛出的日志事件的候选项,但过滤器已经阻止了其中一些被处理和记录。您可以添加这个日志空间概念来完全理解日志记录的概念。
Log4j2 布局
由于消耗日志事件的不同附加器和每个附加器的特性,布局被设计成以符合将消耗日志事件的人的需求的格式形成日志事件。在Log4j 1.x和Logback API中,日志事件的布局转换为字符串,而Log4j2布局考虑了一种不同的转换方式;那就是将LogEvent转换为字节数组。这种新类型的转换会强制您配置Charset,以确保字节数组包含正确的值。强烈建议您返回到Apache Log4j2官方网站,并了解更多关于Log4j2提供的布局和不同类型的信息。在本节中,我们将考虑最著名的布局,这是大多数开发人员总是使用的,您肯定也听说过它;那就是PatternLayout。
Log4j2 PatternLayout
模式布局是可配置的、灵活的字符串模式,旨在格式化日志事件。这种格式化取决于转换模式概念。本节将向您描述模式布局提供的最重要功能。转换模式与C语言中的printf提供的转换模式相关。通常,转换模式由字面文本和格式控制表达式组成,称为转换说明符。下图描述了转换模式由哪些部分组成:上面的这个图是试图简化转换模式,但对于您来说,最好参考Apache Log4j2文档,了解布局和模式布局的更多细节。此外,您可以参考上面的日志事件,并随时查看所使用的转换模式以格式化消息。
您应该使用哪个Log4j2级别
您可能会问自己的最大问题是何时应该使用特定的日志事件级别。在开发领域,通常会使用DEBUG日志事件,而在生产中,我们应该使用INFO或WARN级别。下表应该指导您在哪种情况下应该使用哪个log4j2级别。
Log Event Level | When It Should Be Used |
---|---|
OFF | When no events will be logged |
FATAL | When a severe error will prevent the application from continuing |
ERROR | When an error in the application, possibly recoverable |
WARN | When an event that might possible lead to an error |
INFO | When an event for informational purposes |
DEBUG | When a general debugging event required |
TRACE | When a fine grained debug message, typically capturing the flow through the application |
ALL | When all events should be logged |
Log4j2 教程摘要
Log4j2 是 Apache 日志框架的更新版本。Log4j2 从 Log4j1.x 中提供了一套新功能和性能增强。本 Log4j2 教程旨在帮助您在一个地方掌握所有内容。由于其中一些概念不太容易一次性覆盖,我们决定将我们的努力集中在解释概念并使用一些示例进行更清晰的说明上。附录、过滤器、布局和查找器都适用于此规则。一些重要的要点 为了确保您能够运行下面的应用程序并避免任何障碍,请验证以下内容:
- 您的 Eclipse IDE 已启用 Maven。
- 您的 Apache Tomcat 在 Apache Home lib 文件夹中有一个 mysql-connector JAR。
- 您知道如何使用 Maven。
以上就是 log4j2 教程的全部内容,我希望大部分重要点都被涵盖了,让您能够开始在应用程序中使用 Log4j2。