工作經驗:Java 系統記錄調用日志,並且記錄錯誤堆棧


前言:現在有一個系統,主要是為了給其他系統提供數據查詢接口的,這個系統上線不會輕易更新,更不會跟隨業務系統的更新而更新(這也是有一個數據查詢接口系統的原因,解耦)。這時,這個系統就需要有一定的方便的線上查錯方式,我便想到了記錄每一次的調用日志,而且需要記錄錯誤堆棧,同時被白名單過濾的也要記錄下來。

想法

  這個日志記錄,需要在每一次訪問接口時記錄一下,在有異常時將異常的堆棧信息記錄在每次訪問記錄里。這里由於要使用數據庫信息,所以選擇了 spring 的攔截器

  在攔截器拋放心之后,運行業務代碼,如果拋異常(包括自定義異常),應該在拋異常之后,記錄錯誤信息到堆棧,這時需要知道在攔截器時插入數據庫的那條記錄的 id,拿到這個id就可以直接更新數據,將堆棧記錄。這里通過 ThreadLocal 線程本地變量來記錄每一次訪問插入數據庫后返回的主鍵 id。

  而每一次的異常都需要做統一異常處理,在統一異常處理這里訪問數據庫,記錄錯誤信息。

  白名單被過濾的也要記錄下來,這個利用拋自定義業務異常,然后使用統一異常類來處理就好。

實現

   接口調用日志需要一張表來記錄,字段如下:

create table t_interface_log
(
  id             number not null,
  interface_name varchar2(100),
  caller_ip      varchar2(100),
  local_ip       varchar2(100),
  caller_params  varchar2(1000),
  caller_date    date,
  msg            varchar2(4000),
  status         varchar2(1)
)
;
-- Add comments to the table
comment on table t_interface_log
  is '接口調用日志記錄表';
-- Add comments to the columns
comment on column t_interface_log.id
  is '主鍵id';
comment on column t_interface_log.interface_name
  is '接口名';
comment on column t_interface_log.caller_ip
  is '調用者ip';
comment on column t_interface_log.local_ip
  is '本機ip';
comment on column t_interface_log.caller_params
  is '調用參數';
comment on column t_interface_log.caller_date
  is '調用時間';
comment on column t_interface_log.msg
  is '信息記錄';
comment on column t_interface_log.status
  is '狀態:0:失敗,1:成功';

  配置如下:

<bean id="interfaceLogInterceptor" class="com.yule.common.interceptor.InterfaceLogInterceptor" />
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/interface/**"/>
            <ref bean="interfaceLogInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>

  Java 代碼如下:

線程變量

package com.yule.manage.interfacelog.entity;

/**
 * 接口調用日志線程變量
 * @author yule
 */
public class InterfaceLogHolder {

    /**
     * 本地線程變量,用於控制每一次新增日志后返回的id
     */
    private static final ThreadLocal<String> ID_STRING_THREAD_LOCAL = new ThreadLocal<>();

    /**
     * 獲取本地線程變量的id
     * @return id
     */
    public static String getIdStringThreadLocalValue() {
        return ID_STRING_THREAD_LOCAL.get();
    }

    /**
     * 設置本地線程變量的id
     * @param value id
     */
    public static void setIdStringThreadLocalValue(String value) {
        ID_STRING_THREAD_LOCAL.set(value);
    }

    /**
     * 移除當前線程的當前本地線程變量
     */
    public static void removeStringThreadLocal() {
        ID_STRING_THREAD_LOCAL.remove();
    }

}

攔截器

package com.yule.common.interceptor;

import com.ch.common.util.CommonTool;
import com.yule.manage.interfacelog.entity.InterfaceLog;
import com.yule.manage.interfacelog.entity.InterfaceLogHolder;
import com.yule.manage.interfacelog.service.InterfaceLogService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * 日志攔截器:記錄調用日志
 * @author yule
 */
public class InterfaceLogInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private InterfaceLogService interfaceLogService;

    private final Logger logger = LoggerFactory.getLogger(InterfaceLogInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        try{

            InterfaceLog interfaceLog = new InterfaceLog();
            interfaceLog.setStatus(InterfaceLog.STATUS_SUCCESS);

            //方法返回發出請求的客戶機的IP地址
            interfaceLog.setCallerIp(request.getRemoteAddr());
            interfaceLog.setInterfaceName(request.getRequestURI());//
            interfaceLog.setLocalIp(request.getLocalAddr());// 方法返回WEB服務器的IP地址。

            //返回一個包含請求消息中的所有參數名的Enumeration對象。通過遍歷這個Enumeration對象,就可以獲取請求消息中所有的參數名。
            Map<String, String[]> paramsMap =  request.getParameterMap();
            if(CommonTool.isNotNullOrBlock(paramsMap)){
                StringBuilder stringBuilder = new StringBuilder();
                for(Map.Entry<String, String[]> entry : paramsMap.entrySet()){
                    stringBuilder.append(entry.getKey()).append(": ").append(StringUtils.join(entry.getValue())).append("; ");
                }
                interfaceLog.setCallerParams(stringBuilder.toString());
            }

            this.interfaceLogService.insert(interfaceLog);

            //線程變量存值
            InterfaceLogHolder.setIdStringThreadLocalValue(interfaceLog.getId());

        } catch (Exception e) {
            logger.error("接口調用記錄錯誤信息出錯;調用者ip:" + request.getRemoteHost() + ", 調用者ip:" + request.getRemoteAddr() + ", 接口名:" + request.getRequestURI(), e);
        }

        return true;
    }
}

統一異常處理

package com.yule.common.dealexception;

import com.yule.common.entity.ResponseBase;
import com.yule.manage.interfacelog.entity.InterfaceLog;
import com.yule.manage.interfacelog.entity.InterfaceLogHolder;
import com.yule.manage.interfacelog.service.InterfaceLogService;
import com.yule.interfacepackage.pibdata.web.ctrl.PibDataCtrl;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;

/**
 * 接口 統一異常處理,並記錄錯誤日志
 * @author yule
 */
@ControllerAdvice("com.yule.interfacepackage")
public class DealInterfaceException {
    @Autowired
    private InterfaceLogService interfaceLogService;

    private Logger logger = LoggerFactory.getLogger(DealInterfaceException .class);

    @ExceptionHandler
    @ResponseBody
    public ResponseBase dealException(HttpServletRequest request, Exception ex) {
        //異常處理
        logger.error(ex.getMessage(), ex);
        ResponseBase responseBase = new ResponseBase();
        responseBase.setErrorMsg(ex.getMessage());
        responseBase.setSuccess(false);

        this.interfaceLogService.update(ExceptionUtils.getStackTrace(ex), InterfaceLog.STATUS_ERROR, InterfaceLogHolder.getIdStringThreadLocalValue());

        return responseBase;
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM