公司的項目是一個爬蟲的管理中心,需要對爬蟲進行監控,監控它們是不是在正常運行。
從網上搜到的解決方案都是 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 值判斷。
水平有限,若有錯誤,請指正。