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

news/2024/12/2 15:57:31/

前言:现在有一个系统,主要是为了给其他系统提供数据查询接口的,这个系统上线不会轻易更新,更不会跟随业务系统的更新而更新(这也是有一个数据查询接口系统的原因,解耦)。这时,这个系统就需要有一定的方便的线上查错方式,我便想到了记录每一次的调用日志,而且需要记录错误堆栈,同时被白名单过滤的也要记录下来。

想法

  这个日志记录,需要在每一次访问接口时记录一下,在有异常时将异常的堆栈信息记录在每次访问记录里。这里由于要使用数据库信息,所以选择了 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_logis '接口调用日志记录表';
-- Add comments to the columns
comment on column t_interface_log.idis '主键id';
comment on column t_interface_log.interface_nameis '接口名';
comment on column t_interface_log.caller_ipis '调用者ip';
comment on column t_interface_log.local_ipis '本机ip';
comment on column t_interface_log.caller_paramsis '调用参数';
comment on column t_interface_log.caller_dateis '调用时间';
comment on column t_interface_log.msgis '信息记录';
comment on column t_interface_log.statusis '状态: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 {@Autowiredprivate InterfaceLogService interfaceLogService;private final Logger logger = LoggerFactory.getLogger(InterfaceLogInterceptor.class);@Overridepublic 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 {@Autowiredprivate InterfaceLogService interfaceLogService;private Logger logger = LoggerFactory.getLogger(DealInterfaceException .class);@ExceptionHandler@ResponseBodypublic 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;}
}

 

转载于:https://www.cnblogs.com/yuxiaole/p/9230746.html


http://www.ppmy.cn/news/614432.html

相关文章

ASML光刻机PK 原子弹,难度?

ASML光刻机PK 原子弹&#xff0c;难度&#xff1f; 一. 物理世界和网络世界的交汇点&#xff1a;光刻机 光刻机的技术有多高级&#xff0c;看看这个知乎提问&#xff0c;可以感受一下&#xff1a; 有人这样形容光刻机&#xff1a;这是一种集合了数学、光学、流体力学、高分子…

uniapp 封装 navbar tabbar

最近换了一家公司工作 因为上家公司老板给的钱不多 还特别会压榨员工 好了好了&#xff0c;不扯皮了 1、封装navbar&#xff1a; 首先需要在pages.json中将对应页面的原生navbar给取消 举例&#xff1a; {"pages": [{"path" : "pages/home/inde…

shell访问php文件夹,Shell获取某目录下所有文件夹的名称

Shell获取某目录下所有文件夹的名称以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;Shell获取某目录下所有文件夹的名称, 已知目录D:/temp,获取temp下所有文件夹的名称并输出&#xff0c;She…

深度学习与传统图像识别

深度学习与传统图像识别 概述 传统方法中特征提取主要依赖人工设计的提取器&#xff0c;需要有专业知识及复杂的调参过程&#xff0c;同时每个方法都是针对具体应用&#xff0c;泛化能力及鲁棒性较差。 深度学习主要是数据驱动进行特征提取&#xff0c;根据大量样本的学习能…

matlab getstart,matlab帮助文件(matlab get start.pdf)

【实例简介】Matlab Primer【实例截图】【核心代码】Quick Start1MATLAB Product Description ........................... 1-2Key Features ..................................... 1-2Desktop Basics ....................................... 1-3Matrices and Arrays .....…

Js时间格式[转载]

var myDate new Date();myDate.getYear(); //获取当前年份(2位)myDate.getFullYear(); //获取完整的年份(4位,1970-????)myDate.getMonth(); //获取当前月份(0-11,0代表1月)myDate.getDate(); //获取当前日(1-31)myDate.getDay(); //获取当…

虚拟纹理与几何图像技术

虚拟纹理与几何图像技术 一&#xff0e; 基本图形学概念 图1. 几何与纹理。 曲面一般表示成三角网格和纹理图像&#xff0c;三角网格表示曲面的几何拓扑信息&#xff0c;纹理图像给出曲面的颜色材质等信息。将三角网格映射到平面区域的过程被称为曲面参数化&#xff0c;将纹理…

mysql 4.0.27 下载,CentOS编译Mysql 4.0.27数据库问题解决

CentOS编译Mysql 4.0.27数据库问题解决以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;现在好多人都用起了centos 但有些特殊要求还必须安装mysql 的数据库 在第一次安装过程中还遇到了不少问…