当前位置: 首页 > news >正文

分布式中traceId链接服务间的日志

使用技术:

        网关:SpringCloudGateway

        RPC调用:Feign

一:在网关入口处设置header:key-traceId,value-UUID


import com.kw.framework.common.croe.constant.CommonConstant;
import com.kw.framework.gateway.utils.BuildHeaderFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.LinkedHashMap;
import java.util.UUID;@Component
@Slf4j
public class HeaderFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 封装需要向后续封装的header对象LinkedHashMap<String, String> headerMap = new LinkedHashMap<>();headerMap.put(CommonConstant.TRACE_ID, UUID.randomUUID().toString());exchange = BuildHeaderFilter.chainFilterAndSetHeaders(chain, exchange, headerMap);return chain.filter(exchange);}@Overridepublic int getOrder() {return 0;}}

二:新建FeignRequestInterceptor实现RequestInterceptor来实现header透传


import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;/*** feign请求头传递*/
@Slf4j
public class FeignRequestInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate template) {HttpServletRequest httpServletRequest = getHttpServletRequest();if (httpServletRequest != null) {Map<String, String> headers = getHeaders(httpServletRequest);// 传递所有请求头,防止部分丢失Iterator<Map.Entry<String, String>> iterator = headers.entrySet().iterator();while (iterator.hasNext()) {Map.Entry<String, String> entry = iterator.next();// 防止添加重复if(!template.headers().containsKey(entry.getKey())){template.header(entry.getKey(), entry.getValue());}}if (log.isDebugEnabled()) {log.debug("FeignRequestInterceptor:{}", template.toString());}}}private HttpServletRequest getHttpServletRequest() {try {return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();} catch (Exception e) {return null;}}private Map<String, String> getHeaders(HttpServletRequest request) {Map<String, String> map = new LinkedHashMap<>();Enumeration<String> enumeration = request.getHeaderNames();if(enumeration!=null){while (enumeration.hasMoreElements()) {String key = enumeration.nextElement();String value = request.getHeader(key);map.put(key, value);}}return map;}}

三:构建服务请求拦截器TraceIdFilter,实现MDC(用于日志打印)赋值,并在返回response的header中返回traceId

import cn.hutool.core.util.StrUtil;
import com.kw.framework.common.croe.constant.CommonConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.slf4j.MDC;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.UUID;@Slf4j
@WebFilter(filterName = "traceIdFilter", urlPatterns = "/*")
@Order(0)
public class TraceIdFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest)servletRequest ;String traceId =  request.getHeader(CommonConstant.TRACE_ID);MDC.put(CommonConstant.TRACE_ID, StrUtil.isEmpty(traceId)? UUID.randomUUID().toString() :traceId);HttpServletResponse response = (HttpServletResponse) servletResponse;response.setHeader(CommonConstant.TRACE_ID,traceId);filterChain.doFilter(servletRequest, servletResponse);}@Overridepublic void destroy() {MDC.clear();}}

