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);
效果如下