使用MDC和AOP及拦截器为日志加跟踪标识

本文介绍了如何结合MDC(Mapped Diagnostic Context)、AOP切面和拦截器来为日志添加跟踪标识。在解决MQ消息日志跟踪问题中,发现TreadLocal和拦截器无法满足需求,转而使用MDC。文中提到了参考文章,并详细说明了拦截器的配置、AOP的设置以及logback.xml的日志格式配置,确保跟踪ID能正确打印。

1. 参考文章:https://segmentfault.com/a/1190000022721682https://blog.csdn.net/millery22/article/details/86672354

2. 开始使用了TreadLocal和拦截器增加标识,但是发现想中消费mq消息的日志,无法打印唯一标识。文章:https://blog.csdn.net/whxwkb/article/details/111594712。查询到MDC可以。

3. 拦截器:


public class LogInterceptor implements HandlerInterceptor {

    public final static String REQUEST_ID = "requestId";
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        try{
            String uuid = UUID.randomUUID().toString().replace("-", "");
            MDC.put(REQUEST_ID, uuid);
        } catch (Exception e) {
            // 捕捉异常,不可阻断流程,无需做任何处理
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
          //移除id
        MDC.remove(REQUEST_ID);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}

拦截器配置:

@Configuration
public class WebInterceptorConfig extends WebMvcConfigurationSupport {


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**");
    }

    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//解决拦截器导致swagger被拦截   
     registry.addResourceHandler("/statics/**").addResourceLocations("classpath:/statics/");
        // 解决 SWAGGER 404报错
        registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}

MDC的AOP配置:

@Configuration
@Aspect
public class LogMDCAspectConfig {
    @Pointcut("execution(public * com.cnpc.cmp.ws.amqp..*.*(..))")
    public void webLog(){}

    @Before("webLog()")
    public void before(JoinPoint joinPoint){
        String uuid = UUID.randomUUID().toString().replace("-", "");
        MDC.put(LogInterceptor.REQUEST_ID, uuid);
    }

    /**
     * 对于涉及到ThreadLocal相关使用的接口,都需要去考虑在使用完上下文对象时,
     * 清除掉对应的数据,以避免内存泄露问题
     * @param ret
     */
    @AfterReturning(pointcut = "webLog()", returning = "ret")
    public void afterReturning(Object ret){
        MDC.remove(LogInterceptor.REQUEST_ID);
    }
 

}

logback.xml中配置日志打印格式,其中注意配置的变量和mdc中put的变量一致:MDC.put(REQUEST_ID, uuid); 

如:[%X{requestId}]

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{requestId}] %-5level %logger{36}-%msg%n</pattern>

xml文件:

<!-- Logback configuration. See http://logback.qos.ch/manual/index.html -->
<configuration scan="true" scanPeriod="10 seconds">
	<include resource="org/springframework/boot/logging/logback/base.xml" />
	<jmxConfigurator/>
<!--	<property name="LOG_PATH"	value="${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}" />-->
<!--	配置相当路径会在本地生成日志文件-->
	<property name="LOG_PATH"	value="wslog" />
	<!--控制台输出 需要在root标签中引入才生效 -->
	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<!--格式化输出:%d表示日期,%thread表示线程名,[%X{requestId}]为MDC设置的值跟踪日志的traceId,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{requestId}] %-5level %logger{36}-%msg%n</pattern>
		</encoder>
	</appender>
	<appender name="DEBUG_FILE"
			  class="ch.qos.logback.core.rolling.RollingFileAppender">
		<File>${LOG_PATH}/debug.log</File>
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

			<fileNamePattern>${LOG_PATH}/debug-%d{yyyyMMdd}.log.%i
			</fileNamePattern>
			<timeBasedFileNamingAndTriggeringPolicy
					class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<maxFileSize>500MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>
			<!--日志文件保留的天数-->
			<maxHistory>2</maxHistory>
		</rollingPolicy>
		<layout class="ch.qos.logback.classic.PatternLayout">
			<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{requestId}] %-5level %logger{36}-%msg%n</Pattern>
		</layout>
		<filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印ERROR日志 -->
			<level>DEBUG</level>
			<onMatch>ACCEPT</onMatch>
			<onMismatch>DENY</onMismatch>
		</filter>
	</appender>
	<appender name="INFO_FILE"
		class="ch.qos.logback.core.rolling.RollingFileAppender">
		<File>${LOG_PATH}/info.log</File>
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

			<fileNamePattern>${LOG_PATH}/info-%d{yyyyMMdd}.log.%i
			</fileNamePattern>
			<timeBasedFileNamingAndTriggeringPolicy
				class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<maxFileSize>500MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>
			<!--日志文件保留的天数-->
			<maxHistory>2</maxHistory>
		</rollingPolicy>
		<layout class="ch.qos.logback.classic.PatternLayout">
			<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{requestId}] %-5level %logger{36}-%msg%n</Pattern>
		</layout>
		<filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印ERROR日志 -->
			<level>INFO</level>
			<onMatch>ACCEPT</onMatch>
			<onMismatch>DENY</onMismatch>
		</filter>
	</appender>
	<appender name="WARN_FILE"
		class="ch.qos.logback.core.rolling.RollingFileAppender">
		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
			<level>WARN</level>
		</filter>
		<File>${LOG_PATH}/warn.log</File>
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<fileNamePattern>${LOG_PATH}/warn-%d{yyyyMMdd}.log.%i
			</fileNamePattern>
			<timeBasedFileNamingAndTriggeringPolicy
				class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<maxFileSize>500MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>
			<maxHistory>2</maxHistory>
		</rollingPolicy>
		<layout class="ch.qos.logback.classic.PatternLayout">
			<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{requestId}] %-5level %logger{36}-%msg%n</Pattern>
		</layout>
		<filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印ERROR日志 -->
			<level>WARN</level>
			<onMatch>ACCEPT</onMatch>
			<onMismatch>DENY</onMismatch>
		</filter>
	</appender>

	<appender name="ERROR_FILE"
		class="ch.qos.logback.core.rolling.RollingFileAppender">
		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
			<level>ERROR</level>
		</filter>
		<File>${LOG_PATH}/error.log</File>
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<fileNamePattern>${LOG_PATH}/error-%d{yyyyMMdd}.log.%i
			</fileNamePattern>
			<timeBasedFileNamingAndTriggeringPolicy
				class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<maxFileSize>500MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>
			<maxHistory>2</maxHistory>
		</rollingPolicy>
		<layout class="ch.qos.logback.classic.PatternLayout">
			<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{requestId}] %-5level %logger{36}-%msg%n</Pattern>
		</layout>
		<filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印ERROR日志 -->
			<level>ERROR</level>
			<onMatch>ACCEPT</onMatch>
			<onMismatch>DENY</onMismatch>
		</filter>
	</appender>

	<!--<root level=""> 最基础的日志输出级别默认是debug-->
	<root >
		<appender-ref ref="DEBUG_FILE" />
		<appender-ref ref="INFO_FILE" />
		<appender-ref ref="WARN_FILE" />
		<appender-ref ref="ERROR_FILE" />
	</root>
</configuration>

 

 

 

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值