使用场景:
一般log4j2的配置都是通过log4j2.xml配置来实现相关的日志操作。
假如项目上使用的的公司封装好的log4j2框架,里面的日志配置都是固定的,如果自己想扩展要么就是升级日志框架(不方便),或者自己重写覆盖log4j2.xml(后续如果升级自己也需要跟着改动)。这里不改动日志框架的前提下,动态添加自己的想要的appender。
代码示例
1.添加appender代码如下,借助springboot的启动事件操作
这里以日志输出到文件添加一个Appender为例
package com.zhoule.demoactuator.log;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.apache.logging.log4j.core.appender.rolling.*;
import org.apache.logging.log4j.core.appender.rolling.action.Action;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
import org.apache.logging.log4j.core.filter.ThresholdFilter;
import org.apache.logging.log4j.core.util.Builder;
import org.apache.logging.log4j.core.util.ReflectionUtil;
import org.apache.logging.log4j.core.util.TypeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.ConfigurableEnvironment;
import java.io.File;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* ApplicationEnvironmentPreparedEvent 表示环境准备好事件
*/
public class RollingFileAppenderPlugin implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
//appender名称
private static final String APPENDER_NAME = "rollingFile";
//log4j2.xml中已经定义好的一个appender
private static final String ROOT_APPENDER_NAME = "consoleRoot";
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
generateAppender(event.getEnvironment());
}
/**
* 生成appender
*/
private void generateAppender(ConfigurableEnvironment environment) {
String applicationName = environment.getProperty("spring.application.name");
String basePath = environment.resolvePlaceholders("${rolling-file.appender.base-path:}");
String maxFileSize = environment.resolvePlaceholders("${rolling-file.appender.max-file-size:20 MB}");
String maxFileNum = environment.resolvePlaceholders("${rolling-file.appender.max-file-num:50}");
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
Configuration configuration = loggerContext.getConfiguration();
synchronized (configuration) {
try {
if (configuration.getAppender(APPENDER_NAME) != null) {
return;
}
Builder<RollingFileAppender> builder = null;
for (final Method method : RollingFileAppender.class.getDeclaredMethods()) {
if (method.isAnnotationPresent(PluginBuilderFactory.class) &&
Modifier.isStatic(method.getModifiers()) &&
TypeUtil.isAssignable(Builder.class, method.getReturnType())) {
ReflectionUtil.makeAccessible(method);
builder = (Builder<RollingFileAppender>) method.invoke(null);
break;
}
}
if (StringUtils.isBlank(basePath)) {
basePath = System.getProperty("user.dir");
}
String currentPath = basePath + File.separator + "logs" + File.separator + applicationName;
List<Field> fields = TypeUtil.getAllDeclaredFields(builder.getClass());
AccessibleObject.setAccessible(fields.toArray(new Field[]{}), true);
//必要字段赋值,字段和配置xml里面的属性是对应的
for (Field field : fields) {
if ("name".equals(field.getName())) {
field.set(builder, APPENDER_NAME);
}
if ("fileName".equals(field.getName())) {
field.set(builder, currentPath + File.separator + "console.log");
}
if ("filePattern".equals(field.getName())) {
field.set(builder, currentPath + File.separator + "${date:yyyy-MM}" + File.separator + "console-%d{yyyy-MM-dd}-%i.log");
}
if ("filter".equals(field.getName())) {
field.set(builder, ThresholdFilter.createFilter(Level.DEBUG, Filter.Result.ACCEPT, Filter.Result.DENY));
}
//layout的格式取得是ROOT_APPENDER_NAME里面的
if ("layout".equals(field.getName())) {
field.set(builder, configuration.getAppender(ROOT_APPENDER_NAME).getLayout());
}
if ("policy".equals(field.getName())) {
field.set(builder, CompositeTriggeringPolicy.createPolicy(
OnStartupTriggeringPolicy.createPolicy(1),
TimeBasedTriggeringPolicy.newBuilder().build(),
SizeBasedTriggeringPolicy.createPolicy(maxFileSize)));
}
if ("strategy".equals(field.getName())) {
field.set(builder, DefaultRolloverStrategy.newBuilder()
.withMax(maxFileNum)
.withCustomActions(new Action[]{})
.withConfig(configuration)
.build());
}
if ("configuration".equals(field.getName())) {
field.set(builder, configuration);
}
}
RollingFileAppender rollingFileAppender = builder.build();
//启动appender
rollingFileAppender.start();
//添加appender
configuration.addAppender(rollingFileAppender);
Map<String, LoggerConfig> loggers = configuration.getLoggers();
Iterator<Map.Entry<String, LoggerConfig>> iterator = loggers.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, LoggerConfig> next = iterator.next();
//给logger关联 appender
next.getValue().addAppender(rollingFileAppender, null, null);
}
//更新 loggerContext
loggerContext.updateLoggers(configuration);
} catch (Exception e) {
Logger logger = LoggerFactory.getLogger(RollingFileAppenderPlugin.class);
logger.error(e.toString());
}
}
}
}
2.上面的代码类似与log4j2.xml的如下配置
<!--按照一定规则拆分的日志文件的 appender-->
<RollingFile name="rollingFile" fileName="${LOG_HOME}/console.log"
filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/console-%d{yyyy-MM-dd}-%i.log">
<!--日志级别过滤器-->
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" />
<!--日志消息格式-->
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %msg%n" />
<Policies>
<!--在系统启动时,触发拆分规则,生产一个新的日志文件-->
<OnStartupTriggeringPolicy />
<!--按照文件大小拆分,20MB -->
<SizeBasedTriggeringPolicy size="20 MB" />
<!--按照时间节点拆分,规则根据filePattern定义的-->
<TimeBasedTriggeringPolicy />
</Policies>
<!--在同一个目录下,文件的个数限定为 50个,超过进行覆盖-->
<DefaultRolloverStrategy max="50" />
</RollingFile>
3.启动类上添加监听类
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(DemoActuatorApplication.class);
//添加监听事件
springApplication.addListeners(new RollingFileAppenderPlugin());
springApplication.run(args);
}