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

想法

  这个日志记录,需要在每一次访问接口时记录一下,在有异常时将异常的堆栈信息记录在每次访问记录里。这里由于要使用数据库信息,所以选择了 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 系统记录调用日志,并且记录错误堆栈的更多相关文章

  1. java后端程序员1年工作经验总结

    java后端1年经验和技术总结(1) 1.引言 毕业已经一年有余,这一年里特别感谢技术管理人员的器重,以及同事的帮忙,学到了不少东西.这一年里走过一些弯路,也碰到一些难题,也受到过做为一名开发却经常为 ...

  2. 一年工作经验的大专生程序员(java后台)

    1.文章前言     作为18应届毕业大专生已工作一年,相信这也是大部分同届生的现状.       那么,一个萌新进入职场一年都经历了什么呢?在校那会我是挺好奇的.       这篇文章是根据自己一年 ...

  3. 精干货! Java 后端程序员 1 年工作经验总结

    一.引言   毕业已经一年有余,这一年里特别感谢技术管理人员的器重,以及同事的帮忙,学到了不少 东西.这一年里走过一些弯路,也碰到一些难题,也受到过做为一名开发却经常为系统维护 和发布当救火队员的苦恼 ...

  4. 写给java web一年左右工作经验的人

      摘要 大学就开始学习web,磕磕绊绊一路走过来,当中得到过开源社区很多的帮助,总结了这些年来的技术积累,回馈给开源社区. ps:图片都是从网上盗...感谢原作者. ps:文字千真万确都是我自己写的 ...

  5. 一位10年Java工作经验的架构师聊Java和工作经验

    从事近十年的 JavaEE 应用开发工作,现任阿里巴巴公司系统架构师.对分布式服务架构与大数据技术有深入研究,具有丰富的 B/S 架构开发经验与项目实战经验,擅长敏捷开发模式.国内开源软件推动者之一, ...

  6. 转:一位10年Java工作经验的架构师聊Java和工作经验

    黄勇( 博客),从事近十年的 JavaEE 应用开发工作,现任阿里巴巴公司系统架构师.对分布式服务架构与大数据技术有深入研究,具有丰富的 B/S 架构开发经验与项目实战经验,擅长敏捷开发模式.国内开源 ...

  7. Java(JCo3)与SAP系统相互调用

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  8. 经过本人 6 年.net 工作经验证明 .net 工资确实比 Java 低

    很久没有逛博客园了,很久没有出来吐槽了.怎么呢?生活压力大,就需要找地方吐槽. 排版不怎么好,文笔不怎么好,可以看出我不是雇佣的写手. 工作到今夏,已经有6个年头了,一直使用着.net技术,但是最近工 ...

  9. 2年Java开发工作经验面试总结

    最近换了个公司,从三月底开始面,面到四月底,面了有快二十家公司.我是一个喜欢总结经验的人,每经过一场面试,我在回来的路上都会仔细回想今天哪些问题可以答的更好,或者哪些问题是自己之前没遇到过的,或者是哪 ...

随机推荐

  1. ElasticSearch学习笔记(三)logstash安装和logstash-input-jdbc插件

    ElasticSearch的索引可以手动添加索引的,就是类似下面这样添加的 PUT /movies/movie/1 { "title": "The Godfather&q ...

  2. combining-filters

    The previous two examples showed a single filter in use. In practice, you will probably need to filt ...

  3. 【vim】搜索与替换

    1. 搜索 命令 功能 备注 * 向下搜索光标所在处的词 n下一个,N上一个 # 向上搜索光标所在处的词 同上 [+<Ctrl>+i 跳转到光标所在处的变量的声明 使用tag [+< ...

  4. 基本项目框架搭建 sqlserver druid配置

    1.  我的连接池采用的是阿里云的druid的连接池,工具是IDEA 框架是springboot+maven 以下是我的项目框架结构 2. pom  中配置 <?xml version=&quo ...

  5. YC全球总裁:我招揽陆奇好多年,如今终于如愿了!

    简评:YC 老大多年来一直在努力招揽陆奇,终于如愿了. YC 总裁 Sam Altman 在 8 月 14 日,发布了关于陆奇以及 YC 中国的公告,全文如下: 我非常荣幸地宣布,陆奇加入 YC 并且 ...

  6. iOS开发~制作同时支持armv7,armv7s,arm64,i386,x86_64的静态库.a以及 FrameWork 的创建

    armv7,armv7s,arm64,i386,x86_64 详解 一.概要 平时项目开发中,可能使用第三方提供的静态库.a,如果.a提供方技术不成熟,使用的时候就会出现问题,例如: 在真机上编译报错 ...

  7. 再也不怕aop的原理了

    1 aop是什么 java的核心思想是面向对象,aop是面向切面编程.是对面向对象的一个补充,简单通俗的理解下aop,假设我们上楼梯,我的目标是三楼,我直接朝我的三楼直接过去,但是我可以在二楼的时候去 ...

  8. 爬虫之chrome浏览器的使用方法

    chrome浏览器使用方法介绍 1. 新建隐身窗口 1.1 为什么需要新建隐身窗口 在打开隐身窗口的时候,第一次请求某个网站是没有携带cookie的,和代码请求一个网站一样,不携带cookie.这样就 ...

  9. json兼容ie8

    今天遇到一个问题,后台传递过来的json对象,在前端解析的时候用JSON.parse(result)方法不好使,查了一下是因为ie浏览器的问题.然后在网上翻了翻,找到了这个办法,可以使这个函数在ie中 ...

  10. 图解http 笔记

    一,了解web以及网络基础 1,使用http协议访问web web页面是由web浏览器根据地址栏中指定的url从web服务器获取文件资源等信息然后显示的页面. 像这种通过发送请求获取服务器资源的web ...