Log4j2示例教程-配置、级别、追加器

欢迎来到Apache Log4j2示例教程。如果你问一位专业的开发者关于应用程序中最烦人的事情,答案可能与日志记录有关。如果一个应用程序没有合适的日志记录,维护将会成为一场噩梦。大多数应用程序都经历了开发测试、单元测试、集成测试。但是当涉及到生产环境时,你总会面临独特的情况和异常。所以在特定情况下弄清楚发生了什么的唯一方法就是通过日志进行调试。许多框架提供了一些默认的日志记录方式,但最好还是使用行业标准的日志记录机制。Apache Log4j是最广泛使用的日志记录框架之一。Apache Log4j 2是下一个版本,比Log4j好得多。

Log4j示例教程

在这个Log4j2示例教程中,您将学习如何开始使用Apache Log4j2。我们还将探讨Log4j2的架构、Log4j2配置、Log4j2日志级别、追加器、过滤器等等。

  1. Log4j2概述
  2. Log4j2架构
  3. Log4j2配置
  4. Log4j2级别
  5. Log4j2查找
  6. Log4j2追加器
  7. Log4j2过滤器
  8. Log4j2布局
  9. 应该使用哪个Log4j2级别
  10. Log4j2教程摘要

Log4j2概述

在应用程序中使用日志记录API不是奢侈品,而是必备品。Log4j是一个开源库,以Apache软件的名义发布和许可。您可以使用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

  • 如果日志记录器尚未创建,则将根据以下三种选择创建并与LoggerConfig相关联:

    1. 将创建日志记录器实例,并与具有相同名称的LoggerConfig相关联。例如,在getLogger(App.class)中,App.class将被评估为字符串com.journaldev.App。LoggerConfig名称与完全限定类名(软件组件)相同。
    2. 将创建日志记录器实例,并与具有相同Loggers父包的LoggerConfig相关联。例如,在getLogger("com.journaldev")中的com.journaldev
    3. 将创建日志记录器实例,并与根LoggerConfig相关联。当没有配置文件或者正在获取未在日志记录器声明中定义的名称的记录器时,将使用根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 对象组成。每个日志记录器层次结构中最顶层的元素是根日志记录器。如果 Log4j2 没有找到配置文件,则只会使用根日志记录器进行记录,记录级别为 ERROR。下面的图像显示了在这种情况下您将收到的警告消息。错误状态记录器未找到log4j2配置文件。使用默认配置:仅将错误记录到控制台。 下表显示了日志记录器层次结构中的父子关系。

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的祖先,如果其名称后跟一个点,作为后代名称的前缀。

Log4j2配置

有许多方法可以在您的应用程序中使用Log4j2配置。

  1. 使用以XML、JSON、YAML或属性文件编写的配置文件。
  2. 通过创建配置工厂和配置实现,以编程方式。
  3. 通过调用配置接口中公开的API,以编程方式。
  4. 通过在内部记录器类上调用方法,以编程方式。

我们将主要关注配置文件。然而,了解编程方法也很重要,以防您想为某些特定的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 参数传递,就像您在下面的图中看到的那样。还要注意文件协议前缀。

  • 如果未定义系统属性,则配置顺序如下优先级:
    • 属性配置工厂将在类路径中查找log4j2-test.properties
    • YAML 配置工厂将在类路径中查找log4j2-test.yamllog4j2-test.yml
    • JSON 配置工厂将在类路径中查找log4j2-test.jsnlog4j2-test.json
    • XML 配置工厂将在类路径中查找log4j2-test.xml
    • 属性配置工厂将在类路径中查找log4j2.properties
    • YAML 配置工厂将在类路径中查找log4j2.ymllog4j2.yaml
    • JSON 配置工厂将在类路径中查找log4j2.jsnlog4j2.json
    • XML 配置工厂将在类路径中查找log4j2.xml
    • 如果未提供配置文件,则将使用DefaultConfiguration,这将导致一组默认行为:
      • 将使用根记录器。
      • 根记录器级别将设置为ERROR
      • 根记录器将将日志消息传播到控制台。
      • PatternLayout 设置为%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n

使用 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);
 
    	// 将控制台附加器添加到配置中
    	ConsoleAppender appender = ConsoleAppender.createDefaultAppenderForLayout(PatternLayout.createDefaultLayout());
 
    	// 创建loggerConfig
    	configuration.addAppender(appender);
 
    	// 添加附加器
    	LoggerConfig loggerConfig = new LoggerConfig("com",Level.FATAL,false);
 
    	// 添加logger并将其与loggerConfig实例关联
    	loggerConfig.addAppender(appender,null,null);
 
    	// 获取上下文实例
    	configuration.addLogger("com", loggerConfig);
 
    	// 启动日志系统
    	LoggerContext context = new LoggerContext("JournalDevLoggerContext");
 
    	// 获取logger的引用
    	context.start(configuration);
 
    	// 记录DEBUG消息的LogEvent
    	Logger logger = context.getLogger("com");
 
    	// 针对配置为FATAL的Logger记录错误消息的LogEvent
    	logger.log(Level.FATAL, "Logger Name :: "+logger.getName()+" :: Passed Message ::");
 
    	// 记录将由Root处理的ERROR消息的LogEvent
    	logger.log(Level.ERROR, "Logger Name :: "+logger.getName()+" :: Not Passed Message ::");
 
    	您可以使用Log4j2提供的任何ConfigurationFactory,也可以使用默认的ConfigurationFactory。
    	logger.getParent().log(Level.ERROR, "Root Logger :: Passed Message As Root Is Configured For ERROR Level messages");
    }
}
  • 我们已经使用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.
  • 已创建具有提供的名称、级别和未使用过滤器的LoggerConfig实例。创建的附加器将分配给此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级别部分进行解释。
  • com记录器已配置为打印级别为FATAL的消息。
  • 默认情况下,Root记录器配置为打印级别为ERROR的消息。
  • ERROR消息不会被’com’记录器记录,因为它的级别是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

日志的第一行来自com记录器,第二行来自Root Logger。 com记录器的错误消息未打印,因为其级别为Fatal。

Log4j2级别

你可以在上述代码示例中看到,每次我们定义一个LoggerConfig时,也会提供日志级别。默认情况下,log4j2日志是累加的。这意味着当使用特定的记录器时,所有父记录器也将被使用。下图澄清了这种情况。以下是对此的澄清要点:

  • 正如我们之前所述,每个记录器都与LoggerConfig实例相关联。此loggerConfig已在配置范围内定义。
  • 日志记录级别可以在LoggerConfig范围内确定。
  • 您可以通过其名称、父包或直接指向Root Logger来获取记录器。
  • Root Logger是每个LoggerConfig层次结构的顶级节点。
  • 一旦您获取了com.journaldev记录器并启动用于记录的logEvent,loggerConfig(net.journaldev)将记录消息,并且消息将以不考虑父记录器日志级别的方式传播到层次结构中。因此,log事件将传播到com和Root记录器,并且它们将根据定义的级别分别记录消息。
  • 一旦您获取了com记录器并启动用于记录的logEvent,loggerConfig(com)将记录消息,并且消息将以不考虑父记录器日志级别的方式传播到层次结构中。也就是说,Root logger将传播日志事件,并且它也将记录消息。
  • 对于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>

如果执行主类,您将会看到以下结果:

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类中有类似的情况。
  • 父级以添加指示器默认设置为true传播。

记录空间考虑日志事件级别、日志记录器配置的级别以及日志记录器层次结构。

那么,如果我们将 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

当然,上表比文字更清楚地解释了为什么当 LoggerConfig 的级别为 INFO 时,日志事件 TRACE 不会显示。

请注意,日志事件在记录器层次结构中向上传播不包括这种计算,它忽略了级别。

但是,如果我们从配置中移除 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 实例但没有指定级别,它将继承其父级的级别。

但是,如果您在配置文件中定义了 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 未定义,日志层次结构已达到顶部,现在它正在引用根。根日志级别为 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 Lookup 的基础知识。 环境查找 代表了您可以传递环境值的方式(通过 Linux 等 /profile,Windows 系统环境或应用程序的启动脚本。正如大多数人所知,我们可以定义一组环境值供应用程序使用。让我们看看定义环境变量的最流行方法。

  1. 使用 Windows 环境设施定义环境变量:
    • 右键单击计算机图标,然后选择属性。 控制面板主页应该显示。
    • 单击高级系统设置,然后打开环境变量窗口。
    • 系统变量 部分,使用 JournalDev 值定义变量 JournalDevVar。
    • 更新 log4j2.xml 中的 PatternLayout 以包含您新添加的变量。

  1. 使用启动脚本设施定义您的环境变量。
    • 而不是使用正常的默认脚本,您可以使用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的列表。

  1. ConsoleAppender
  2. AsyncAppender
  3. FailoverAppender
  4. FileAppender
  5. FlumeAppender
  6. JDBCAppender
  7. JMSAppender
  8. JPAAppender
  9. MemoryMappedFileAppender
  10. NoSQLAppender
  11. OutputStreamAppender
  12. RandomAccessFileAppender
  13. RewriteAppender
  14. RollingFileAppender
  15. RollingRandomAccessFileAppender
  16. RoutingAppender
  17. SMTPAppender
  18. SocketAppender
  19. 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应用程序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>
  • 创建任何网络资源,使您能够获取日志记录器的引用,然后记录事件。
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>
  • 数据库追加器考虑了 BurstFilter,而控制台追加器则不考虑。
  • 使用控制台记录器将导致所有日志事件都被记录,但数据库追加器不会这样做,因为 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 布局考虑了一种不同的转换方式;通过将日志事件转换为字节数组。这种新类型的转换会强制您配置 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 Logging 框架的改进版本。Log4j2 提供了一套新功能和性能增强,相比于 Log4j1.x。本 Log4j2 教程旨在帮助您在一个地方掌握所有内容。由于其中一些概念不容易一次性覆盖,我们决定将我们的努力集中在解释概念并使用一些示例来进一步阐明。Appenders、Filters、layouts 和 lookups 都适用于此规则。一些重要的要点 为了确保您能够运行以下应用程序并避免任何障碍,请验证以下内容:

  • 您的 Eclipse IDE 已启用 Maven。
  • 您的 Apache Tomcat 在 Apache Home lib 文件夹中有一个 mysql-connector JAR。
  • 您了解如何使用 Maven。

下载 Apache Log4j 2 示例项目

这就是关于 log4j2 教程的全部内容,我希望大部分重要内容都已涵盖,让您能够开始在您的应用程序中使用 Log4j2。

Source:
https://www.digitalocean.com/community/tutorials/log4j2-example-tutorial-configuration-levels-appenders