SpringBoot+log4j2+MDC+AOP記錄requestId


前言

在AOP切面注入RequestId,攔截Controller、Service方法,打印入參出參耗時等,方便排查問題。

 

可以在服務上通過RequestId查詢一次調用鏈日志:

 

 

 可以使用 Linux grep 命令查詢日志:

grep 命令用於查找文件里符合條件的字符串。
日志文件太大無法直接 cat 查看,可以用grep
常用參數:
  • -A<顯示行數> : 除了顯示符合范本樣式的那一列之外,並顯示該行之后的內容。
  • -C<顯示行數> : 除了顯示符合樣式的那一行之外,並顯示該行之前后的內容。
  • -E  : 將樣式為延伸的正則表達式來使用。
eg:
查詢 包含 trainSegment/detail 的字符串的前后10行
  grep -C 10 trainSegment/detail webapi-info-VM-130-116-centos.log
正則
grep -E -C 30 "keyword1.* keyword2.* " webapi-info-VM-130-116-centos.log
 

代碼

Controller

/**
 * @author lihaoyang
 * @date 2021/3/17
 */
@RestController
public class HiController {

    @Autowired
    HiServiceImpl hiService;

    @GetMapping("get")
    public String get(String msg) {
        String rs = hiService.getHi(msg);
        return rs;
    }


}

Service

@Service
public class HiServiceImpl {

    public String getHi(String msg) {
        return "service:  >>>>> " + msg;
    }

}

切面類

RequestId切面

@Aspect
@Component
@Slf4j
@Order(-10)
public class ControllerRequestIdAspect {

    @Pointcut("execution(public * com.nb.log.controller..*.*(..))")
    public void controllerPoint() {
    }

    @Around("controllerPoint()")
    public Object doControllerPointAround(ProceedingJoinPoint joinPoint) throws Throwable {
        String requestId = UUID.randomUUID().toString();
        MDC.put("requestId", requestId);
        Object result = null;
        try {
            result = joinPoint.proceed();
        } finally {
            MDC.remove("requestId");
        }
        return result;
    }
}

 

Controller切面

package com.nb.log.aop;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
 * controller切面
 */
@Aspect
@Slf4j
@Component
@Order(0)
public class ControllerLogAspect {

    @Pointcut("execution(public * com.nb.log.controller..*.*(..))")
    public void controllerPoint() {}

    @Around("controllerPoint()")
    public Object doControllerAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        Object result = null;
        try {
            HttpServletRequest request =
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            Object[] params = joinPoint.getArgs();
            log.info("Controller層 className={}, methodName={}, params={},url={},ip={}",
                joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(),
                Arrays.toString(params), request.getRequestURL(), IpUtil.getIpAddr(request));
            result = joinPoint.proceed();
        } finally {
            log.info("Controller層 耗時={}(ms), className={}, methodName={}, result={}",
                System.currentTimeMillis() - startTime, joinPoint.getSignature().getDeclaringTypeName(),
                joinPoint.getSignature().getName(), JSONObject.toJSONString(result));
        }
        return result;
    }
}

Service切面

package com.nb.log.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * service層切面日志
 */
@Aspect
@Slf4j
@Order(0)
@Component
public class ServiceLogAspect {


    @Pointcut("execution(public * com.nb.log.service..*ServiceImpl.*(..))")
    public void servicePoint() {
    }

    @Around("servicePoint()")
    public Object doServiceAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = null;
        try {
            Object[] params = joinPoint.getArgs();
            log.info("Service層 className={}, methodName={}, params={}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), Arrays.toString(params));
            result = joinPoint.proceed();
        }
        finally {
            log.info("Service層 耗時={}(ms), className={}, methodName={}", System.currentTimeMillis() - startTime, joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
        }
        return result;
    }
}

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--設置log4j2的自身log級別為warn-->
<!--日志級別以及優先級排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,這個用於設置log4j2自身內部的信息輸出,可以不設置,
    當設置成trace時,你會看到log4j2內部各種詳細輸出-->
<!--monitorInterval:Log4j能夠自動檢測修改配置 文件和重新配置本身,設置間隔秒數-->
<configuration status="warn" monitorInterval="30">
    <Properties>
        <property name="log_pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} [%level] -%X{filter} &lt;%t|%C{1.}.%M(%L)&gt; -%X{requestId} %m%n%ex</property>
        <property name="project">remix-collector</property>
        <property name="basePath">D:/log/${project}/</property>
        <!--<property name="basePath">F:\neworiental\www\logs</property>-->
        <property name="rolling_file_name">${basePath}/%d{yyyy-MM-dd}-%i</property>
        <!-- 日志切割的最小單位 -->
        <property name="every_file_size">500 MB</property>
    </Properties>
    <!--先定義所有的appender-->
    <appenders>
        <!--這個輸出控制台的配置-->
        <console name="Console" target="SYSTEM_OUT">
            <!--輸出日志的格式-->
            <PatternLayout pattern="${log_pattern}"/>
        </console>
        <!--文件會打印出所有信息,這個log每次運行程序會自動清空,由append屬性決定,這個也挺有用的,適合臨時測試用-->
        <!--      <File name="log" fileName="${sys:user.home}/old_log/test.log" append="false">
                  <PatternLayout pattern="${log_pattern}"/>
              </File>-->
        <!-- 這個會打印出所有的info及以下級別的信息,每次大小超過size,
        則這size大小的日志會自動存入按年份-月份建立的文件夾下面並進行壓縮,作為存檔-->
        <RollingFile name="RollingFile" fileName="${basePath}/${project}-info-${hostName}.log" filePattern="${basePath}/${project}-info-${hostName}-%d{yyyy-MM-dd}-%i.log">
            <Filters>
                <!--控制台只輸出level及以上級別的信息(onMatch),其他的直接拒絕(onMismatch)-->
                <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
                <ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
            </Filters>
            <PatternLayout pattern="${log_pattern}"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="${every_file_size}"/>
            </Policies>

            <!-- 最多備份7天以內-->
            <DefaultRolloverStrategy>
                <Delete basePath="${basePath}" maxDepth="1">
                    <IfFileName glob="*.log" />
                    <IfLastModified age="3d">
                        <!-- <IfAny>
                             <IfAccumulatedFileSize exceeds="10 GB" />
                             <IfAccumulatedFileCount exceeds="10" />
                         </IfAny>-->
                    </IfLastModified>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>

        <!--warn-->
        <RollingFile name="RollingFileWarn"
                     fileName="${basePath}/${project}-warn-${hostName}.log"
                     filePattern="${rolling_file_name}-warn-${hostName}-%d{yyyy-MM-dd}-%i.log">
            <Filters>
                <ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
                <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
            </Filters>
            <PatternLayout pattern="${log_pattern}"/>
            <Policies>
                <TimeBasedTriggeringPolicy modulate="true" interval="24"/>
                <SizeBasedTriggeringPolicy/>
            </Policies>
            <!-- DefaultRolloverStrategy屬性如不設置,則默認為最多同一文件夾下7個文件,這里設置了20 -->
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>

        <!--Error級別日志輸出-->
        <RollingFile name="RollingFileError"
                     fileName="${basePath}/${project}-error-${hostName}.log"
                     filePattern="${rolling_file_name}-error-${hostName}-%d{yyyy-MM-dd}-%i.log">
            <Filters>
                <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY" />
            </Filters>
            <PatternLayout pattern="${log_pattern}"/>
            <SizeBasedTriggeringPolicy size="${every_file_size}"/>
        </RollingFile>


    </appenders>
    <!--然后定義logger,只有定義了logger並引入的appender,appender才會生效-->
    <loggers>
        <!-- druid日志 -->
        <logger name="druid.sql.Statement" level="debug" additivity="false">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="druidSqlAppender"/>
            <AppenderRef ref="invokeAppender"/>
        </logger>
        <logger name="druid.sql.ResultSet" level="debug" additivity="false">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="druidSqlAppender"/>
            <AppenderRef ref="invokeAppender"/>
        </logger>
        <logger name="com.nb.log.controller" level="INFO">
        </logger>

        <root level="info">
            <appender-ref ref="Console"/>
            <appender-ref ref="RollingFile"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
        </root>
    </loggers>

</configuration>

maven pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.nb</groupId>
    <artifactId>nb-log</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>nb-log</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--集成springmvc框架並實現自動配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions><!-- 去掉springboot默認配置 -->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 引入log4j2依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.18</version>
            <scope>provided</scope>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.7.RELEASE</version>
                <configuration>
                    <mainClass>com.nb.log.NbLogApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

 

完整代碼

https://gitee.com/haoyangli/mdc-log.git


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM