1. Background

During our development, the following situations may exist:
1. 有些时候我们需要调用第三方的接口 general, when calling the interface, we will record the input parameters and responses of the request. If our own system logs and third-party logs are mixed into one log file, it may be more troublesome to find logs. 那么我们是否可以将第三方系统的日志单独放到另外的文件中呢?

2. Or sometimes our system needs to perform data migration. If a certain data migration fails, 是否单独放到一个日志文件中比较清晰呢?

2. Demand

需求
As can be seen from the above figure, our requirements are relatively simple

1. The system startup log and login module log are recorded in the springboot-spring.log file.
2. The log of the third-party business ( QQ ) module is recorded in the springboot-qq.log file.
3、第三方业务( QQ )模块login(loginName)方法, 方法的入参loginNamespringboot-qqLoginName.log文件中,模拟一、背景 The data migration mentioned in 一、背景 failed, and the failed data was recorded in a separate log file.

3. Technical realization

1. The logging framework used

Here we use logback to complete the log recording, because SpringBoot the application default is logback to record the log.

2. If you implement sub-module and sub-file logging

1. Write appender , which can easily understand where the log needs to be output.

for example:

 <!-- 此处定义的日志输出到控制台 -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [${PID:- }] [%thread] %-5level %logger{50}#%method:%L -%msg%n</pattern>

    </encoder>
</appender>

<!-- 此处定义日志输出到 springboot-qq-日期.第几个.log 文件中 -->
<appender name="qqAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>logs/springboot-qq-%d{yyyy-MM-dd}-.%i.log</fileNamePattern>
    </rollingPolicy>
   
</appender>

2. How to implement module output log

Here we need to configure logger . The logger's name attribute is assigned to the specific full package name, and then quoting the appender defined above.

 <!-- 在QQ这个包下的日志单独使用 qqAppender 来输出 -->
<logger name="com.huan.springboot.qq" level="info" additivity="false">
    <appender-ref ref="qqAppender"/>
    <appender-ref ref="stdout"/>
</logger>

Configure the logger, the name of the logger is the full package name of the package that needs to generate files separately, and then reference the appender defined above in it

3. If the implementation outputs the loginName to the specified file

In fact, still use logger to achieve, the logger's name needs and LoggerFactory.getLogger("此处写具体logger的name的值")
如果实现将loginName输出到指定的文件
Notice:
There may be a pit here, that is, the class name may be lost, so how do we solve it? It can be solved by MDC .

 .... %X{CLASSNAME}#%method:%L -%msg%n
 MDC.put("CLASSNAME", QQService.class.getName());
qqLoginName.info("登录用户:[{}]", loginName);

xml %X{CLASSNAME}java代码MDC CLASSNAME的值。

Fourth, code implementation

1. Write the xml log file

1. Write the appender

1. Output log to console
 <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [${PID:- }] [%thread] %-5level %logger{50}#%method:%L -%msg%n</pattern>
        <charset>UTF-8</charset>
    </encoder>
</appender>
2. Write login template log
 <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>logs/springboot-spring-%d{yyyy-MM-dd}-.%i.log</fileNamePattern>
            <maxHistory>7</maxHistory>
            <maxFileSize>1MB</maxFileSize>
            <totalSizeCap>2GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [${PID:- }] [%thread] %-5level %logger{50}#%method:%L -%msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
3. Write qq template log
 <appender name="qqAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>logs/springboot-qq-%d{yyyy-MM-dd}-.%i.log</fileNamePattern>
            <maxHistory>7</maxHistory>
            <maxFileSize>1MB</maxFileSize>
            <totalSizeCap>2GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [${PID:- }] [%thread] %-5level %logger{50}#%method:%L -%msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
4. Write the log that the qq module loginName outputs to the file separately
 <appender name="qqLoginNameAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>logs/springboot-qqLoginName-%d{yyyy-MM-dd}-.%i.log</fileNamePattern>
            <maxHistory>7</maxHistory>
            <maxFileSize>1MB</maxFileSize>
            <totalSizeCap>2GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [${PID:- }] [%thread] %-5level %X{CLASSNAME}#%method:%L -%msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

2. Configure the log output to a specific location

1. Configure the login module
 <root level="INFO">
    <appender-ref ref="stdout"/>
    <appender-ref ref="file"/>
</root>

login The module belongs to our own system module, here we use the root tag to configure.

2. Configure the qq module
 <!-- 在QQ这个包下的日志单独使用 qqAppender 来输出 -->
<logger name="com.huan.springboot.qq" level="info" additivity="false">
    <appender-ref ref="qqAppender"/>
    <appender-ref ref="stdout"/>
</logger>

Here the value of name is directly assigned to the full package name path of qq .

3. Configure loginName to output to a file separately
 <!-- 将所有的QQ登录名防止在另外的文件中 -->
<logger name="qqLoginName" level="info" additivity="false">
    <appender-ref ref="qqLoginNameAppender"/>
    <appender-ref ref="stdout"/>
</logger>

2. Write the code of the QQ module

 @Component
public class QQService {

    private static final Logger log = LoggerFactory.getLogger(QQService.class);

    // getLogger("qqLoginName") 里的 qqLoginName 需要和 logback-spring.xml 中 logger的name一致,才会应用
    private static final Logger qqLoginName = LoggerFactory.getLogger("qqLoginName");

    public void login(String loginName) {
        log.info("QQ业务: 用户:[{}]开始使用QQ来登录系统", loginName);

        MDC.put("CLASSNAME", QQService.class.getName());
        qqLoginName.info("登录用户:[{}]", loginName);
    }
}

3. Write the code of the login module

 @RestController
public class LoginController {

    private static final Logger log = LoggerFactory.getLogger(LoginController.class);

    @Resource
    private QQService qqService;

    @GetMapping("login/{loginName}")
    public String login(@PathVariable("loginName") String loginName) {
        log.info("自己业务:用户:[{}]进行登录", loginName);
        qqService.login(loginName);
        return "ok";
    }
}

5. Running results

运行结果
It can be seen that the results we expect are obtained.

6. Complete code

https://gitee.com/huan1993/spring-cloud-parent/tree/master/springboot/springboot-logger-split-file

Seven, a little knowledge

In SpringBoot, if we want to override the default logback configuration, it is recommended to use logback-spring.xml to configure.
一个小知识点


huan1993
207 声望31 粉丝

java工程师