一、 尝试用编程的方式去配置Log4j2
上一篇我们说了 log4j2 的配置方式,官方给我们说了很多种配置方式,我们回忆一下:
markdown复制代码1. 通过XML、JSON、YAML或者properties格式的配置文件;
2. 通过创建一个 ConfigurationFactory 和 Configuration 接口的实现
3. 调用 Configuration 接口暴露的方法来在默认配置的基础上添加其他组件
4. 通过在内部 Logger 类上调用方法
官方文档说了,除了用配置文件之外,还能用编程的方式实现配置,关于这点呢,我们来看看更详细的官方文档
Log4j 2 provides a few ways for applications to create their own programmatic configuration:
- Specify a custom ConfigurationFactory to start Log4j with a programmatic configuration
- Use the Configurator to replace the configuration after Log4j started
- Initialize Log4j with a combination of a configuration file and programmatic configuration
- Modify the current Configuration after initialization
翻译一哈:
Log4j2 提供一些编程配置的方式
- 指定一个自定义的 ConfigurationFactory 去启动 log4j 完成编程配置
- 在Log4j 启动之后 用 Configurator 去替换 Configuration
- 使用配置文件和 编程配置的方式去初始化Log4j (原来还能混着用啊)
- 初始化之后修改当前配置 (嗯哼,配置原来还能修改啊)
方式也是非常之多,如果大家真的感兴趣,可以去官网瞅瞅
logging.apache.org/log4j/2.x/m…
今天我们就用上面说的第一种方法 ,尝试去编程配置下 Log4j2, 主要是配置的方式太多了,都写出来累死了,但是如果大家真的想看,评论区留言,反馈多的话我给补全。
二、案例实战
将那么多理论没啥用,直接上demo, 他说可以用自定义的ConfigurationFactory去作为配置启动Log4j, OK 我们看看官网上给的案例是啥样子。
scala复制代码@Plugin(name = "CustomConfigurationFactory", category = ConfigurationFactory.CATEGORY)
@Order(50)
public class CustomConfigurationFactory extends ConfigurationFactory {
static Configuration createConfiguration(final String name, ConfigurationBuilder<BuiltConfiguration> builder) {
builder.setConfigurationName(name);
builder.setStatusLevel(Level.ERROR);
builder.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.NEUTRAL).
addAttribute("level", Level.DEBUG));
AppenderComponentBuilder appenderBuilder = builder.newAppender("Stdout", "CONSOLE").
addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
appenderBuilder.add(builder.newLayout("PatternLayout").
addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable"));
appenderBuilder.add(builder.newFilter("MarkerFilter", Filter.Result.DENY,
Filter.Result.NEUTRAL).addAttribute("marker", "FLOW"));
builder.add(appenderBuilder);
builder.add(builder.newLogger("org.apache.logging.log4j", Level.DEBUG).
add(builder.newAppenderRef("Stdout")).
addAttribute("additivity", false));
builder.add(builder.newRootLogger(Level.ERROR).add(builder.newAppenderRef("Stdout")));
return builder.build();
}
@Override
public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) {
return getConfiguration(loggerContext, source.toString(), null);
}
@Override
public Configuration getConfiguration(final LoggerContext loggerContext, final String name, final URI configLocation) {
ConfigurationBuilder<BuiltConfiguration> builder = newConfigurationBuilder();
return createConfiguration(name, builder);
}
@Override
protected String[] getSupportedTypes() {
return new String[] {"*"};
}
}
嗯哼,看起来使用方法就是我们继承 ConfigurationFactory 然后在里面填入自己的配置嘛~
我们直接运行一下官方提供的配置
yaml复制代码2023-02-22 13:43:51,413 [main] DEBUG: debug level
2023-02-22 13:43:51,417 [main] INFO : info level
2023-02-22 13:43:51,417 [main] WARN : warn level
2023-02-22 13:43:51,417 [main] ERROR: error level
2023-02-22 13:43:51,417 [main] FATAL: fatal level
嗯哼,我们发现是可以直接运行的, 但是上面的代码实在是太抽象了,我们来重新整理下代码
ini复制代码@Plugin(name = "CustomConfigurationFactory", category = ConfigurationFactory.CATEGORY)
@Order(50)
public class CustomConfigurationFactory extends ConfigurationFactory {
/**
* 创建Configuration 在这里面干的活我们可以理解为我们之前配置文件中干的活
* @param name
* @param builder
* @return
*/
static Configuration createConfiguration(final String name, ConfigurationBuilder<BuiltConfiguration> builder) {
/*
* 对比xml 配置文件中的配置,这个是不是就是
* <Configuration name="ConfigTest" status="ERROR" monitorInterval="5">
* 里面的name 字段和 status 字段嘛
* 当然里面的 monitorInterval 我们也可以在这里配置
*/
builder.setConfigurationName(name);
builder.setStatusLevel(Level.ERROR);
/*
* 下面这三句是不是 其实就是 xml 配置文件中的
* <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
* 为了确认,我们点进去看一下 newFilter的构造方法
* FilterComponentBuilder newFilter(String pluginName, Result onMatch, Result onMismatch);
* 发现确实是这样的
*/
FilterComponentBuilder filter = builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.NEUTRAL);
filter.addAttribute("level", Level.DEBUG);
builder.add(filter);
/*
* 这两句是不是就相当于
* <PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable"/>
* 当然这个layout 要在appender中才对嘛,所以后面还有个 add(layout)
*/
LayoutComponentBuilder layout = builder.newLayout("PatternLayout");
layout.addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable");
/*
* 下面这些是不是相当于
* <Console name="Stdout" target="SYSTEM_OUT">
* <MarkerFilter marker="FLOW" onMatch="DENY" onMismatch="NEUTRAL"/>
* <PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable"/>
* </Console>
*/
AppenderComponentBuilder console = builder.newAppender("Stdout", "CONSOLE").addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
console.add(layout);
// 这里自定义了另一个filter 跟上面的filter 差不多我就不展开到xml中了
console.add(builder.newFilter("MarkerFilter", Filter.Result.DENY, Filter.Result.NEUTRAL).addAttribute("marker", "FLOW"));
builder.add(console);
/*
* 最后这些当然就是RootLogger了
* <Loggers>
<Root level="ERROR">
<AppenderRef ref="Stdout"/>
</Root>
</Loggers>
*
*/
RootLoggerComponentBuilder rootLogger = builder.newRootLogger(Level.ERROR);
rootLogger.add(builder.newAppenderRef("Stdout"));
builder.add(rootLogger);
return builder.build();
}
@Override
public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) {
return getConfiguration(loggerContext, source.toString(), null);
}
/**
* 这是Log4j获取我们自定的Configuration的入口方法
* @param loggerContext
* @param name
* @param configLocation
* @return
*/
@Override
public Configuration getConfiguration(final LoggerContext loggerContext, final String name, final URI configLocation) {
ConfigurationBuilder<BuiltConfiguration> builder = newConfigurationBuilder();
return createConfiguration(name, builder);
}
@Override
protected String[] getSupportedTypes() {
// 当我们把支持类型设置为 * 意味着任何类型的配置都是支持的,代码配置的方式当然也不例外了
return new String[] {"*"};
}
}
强烈建议把代码配置对照着 xml 文件看,看完你就会发现,只是xml文件的写法变成了我代码中去 new set 和 add 罢了。
我们发现
markdown复制代码1. 新建一个标签 在代码中就是 new 对象
2. 标签中的属性 我们就用 setAttribute() 去做 放一个键值对
3. 标签嵌套呢,我们就用父对象 执行 .add() 把子对象加进来就行了
怎么样,是不是很简单?
在官方的案例基础上,我们把File类型的日志 也写一下吧,我展示下最终的成果
ini复制代码@Plugin(name = "CustomConfigurationFactory", category = ConfigurationFactory.CATEGORY)
@Order(50)
public class CustomConfigurationFactory extends ConfigurationFactory {
private static final String LOG_PATH = "D:/logs/conf.log";
static Configuration createConfiguration(final String name, ConfigurationBuilder<BuiltConfiguration> builder) {
builder.setConfigurationName(name);
builder.setStatusLevel(Level.DEBUG);
builder.setMonitorInterval("30");
FilterComponentBuilder filter = builder.newFilter("ThresholdFilter", Filter.Result.DENY, Filter.Result.ACCEPT);
filter.addAttribute("level", Level.WARN);
builder.add(filter);
LayoutComponentBuilder layout = builder.newLayout("PatternLayout").addAttribute("pattern", "%d [%t] %-5level: --> %m%n");
AppenderComponentBuilder consoleAppender = builder.newAppender("Stdout", "Console");
consoleAppender.addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
consoleAppender.add(layout);
consoleAppender.add(filter);
builder.add(consoleAppender);
AppenderComponentBuilder fileAppender = builder.newAppender("FileAppender", "File");
fileAppender.addAttribute("fileName", LOG_PATH);
fileAppender.addAttribute("immediateFlush", true);
fileAppender.add(layout);
builder.add(fileAppender);
// builder.add(builder.newLogger("org.apache.logging.log4j", Level.DEBUG).add(builder.newAppenderRef("Stdout")).addAttribute("additivity", false));
RootLoggerComponentBuilder rootLogger = builder.newRootLogger(Level.ERROR);
AppenderRefComponentBuilder stdoutRef = builder.newAppenderRef("Stdout");
AppenderRefComponentBuilder fileRef = builder.newAppenderRef("FileAppender");
rootLogger.add(stdoutRef);
rootLogger.add(fileRef);
builder.add(rootLogger);
return builder.build();
}
@Override
protected String[] getSupportedTypes() {
return new String[] {"*"};
}
@Override
public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) {
return getConfiguration(loggerContext, source.toString(), null);
}
@Override
public Configuration getConfiguration(final LoggerContext loggerContext, final String name, final URI configLocation) {
ConfigurationBuilder<BuiltConfiguration> builder = newConfigurationBuilder();
return createConfiguration(name, builder);
}
}
运行结果:
yaml复制代码2023-02-22 14:19:59,108 [main] DEBUG: --> debug level
2023-02-22 14:19:59,112 [main] INFO : --> info level
对应的地方也有文件啦
是不是非常简单? 这次我们就到这里,使用编程化配置文件这种方式我们使用的不多,这次作为一个知识点讲一讲。
当然了 生产中 File 类型的 Appender 我们也是基本不用, 我们一般用 RollingFile ,下一节我们重点介绍下 RollingFile的 配置,我们从xml 文件开始,再到 编程化配置都来一遍。