赞
踩
调用http请求后先进入过滤器,生成traceId,在feign调用会重新生成Http请求,将traceId存入新生成http请求header,
调用到另一个服务后,先进入过滤器,从header中获取traceId,实现通过feign调用时其他服务中traceId保持一致,
在日志中加入[PID=%X{traceId}],查看日志实现链路追踪
其他服务需引入此基础服务
不同线程的调用链日志应互不影响。故调用链信息可基于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); } }
@FeignClient(value = "service-admin", path = "/admin/feign/", configuration = FeignConfig.class, fallback = AdminClient.AdminClientHystrix.class)
public interface AdminClient {
}
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(); } } }
各个日志级别打印均需添加,查看当前日志中相同部分。批量替换: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> <!-- <!–文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用–>--> <!-- <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>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。