1. Introduction
1.介绍
In this tutorial, we’ll take a look at different ways to programmatically configure Apache Log4j 2.
在本教程中,我们将看一下以编程方式配置Apache Log4j 2的不同方法。
2. Initial Setup
2.初始设置
To start using Log4j 2, we merely need to include the log4j-core and log4j-slf4j-impl dependencies in our pom.xml:
要开始使用 Log4j 2,我们只需要在我们的 pom.xml 中包括 log4j-core 和 log4j-slf4j-impl 依赖项。
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.11.0</version>
</dependency>
3. ConfigurationBuilder
3.ConfigurationBuilder
Once we have Maven configured, then we need to create a ConfigurationBuilder, which is the class that lets us configure appenders, filters, layouts, and loggers.
配置好Maven后,我们需要创建一个ConfigurationBuilder,这个类可以让我们配置appenders、过滤器、布局和loggers。
Log4j 2 provides several ways to get a ConfigurationBuilder.
Log4j 2提供了几种获取ConfigurationBuilder的方法。
Let’s start with the most direct way:
让我们从最直接的方式开始。
ConfigurationBuilder<BuiltConfiguration> builder
= ConfigurationBuilderFactory.newConfigurationBuilder();
And to begin configuring components, ConfigurationBuilder is equipped with a corresponding new method, like newAppender or newLayout, for each component.
而为了开始配置组件,ConfigurationBuilder为每个组件配备了相应的new方法,比如newAppender或newLayout。
Some components have different subtypes, like FileAppender or ConsoleAppender, and these are referred to in the API as plugins.
一些组件有不同的子类型,如FileAppender或ConsoleAppender,这些在API中被称为plugins。
3.1. Configuring Appenders
3.1.配置应用者
Let’s tell the builder where to send each log line by configuring an appender:
让我们通过配置一个appender来告诉builder将每个日志行发送到哪里。
AppenderComponentBuilder console
= builder.newAppender("stdout", "Console");
builder.add(console);
AppenderComponentBuilder file
= builder.newAppender("log", "File");
file.addAttribute("fileName", "target/logging.log");
builder.add(file);
While most new methods don’t support this, newAppender(name, plugin) allows us to give the appender a name, which will turn out to be important later on. These appenders, we’ve called stdout and log, though we could’ve named them anything.
虽然大多数new方法不支持这一点,但newAppender(name, plugin)允许我们给appender一个名字,这在后面会变得很重要。这些应用者,我们称之为stdout和log,尽管我们可以给它们起任何名字。
We’ve also told the builder which appender plugin (or, more simply, which kind of appender) to use. Console and File refer to Log4j 2’s appenders for writing to standard out and the file system, respectively.
我们还告诉builder要使用哪个appender plugin(或者更简单地说,哪种appender)。Console和File分别指的是Log4j 2的appender,用于向标准输出和文件系统写入。
Though Log4j 2 supports several appenders, configuring them using Java can be a bit tricky since AppenderComponentBuilder is a generic class for all appender types.
尽管Log4j 2支持几个应用程序,使用Java配置它们可能有点棘手,因为AppenderComponentBuilder是一个适用于所有应用程序类型的通用类。。
This makes it have methods like addAttribute and addComponent instead of setFileName and addTriggeringPolicy:
这使得它拥有像addAttribute和addComponent这样的方法,而不是setFileName和addTriggeringPolicy。
AppenderComponentBuilder rollingFile
= builder.newAppender("rolling", "RollingFile");
rollingFile.addAttribute("fileName", "rolling.log");
rollingFile.addAttribute("filePattern", "rolling-%d{MM-dd-yy}.log.gz");
builder.add(rollingFile);
And, finally, don’t forget to call builder.add to append it to the main configuration!
最后,别忘了调用builder.add,将其追加到主配置中!。
3.2. Configuring Filters
3.2.配置过滤器
We can add filters to each of our appenders, which decide on each log line whether it should be appended or not.
我们可以为我们的每一个appender添加过滤器,在每一个日志行上决定它是否应该被追加。
Let’s use the MarkerFilter plugin on our console appender:
让我们在我们的控制台appender上使用MarkerFilter插件。
FilterComponentBuilder flow = builder.newFilter(
"MarkerFilter",
Filter.Result.ACCEPT,
Filter.Result.DENY);
flow.addAttribute("marker", "FLOW");
console.add(flow);
Note that this new method doesn’t allow us to name the filter, but it does ask us to indicate what to do if the filter passes or fails.
请注意,这个new方法不允许我们给过滤器命名,但它确实要求我们指出如果过滤器通过或失败该怎么做。
In this case, we’ve kept it simple, stating that if the MarkerFilter passes, then ACCEPT the logline. Otherwise, DENY it.
在这种情况下,我们保持简单,说明如果MarkerFilter通过,那么ACCEPTlogline。否则,拒绝它。
Note in this case that we don’t append this to the builder but instead to the appenders that we want to use this filter.
注意,在这种情况下,我们并没有将其追加到builder,而是追加到我们想要使用这个过滤器的appender。
3.3. Configuring Layouts
3.3.配置布局
Next, let’s define the layout for each log line. In this case, we’ll use the PatternLayout plugin:
接下来,让我们为每个日志行定义布局。在这种情况下,我们将使用PatternLayout插件。
LayoutComponentBuilder standard
= builder.newLayout("PatternLayout");
standard.addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable");
console.add(standard);
file.add(standard);
rolling.add(standard);
Again, we’ve added these directly to the appropriate appenders instead of to the builder directly.
同样,我们把这些东西直接添加到相应的应用器中,而不是直接添加到builder中。
3.4. Configuring the Root Logger
3.4.配置根记录器
Now that we know where logs will be shipped to, we want to configure which logs will go to each destination.
现在我们知道了日志将被运往何处,我们要配置哪些日志将被运往每个目的地。
The root logger is the highest logger, kind of like Object in Java. This logger is what will be used by default unless overridden.
根记录器是最高的记录器,有点像Java中的Object。除非被重写,否则这个日志记录器将被默认使用。
So, let’s use a root logger to set the default logging level to ERROR and the default appender to our stdout appender from above:
因此,让我们使用一个根记录器,将默认的日志级别设置为ERROR,默认的appender为我们上面的stdoutappender。
RootLoggerComponentBuilder rootLogger
= builder.newRootLogger(Level.ERROR);
rootLogger.add(builder.newAppenderRef("stdout"));
builder.add(rootLogger);
To point our logger at a specific appender, we don’t give it an instance of the builder. Instead, we refer to it by the name that we gave it earlier.
为了将我们的日志记录器指向一个特定的appender,我们不给它一个构建器的实例。相反,我们用我们之前给它的名称来引用它。
3.5. Configuring Additional Loggers
3.5.配置额外的记录器
Child loggers can be used to target specific packages or logger names.
子记录器可用于针对特定的包或记录器名称。
Let’s add a logger for the com package in our application, setting the logging level to DEBUG and having those go to our log appender:
让我们在我们的应用程序中为com包添加一个日志器,将日志级别设置为DEBUG,并让这些日志转到我们的logappender。
LoggerComponentBuilder logger = builder.newLogger("com", Level.DEBUG);
logger.add(builder.newAppenderRef("log"));
logger.addAttribute("additivity", false);
builder.add(logger);
Note that we can set additivity with our loggers, which indicates whether this logger should inherit properties like logging level and appender types from its ancestors.
请注意,我们可以为我们的记录器设置additivity ,这表明这个记录器是否应该从其祖先那里继承记录级别和appender类型等属性。
3.6. Configuring Other Components
3.6.配置其他组件
Not all components have a dedicated new method on ConfigurationBuilder.
并非所有组件在ConfigurationBuilder上都有一个专门的new方法。
So, in that case, we call newComponent.
因此,在这种情况下,我们调用newComponent.。
For example, because there isn’t a TriggeringPolicyComponentBuilder, we need to use newComponent for something like specifying our triggering policy for rolling file appenders:
例如,由于没有一个TriggeringPolicyComponentBuilder,我们需要使用newComponent来指定我们对滚动文件appenders的触发策略。
ComponentBuilder triggeringPolicies = builder.newComponent("Policies")
.addComponent(builder.newComponent("CronTriggeringPolicy")
.addAttribute("schedule", "0 0 0 * * ?"))
.addComponent(builder.newComponent("SizeBasedTriggeringPolicy")
.addAttribute("size", "100M"));
rolling.addComponent(triggeringPolicies);
3.7. The XML Equivalent
3.7.XML等价物
ConfigurationBuilder comes equipped with a handy method to print out the equivalent XML:
ConfigurationBuilder配备了一个方便的方法来打印出同等的XML。
builder.writeXmlConfiguration(System.out);
Running the above line prints out:
运行上述一行打印出来的结果。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="stdout">
<PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable" />
<MarkerFilter onMatch="ACCEPT" onMisMatch="DENY" marker="FLOW" />
</Console>
<RollingFile name="rolling"
fileName="target/rolling.log"
filePattern="target/archive/rolling-%d{MM-dd-yy}.log.gz">
<PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable" />
<Policies>
<CronTriggeringPolicy schedule="0 0 0 * * ?" />
<SizeBasedTriggeringPolicy size="100M" />
</Policies>
</RollingFile>
<File name="FileSystem" fileName="target/logging.log">
<PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable" />
</File>
</Appenders>
<Loggers>
<Logger name="com" level="DEBUG" additivity="false">
<AppenderRef ref="log" />
</Logger>
<Root level="ERROR" additivity="true">
<AppenderRef ref="stdout" />
</Root>
</Loggers>
</Configuration>
This comes in handy when we want to double-check our configuration or if we want to persist our configuration, say, to the file system.
当我们想仔细检查我们的配置,或者我们想把我们的配置持久化,比如说,保存到文件系统中时,这就很方便了。
3.8. Putting It All Together
3.8.把一切放在一起
Now that we are fully configured, let’s tell Log4j 2 to use our configuration:
现在我们已经完全配置好了,让我们告诉Log4j 2使用我们的配置。
Configurator.initialize(builder.build());
After this is invoked, future calls to Log4j 2 will use our configuration.
调用后,未来对Log4j 2的调用将使用我们的配置。
Note that this means that we need to invoke Configurator.initialize before we make any calls to LogManager.getLogger.
注意,这意味着我们需要在调用Configurator.initialize之前调用LogManager.getLogger。
4. ConfigurationFactory
4.ConfigurationFactory
Now that we’ve seen one way to get and apply a ConfigurationBuilder, let’s take a look at one more:
现在我们已经看到了获取和应用ConfigurationBuilder的一种方法,让我们再看看另一种。
public class CustomConfigFactory
extends ConfigurationFactory {
public Configuration createConfiguration(
LoggerContext context,
ConfigurationSource src) {
ConfigurationBuilder<BuiltConfiguration> builder = super
.newConfigurationBuilder();
// ... configure appenders, filters, etc.
return builder.build();
}
public String[] getSupportedTypes() {
return new String[] { "*" };
}
}
In this case, instead of using ConfigurationBuilderFactory, we subclassed ConfigurationFactory, an abstract class targetted for creating instances of Configuration.
在这种情况下,我们没有使用ConfigurationBuilderFactory,而是子类化了ConfigurationFactory,这是一个抽象类,目标是创建Configuration的实例。
Then, instead of calling Configurator.initialize like we did the first time, we simply need to let Log4j 2 know about our new configuration factory.
然后,我们不需要像第一次那样调用Configurator.initialize,而只需要让Log4j 2知道我们的新配置工厂。
There are three ways to do this:
有三种方法可以做到这一点。
- Static initialization
- A runtime property, or
- The @Plugin annotation
4.1. Use Static Initialization
4.1.使用静态初始化
Log4j 2 supports calling setConfigurationFactory during static initialization:
Log4j 2支持在静态初始化过程中调用setConfigurationFactory。
static {
ConfigurationFactory custom = new CustomConfigFactory();
ConfigurationFactory.setConfigurationFactory(custom);
}
This approach has the same limitation as for the last approach we saw, which is that we’ll need to invoke it before any calls to LogManager.getLogger.
这个方法与我们看到的上一个方法有相同的限制,那就是我们需要在调用LogManager.getLogger之前调用它。
4.2. Use a Runtime Property
4.2.使用运行时属性
If we have access to the Java startup command, then Log4j 2 also supports specifying the ConfigurationFactory to use via a -D parameter:
如果我们能够访问Java启动命令,那么Log4j 2也支持通过-D参数指定要使用的ConfigurationFactory。
-Dlog4j2.configurationFactory=com.baeldung.log4j2.CustomConfigFactory
The main benefit of this approach is that we don’t have to worry about initialization order as we do with the first two approaches.
这种方法的主要好处是,我们不必像前两种方法那样担心初始化顺序。。
4.3. Use the @Plugin Annotation
4.3.使用@PluginAnnotation
And finally, in circumstances where we don’t want to fiddle with the Java startup command by adding a -D, we can simply annotate our CustomConfigurationFactory with the Log4j 2 @Plugin annotation:
最后,在我们不想通过添加-D来摆弄Java启动命令的情况下,我们可以简单地用Log4j 2 @Plugin注解我们的CustomConfigurationFactory。
@Plugin(
name = "CustomConfigurationFactory",
category = ConfigurationFactory.CATEGORY)
@Order(50)
public class CustomConfigFactory
extends ConfigurationFactory {
// ... rest of implementation
}
Log4j 2 will scan the classpath for classes having the @Plugin annotation, and, finding this class in the ConfigurationFactory category, will use it.
Log4j 2将扫描classpath中具有@Pluginannotation的类,并在ConfigurationFactory类别中找到这个类,将使用它。
4.4. Combining With Static Configuration
4.4.与静态配置相结合
Another benefit to using a ConfigurationFactory extension is that we can easily combine our custom configuration with other configuration sources like XML:
使用ConfigurationFactory扩展的另一个好处是,我们可以轻松地将我们的自定义配置与其他配置源(如XML)相结合。
public Configuration createConfiguration(
LoggerContext context,
ConfigurationSource src) {
return new WithXmlConfiguration(context, src);
}
The source parameter represents the static XML or JSON configuration file that Log4j 2 finds if any.
source参数代表Log4j 2找到的静态XML或JSON配置文件,如果有的话。
We can take that configuration file and send it to our custom implementation of XmlConfiguration where we can place whatever overriding configuration we need:
我们可以采取该配置文件,并将其发送到我们的XmlConfiguration的自定义实现,在那里我们可以放置我们需要的任何覆盖性配置。
public class WithXmlConfiguration extends XmlConfiguration {
@Override
protected void doConfigure() {
super.doConfigure(); // parse xml document
// ... add our custom configuration
}
}
5. Conclusion
5.结论
In this article, we looked at how to use the new ConfigurationBuilder API available in Log4j 2.
在这篇文章中,我们研究了如何使用Log4j 2中的新ConfigurationBuilder API。
We also took a look at customizing ConfigurationFactory in combination with ConfigurationBuilder for more advanced use cases.
我们还看了一下定制ConfigurationFactory与ConfigurationBuilder的结合,以满足更多高级用例。
Don’t forget to check out my complete examples over on GitHub.
不要忘记查看我在GitHub上的完整示例over。