springboot项⽬⽇志使⽤MDC添加TraceId
0. ⽇志打印添加traceid, 每次请求有不同的traceId
1. 引⼊springboot的aop, web
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
2. 编写
demo.interceptor;
slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;
/**
* ⽇志⽣成THREAD_ID,供⽇志打印和返回对象使⽤
*
* @Author: zy
*/
@Slf4j
public class LogInterceptor implements HandlerInterceptor {
/**
* 线程ID常量
*/
public static final String THREAD_ID = "THREAD_ID";
/**
* controller⽅法前调⽤
*/
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
log.debug("preHandle running ...");
// 使⽤UUID⽣成唯⼀编号
String threadId = UUID.randomUUID().toString().trim().replaceAll("-", "");
// 判断MDC(log4j中的上下⽂对象) 中是否有该threadId
if (StringUtils.(THREAD_ID))) {
// 如果没有,添加
MDC.put(THREAD_ID, threadId);
}
// 永远返回truespring ioc注解
return true;
}
/**
* preHandle⽅法返回true之后
* 在controller⽅法处理完之后调⽤
*/
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object, ModelAndView modelAndView) throws Exception {        log.debug("postHandle running ...");
//controller结束之后删除对应的唯⼀值
}
/**
* preHandle⽅法返回true之后
* 在DispatcherServlet进⾏视图的渲染之后调⽤
*/
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServle
tResponse, Object object, Exception e) throws Exception {
log.debug("afterCompletion running ...");
}
}
3. 编写Config类, 将LogInterceptor添加到容器
fig;
demo.interceptor.LogInterceptor;
import t.annotation.Bean;
import t.annotation.Configuration;
import org.springframework.fig.annotation.EnableWebMvc;
import org.springframework.fig.annotation.InterceptorRegistry;
import org.springframework.fig.annotation.ResourceHandlerRegistry;
import org.springframework.fig.annotation.WebMvcConfigurer;
/**
* ⽣产traceid的LogInterceptor到容器
* @Author: zy
*/
@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 注解LogInterceptor类到IOC容器中
*/
@Bean
public LogInterceptor logInterceptor() {
return new LogInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册⽇志
registry.addInterceptor(logInterceptor());
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
/**
* 解决 webmvc 与 swagger 页⾯冲突的问题
* blog.csdn/Kerwin_luo/article/details/114266444
* @param registry
*/
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/ /webjars/");
//添加静态页⾯资源,⽂件下载资源等
registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/","file:/E:/","file:/");
}
}
4. 编写aop
demo.aop;
le.gson.Gson;
demo.bean.BaseResponse;
demo.interceptor.LogInterceptor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.t.request.RequestContextHolder;
import org.t.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
@Aspect
@Component
public class aopWebLogAspect {
private final static Logger LOGGER = Logger(aopWebLogAspect.class);
/**
* 以Controller包下定义所有请求的⽅法
*/
@Pointcut("execution(public * demo..*Controller.*(..)) ")
private void webLog() {
}
/**
* 在切⼊点之前进⾏
*/
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestAttributes();
HttpServletRequest request = Request();
LOGGER.info("========================================== Start ==========================================");
//打印请求参数相关⽇志
// 打印请求 url
LOGGER.info("url:{}", RequestURI());
// 打印 Http method
LOGGER.info("HTTP Method:{}", Method());
// 打印调⽤ controller 的全路径以及执⾏⽅法
LOGGER.info("Class Method:{}.{}", Signature().getDeclaringTypeName(), Signature().getName());
// 打印请求的 IP
LOGGER.info("IP :{}", RemoteAddr());
// 打印请求⼊参
LOGGER.info("Request Args:{}", new Gson().Args()));
}
/**
* 在切⼊点之后执⾏
*/
@After("webLog()")
public void doAfter() {
LOGGER.info("========================================== end ==========================================");
}
@Around("webLog()")
public Object doAroud(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
BaseResponse result = (BaseResponse) proceedingJoinPoint.proceed();
if (result != null) {
// 将线程id赋值给返回的traceId
result.(LogInterceptor.THREAD_ID));
//打印出参
LOGGER.info("Response Args : {}", new Gson().toJson(result));
// 执⾏耗时
LOGGER.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);
}
return result;
}
}
5. 拷贝l到resources⽬录下
配置⽂件中使⽤ %X{THREAD_ID}
<?xml version="1.0" encoding="UTF-8"?>
<!-- ⽇志级别从低到⾼分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置⽂件如果发⽣改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置⽂件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性⽣效。默认的时间间隔为1分钟。 --> <!-- debug:当此属性设置为true时,
将打印出logback内部⽇志信息,实时查看logback运⾏状态。默认值为false。 -->
<configuration  scan="true" scanPeriod="10 seconds">
<!--<include resource="org/springframework/boot/logging/l" />-->
<contextName>logback</contextName>
<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插⼊到logger上下⽂中。定义变量后,可以使“${}”来使⽤变量。 -->
<property name="log.path" value="/opt/devopscloud/logs" />
<!-- 彩⾊⽇志 -->
<!-- 彩⾊⽇志依赖的渲染类 -->
<!--<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />-->
<!--<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logb
ack.WhitespaceThrowableProxyConverter" />-->
<!--<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />-->
<!-- 彩⾊⽇志格式 -->
<!--<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t])    <!--控制台打印彩⾊⽇志-->
<property name="CONSOLE_LOG_PATTERN"
value="%red(%d{yyyy-MM-dd HH:mm:ss.SSS})-%green([%X{THREAD_ID}])-%highlight(%-5level[%4line])-%boldMagenta(%logger{10}) : %cyan(%msg%n)"/>
<!--    ⽇志⽂件输⼊时的格式-->
<property name="FILE_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS}-[%X{THREAD_ID}]-%-5level[%4line] - %logger{10}  - %msg%n"/>
<!--输出到控制台-->
<appender name="CONSOLE" class="ch.ConsoleAppender">
<!--此⽇志appender是为开发使⽤,只配置最底级别,控制台输出的⽇志级别是⼤于或等于此级别的⽇志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>info</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--输出到⽂件-->
<appender name="dailyRollingFileAppender" class="ch.olling.RollingFileAppender">
<file>${log.path}/devops-cloud.log</file>
<!-- 30个⽇志⽂件做切换 -->
<rollingPolicy class="ch.olling.FixedWindowRollingPolicy">
<fileNamePattern>./log/devops-cloud.%i.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>30</maxIndex>
</rollingPolicy>
<!-- 每⼀个100MB -->
<triggeringPolicy class="ch.olling.SizeBasedTriggeringPolicy">
<maxFileSize>100MB</maxFileSize>
</triggeringPolicy>
<encoder>
<Pattern>^A[%date{yyyy-MM-dd HH:mm:ss.SSS}]-[%X{THREAD_ID}] [%level] [DEVOPS-CLOUD] [%t] %msg %n</Pattern>
</encoder>
</appender>
<!--<!– 时间滚动输出 level为 DEBUG ⽇志 –>-->
<!--<appender name="DEBUG_FILE" class="ch.olling.RollingFileAppender">-->
<!--<!– 正在记录的⽇志⽂件的路径及⽂件名 –>-->
<!--<file>${log.path}/log_debug.log</file>-->
<!--<!–⽇志⽂件输出格式–>-->
<!--<encoder>-->
<!--<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>-->
<!--<charset>UTF-8</charset> <!– 设置字符集 –>-->
<!--</encoder>-->
<!--<!– ⽇志记录器的滚动策略,按⽇期,按⼤⼩记录 –>-->
<!--<rollingPolicy class="ch.olling.TimeBasedRollingPolicy">-->
<!--<!– ⽇志归档 –>-->
<!--<fileNamePattern>${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>-->
<!--<timeBasedFileNamingAndTriggeringPolicy class="ch.olling.SizeAndTimeBasedFNATP">-->
<!--<maxFileSize>100MB</maxFileSize>-->
<!--</timeBasedFileNamingAndTriggeringPolicy>-->
<!--<!–⽇志⽂件保留天数–>-->
<!--<maxHistory>15</maxHistory>-->
<!--</rollingPolicy>-->
<!--<!– 此⽇志⽂件只记录debug级别的 –>-->
<!--<filter class="ch.qos.logback.classic.filter.LevelFilter">-->
<!--<level>debug</level>-->
<!--<onMatch>ACCEPT</onMatch>-->
<!--<onMismatch>DENY</onMismatch>-->
<!--</filter>-->
<!--</appender>-->
<!--<!– 时间滚动输出 level为 INFO ⽇志 –>-->
<!--<appender name="INFO_FILE" class="ch.olling.RollingFileAppender">-->
<!--<!– 正在记录的⽇志⽂件的路径及⽂件名 –>-->
<!--<file>${log.path}/log_info.log</file>-->
<!--<!–⽇志⽂件输出格式–>-->
<!--<encoder>-->
<!--<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>-->
<!--<charset>UTF-8</charset>-->
<!--</encoder>-->
<!--<!– ⽇志记录器的滚动策略,按⽇期,按⼤⼩记录 –>-->
<!--<rollingPolicy class="ch.olling.TimeBasedRollingPolicy">-->
<!--<!– 每天⽇志归档路径以及格式 –>-->
<!--<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>-->
<!--<timeBasedFileNamingAndTriggeringPolicy class="ch.olling.SizeAndTimeBasedFNATP">-->
<!--<maxFileSize>100MB</maxFileSize>-->
<!--</timeBasedFileNamingAndTriggeringPolicy>-->
<!--<!–⽇志⽂件保留天数–>-->
<!--<maxHistory>15</maxHistory>-->
<!--</rollingPolicy>-->
<!--<!– 此⽇志⽂件只记录info级别的 –>-->
<!--<filter class="ch.qos.logback.classic.filter.LevelFilter">-->
<!--<level>info</level>-->
<!--<onMatch>ACCEPT</onMatch>-->
<!--<onMismatch>DENY</onMismatch>-->
<!--</filter>-->
<!--</appender>-->
<!--<!– 时间滚动输出 level为 WARN ⽇志 –>-->
<!--<appender name="WARN_FILE" class="ch.olling.RollingFileAppender">-->
<!--<!– 正在记录的⽇志⽂件的路径及⽂件名 –>-->
<!--<file>${log.path}/log_warn.log</file>-->
<!--<!–⽇志⽂件输出格式–>-->
<!--<encoder>-->
<!--<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>-->
<!--<charset>UTF-8</charset> <!– 此处设置字符集 –>-->
<!--</encoder>-->
<!--<!– ⽇志记录器的滚动策略,按⽇期,按⼤⼩记录 –>-->
<!--<rollingPolicy class="ch.olling.TimeBasedRollingPolicy">-->
<!--<fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>-->
<!--<timeBasedFileNamingAndTriggeringPolicy class="ch.olling.SizeAndTimeBasedFNATP">-->
<!--<maxFileSize>100MB</maxFileSize>-->
<!--</timeBasedFileNamingAndTriggeringPolicy>-->
<!--<!–⽇志⽂件保留天数–>-->
<!--<maxHistory>15</maxHistory>-->
<!--</rollingPolicy>-->
<!--<!– 此⽇志⽂件只记录warn级别的 –>-->
<!--<filter class="ch.qos.logback.classic.filter.LevelFilter">-->
<!--<level>warn</level>-->
<!--<onMatch>ACCEPT</onMatch>-->
<!--<onMismatch>DENY</onMismatch>-->
<!--</filter>-->
<!--</appender>-->
<!--<!– 时间滚动输出 level为 ERROR ⽇志 –>-->
<!--<appender name="ERROR_FILE" class="ch.olling.RollingFileAppender">-->
<!--<!– 正在记录的⽇志⽂件的路径及⽂件名 –>-->
<!--<file>${log.path}/log_error.log</file>-->
<!--<!–⽇志⽂件输出格式–>-->
<!--<encoder>-->
<!--<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>-->
<!--<charset>UTF-8</charset> <!– 此处设置字符集 –>-->
<!--</encoder>-->
<!--<!– ⽇志记录器的滚动策略,按⽇期,按⼤⼩记录 –>-->
<!--<rollingPolicy class="ch.olling.TimeBasedRollingPolicy">-->
<!--<fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>-->
<!--<timeBasedFileNamingAndTriggeringPolicy class="ch.olling.SizeAndTimeBasedFNATP">-->
<!--<maxFileSize>100MB</maxFileSize>-->
<!--</timeBasedFileNamingAndTriggeringPolicy>-->
<!--<!–⽇志⽂件保留天数–>-->
<!--<maxHistory>15</maxHistory>-->
<!--</rollingPolicy>-->
<!--<!– 此⽇志⽂件只记录ERROR级别的 –>-->
<!--<filter class="ch.qos.logback.classic.filter.LevelFilter">-->
<!--<level>ERROR</level>-->
<!--<onMatch>ACCEPT</onMatch>-->
<!--<onMismatch>DENY</onMismatch>-->
<!--</filter>-->
<!--</appender>-->
<!--
<logger>⽤来设置某⼀个包或者具体的某⼀个类的⽇志打印级别、
以及指定<appender>。<logger>仅有⼀个name属性,
⼀个可选的level和⼀个可选的addtivity属性。
name:⽤来指定受此logger约束的某⼀个包或者具体的某⼀个类。
level:⽤来设置打印级别,⼤⼩写⽆关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
还有⼀个特俗值INHERITED或者同义词NULL,代表强制执⾏上级的级别。
如果未设置此属性,那么当前logger将会继承上级的级别。
addtivity:是否向上级logger传递打印信息。默认是true。
-
->
<!--<logger name="org.springframework.web" level="info"/>-->
<!--<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>-->    <!--
使⽤mybatis的时候,sql语句是debug下才会打印,⽽这⾥我们只配置了info,所以想要查看sql语句的话,有以下两种操作:第⼀种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样⽇志那边会出现很多其他消息
第⼆种就是单独给dao下⽬录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别:
-->
<!--
root节点是必选节点,⽤来指定最基础的⽇志输出级别,只有⼀个level属性
level:⽤来设置打印级别,⼤⼩写⽆关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
不能设置为INHERITED或者同义词NULL。默认是DEBUG
可以包含零个或多个元素,标识这个appender将会添加到这个logger。
-->
<!--开发环境:打印控制台-->
<!--<springProfile name="dev">-->
<!--<logger name="s.view" level="debug"/>-->
<!--</springProfile>-->
<!-- 打印sql -->
<!--    <logger name="com.wisedu.devops.devopscloud.mapper" level="info"/>-->
<root level="info">
<appender-ref ref="CONSOLE" />
<appender-ref ref="dailyRollingFileAppender" />
<!--<appender-ref ref="DEBUG_FILE" />-->
<!--<appender-ref ref="INFO_FILE" />-->
<!--<appender-ref ref="WARN_FILE" />-->
<!--<appender-ref ref="ERROR_FILE" />-->
</root>
<!--⽣产环境:输出到⽂件-->
<!--<springProfile name="pro">-->
<!--<root level="info">-->
<!--<appender-ref ref="CONSOLE" />-->
<!--<appender-ref ref="DEBUG_FILE" />-->
<!--<appender-ref ref="INFO_FILE" />-->
<!--<appender-ref ref="ERROR_FILE" />-->
<!--<appender-ref ref="WARN_FILE" />-->
<!--</root>-->
<!--</springProfile>-->
</configuration>

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。