本篇为扩展appender标签,如果需要扩展filer、marker等其他的log4j2提供的扩展方式,可以查看相关的文档:
中文文档:https://www.docs4dev.com/docs/zh/log4j2/2.x/all/javadoc.html
英文文档:http://logging.apache.org/log4j/2.x/
api:http://logging.apache.org/log4j/2.x/log4j-api/apidocs/index.html
通过本篇,你可以看到我在扩展插件时所遇到的问题以及解决方案,根据ApplicationContext获取bean的工具类源码等。
不再是千篇一律的抄袭,真正将解决自己所遇到的问题的代码分享出来。
目录
自定义Appender插件类
关键代码
代码说明:
1.引入log4j2的插件注解,并声明相关属性的值
2.如何自定义变量
3.如何引入原生的插件,如rollingfile和console
4.为何本地自测有效,一到服务器就不生效?
5.我的bean注入是null?明明配置都对
log4j2.xml配置
package com.xxx.util;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.*;
import org.apache.logging.log4j.core.appender.*;
import org.apache.logging.log4j.core.config.AppenderControl;
import org.apache.logging.log4j.core.config.AppenderRef;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
import java.io.Serializable;
@Slf4j
@Plugin(name = "MyAppender", category = "Core", elementType = "appender", printObject = true)
public class MyAppender extends AbstractAppender {
private Configuration configuration;
private Property[] properties;
protected MyAppender(String name, Filter filter, Layout extends Serializable> layout,
boolean ignoreExceptions, Property[] properties, AppenderRef[] appenderRefs) {
super(name, filter, layout, ignoreExceptions, properties);
this.appenderRefs = appenderRefs;
this.properties = properties;
}
@Override
public void append(LogEvent event) {
try {
// 可以增加处理业务日志逻辑
try {
callAppender(event);
} catch (Exception e) {
log.error("自定义异常信息", e);
}
try {
// 可以根据日志级别处理日志-如写入数据库
if (event.getLevel().intLevel() <= Level.ERROR.intLevel()) {}
} catch (Exception e) {
log.error("自定义异常信息", e);
}
} catch (Exception ex) {
if (!ignoreExceptions()) {
throw new AppenderLoggingException(ex);
}
}
}
@PluginFactory
public static MyAppender createAppender(@PluginAttribute("name") String name,
@PluginElement("Filter") Filter filter,
@PluginElement("Layout") Layout extends Serializable> layout,
@PluginAttribute("ignoreExceptions") boolean ignoreExceptions,
@PluginElement("property") Property[] properties,
@PluginElement("AppenderRef") AppenderRef[] appenderRefs) {
if (name == null) {
log.error("No name provided for MyAppenderImpl");
return null;
}
if (layout == null) {
layout = PatternLayout.createDefaultLayout();
}
return new MyAppender(name, filter, layout, ignoreExceptions, properties, appenderRefs);
}
private void callAppender(LogEvent event) {
if (null == configuration) {
refreshLogcontext();
}
for (AppenderRef appenderRef : appenderRefs) {
if (null == configuration.getAppender(appenderRef.getRef())) {
log.info("appenderRef null {}", appenderRef.getRef());
continue;
}
recursion(event, appenderRef.getRef());
}
}
private void refreshLogcontext() {
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
this.configuration = loggerContext.getConfiguration();
}
private void recursion(LogEvent event, String ref) {
if (configuration.getAppender(ref) instanceof RollingFileAppender) {
Appender appender = configuration.getAppender(ref);
AppenderControl control = new AppenderControl(appender, null, null);
control.callAppender(event);
} else if (configuration.getAppender(ref) instanceof AsyncAppender) {
AsyncAppender asyncAppender = configuration.getAppender(ref);
if (null != asyncAppender.getAppenderRefStrings()) {
for (int j = 0; j < asyncAppender.getAppenderRefStrings().length; j++) {
// 递归
recursion(event, asyncAppender.getAppenderRefStrings()[j]);
}
}
} else if (configuration.getAppender(ref) instanceof ConsoleAppender) {
Appender appender = configuration.getAppender(ref);
AppenderControl control = new AppenderControl(appender, null, null);
control.callAppender(event);
}
}
}
@Plugin(name = "MyAppender", category = "Core", elementType = "appender", printObject = true)
createAppender方法,用于log4j2在扫描到插件之后根据配置文件中的配置穿件自定义的插件对象。
会获取标签下AppenderRef 元素的值,如果是多个AppenderRef 子元素,将会获取都一个数组
可以根据业务需要自定义元素或者属性。
本篇中实现了自定义的标签引入log4j2原生的插件rollingfile和console,并控制原生的rollingfile和console向文件写入和控制台打印。
这一点区分于网上大部分的给出的代码示例,记住生产业务系统不能使用System.out.print,而且要注重高效,最好能用到log4j2自己实现的异步机制来完成我们想要的效果。
其实引入原生的插件rollingfile和console,并使它们发挥作用很简单,只需要call一下就可以,关键代码:
Appender appender = configuration.getAppender(ref);
AppenderControl control = new AppenderControl(appender, null, null);
control.callAppender(event);
本功能在实现的时候遇到一个难以预料的问题,就是本地怎么测试都好用,但是一部署到服务器就不好用,查过很多资料,不得不说国内的资料千篇一律的居多,参考意义不大。无奈自己搭建了xx谷歌了一下,发现如果你的自定义插件失效,可以从三个方面排查:
再说一个上下文的问题,log4j2在dao层service层初始化结束之前就已经初始化了,如果采用@Resource这种依赖注入的方式构建bean是行不通的,获取到的只能是null,但是ApplicationContext已经加载(已经扫描哪些带有注解的类),我们可以通过ApplicationContext手动获取bean。
SpringBeanUtil源码:
package com.xxx.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
@Component
@Configuration
public class SpringBeanUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringBeanUtils.applicationContext == null) {
SpringBeanUtils.applicationContext = applicationContext;
}
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
public static T getBean(Class clazz){
return getApplicationContext().getBean(clazz);
}
public static T getBean(String name, Class clazz){
return getApplicationContext().getBean(name, clazz);
}
public static T getBean(Class extends T> clazz, Class targetClazz){
return (T)getBean(clazz);
}
}
用法:
注意第一个入参如果没有指定,默认是小写,这个应该都懂吧,spring的东西了
SpringBeanUtils.getBean("testAppenderDao", TestAppenderDao.class);
%d{yyyy-MM-dd HH:mm:ss.SSS} |-%-5level [%thread] %c [%L] -| %msg%n
his/app-%d{MM-dd-yyyy}-%i.log
配置说明:
我们可以使用自己扩展的标签引入不同的console和rollingfile等标签,可以配置不同的属性和元素,也可以监控不同的项目路径以达到业务需要的效果。