可编程配置
Log4j 2为应用程序提供了几种创建自己的编程配置的方法:
-
指定一个自定义的ConfigurationFactory,用程序化配置启动Log4j
-
启动Log4j后,使用配置器替换配置
-
使用配置文件和程序化配置的组合初始化Log4j
-
初始化后修改当前配置
ConfigurationBuilder API
从2.4版本开始,Log4j提供了一个ConfigurationBuilder和一组组件构建器,允许相当容易地创建配置。实际的配置对象,如LoggerConfig或Appender可能是笨拙的;它们需要很多关于Log4j内部的知识,如果你只想创建一个配置,这使得它们很难使用。
新的ConfigurationBuilder API(在org.apache.logging.log4j.core.config.builder中)。api包)允许用户通过构造组件定义在代码中创建配置。不需要直接使用实际的配置对象。组件定义被添加到ConfigurationBuilder中,一旦收集了所有定义,就会构建所有实际的配置对象(如logger和Appenders)。
ConfigurationBuilder为可以配置的基本组件提供了方便的方法,如logger, Appenders, Filter, Properties等。然而,Log4j 2的插件机制意味着用户可以创建任意数量的自定义组件。作为权衡,ConfigurationBuilder API只提供了有限数量的“强类型”方便方法,如newLogger()、newLayout()等。如果要配置的组件没有方便的方法,可以使用通用的builder.newComponent()方法。
例如,构建器不知道哪些子组件可以配置在特定的组件上,比如RollingFileAppender和RoutingAppender。要在RollingFileAppender上指定触发策略,可以使用builder.newComponent()。
使用ConfigurationBuilder API的示例在下面几节中。
理解ConfigurationFactory
在初始化期间,Log4j 2将搜索可用的ConfigurationFactories,然后选择一个来使用。
选中的ConfigurationFactory创建Log4j将使用的配置。下面是Log4j如何找到可用的ConfigurationFactories:
一个名为log4j2的系统属性。可以用要使用的configurationFactory的名称来设置configurationFactory。
可以使用ConfigurationFactory的实例来调用ConfigurationFactory(ConfigurationFactory)。在对Log4j进行任何其他调用之前,必须调用该函数。
可以将ConfigurationFactory实现添加到classpath中,并在“ConfigurationFactory”类别中配置为插件。当发现多个可应用的ConfigurationFactories时,Order注解可用于指定相对优先级。
ConfigurationFactories具有“受支持类型”的概念,它基本上映射到ConfigurationFactory可以处理的配置文件的文件扩展名。如果指定了配置文件位置,则不使用支持类型不包括“*”或匹配的文件扩展名的ConfigurationFactories。
使用带有自定义ConfigurationFactory的ConfigurationBuilder初始化Log4j
一种以编程方式配置Log4j 2的方法是创建一个自定义的ConfigurationFactory,它使用ConfigurationBuilder创建一个配置。
下面的例子重写了getConfiguration()方法,返回一个由ConfigurationBuilder创建的配置。
这将导致在创建LoggerContext时,配置自动连接到Log4j。
在下面的示例中,因为它指定了支持的类型“*”,所以它将覆盖提供的任何配置文件。
@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[] {"*"};
}
}
从2.7版本开始,ConfigurationFactory.getConfiguration()方法需要一个额外的LoggerContext参数。
使用配置器中的ConfigurationBuilder重新配置Log4j
自定义ConfigurationFactory的另一种选择是使用配置器进行配置。
一旦构造了配置对象,就可以将其传递给其中一个配置器。初始化方法来设置Log4j配置。
以这种方式使用配置器允许应用程序控制Log4j何时初始化。
但是,如果在调用 configuration.initialize() 之前尝试记录日志,那么这些日志事件将使用默认配置。
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
builder.setStatusLevel(Level.ERROR);
builder.setConfigurationName("BuilderTest");
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")));
ctx = Configurator.initialize(builder.build());
这个例子展示了如何创建一个包含RollingFileAppender的配置:
ConfigurationBuilder< BuiltConfiguration > builder = ConfigurationBuilderFactory.newConfigurationBuilder();
builder.setStatusLevel( Level.ERROR);
builder.setConfigurationName("RollingBuilder");
// create a console appender
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"));
builder.add( appenderBuilder );
// create a rolling file appender
LayoutComponentBuilder layoutBuilder = builder.newLayout("PatternLayout")
.addAttribute("pattern", "%d [%t] %-5level: %msg%n");
ComponentBuilder triggeringPolicy = builder.newComponent("Policies")
.addComponent(builder.newComponent("CronTriggeringPolicy").addAttribute("schedule", "0 0 0 * * ?"))
.addComponent(builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", "100M"));
appenderBuilder = builder.newAppender("rolling", "RollingFile")
.addAttribute("fileName", "target/rolling.log")
.addAttribute("filePattern", "target/archive/rolling-%d{MM-dd-yy}.log.gz")
.add(layoutBuilder)
.addComponent(triggeringPolicy);
builder.add(appenderBuilder);
// create the new logger
builder.add( builder.newLogger( "TestLogger", Level.DEBUG )
.add( builder.newAppenderRef( "rolling" ) )
.addAttribute( "additivity", false ) );
builder.add( builder.newRootLogger( Level.DEBUG )
.add( builder.newAppenderRef( "rolling" ) ) );
LoggerContext ctx = Configurator.initialize(builder.build());
通过组合配置文件和程序配置来初始化Log4j
有时您希望使用配置文件进行配置,但要进行一些额外的程序化配置。
一种可能的用例是,您希望允许使用XML进行灵活的配置,但同时确保有一些配置元素始终存在,不能删除。
实现这一点的最简单方法是扩展其中一个标准配置类(XMLConfiguration、JSONConfiguration),然后为扩展的类创建一个新的ConfigurationFactory。
标准配置完成后,可以将自定义配置添加到标准配置中。
下面的示例展示了如何扩展XMLConfiguration,以手动将Appender和LoggerConfig添加到配置中。
@Plugin(name = "MyXMLConfigurationFactory", category = "ConfigurationFactory")
@Order(10)
public class MyXMLConfigurationFactory extends ConfigurationFactory {
/**
* Valid file extensions for XML files.
*/
public static final String[] SUFFIXES = new String[] {".xml", "*"};
/**
* Return the Configuration.
* @param source The InputSource.
* @return The Configuration.
*/
public Configuration getConfiguration(InputSource source) {
return new MyXMLConfiguration(source, configFile);
}
/**
* Returns the file suffixes for XML files.
* @return An array of File extensions.
*/
public String[] getSupportedTypes() {
return SUFFIXES;
}
}
public class MyXMLConfiguration extends XMLConfiguration {
public MyXMLConfiguration(final ConfigurationFactory.ConfigurationSource configSource) {
super(configSource);
}
@Override
protected void doConfigure() {
super.doConfigure();
final LoggerContext context = (LoggerContext) LogManager.getContext(false);
final Configuration config = context.getConfiguration();
final Layout layout = PatternLayout.createDefaultLayout(config);
final Appender appender = FileAppender.createAppender("target/test.log", "false", "false", "File", "true",
"false", "false", "4000", layout, null, "false", null, config);
appender.start();
addAppender(appender);
LoggerConfig loggerConfig = LoggerConfig.createLogger("false", "info", "org.apache.logging.log4j",
"true", refs, null, config, null );
loggerConfig.addAppender(appender, null, null);
addLogger("org.apache.logging.log4j", loggerConfig);
}
}
在初始化后以编程方式修改当前配置
应用程序有时需要自定义日志记录,而不是实际配置。Log4j允许这样做,尽管它有一些限制:
-
如果配置文件被更改,配置将被重新加载,手动更改将丢失。
-
对正在运行的配置进行修改,需要同步调用的所有方法(addAppender和addLogger)。
因此,推荐的自定义配置方法是扩展一个标准配置类,覆盖setup方法,首先执行super.setup(),然后在注册使用之前将自定义Appenders、Filters和LoggerConfigs添加到配置中。
下面的示例将添加一个Appender和一个使用该Appender的新的LoggerConfig到当前配置中。
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
final Configuration config = ctx.getConfiguration();
final Layout layout = PatternLayout.createDefaultLayout(config);
Appender appender = FileAppender.createAppender("target/test.log", "false", "false", "File", "true",
"false", "false", "4000", layout, null, "false", null, config);
appender.start();
config.addAppender(appender);
AppenderRef ref = AppenderRef.createAppenderRef("File", null, null);
AppenderRef[] refs = new AppenderRef[] {ref};
LoggerConfig loggerConfig = LoggerConfig.createLogger("false", "info", "org.apache.logging.log4j",
"true", refs, null, config, null );
loggerConfig.addAppender(appender, null, null);
config.addLogger("org.apache.logging.log4j", loggerConfig);
ctx.updateLoggers();
}
以编程方式向写入器和输出流添加日志事件
Log4j 2.5提供了将日志事件附加到写入器和输出流的功能。
例如,它为那些内部使用Log4j但仍然希望支持JDBC api CommonDataSource.setLogWriter(PrintWriter)、java.sql.DriverManager.setLogWriter(PrintWriter)和java.sql.DriverManager.setLogStream(PrintStream)的JDBC驱动程序实现提供了简单的集成。
给定任何Writer,比如PrintWriter,你可以通过创建WriterAppender并更新Log4j配置来告诉Log4j将事件附加到该Writer:
void addAppender(final Writer writer, final String writerName) {
final LoggerContext context = LoggerContext.getContext(false);
final Configuration config = context.getConfiguration();
final PatternLayout layout = PatternLayout.createDefaultLayout(config);
final Appender appender = WriterAppender.createAppender(layout, null, writer, writerName, false, true);
appender.start();
config.addAppender(appender);
updateLoggers(appender, config);
}
private void updateLoggers(final Appender appender, final Configuration config) {
final Level level = null;
final Filter filter = null;
for (final LoggerConfig loggerConfig : config.getLoggers().values()) {
loggerConfig.addAppender(appender, level, filter);
}
config.getRootLogger().addAppender(appender, level, filter);
}
你可以用OutputStream实现相同的效果,比如PrintStream:
void addAppender(final OutputStream outputStream, final String outputStreamName) {
final LoggerContext context = LoggerContext.getContext(false);
final Configuration config = context.getConfiguration();
final PatternLayout layout = PatternLayout.createDefaultLayout(config);
final Appender appender = OutputStreamAppender.createAppender(layout, null, outputStream, outputStreamName, false, true);
appender.start();
config.addAppender(appender);
updateLoggers(appender, config);
}
区别在于使用的是OutputStreamAppender而不是WriterAppender。
参考资料
https://logging.apache.org/log4j/2.x/manual/customconfig.html