四:在logback.xml中设置日志输出格式:

	<property name="log.pattern" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] - [%X{TRACE_ID}] - [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />

完整输出日志配置:

<?xml version="1.0" encoding="UTF-8"?>
<configuration><springProperty scope="context" name="log.home" source="spring.application.name"/><!-- 日志存放路径 --><property name="log.path" value="opt/logs/" /><!-- 日志输出格式 --><property name="log.pattern" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] - [%X{TRACE_ID}] - [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" /><!-- 控制台输出 --><appender name="console" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>${log.pattern}</pattern></encoder></appender><!-- 系统日志输出 --><appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${log.path}/${log.home}/sys-info.log</file><!-- 循环政策:基于时间创建日志文件 --><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><!-- 日志文件名格式 --><fileNamePattern>${log.path}/${log.home}/sys-info.%d{yyyy-MM-dd}.%i.log</fileNamePattern><!-- 日志最大的历史 30天 --><MaxHistory>30</MaxHistory><maxFileSize>1GB</maxFileSize><totalSizeCap>15GB</totalSizeCap></rollingPolicy><encoder><pattern>${log.pattern}</pattern><charset>UTF-8</charset></encoder></appender><appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${log.path}/${log.home}/sys-error.log</file><!-- 循环政策:基于时间创建日志文件 --><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><!-- 日志文件名格式 --><fileNamePattern>${log.path}/${log.home}/sys-error.%d{yyyy-MM-dd}.%i.log</fileNamePattern><!-- 日志最大的历史 30天 --><MaxHistory>30</MaxHistory><maxFileSize>50MB</maxFileSize></rollingPolicy><encoder><pattern>${log.pattern}</pattern><charset>UTF-8</charset></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 过滤的级别 --><level>ERROR</level><!-- 匹配时的操作:接收(记录) --><onMatch>ACCEPT</onMatch><!-- 不匹配时的操作:拒绝(不记录) --><onMismatch>DENY</onMismatch></filter></appender><!--配置logstash 发送日志数据的地址 --><!--	<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender"><destination>192.168.56.30:5000</destination><encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder" /></appender>--><!--springboot的日志  --><include resource="org/springframework/boot/logging/logback/base.xml" /><!--系统操作日志--><root level="info"><appender-ref ref="file_info" /><appender-ref ref="file_error" /><appender-ref ref="console" /><!--	<appender-ref ref="LOGSTASH" />--></root></configuration> 

输出结果:

        日志输出:

        

        请求返回:

        

至此,我们就可以在发现问题时在浏览器上拿到对应的traceId,快速定位到日志位置

相关文章:

  • 短剧系统源码解析与应用
  • 详细分析Vue3中的emit用法(子传父)
  • Java-常见面试题收集(十六)
  • 2024年顶级算法-黑翅鸢优化算法(BKA)-详细原理(附matlab代码)
  • Python基础知识归纳总结
  • 2024 电工杯高校数学建模竞赛(B题)| 平衡膳食食谱 |建模秘籍文章代码思路大全
  • Mac下QT开发环境搭建详细教程
  • 计算机毕业设计 | SpringBoot社区物业管理系统 小区管理(附源码)
  • <MySQL> 【数据类型】
  • ChatGPT、Llama等大模型回答脑筋急转弯
  • 计算机操作系统总结(1)
  • BGP选路规则实验
  • NoSQL Redis配置与优化
  • SD3303A大功率高精度LED驱动芯片3W低功耗高效率工作温度40c+85%
  • 这台电脑无法运行Windows11问题解决方案
  • SegmentFault for Android 3.0 发布
  • __proto__ 和 prototype的关系
  • 【翻译】Mashape是如何管理15000个API和微服务的(三)
  • CSS居中完全指南——构建CSS居中决策树
  • docker-consul
  • java2019面试题北京
  • jquery ajax学习笔记
  • MySQL Access denied for user 'root'@'localhost' 解决方法
  • RxJS: 简单入门
  • Sass 快速入门教程
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 从0搭建SpringBoot的HelloWorld -- Java版本
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 每天一个设计模式之命令模式
  • [地铁译]使用SSD缓存应用数据——Moneta项目: 低成本优化的下一代EVCache ...
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​Distil-Whisper:比Whisper快6倍,体积小50%的语音识别模型
  • ​flutter 代码混淆
  • #、%和$符号在OGNL表达式中经常出现
  • #include到底该写在哪
  • (01)ORB-SLAM2源码无死角解析-(66) BA优化(g2o)→闭环线程:Optimizer::GlobalBundleAdjustemnt→全局优化
  • (2)leetcode 234.回文链表 141.环形链表
  • (3)Dubbo启动时qos-server can not bind localhost22222错误解决
  • (android 地图实战开发)3 在地图上显示当前位置和自定义银行位置
  • (C++二叉树05) 合并二叉树 二叉搜索树中的搜索 验证二叉搜索树
  • (k8s)kubernetes集群基于Containerd部署
  • (poj1.3.2)1791(构造法模拟)
  • (Repost) Getting Genode with TrustZone on the i.MX
  • (STM32笔记)九、RCC时钟树与时钟 第二部分
  • (笔记自用)LeetCode:快乐数
  • (二十九)STL map容器(映射)与STL pair容器(值对)
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (四)c52学习之旅-流水LED灯
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (转)GCC在C语言中内嵌汇编 asm __volatile__
  • (转)linux 命令大全
  • (转)大型网站架构演变和知识体系
  • (自用)交互协议设计——protobuf序列化