公司的项目是一个爬虫的管理中心,需要对爬虫进行监控,监控它们是不是在正常运行。
从网上搜到的解决方案都是 spring boot 下的,由于公司的项目是老项目,所以不适用。于是想到了从log4j下手,通过自定义Appender,拦截日志,并从日志中提取出我需要的信息入库。然后调用父类的同方法,保证日志以前能干的事不会少了。
由于自定义所考虑的东西太多,所以继承了 DailyRollingFileAppender 减少工作量。
第一反应的写法:
- 每当产生一条日志时,根据配置的日志级别进行调用
- LoggingEvent 对象内包含了日志内容,如果是异常日志,还会包含整个的异常栈的信息。
- 通过LoggingEvent对象的getLevel拿到我们想要的日志级别。
- 从日志中解析我们需要的内容
- 将日志保存到数据库中
- 调用父类的doAppend方法,保证之前逻辑的执行。
@Override
public void doAppend(LoggingEvent event) {
if (isAsSevereAsThreshold(event.getLevel())) {
if(service == null) {
init();
}
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(event.getTimeStamp());
ThrowableInformation throwableInfo = event.getThrowableInformation();
ExceptionBean eb = parseException(throwableInfo);
eb.setMessage(event.getMessage().toString());
eb.setTime(df.format(date));
eb.setLevel(event.getLevel().toString());
service.save(eb);
}
super.doAppend(event);
}
存在的问题:
由于这个自定义的类是直接通过log4j的配置文件,被加载到内存的,不是spring直接管理的。所以入库需要的资源不能自动注入,也就没有办法入库了。
解决方案:
虽然这个自定义类不是spring直接管理的,但是我可以拿到spring的上下文信息啊
拿到了spring的上下文信息之后,通过其获取我需要的 Bean 并存起来,不就可以实现入库了吗
SpringContextUtil类代码:
public class SpringContextUtil {
private static ApplicationContext applicationContext = null;
private synchronized static void init() {
if(applicationContext == null) {
ServletContext sc = ContextLoader.getCurrentWebApplicationContext().getServletContext();
applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
}
}
public static Object getBean(String beanName) {
return applicationContext.getBean(beanName);
}
public static Object getBean(Class c) {
if(applicationContext == null) {
init();
}
return applicationContext.getBean(c);
}
}
再次遇到问题:
由于我在 Appender 初始化的时候就去调用 SpringContextUtil 去获取 Service 层的 Bean,看起来是没有问题的。但是系统在启动的时候也是要打日志的啊,这个时候 SpringContext 还没有初始化,于是便出问题了。
最后的解决方案是,用一个懒加载的办法去获取 Bean 这样就避免了初始化 Bean 的时候出现的问题。
初始化 Service Bean 的代码:
private synchronized void init() {
if(service == null) {
service = (ExceptionService) SpringContextUtil.getBean(ExceptionService.class);
}
}
这段代码调用是在最开始那段代码,为了防止出现并发问题,所以在外层也做了 null 值判断。
水平有限,若有错误,请指正。