1. 程式人生 > >通過ThreadLocal和HandlerInterceptor實現java後臺業務埋點日誌功能

通過ThreadLocal和HandlerInterceptor實現java後臺業務埋點日誌功能

目前公司的方案是用mdc來實現一個請求的業務資料埋點記錄,但是mdc是map方式,需要手動設定key,而且每次都要手動clear,一是不方便管理,再者如果忘記clear會造成業務埋點資料混亂。所以有了想要把埋點資料欄位統一封裝的想法,這樣方便維護,後面如果修改,也很容易找到所有修改點。於是我就想把封裝好的bean放到mdc裡,然後再用切面統一處理,但是,研究發現,mdc的value不支援object只能用String,很失望。我順手翻了一下mdc的原始碼,如下

MDC的put方法原始碼
private InheritableThreadLocal<Map<String, String
>> inheritableThreadLocal = new InheritableThreadLocal<Map<String, String>>() { protected Map<String, String> childValue(Map<String, String> parentValue) { return parentValue == null?null:new HashMap(parentValue); } }; public BasicMDCAdapter() { } public
void put(String key, String val) { if(key == null) { throw new IllegalArgumentException("key cannot be null"); } else { Map<String, String> map = (Map)this.inheritableThreadLocal.get(); if(map == null) { map = new HashMap(); this.inheritableThreadLocal.
set(map); } ((Map)map).put(key, val); } }

發現是用ThreadLocal實現的,它直接把裡面的物件定義成map,我想,那我是不是可以定義成我自己的日誌實體,開始動手,寫了一個簡單的類,如下

public class BusinessLogUtil {

  private final static ThreadLocal<BusinessLog> threadLocal = new InheritableThreadLocal<>();

  public static BusinessLog getThreadLocalLog() {
    if (threadLocal.get() == null) {
      return new BusinessLog();
    }
    return threadLocal.get();
  }

  public static void setThreadLocalLog(final BusinessLog businessLog) {
    threadLocal.set(businessLog);
  }

  public static void remove() {
    threadLocal.remove();
  }
}

好了,操作類實現完了,來實現一下統一的前置和後置操作,剛開始用aspectj實現的,但是發現,不是在同一執行緒裡操作的,具體原理不細說。於是換成HandlerInterceptor,具體程式碼如下

@Slf4j
public class BusinessLogInterceptor implements HandlerInterceptor {

  @Override
  public boolean preHandle(final HttpServletRequest httpServletRequest,
      final HttpServletResponse httpServletResponse, final Object o) throws Exception {
    final BusinessLog businessLog = new BusinessLog();
    businessLog.setUserId(String.valueOf(Math.random()));
    BusinessLogUtil.setThreadLocalLog(businessLog);
    log.info("開始訪問:" + BusinessLogUtil.getThreadLocalLog().toString());
    return true;// 只有返回true才會繼續向下執行,返回false取消當前請求
  }

  @Override
  public void postHandle(final HttpServletRequest httpServletRequest,
      final HttpServletResponse httpServletResponse, final Object o,
      final ModelAndView modelAndView)
      throws Exception {

  }

  @Override
  public void afterCompletion(final HttpServletRequest httpServletRequest,
      final HttpServletResponse httpServletResponse, final Object o, final Exception e)
      throws Exception {
    log.info("訪問結束:" + BusinessLogUtil.getThreadLocalLog().toString());
    BusinessLogUtil.remove();
    log.info("清理結束:" + BusinessLogUtil.getThreadLocalLog().toString());
  }
}

測試結果如下


