1. 程式人生 > >Spring統一日誌處理(AOP)

Spring統一日誌處理(AOP)

來現在公司的緣故,公司大佬使用的AOP比較多,本來在校時候對AOP只是一知半解,在現在公司呆了半年,用到了各種攔截器,對AOP有了許多新的認識。此處用統一日誌處理就是使用的是Spring AOP實現的。

定義統一日誌表,用在將所有請求都記錄在表中,方便以後查閱。

CREATE TABLE `t_union_log`  (
  `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `dev_mac` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '裝置MAC',
  `user_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '使用者身份',
  `level` int(11) NULL DEFAULT 0 COMMENT '日誌級別,0-最高級別',
  `method` varchar(25) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作方式',
  `remote_host` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作IP地址',
  `request_uri` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '請求URI',
  `user_agent` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '使用者代理',
  `module_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '模組名稱',
  `param` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '請求引數',
  `result` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '結果',
  `spent` bigint(20) NULL DEFAULT NULL COMMENT '耗費時間-毫秒',
  `ctime` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間',
  PRIMARY KEY (`id`) USING BTREE
) 

定義實體類與DAO,此處使用的是mybatis-plus,使用其他的也可以,自行選擇,這些都不重要,重要的是後面的aop攔截器。

@TableName("t_union_log")
public class UnionLogEntity {

    @TableId(value = "id",type = IdType.AUTO)
    private Long id;

    private String devMac;

    private String userId;

    private Integer level;

    private String method;

    private String remoteHost;

    private String requestUri;

    private String userAgent;

    private String moduleName;

    private String param;

    private String result;

    private Long spent;

    private Date ctime;

    Getter...
    Setter...
}
@Mapper
public interface UnionLogMapper extends BaseMapper<UnionLogEntity>{
}

實現統一日誌的攔截。原理很簡單,只要懂得一些AOP方面的知識,看程式碼很容易看的懂。特別注意的是,在不需要攔截的URI,一定不能進行日誌列印(如:檔案下載介面,會報記憶體溢位錯誤,以前寫檔案下載的時候遇到,再次銘記)

/**統一日誌處理*/
@Aspect
@Component
public class GlobalLogAspect {
    private Logger logger = LoggerFactory.getLogger(GlobalLogAspect.class);
    private static final String POINTCUT = "execution(* com.xichuan.wechat.*.controller.*.*(..)) ";

    private static final ThreadLocal<Map<Object,Object>> paramLocal = new ThreadLocal<>();
    private static final ThreadLocal<Map<Object,Object>> resultsLocal = new ThreadLocal<>();
    private static final ThreadLocal<Long> start = new ThreadLocal<>();
    private static final ThreadLocal<Long> end = new ThreadLocal<>();
    private static final ThreadLocal<HttpServletRequest> httpRequest = new ThreadLocal<>();

    @Autowired
    UnionLogMapper unionLogMapper;

    @Before(POINTCUT)
    public void beforeAspectHandler(){
        start.set(System.currentTimeMillis());
    }

    @After(POINTCUT)
    public void afterAspectHandler(){

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String uri = request.getRequestURI();
        if(uri.equals("")){ /**不進行日誌攔截的URI*/
            logger.info("global log Aspect untreated uri:"+uri);
        }else{
            end.set(System.currentTimeMillis());
            this.tracer();
        }
    }

    @Around(POINTCUT)
    public Object aroundAspectHandler(ProceedingJoinPoint joinPoint)throws Throwable{
        Object[] param = joinPoint.getArgs();
        Map<Object,Object> argMap = new LinkedHashMap<>();
        argMap.put("params",param);
        paramLocal.set(argMap);

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        httpRequest.set(request);
        Object result = joinPoint.proceed();

        Map<Object,Object> resMap = new LinkedHashMap<>();
        resMap.put("results",result);
        resultsLocal.set(resMap);

        return result;
    }

    public void tracer(){
        try {
            HttpServletRequest request = httpRequest.get();
            Gson gson = new Gson();
            logger.info("Request PATH: {}, Time: {}, Consumed: {}ms, Param: {}, Result: {}\n",
                    "["+request.getRequestURI()+"]",
                    new SimpleDateFormat("yyyy-MM-dd HH:mm:s").format(start.get()),
                    (end.get()-start.get()),
                    gson.toJson(paramLocal.get()),
                    gson.toJson(resultsLocal.get()));

            UnionLogEntity unionLog = new UnionLogEntity();
            unionLog.setCtime(new Date());
            unionLog.setMethod(request.getMethod());
            unionLog.setRequestUri(request.getRequestURI());
            unionLog.setRemoteHost(request.getRemoteHost());
            unionLog.setUserAgent(request.getHeader("User-Agent"));
            unionLog.setParam(gson.toJson(paramLocal.get()));
            unionLog.setResult(gson.toJson(resultsLocal.get()));
            unionLog.setSpent(end.get()-start.get());
            unionLog.setUserId(request.getHeader(("account_id")));
            unionLog.setDevMac(request.getHeader(("dev_mac")));
            unionLog.setModuleName("cmd");
            unionLogMapper.insert(unionLog);

        }finally {
            httpRequest.remove();
            paramLocal.remove();
            resultsLocal.remove();
            start.remove();
            end.remove();
        }
    }
}