当前位置:   article > 正文

通过日志手动实现微服务间OpenFeign调用链路追踪_feign实现链路日志

feign实现链路日志

调用http请求后先进入过滤器,生成traceId,在feign调用会重新生成Http请求,将traceId存入新生成http请求header,

调用到另一个服务后,先进入过滤器,从header中获取traceId,实现通过feign调用时其他服务中traceId保持一致,

在日志中加入[PID=%X{traceId}],查看日志实现链路追踪

1. 在通用基础板块加入过滤器

其他服务需引入此基础服务

不同线程的调用链日志应互不影响。故调用链信息可基于MDC技术实现,查看MDC的实现原理,本质还是基于ThreadLocal实现

package com.wxd.iot.web.filter;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.UUID;

/**
 * @author xuyu
 * @date 2020/5/6
 */
@Slf4j
@WebFilter("/**")
@Component
public class TraceFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        // feign调用时 目标服务从请求头中获取traceId
        String traceId = request.getHeader("traceId");

        // http请求时 不存在就生成一个
        if (StringUtils.isBlank(traceId)) {
            traceId = UUID.randomUUID().toString().replaceAll("-", "");
        }
        // 放入MDC中
        MDC.put("traceId", traceId);
        chain.doFilter(servletRequest, servletResponse);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

2. feignConfig设置拦截器

  • 指定配置类
@FeignClient(value = "service-admin", path = "/admin/feign/", configuration = FeignConfig.class, fallback = AdminClient.AdminClientHystrix.class)
public interface AdminClient {

}
  • 1
  • 2
  • 3
  • 4
  • 配置类实现RequestInterceptor接口
package com.wxd.iot.web.config;

import com.wxd.iot.web.feign.logger.FeignLoggerFactory;
import feign.Logger;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
/**
 * @author: wuhonggu
 * @date: 2022/12/18
 */
@Slf4j
@Configuration
public class FeignConfig implements RequestInterceptor {

