分布式中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 {
    @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,快速定位到日志位置

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

麦田小猪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值