Dropwizard+jersey+MDC实现日志跟踪以及接口响应时间统计
一、实现接口响应时间统计
1.1添加全局请求过滤器并注册
import org.apache.log4j.MDC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider; /**
* @Autor zhangjiawen
* @Date: 2020/4/10 13:33
*/ @Provider
public class RequestLogFilter implements ContainerRequestFilter, ContainerResponseFilter {
private final String BEGIN_LOG="beginLog";
private static final Logger logger = LoggerFactory.getLogger(RequestLogFilter.class); @Override
public void filter(ContainerRequestContext requestContext) {
try {
String traceId = TraceLogUtils.getTraceId();
MDC.put(Constants.LOG_TRACE_ID, traceId);
LogBean logBean = new LogBean(traceId, System.currentTimeMillis());
// MDC.put(traceId,logBean); 也可将请求参数放到MDC不用缓存,但是我用MDC存对象报类型转换异常不知咋回事,故缓存缓存保存
requestContext.setProperty(BEGIN_LOG, traceId);
LogThreadPoolExecutor.getInstance().getThreadPool().submit(() -> {
//将请求存入缓存
RequestCache.getInstance().add(logBean.getTraceID(), logBean);
logger.info("traceid:" + logBean.getTraceID() + " uri: " + requestContext.getUriInfo().getRequestUri()
+ " method:" + requestContext.getMethod()
+ " parameters:" + requestContext.getUriInfo().getPathParameters().toString());
});
}catch (Exception e){
e.printStackTrace();
} } @Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
try{
long currentTime=System.currentTimeMillis();
if(requestContext.getProperty(BEGIN_LOG)!=null){
String traceID=(String) requestContext.getProperty(BEGIN_LOG);
LogThreadPoolExecutor.getInstance().getThreadPool().submit(() -> {
try {
//读取traceid 异步去缓存中取 并消费
LogBean logBean = RequestCache.getInstance().get(traceID);
StringBuilder sb = new StringBuilder();
sb.append("traceid:" + logBean.getTraceID()).append(" ,").append(responseContext.getStatus()).append(" ,")
.append(responseContext.getStatusInfo()).append(", response:{");
if(responseContext.getStatus()== Response.Status.CREATED.getStatusCode()){ //文件上传类型没有Entity 故单独处理
sb.append(responseContext.getHeaders().getFirst("Location"));
}else if(responseContext.getEntity()!=null){
sb.append(responseContext.getEntity());}
sb.append("} usedTime:").append((currentTime - logBean.getRequestDate()) + "(ms)");
logger.info(sb.toString());
RequestCache.getInstance().remove(traceID);
MDC.clear();
}catch (Exception e){
e.printStackTrace();
}
});
}
}catch (Exception e){
e.printStackTrace();
}
}
}
注册过滤器
environment.jersey().register(RequestLogFilter.class);
缓存类
import java.util.concurrent.ConcurrentHashMap; /**
* @Autor zhangjiawen
* @Date: 2020/4/13 8:55
*/
public class RequestCache {
private ConcurrentHashMap<String, LogBean> requesMap=null; private RequestCache(){
requesMap=new ConcurrentHashMap<>();
} private static RequestCache instance = new RequestCache(); public static RequestCache getInstance(){
return instance;
} public void add(String key,LogBean logBean){
this.requesMap.put(key,logBean);
}
public LogBean get(String key){
return this.requesMap.get(key);
}
public void remove(String key){
this.requesMap.remove(key);
} }
线程池
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; /**
* @Autor zhangjiawen
* @Date: 2020/4/13 15:48
*/
public class LogThreadPoolExecutor {
private ThreadPoolExecutor tpe =null;
BlockingQueue<Runnable> bq = new ArrayBlockingQueue<Runnable>(10);
private static LogThreadPoolExecutor logThreadPool=new LogThreadPoolExecutor(); private LogThreadPoolExecutor(){
tpe= new ThreadPoolExecutor(5,
10,
500,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
}
public static LogThreadPoolExecutor getInstance(){
return logThreadPool;
}
public ThreadPoolExecutor getThreadPool(){
return this.tpe;
} }
常量类
/**
* @Autor zhangjiawen
* @Date: 2020/4/13 10:15
*/
public class Constants { /**
* 日志跟踪id名。
*/
public static final String LOG_TRACE_ID = "traceid"; /**
* 请求头跟踪id名。
*/
public static final String HTTP_HEADER_TRACE_ID = "app_trace_id";
}
日志实体类
import java.io.Serializable;
import java.util.UUID; /**
* @Autor zhangjiawen
* @Date: 2020/4/10 15:43
*/ public class LogBean implements Serializable {
private String traceID;
private long requestDate; public LogBean(String traceID, long requestDate) {
this.traceID = traceID;
this.requestDate = requestDate;
} public LogBean(long requestDate) {
this.requestDate = requestDate;
this.traceID=UUID.randomUUID().toString();
} public String getTraceID() {
return traceID;
} public void setTraceID(String traceID) {
this.traceID = traceID;
} public long getRequestDate() {
return requestDate;
} public void setRequestDate(long requestDate) {
this.requestDate = requestDate;
}
}
二、实现日志报错跟踪(为每个异常添加一个串联的traceid)
添加一个全局异常过滤器
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC; import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider; /**全局异常过滤
* @Autor zhangjiawen
* @Date: 2020/4/13 11:24
*/
@Provider
public class GlobalTraceException implements ExceptionMapper<Exception> {
private static final Logger logger = LoggerFactory.getLogger(GlobalTraceException.class);
@Override
public Response toResponse(Exception e) {
Response.ResponseBuilder responseBuilder = null;
try {
StringBuilder sb = new StringBuilder();
sb.append(Constants.LOG_TRACE_ID + ": ").append(MDC.get(Constants.LOG_TRACE_ID) == null ? "" : MDC.get(Constants.LOG_TRACE_ID));
//此处将MDC中traceid 取出并加到报错信息前边
logger.error(sb + " error: {}", e.getMessage(), e);
ErrorEntity entity = new ErrorEntity(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e.getMessage());
responseBuilder = Response.ok(entity);
}catch (Exception e2){
e2.printStackTrace();
}
return responseBuilder.build();
} }
错误实体类
//import javax.xml.bind.annotation.XmlRootElement; /**
* @Autor zhangjiawen
* @Date: 2020/4/13 13:23
*/
//@XmlRootElement//标识该资源可以被jersey转为json或者xml
public class ErrorEntity {
private int resp_err_code;
private String resp_err_msg; public ErrorEntity(int resp_err_code, String resp_err_msg) {
this.resp_err_code = resp_err_code;
this.resp_err_msg = resp_err_msg;
} public int getResp_err_code() {
return resp_err_code;
} public void setResp_err_code(int resp_err_code) {
this.resp_err_code = resp_err_code;
} public String getResp_err_msg() {
return resp_err_msg;
} public void setResp_err_msg(String resp_err_msg) {
this.resp_err_msg = resp_err_msg;
}
}
注册过滤器
environment.jersey().register(GlobalTraceException.class);
效果如下

Dropwizard+jersey+MDC实现日志跟踪以及接口响应时间统计的更多相关文章
- EF Core 日志跟踪sql语句
EF Core 日志跟踪sql语句 官方文档链接:https://docs.microsoft.com/en-us/ef/core/miscellaneous/logging 1.新增自定义ILogg ...
- 推荐windows下的日志跟踪工具:SnakeTail
用过Linux的同学都知道,在Linux中要实时跟踪日志文件那是非常的方便,Tail.Less都可以做到. 开启动态跟踪后,程序会监视文件修改,从而不断刷新出最新的内容,对于线上运维特别有用. 今 ...
- Entity Framework实体框架使用TrackerEnabledDbContext进行操作日志跟踪
在EF实体框架中进行日志跟踪,一般都是自己写个Log实体类,在数据保存时进行属性原始值验证来进行日志跟踪.当然还可以使用一些第三扩展库例如:entity framework extended进行日志记 ...
- 循序渐进看Java web日志跟踪(3)-Log4J的使用和配置
之前说过关于java日志跟踪的几大主要用的框架,也说到了,其实在其中,Log4J充当着一个相当重要的角色.目前,大部分框架也都是采用的是Log4J,虽然说它已经停止了更新,作者也重新起了LogBack ...
- 循序渐进看Java web日志跟踪(1)-Tomcat 日志追踪与配置
日志,是软件运行过程中,对各类操作中重要信息的记录. 日志跟踪,不管对于怎么样的项目来说,都是非常重要的一部分,它关系到项目后期的维护和排错,起着举足轻重的作用.项目开发过程中,对日志的记录规则,也将 ...
- 【原创】基于UDP广播的局域网Web Window Service日志跟踪小工具
一直感觉Web开发或者windows服务的日志跟踪调试不是很方便 特别是在生产环境服务器上面 目前一般的解决方案是通过各种日志工具把错误信息和调试信息 ...
- MySQL开启日志跟踪
在开发过程中有时候会遇到sql相关的问题,但是有时候代码中不会直接看到真实的sql,想要看到mysql中实际执行的是什么sql,可以通过开启日志跟踪方式查看. 1 开启日志跟踪 SET GLOBAL ...
- Mbp通过筛选器和中间件实现异常,日志,事务及接口返回数据格式化aop处理.
Mbp应用服务层的AOP实现 实现方法:asp.net core mvc 筛选器 + 中间件 日志,事务,和接口返回结果统一格式化采用操作筛选器,而异常处理采用中间件来处理. 最开始,我是打算用aut ...
- asp.net mvc,基于aop实现的接口访问统计、接口缓存等
其实asp.net 上aop现有的框架应该蛮多的,比如静态注入式的PostSharp(新版本好像已经商业化了,旧版本又不支持.net4.0+),或者通过反射的(性能会降低). 本文则是通过mvc其中一 ...
随机推荐
- 机器学习实战基础(二十):sklearn中的降维算法PCA和SVD(一) 之 概述
概述 1 从什么叫“维度”说开来 我们不断提到一些语言,比如说:随机森林是通过随机抽取特征来建树,以避免高维计算:再比如说,sklearn中导入特征矩阵,必须是至少二维:上周我们讲解特征工程,还特地提 ...
- python生成器原理剖析
python生成器原理剖析 函数的调用满足"后进先出"的原则,也就是说,最后被调用的函数应该第一个返回,函数的递归调用就是一个经典的例子.显然,内存中以"后进先出&quo ...
- hihoCoder 1052 基因工程 最详细的解题报告
题目来源:基因工程 解题思路:假设基因序列长度为N,则需要计算基因序列前K个和后K个相同所需要的最少改变次数sum. 假设基因序列为 ATACGTCT (即M=8),K=6:interval=M-K= ...
- JavaScript动画基础:canvas绘制简单动画
动画是将静止的画面变为动态的艺术.实现由静止到动态,主要是靠人眼的视觉残留效应.视觉残留也叫视觉暂留现象,物体在快速运动时, 当人眼所看到的影像消失后,人眼仍能继续保留其影像0.1~0.4秒左右的图像 ...
- 图解:有向环、拓扑排序与Kosaraju算法
图算法第三篇 图解:有向环.拓扑排序与Kosaraju算法 首先来看一下今天的内容大纲,内容非常多,主要是对算法思路与来源的讲解,图文并茂,希望对你有帮助~ 1.有向图的概念和表示 概念 有向图与上一 ...
- three.js 绘制3d地图
通过地图数据配合three可以做出非常酷炫的地图,在大数据展示中十分常见. 这篇郭先生就来说说使用three.js几何体制作3D地图.在线案例点击原文地址. 地图的数据是各个地图块的点数组,通过THR ...
- OSCP Learning Notes - Buffer Overflows(2)
Finding the Offset 1. Use the Metasploite pattern_create.rb tool to create 5900 characters. /usr/sha ...
- antd踩坑:value.locale is not a function
这个问题来源于日期选择器 RangerPicker 的特殊情况. <Col span={7} key={9}> <FormItem label="投运时间" {. ...
- vue : 检测用户上传的图片的宽高
需求: 用户可上传3-6张图片(第 1 2 3 张必须传),上传的图片必须是540 * 330 像素. 第一步,获取上传的图片的宽高. 初始化一个对象数组,宽高均设为0. 如果用户上传的图片没有上限, ...
- python爬虫入门(3)----- scrapy
scrapy 简介 Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架. 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中. 其最初是为了 页面抓取 (更确切来说, 网络 ...