[2017-12-01T11:25:40,015] | INFO | com.aaa.interceptor.BusinessLogInterceptor.preHandle(BusinessLogInterceptor.java:23) | http-nio-8080-exec-1  開始訪問:BusinessLog(userId=0.1528058009781007, type=null, message=null, phone=null)
[2017-12-01T11:25:40,033] | INFO | com.aaa.api.rest.IntegrateController.getIntegrateList(IntegrateController.java:70) | http-nio-8080-exec-1訪問controller:BusinessLog(userId=0.1528058009781007, type=null, message=null, phone=null)
[2017-12-01T11:25:40,033] | INFO | com.aaa.api.rest.IntegrateController.getIntegrateList(IntegrateController.java:74) | http-nio-8080-exec-1 設定controller變數:BusinessLog(userId=0.1528058009781007, type=integrate, message=null, phone=null)
[2017-12-01T11:25:40,033] | INFO | com.aaa.service.impl.BaseServiceImpl.getBatchList(BaseServiceImpl.java:39) | http-nio-8080-exec-1  訪問service:BusinessLog(userId=0.1528058009781007, type=integrate, message=null, phone=null)
[2017-12-01T11:25:40,034] | INFO | com.aaa.service.impl.BaseServiceImpl.getBatchList(BaseServiceImpl.java:43) | http-nio-8080-exec-1 設定service變數:BusinessLog(userId=0.1528058009781007, type=integrate, message=null, phone=18600732726)
[2017-12-01T11:25:41,024] | INFO | com.aaa.oplog.OpLogAspect.logEvent(OpLogAspect.java:75) | http-nio-8080-exec-1  EventParameter{source='com.aaa.api.rest.IntegrateController', level='INFO', name='獲取整合請求列表', desc='', arguments='ListBatchRequest(batchId=null, batchNo=null, state=null, createPerson=null, updateTime=null, createTime=null, type=null, pageNum=1, pageSize=10)', message='', method='getIntegrateList', url='http://localhost:8080/api/online-tickets/v1/integrate'}
[2017-12-01T11:25:41,066] | INFO | com.aaa.interceptor.BusinessLogInterceptor.afterCompletion(BusinessLogInterceptor.java:39) | http-nio-8080-exec-1 訪問結束:BusinessLog(userId=0.1528058009781007, type=integrate, message=null, phone=18600732726)
[2017-12-01T11:25:41,067] | INFO | com.aaa.interceptor.BusinessLogInterceptor.afterCompletion(BusinessLogInterceptor.java:41) | http-nio-8080-exec-1 清理結束:BusinessLog(userId=null, type=null, message=null, phone=null)
[2017-12-01T11:33:18,880] | INFO | com.aaa.interceptor.BusinessLogInterceptor.preHandle(BusinessLogInterceptor.java:23) | http-nio-8080-exec-3 開始訪問:BusinessLog(userId=0.203277605749153, type=null, message=null, phone=null)
[2017-12-01T11:33:18,881] | INFO | com.aaa.api.rest.IntegrateController.getIntegrateList(IntegrateController.java:70) | http-nio-8080-exec-3 訪問controller:BusinessLog(userId=0.203277605749153, type=null, message=null, phone=null)
[2017-12-01T11:33:18,882] | INFO | com.aaa.api.rest.IntegrateController.getIntegrateList(IntegrateController.java:74) | http-nio-8080-exec-3 設定controller變數:BusinessLog(userId=0.203277605749153, type=integrate, message=null, phone=null)
[2017-12-01T11:33:18,882] | INFO | com.aaa.service.impl.BaseServiceImpl.getBatchList(BaseServiceImpl.java:39) | http-nio-8080-exec-3 訪問service:BusinessLog(userId=0.203277605749153, type=integrate, message=null, phone=null)
[2017-12-01T11:33:18,882] | INFO | com.aaa.service.impl.BaseServiceImpl.getBatchList(BaseServiceImpl.java:43) | http-nio-8080-exec-3 設定service變數:BusinessLog(userId=0.203277605749153, type=integrate, message=null, phone=18600732726)
[2017-12-01T11:33:18,925] | INFO | com.aaa.oplog.OpLogAspect.logEvent(OpLogAspect.java:75) | http-nio-8080-exec-3  EventParameter{source='com.aaa.api.rest.IntegrateController', level='INFO', name='獲取整合請求列表', desc='', arguments='ListBatchRequest(batchId=null, batchNo=null, state=null, createPerson=null, updateTime=null, createTime=null, type=null, pageNum=1, pageSize=10)', message='', method='getIntegrateList', url='http://localhost:8080/api/online-tickets/v1/integrate'}
[2017-12-01T11:33:18,931] | INFO | com.aaa.interceptor.BusinessLogInterceptor.afterCompletion(BusinessLogInterceptor.java:39) | http-nio-8080-exec-3 訪問結束:BusinessLog(userId=0.203277605749153, type=integrate, message=null, phone=18600732726)
[2017-12-01T11:33:18,931] | INFO | com.aaa.interceptor.BusinessLogInterceptor.afterCompletion(BusinessLogInterceptor.java:41) | http-nio-8080-exec-3  清理結束:BusinessLog(userId=null, type=null, message=null, phone=null)

發現沒有問題,每次訪問的資料可以在整個請求區間內儲存,在訪問結束後,被清理掉,不會影響其它請求。