微服务日志增加链路追踪requestId

微服务⽇志增加链路追踪requestId
⽬录
背景
以下内容主要在原框架的基础上做部分改造,以及新增。改造是因为要兼容原filter等内容,但是实际上⾃⼰重新开始做的话也差不多。实现了微服务下的⽇志链路追踪以及微服务中请求的耗时监控。
⽹关
⽹关中的全局请求过滤器、鉴权过滤器、全局异常处理器改造,增加响应头处理过滤器。
(ps:根据项⽬不同,过滤器、、处理器可能不同,但是原理是相同的,当前的系统只需要修改上⾯三个地⽅就可以了)
全局请求过滤器
ateway.utils.LogGlobalFilterUtil;
import org.apache.logging.log4j.ThreadContext;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import Ordered;
import org.springframework.active.ServerHttpRequest;
import org.springframework.active.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
slf4j.Slf4j;
publisher.Mono;
@Component
@Slf4j
public class GatewayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
try {
ServerHttpRequest serverHttpRequest = Request();
ServerHttpResponse serverHttpResponse = Response();
LogGlobalFilterUtil.setThreadContext(serverHttpRequest, serverHttpResponse);
log.info("请求地址:{}", Request().getURI().getPath());
return chain.filter(exchange);
} finally {
}
}
@Override
public int getOrder() {
return 0;
}
}
1.在请求和响应头中放⼊RequestId
2.在ThreadContext中放⼊RequestId(log4j2 1.x版本⽤MDC,2.x版本⽤ThreadContext)
ServerHttpRequest serverHttpRequest = Request();
ServerHttpResponse serverHttpResponse = Response();
LogGlobalFilterUtil.setThreadContext(serverHttpRequest, serverHttpResponse);
避免内存溢出
finally { ve(LogGlobalFilterUtil.REQUEST_ID_HEADER); }
鉴权过滤器/全局异常处理器改造
跟全局请求过滤器对于request、response和ThreadContext的处理逻辑相同,只是其他业务逻辑不同.
(个⼈觉得这⾥并不完善,可以通过改造异步线程池以及设置request的⼦线程共享去实现在全局请求过滤器中处理⼀次就可以,本⼈没有试过,只是有个思路)
响应头处理过滤器
该过滤器的主要作⽤是处理响应中的header值重复的,因为在⽹关处理过⼀次response的header,在各个微服务中也要处理response的header,调⽤链上多次的处理会导致值重复,所以在返回的时候去重。
slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
publisher.Mono;
import java.util.List;
import java.util.stream.Collectors;
/**
* @description: 头部信息去重过滤器
* @author: gene
* @date: 2021/11/30 13:14
*/
@Component
@Slf4j
public class HeaderFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).then(
Mono.defer(() -> {
HttpHeaders httpHeaders = Response().getHeaders();
httpHeaders.forEach((k, v) -> {
if (null != v && v.size() > 1) {
List<String> list = v.stream().distinct().List());空压机管道
}
});
return chain.filter(exchange);
})
);
}
@Override
public int getOrder() {
// 指定此过滤器位于NettyWriteResponseFilter之后, 待处理完响应体后接着处理响应头
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER + 1;
}
}
指定此过滤器位于NettyWriteResponseFilter之后, 待处理完响应体后接着处理响应头NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER + 1 ThreadContext⼯具类
import org.apache.logging.log4j.ThreadContext;
import org.springframework.http.HttpHeaders;太阳能安全帽
import org.springframework.active.ServerHttpRequest;
import org.springframework.active.ServerHttpResponse;
import java.util.UUID;
import java.util.function.Consumer;
/**
* @description: ThreadContext⼯具类
* @author: gene
* @date: 2021/11/29 17:43
*/
共鸣管测声速
public class LogGlobalFilterUtil {
/**
* 响应头/MDCkey, RequestId
*/
public static final String REQUEST_ID_HEADER = "RequestId";
public static void setThreadContext(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
String requestId = Headers().getFirst(REQUEST_ID_HEADER);
if (null == requestId) {
requestId = UUID.randomUUID().toString();
String finalRequestId = requestId;
// requestId放⼊请求中
Consumer<HttpHeaders> headersConsumer = httpHeaders -> httpHeaders.add(REQUEST_ID_HEADER, finalRequestId);            serverHttpRequest.mutate().headers(headersConsumer);
}
/
/ requestId放⼊响应中重置⼀遍以请求中的为准
ThreadContext.put(REQUEST_ID_HEADER, requestId);
}
}里德穆勒
Common公共包中新增全局过滤器
拦截所有请求,优先级⾼于spring中的security相关的鉴权过滤器,顺便做了⼀下简单的统计耗时。
公共过滤器
阳极钢爪
import lombok.SneakyThrows;
slf4j.Slf4j;
import org.apache.logging.log4j.ThreadContext;
import at.util.http.MimeHeaders;
import Ordered;
import org.springframework.stereotype.Component;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import flect.Field;
import java.time.Duration;
import java.time.LocalTime;
import java.util.UUID;
import java.util.UUID;
import n.constant.ServletConstant.COYOTE_REQUEST;
import n.constant.ServletConstant.HEADERS;
import n.constant.ServletConstant.REQUEST;
import n.constant.ServletConstant.REQUEST_ID_HEADER;
/**
* @description: ⽇志过滤器
* @author: gene
* @date: 2021/11/29 16:45
*/
@WebFilter(filterName = "geneLogFilter", urlPatterns = "/*")
@Component
@Slf4j
public class GeneLogFilter implements Filter, Ordered {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@SneakyThrows
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {        LocalTime startTime = w();
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String requestId = Header(REQUEST_ID_HEADER);
if (requestId == null) {
requestId = UUID.randomUUID().toString();
reflectSetparam(httpRequest, requestId);
}
// TODO: 2021/11/29 这⾥可以扩展⽇志打印IP,参数等
ThreadContext.put(REQUEST_ID_HEADER, requestId);
if (!requestId.Header(REQUEST_ID_HEADER))) {
httpResponse.setHeader(REQUEST_ID_HEADER, requestId);
}
try {
chain.doFilter(request, response);
} finally {
// 计算请求耗时
LocalTime endTime = w();
Duration total = Duration.between(startTime, endTime);
log.info("请求URL:{},耗时:{}ms", RequestURI(), Millis());
}
}
@Override
public void destroy() {
僧侣鞋}
private void reflectSetparam(HttpServletRequest request, String value)
throws NoSuchFieldException, IllegalAccessException {
Class<? extends HttpServletRequest> requestClass = Class();
Field field = DeclaredField(REQUEST);
field.setAccessible(true);
Object o = (request);
Field coyoteRequest = o.getClass().getDeclaredField(COYOTE_REQUEST);
coyoteRequest.setAccessible(true);
Object o1 = (o);
Field headers = o1.getClass().getDeclaredField(HEADERS);

本文发布于:2024-09-21 15:53:47,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/1/164408.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:过滤器   请求   全局   响应   处理
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议