工作经验:Java 系统记录调用日志,并且记录错误堆栈

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;

}

}

以上是 工作经验:Java 系统记录调用日志,并且记录错误堆栈 的全部内容, 来源链接: utcz.com/z/391579.html

回到顶部