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

想法

  这个日志记录,需要在每一次访问接口时记录一下,在有异常时将异常的堆栈信息记录在每次访问记录里。这里由于要使用数据库信息,所以选择了 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

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

  1. 三年工作经验java面试宝典(个人总结,现分享)

    数据库 1.mysql的隔离级别有哪些,解读下脏读幻读 ①Read Uncommitted(读未提交) 所有事务都可以看到其他未提交事务的执行结果.本隔离级别很少用于实际应用,因为它的性能也不比其他级 ...

  2. 架构师面试题:2年工作经验java简历包装,面试为什么公司不通过

    1, 简历适度包装,不等同弄虚作假 2, 实事求是,才能让职场走得更顺 3, 诚信是用人的最基本底线. 对于java工程师的面试,绝大多数的公司不是去给你做两个题,考考你对于整个java系统知识的了解 ...

  3. java面试题:2年工作经验java简历包装,面试为什么公司不通过

    1, 简历适度包装,不等同弄虚作假 2, 实事求是,才能让职场走得更顺 3, 诚信是用人的最基本底线. 对于java工程师的面试,绝大多数的公司不是去给你做两个题,考考你对于整个java系统知识的了解 ...

  4. 两年工作经验java面试题精炼汇总

    1.什么是事务控制? 答:事务控制就是将一系列操作当成一个不可拆分的逻辑单元,保证这些操作要么都成功,要么都失败.在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句或整个程序. 事务是恢复和 ...

  5. java 鼠标 停止工作原理,java系统级的键盘和鼠标状态

    Is there a way to listen to the mouse and keyboard events system-wide without taking these out of sy ...

  6. java中方法未定义_java - Java SE中的未定义方法错误 - 堆栈内存溢出

    我为该问题写了一个代码http://www.spoj.com/problems/PRIME1/ ,该代码的作用是将输入以字符串形式输入,然后将split()拆分为两个整数,并存储在该数组中.然后返回到 ...

  7. 美团的系统是如何记录操作日志?

    来源:美团技术团队 操作日志几乎存在于每个系统中,而这些系统都有记录操作日志的一套 API.操作日志和系统日志不一样,操作日志必须要做到简单易懂.所以如何让操作日志不跟业务逻辑耦合,如何让操作日志的内 ...

  8. 记录操作日志(JAVA版某大厂基础实践)

    1. 操作日志的使用场景 2. 实现方式 2.1 使用 Canal 监听数据库记录操作日志 2.2 通过日志文件的方式记录 2.3 通过 LogUtil 的方式记录日志 2.4 方法注解实现操作日志 ...

  9. 【实践】万字干货:如何优雅地记录操作日志?(附代码)

    猜你喜欢 1.如何搭建一套个性化推荐系统? 2.从零开始搭建创业公司后台技术栈 3.某视频APP推荐详解(万字长文) 4.微博推荐算法实践与机器学习平台演进 5.腾讯PCG推荐系统应用实践 6.强化学 ...

最新文章

  1. 微信支付的坑 返回值 -1
  2. 【Linux入门到精通系列讲解】系统调用和库函数路径
  3. 【Linux】6.服务器会话的screen用法
  4. 上海国际区块链赋能传统产业峰会-王伟:道道人才链启动
  5. 糟糕!复工后,最让人担心的问题又又又来了!
  6. 中芯国际人事变动:蒋尚义回归 传梁孟松要走
  7. 吴恩达神经网络和深度学习-学习笔记-3-参数随机初始化
  8. PHP开源软件《个人管理系统》-希望大家一起来开发
  9. 【渝粤教育】国家开放大学2018年春季 0434-21T高级英语口语 参考试题
  10. Photoshop小技巧集锦八十条
  11. 二分查找递归解法(java)
  12. 科来网络分析系统 6.7 技术交流版序列号
  13. 麒麟KY-RTI分布仿真技术:第一章 简介
  14. python lncrna_[转载]lncrna分析流程
  15. CCleaner软件一键查找/删除重复文件
  16. 木头打大孔的新方法-燃烧法
  17. 2021年全球探针卡收入大约2506.3百万美元,预计2028年达到3823.8百万美元,2022至2028期间,年复合增长率CAGR为 6.2%
  18. 微型计算机系统电子时钟程序设计,基于单片机的电子时钟设计 普通单片机电子时钟的设计的分析...
  19. 蓝桥杯 PREV-43 拉马车(试题解析)
  20. 深入理解Linux内核之主调度器(下)

热门文章

  1. 淘宝获取单笔订单信息服务端调用API及流程
  2. Postman使用Date数据类型,Postman发送Date类型数据,Postman模拟前端调用
  3. 什么是LinkedList?什么时候使用它呢?Java LinkedList结构、用法及源码解析
  4. Ubuntu NFS 服务器和客户端挂载详解
  5. 数据结构与算法——线性结构——线性表及其表示
  6. 实现SSTab单个选项卡代码
  7. 如何学习:自考小组学习
  8. Pytorch的LSTM的理解
  9. NVIDIA深度架构
  10. 从PyTorch到ONNX的端到端AlexNet