1 Star 0 Fork 2

dangdang / skill-list-demo

forked from czc / skill-list-demo 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
第二章、日志管理.md 7.54 KB
一键复制 编辑 原始数据 按行查看 历史
chenzc 提交于 2023-10-26 09:57 . doc

第二章、日志管理

管理系统的日志相关的格式、日志文件、追踪id等功能。查看代码commit:第二章、日志管理

在sld-common下创建模块sld-common-log

<parent>
    <groupId>cn.chenzecheng</groupId>
    <artifactId>sld-common</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <relativePath>../pom.xml</relativePath>
</parent>

<artifactId>sld-common-log</artifactId>

logback-spring.xml

因为springboot默认日志使用logback框架,我们也就使用该框架。在resource下创建logback-spring.xml,设置格式和日志文件滚动配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="LOG_DIR" value="./logs"/>
    <property name="LOG_MAX_FILE_SIZE" value="100MB"/>

    <!--application.yml 传递参数,不使用logback 自带的<property>标签 -->
    <springProperty scope="context" name="APP_ID" source="spring.application.name" default="default-application-name"/>
    <springProperty scope="context" name="LOG_MAX_HISTORY" source="logback.MaxHistory" default="15"/>

    <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | %X{uri} | %X{requestId} | %-5level | ${PID} | ${APP_ID} | ${HOSTNAME}
                | [%thread] | %logger{36}.%M\(%F:%L\) | %msg%n
            </pattern>
            <charset>UTF-8</charset>
        </layout>
    </appender>

    <appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_DIR}/app.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | %X{uri} | %X{requestId} | %-5level | ${PID} | ${APP_ID} | ${HOSTNAME}
                | [%thread] | %logger{36}.%M\(%F:%L\) | %msg%n
            </pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_DIR}/archived/app.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>${LOG_MAX_HISTORY}</maxHistory>
            <totalSizeCap>${LOG_MAX_FILE_SIZE}</totalSizeCap>
        </rollingPolicy>
    </appender>

    <root level="INFO">
        <appender-ref ref="Console"/>
        <appender-ref ref="RollingFile"/>
    </root>
</configuration>

在服务的properties控制各个包的日志级别,配置服务名

server.port=8001
spring.application.name=sld-common-test
# logging.level.xxx(包名)设置对应的包的日志等级,root是所有
logging.level.root=info
logging.level.cn.chenzecheng.sld.common.test.controller.log=trace

在sld-common-test模块下cn.chenzecheng.sld.common.test.controller包和cn.chenzecheng.sld.common.test.controller.log 包创建2个controller,分别调用接口进行日志等级测试

    @GetMapping("/log")
    public String testLog(){
        log.error("test error");
        log.warn("test warn");
        log.info("test info");
        log.debug("test debug");
        log.trace("test trace");
        return"test log";
    }

实际开发工作中,我们把业务代码所在的包设为debug,其余设为info。生产环境则都是info,有问题排查时再改为debug或者trace。

设置urirequestId的值

上面的日志格式中,我们有2个参数urirequestId,需要我们配置拦截器配合MDC进行设值。实际工作中用于查询指定请求的日志。

过滤器核心逻辑


@Slf4j
public class MdcFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse response1 = (HttpServletResponse) response;
        try {
            mdcRequestInfo(httpServletRequest, response1);
        } catch (Exception e) {
            log.error("doFilter error: ", e);
        }
        try {
            chain.doFilter(request, response);
        } finally {
            MdcUtils.clear();
        }
    }

    /**
     * mdc记录请求跟踪信息
     */
    private void mdcRequestInfo(HttpServletRequest httpServletRequest, HttpServletResponse response) {
        MdcUtils.put(MdcConstants.LOCAL_IP_MDC_KEY, localIp);
        String requestId = httpServletRequest.getHeader(MdcConstants.REQUEST_ID_HEADER);
        if (Objects.isNull(requestId) || requestId.length() == 0) {
            requestId = UUID.randomUUID().toString().replace("-", "");
        }
        //将requestId放入mdc
        MdcUtils.put(MdcConstants.REQUEST_ID_MDC_KEY, requestId);
        //将时间戳和uri放入mdc
        MdcUtils.put(MdcConstants.TIMESTAMP, String.valueOf(System.currentTimeMillis()));
        String pathUri = httpServletRequest.getRequestURI();
        if (Objects.nonNull(pathUri) && requestId.length() > 0 && !pathUri.contains("jsessionid") && !pathUri.contains(";")) {
            MdcUtils.put(MdcConstants.URI_MDC_KEY, pathUri);
        }
    }
}

注册拦截器


@Configuration
public class MdcFilterConfig {
    @Bean
    @Order(0)
    public FilterRegistrationBean<MdcFilter> getMdcFilter() {
        FilterRegistrationBean<MdcFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new MdcFilter());
        registration.addInitParameter(MdcConstants.MAPPED_HEADERS_KEY, Boolean.FALSE.toString());
        registration.addInitParameter(MdcConstants.MAPPED_PARAMETERS, Boolean.FALSE.toString());
        registration.addUrlPatterns("/*");
        registration.setName("MdcFilter");
        return registration;
    }
}

设置自动装配

不需要引入的项目配置 @ComponentScan 扫描上述的配置类的包,只要引入了该模块就自动启动

注意:SpringBoot3spring.factories 的写法无法自动装配,改用 spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

resource/META-INF/spring下创建文件 org.springframework.boot.autoconfigure.AutoConfiguration.imports

cn.chenzecheng.sld.common.log.mdc.MdcFilterConfig

设置异步传递请求id

Springboot使用MDC进行日志追踪_mdcutil_繁华尽头满是殇的博客-CSDN博客

核心思路是通过线程池的接口,将MDC上下进行线程间传递。

    @NotNull
    @Override
    public Future<?> submit(@NotNull Runnable task){
        return super.submit(MdcThreadUtil.wrap(task,MDC.getCopyOfContextMap()));
    }

    // MdcThreadUtil.wrap方法
    public static Runnable wrap(final Runnable runnable,final Map<String, String> context){
        return()->{
            if(context==null){
                MdcUtil.clear();
            }else{
                MdcUtil.setContextMap(context);
            }
            //设置requestId
            setRequestIdIfAbsent();
            try{
                runnable.run();
            }finally{
                MdcUtil.clear();
            }
        };
    }

先根据以上资料实现代码线程池的传递,测试。查看代码commit:第二章、日志管理

远程调用实现,在后续章节实现服务间调用之后再实现和测试。

Java
1
https://gitee.com/dangdang2020/skill-list-demo.git
git@gitee.com:dangdang2020/skill-list-demo.git
dangdang2020
skill-list-demo
skill-list-demo
main

搜索帮助