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

news2024/11/19 0:45:59

使用技术:

        网关: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 {
    @Override
    public 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);
    }

    @Override
    public 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 {

    @Override
    public 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 {


    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public 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);
    }

    @Override
    public 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,快速定位到日志位置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1687431.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习高斯贝叶斯算法实战:判断肿瘤是良性还是恶性

概述 我们使用威斯康星乳腺肿瘤数据集&#xff0c;来构建一个机器学习模型&#xff0c;用来判断患者的肿瘤是良性还是恶性。 数据分析 威斯康星乳腺肿瘤数据集&#xff0c;包括569个病例的数据样本&#xff0c;每个样本具有30个特征值。 样本分为两类&#xff1a;恶性Malig…

SHA1获取

这里写目录标题 JDK获取uniapp开发Dcould获取 JDK获取 一、下载jdk 链接: http://www.oracle.com/ 二、安装直接下一步下一步 三、配置环境变量 先新增变量JAVA_HOME变量值为C:\devUtils\jdk (jdk安装路径位置)再配置Path(%JAVA_HOME%\bin) 四、创建SHA1安全证书 win r输入cmd…

常见应用流量特征分析

目录 1.sqlmap 1.常规GET请求 2.通过--os-shell写入shell 3.post请求 2.蚁剑 编码加密后 3.冰蝎 冰蝎_v4.1 冰蝎3.2.1 4.菜刀 5.哥斯拉 1.sqlmap 1.常规GET请求 使用的是sqli-labs的less7 &#xff08;1&#xff09;User-Agent由很明显的sqlmap的标志&#xff0c;展…

如何快速增加外链?

要快速增加外链并不难&#xff0c;相信各位都知道&#xff0c;难的是快速增加外链且没有风险&#xff0c;所以这时候GNB外链的重要性就出现了&#xff0c;这是一种自然的外链&#xff0c;何谓自然的外链&#xff0c;在谷歌的体系当中&#xff0c;自然外链指的就是其他网站资源给…

[Spring Boot]baomidou 多数据源

文章目录 简述本文涉及代码已开源 项目配置pom引入baomidouyml增加dynamic配置启动类增加注解配置结束 业务调用注解DS()TransactionalDSTransactional自定义数据源注解MySQL2 测试调用查询接口单数据源事务测试多数据源事务如果依然使用Transactional会怎样&#xff1f;测试正…

不同类型的区块链钱包有什么特点和适用场景?

区块链钱包是用于存储和管理加密货币的重要工具&#xff0c;市面上有许多不同类型的区块链钱包可供选择。以下是几种主要类型的区块链钱包及其特点和适用场景。 1.软件钱包&#xff1a; 特点&#xff1a;软件钱包是最常见的一种区块链钱包&#xff0c;通常作为软件应用程序提供…

docker不删除容器更改其挂载目录

场景&#xff1a;docker搭建的jenkins通常需要配置很多开发环境&#xff0c;当要更换挂载目录&#xff0c;每次都需要删除容器重新运行&#xff0c;不在挂载目录的环境通常不会保留。 先给一个参考博客docker不删除容器&#xff0c;修改容器挂载或其他_jenkins 修改容器挂载do…

第17讲:C语言内存函数

目录 1.memcpy使用和模拟实现2.memmove使用和模拟实现3.memset函数的使用4.memcmp函数的使用 1.memcpy使用和模拟实现 void * memcpy (void * destination, const void * source, size_t num);• 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存…

分析电脑上处理器的性能报告

这张图片给出了一份详细的第11代Intel(R) Core(TM) i7-1165G7 2.80GHz处理器的性能报告。 CPU型号&#xff1a;11th Gen Intel(R) Core(TM) i7-1165G7&#xff08;这是一个低功耗的移动处理器&#xff0c;常用于轻薄型笔记本电脑&#xff09; 基准速度&#xff1a;2.80 GHz&…

C语言-信号

信号 一、信号是什么东西 信号是事件发生时通知进程的一种机制&#xff0c;有时也称之为软件中断。 信号的到来会打断了程序执行的正常流程。 大多数情况下&#xff0c;无法预测信号到达的精确时间。 一个&#xff08;具有合适权限的&#xff09;进程能够向另一进程发送信…