    /**
     * @Description 拦截feign进行拦截,并在请求头添加,需要添加的header参数
     * @param template
     */
    @Override
    public void apply(RequestTemplate template) {
        try {
            // 获取请求对象
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

            if (servletRequestAttributes != null) {
                HttpServletRequest request = servletRequestAttributes.getRequest();
                // 获取当前请求的header,获取到jwt令牌
                if (request.getHeader("Authorization") != null) {
                    template.header("Authorization", request.getHeader("Authorization"));
                }
                String traceId = MDC.get("traceId");
                if(StringUtils.isNotBlank(traceId)){
                    template.header("traceId", MDC.get("traceId"));
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

3. 服务日志中添加 [PID=%X{traceId}]

各个日志级别打印均需添加,查看当前日志中相同部分。批量替换:level [PID=%X{traceId}]

<?xml version="1.0" encoding="UTF-8" ?>
<!-- configure.status 为设置日志输出级别,级别如下:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE
	、ALL -->
<!-- status属性是用来指定log4j2自身的日志输出级别,monitorInterval属性值如果是非零值则会间隔这个值的秒数,去查看配置文件是否发生变化,如果变化则会动态加载新的配置 -->
<!-- configure.monitorInterval 监控间隔 指log4j2每隔600秒(10分钟),自动监控该配置文件是否有变化,如果变化,则自动根据文件内容重新配置 -->
<configuration status="INFO" monitorInterval="600">
    <properties>
        <property name="LOG_HOME">./logs/data/</property>
        <property name="LOG_BACK_HOME">${LOG_HOME}/backup</property>
        <property name="ERROR_FILE_NAME">data</property>
        <property name="WARN_FILE_NAME">data</property>
        <property name="INFO_FILE_NAME">data</property>
        <property name="DEBUG_FILE_NAME">data</property>

        <!--		<property name="ERROR_FILE_NAME">error</property>-->
        <!--		<property name="WARN_FILE_NAME">warn</property>-->
        <!--		<property name="INFO_FILE_NAME">info</property>-->
        <!--		<property name="DEBUG_FILE_NAME">debug</property>-->
    </properties>
    <!--先定义所有的appender -->
    <appenders>

        <!--        &lt;!&ndash;文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用&ndash;&gt;-->
        <!--        <File name="log" fileName="log/test.log" append="false">-->
        <!--            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level [PID=%X{traceId}] %class{36} %L %M - %msg%xEx%n"/>-->
        <!--        </File>-->

        <!--这个输出控制台的配置 -->
        <Console name="Console" target="SYSTEM_OUT">
            <!--这个都知道是输出日志的格式 -->
            <!-- %logger{36} 36代表限制长度 -->
            <PatternLayout
                    pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level [PID=%X{traceId}] %logger{36} - %msg%n"/>
        </Console>

        <!-- 配置日志输出文件名字 追加读写 -->
        <!-- Error console log -->
        <RollingFile name="ErrLog"
                     fileName="${LOG_HOME}/${ERROR_FILE_NAME}.log"
                     filePattern="${LOG_BACK_HOME}/$${date:yyyy-MM}/${ERROR_FILE_NAME}.%d{yyyy-MM-dd}.log.gz"
                     append="true">
            <!-- 输出格式 -->
            <PatternLayout
                    pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %level [PID=%X{traceId}] [%thread][%file:%line] - %msg%n"/>
            <!-- 设置策略 -->
            <Policies>
                <!-- 基于时间的触发策略。该策略主要是完成周期性的log文件封存工作。有两个参数: interval,integer型,指定两次封存动作之间的时间间隔。单位:以日志的命名精度来确定单位,
                    比如yyyy-MM-dd-HH 单位为小时,yyyy-MM-dd-HH-mm 单位为分钟 modulate,boolean型,说明是否对封存时间进行调制。若modulate=true,
                    则封存时间将以0点为边界进行偏移计算。比如,modulate=true,interval=4hours, 那么假设上次封存日志的时间为03:00,则下次封存日志的时间为04:00,
                    之后的封存时间依次为08:00,12:00,16:00 -->
                <TimeBasedTriggeringPolicy interval="1"
                                           modulate="true"/>
                <!-- <SizeBasedTriggeringPolicy size="1MB" /> 文件大小滚动策略 -->
            </Policies>
            <Filters>
                <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
                <ThresholdFilter level="error" onMatch="ACCEPT"
                                 onMismatch="DENY"/>
            </Filters>
        </RollingFile>

        <!-- Warn console log -->
        <RollingFile name="WarnLog"
                     fileName="${LOG_HOME}/${WARN_FILE_NAME}.log"
                     filePattern="${LOG_BACK_HOME}/$${date:yyyy-MM}/${WARN_FILE_NAME}.%d{yyyy-MM-dd}.log.gz"
                     append="true">
            <!-- 输出格式 -->
            <PatternLayout
                    pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %level [PID=%X{traceId}] [%thread][%file:%line] - %msg%n"/>
            <!-- 设置策略 -->
            <Policies>
                <TimeBasedTriggeringPolicy interval="1"
                                           modulate="true"/>
            </Policies>
            <Filters>
                <ThresholdFilter level="error" onMatch="DENY"
                                 onMismatch="NEUTRAL"/>
                <ThresholdFilter level="warn" onMatch="ACCEPT"
                                 onMismatch="DENY"/>
            </Filters>
        </RollingFile>

        <!-- Info console log -->
        <RollingFile name="InfoLog"
                     fileName="${LOG_HOME}/${INFO_FILE_NAME}.log"
                     filePattern="${LOG_BACK_HOME}/$${date:yyyy-MM}/${INFO_FILE_NAME}.%d{yyyy-MM-dd}.log.gz"
                     append="true">
            <!-- 输出格式 -->
            <PatternLayout
                    pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %level [PID=%X{traceId}] [%thread][%file:%line] - %msg%n"/>
            <!-- 设置策略 -->
            <Policies>
                <TimeBasedTriggeringPolicy interval="1"
                                           modulate="true"/>
            </Policies>
            <DefaultRolloverStrategy max="100"/>
            <Filters>
                <ThresholdFilter level="warn" onMatch="DENY"
                                 onMismatch="NEUTRAL"/>
                <ThresholdFilter level="info" onMatch="ACCEPT"
                                 onMismatch="DENY"/>
            </Filters>

        </RollingFile>

        <!-- Debug console log -->
        <RollingFile name="DebugLog"
                     fileName="${LOG_HOME}/${DEBUG_FILE_NAME}.log"
                     filePattern="${LOG_BACK_HOME}/$${date:yyyy-MM}/${DEBUG_FILE_NAME}.%d{yyyy-MM-dd}.log.gz"
                     append="true">
            <!-- 输出格式 -->
            <PatternLayout
                    pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %level [PID=%X{traceId}] [%thread][%file:%line] - %msg%n"/>
            <!-- 设置策略 -->
            <Policies>
                <TimeBasedTriggeringPolicy interval="1"
                                           modulate="true"/>
            </Policies>
            <Filters>
                <ThresholdFilter level="info" onMatch="DENY"
                                 onMismatch="NEUTRAL"/>
                <ThresholdFilter level="debug" onMatch="ACCEPT"
                                 onMismatch="DENY"/>
            </Filters>
        </RollingFile>
    </appenders>
    <loggers>
        <!-- App Loggers -->
        <!--它是 子Logger 是否继承 父Logger 的 输出源(appender) 的标志位。具体说,默认情况下子Logger会继承父Logger的appender,
            也就是说子Logger会在父Logger的appender里输出。若是additivity设为false, 则子Logger只会在自己的appender里输出,而不会在父Logger的appender里输出。 -->
        <logger name="com.wxd.iot.data" level="info" additivity="false">
            <appender-ref ref="Console"/>
            <appender-ref ref="ErrLog"/>
            <appender-ref ref="WarnLog"/>
            <appender-ref ref="InfoLog"/>
            <appender-ref ref="DebugLog"/>
        </logger>

        <root level="info">
            <appender-ref ref="Console"/>
            <appender-ref ref="ErrLog"/>
            <!--			<appender-ref ref="WarnLog"/>-->
            <appender-ref ref="InfoLog"/>
            <!--			<appender-ref ref="DebugLog"/>-->
        </root>
        <!-- MyBatis Loggers -->
        <logger name="org.mybatis" level="debug"/>
        <!-- Spring Loggers -->
        <logger name="org.springframework" level="info"/>
        <logger name="com.alibaba" level="error"/>
        <logger name="io.lettuce.core.protocol" level="ERROR" />
    </loggers>
</configuration>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154

4. 查看各个服务日志信息

测试结果

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号