捕獲項目中的異常信息,並處理、入庫

公司的項目是一個爬蟲的管理中心,需要對爬蟲進行監控,監控它們是不是在正常運行。

從網上搜到的解決方案都是 spring boot 下的,由於公司的項目是老項目,所以不適用。於是想到了從log4j下手,通過自定義Appender,攔截日誌,並從日誌中提取出我需要的信息入庫。然後調用父類的同方法,保證日誌以前能幹的事不會少了。

由於自定義所考慮的東西太多,所以繼承了 DailyRollingFileAppender 減少工作量。

第一反應的寫法:

  1. 每當產生一條日誌時,根據配置的日誌級別進行調用
  2. LoggingEvent 對象內包含了日誌內容,如果是異常日誌,還會包含整個的異常棧的信息。
  3. 通過LoggingEvent對象的getLevel拿到我們想要的日誌級別。
  4. 從日誌中解析我們需要的內容
  5. 將日誌保存到數據庫中
  6. 調用父類的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 值判斷。

水平有限,若有錯誤,請指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章