python查找内容在文件中的第几行(利用了滑动窗口)

def find_multiline_content(file_path, multiline_content):with open(file_path, r) as file:# 文件内容file_lines file.readlines()# 待检测内容multiline_lines multiline_content.strip().split(\n)# 待检测内容总行数num_multiline_lines len(multiline_lines)matchi…

Postgresql源码(130)ExecInterpExpr转换为IR的流程

相关 《Postgresql源码&#xff08;127&#xff09;投影ExecProject的表达式执行分析》 《Postgresql源码&#xff08;128&#xff09;深入分析JIT中的函数内联llvm_inline》 《Postgresql源码&#xff08;129&#xff09;JIT函数中如何使用PG的类型llvmjit_types》 表达式计算…

计算机系统基础 8 循环程序

概要 两种实现方法——分支指令实现和专门的循环语句实现以及有关循环的优化。 分支指令实现 倒计数 …… MOV ECX&#xff0c;循环次数 LOOPA&#xff1a;…… …… DEC ECX JNE LOOPA 正计数 …… MOV ECX&#xff0c;0 LOOPA&#xff1a; …… INC ECX CMP …

Kafka-集群管理者(Controller)选举机制、任期(epoch)机制

Kafka概述 Kafka-集群管理者&#xff08;Controller&#xff09;选举机制 Kafka中的Controller是Kafka集群中的一个特殊角色&#xff0c;负责对整个集群进行管理和协调。Controller的主要职责包括分区分配、副本管理、Leader选举等。当当前的Controller节点失效或需要进行重新…

【CALayer-CALayer的transform属性 Objective-C语言】

一、接下来,我们来说的是这个,transform的属性 1.layer的transform属性, 把最后一份代码command + C、command + V、一份儿,改个名字, Name:04-CALayer的transform属性, 我们把这个代码稍微修改一下, 我们先添加了一个layer,到控制器的view上, 然后呢,这两句话不…

【安装笔记-20240524-Windows-安装测试 7-Zip】

安装笔记-系列文章目录 安装笔记-20240524-Windows-安装测试 7-Zip 文章目录 安装笔记-系列文章目录安装笔记-20240524-Windows-安装测试 7-Zip 前言一、软件介绍名称&#xff1a;7-Zip主页官方介绍7-Zip 主要特征 二、安装步骤测试版本&#xff1a;24.05 (2024-05-14) for Wi…

保安维稳,四信以科技构筑高速公路安全智慧防线

近日&#xff0c;广东梅大高速发生严重塌方事故&#xff0c;造成了严重的人员伤亡和财产损失。这一事件在公众心中敲响了安全的警钟&#xff0c;再次引起了公众对于交通设施运营安全性的重点关注。 国务院安委会办公室和国家防灾减灾救灾委员会办公室等主管机构先后印发紧急通知…

CentOS7 部署单机版 elasticsearch

一、环境准备 1、准备一台系统为CentOS7的服务器 [rootlocalhost ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) 2、创建新用户&#xff0c;用于elasticsearch服务 # elastic不允许使用root账号启动服务 [rootlocalhost ~]# useradd elastic [rootlo…

58同城如何降低 80%的机器成本 | OceanBase案例

本文作者&#xff1a;58同城架构师刘春雷 一、背景介绍 58同城作为中国互联网生活服务领域的领军者&#xff0c;其平台规模居国内之首&#xff0c;涵盖了包括车辆交易、房产服务、人才招聘、本地生活服务以及金融等多元化的业务场景。 因其业务的广泛性和多样性&#xff0c;我…

自主创新助力科技强军,麒麟信安闪耀第九届军博会

由中国指挥与控制学会主办的中国指挥控制大会暨第九届北京军博会于5月17日-19日在北京国家会议中心盛大开展&#xff0c;政府、军队、武警、公安、交通、人防、航天、航空、兵器、船舶、电科集团等从事国防军工技术与产业领域的30000多名代表到场参加。 麒麟信安作为国产化方案…