欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

系统日志增加mdc全局id等信息,方便快速定位问题

程序员文章站 2022-05-29 18:55:36
...

系统需要优化,在日志中加入MDC全局id等信息,便于根据id快速定位问题

1.先注册个拦截器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 *注册 实现日志拦截器
 *
 * @Author YS.MAO
 * @date 2020/11/19 14:22
 * @Version 1.0
 */
@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {
    @Autowired
    private LogInterceptor logInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(logInterceptor);
        WebMvcConfigurer.super.addInterceptors(registry);
    }
}

2.定义个拦截器

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
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;

/**
 * 日志自定义拦截类,将日志操作加入全局mdc
 *
 * @Author YS.MAO
 * @date 2020/11/19 14:16
 * @Version 1.0
 */
@Component
public class LogInterceptor implements HandlerInterceptor {
    private static final Logger LOGGER = LoggerFactory.getLogger(LogInterceptor.class);
    /**
     * 设备
     */
    private static final String USER_AGENT = "user-agent";
    /**
     * 位置定义
     */
    private static final String UNKNOWN = "unknown";
    /**
     * 本地ip
     */
    private static final String LOCAL_IP = "0:0:0:0:0:0:0:1";
    /**
     * 全局mdc
     */
    private static final String TRACE_ID = "traceId";

    /**
     * 全局ip
     */
    private static final String IP = "ip";

    /**
     * 全局url
     */
    private static final String URL = "url";


    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                             Object object) throws Exception {
        // 定义一个全局的tranceId 使用32为uuid+随机6位数字
        String traceId =
                UUID.randomUUID().toString().replaceAll("-", "") + "-" + ((int)((Math.random() * 9 + 1) * 100000));
        // 放入日志全局id
        MDC.put(TRACE_ID, traceId);
        // 放入ip
        MDC.put(IP, getIpAddr(httpServletRequest));
//        LOGGER.info("put traceId ({}) to logger", traceId);
        // 获取url地址
        String url = httpServletRequest.getRequestURI();
        // 放入设备
        MDC.put(URL,url);
        // 日志打印 地址--设备--访问接口和访问ip
//        LOGGER.info(url);
//        LOGGER.info("UserAgent: {}", httpServletRequest.getHeader(USER_AGENT));
        LOGGER.info("get request interface: {}, get ip address: {}, UserAgent: {}", url, getIpAddr(httpServletRequest),httpServletRequest.getHeader(USER_AGENT));
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                           Object object, ModelAndView modelAndView) throws Exception {
        //执行成功,获取日志全局id
//        String traceId = MDC.get(TRACE_ID);
//        LOGGER.info("remove traceId ({}) from logger", traceId);
        //删除当前操作全局id
        MDC.remove(TRACE_ID);
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                                Object object, Exception e) throws Exception {
        // 自定义
    }

    /**
     * 根据request请求获取ip
     *
     * @param request
     *            请求
     * @return ip 获取的ip
     */
    private String getIpAddr(HttpServletRequest request) {
        // 获取请求头信息
        String ip = request.getHeader("x-forwarded-for");
//        LOGGER.info("get all ip:" + ip);
        // 取不到值 执行不同请求头信息
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 如果本地调用 默认返回 127.0.0.1
        if (LOCAL_IP.equals(ip)) {
            ip = "127.0.0.1";
        }
        // 如果是多级代理,取第一个ip为客户真实ip
        if (ip != null && ip.split(",").length > 1) {
            ip = ip.split(",")[0];
        }
        return ip;
    }

}

拦截器这里可以在定义util类将里面的方法抽出来

3.日志文件增加配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
	<contextName>logback</contextName>
	<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 -->
	<property name="LOG_HOME" value="/opt/logs/bank-common/sys-common" />
	<!-- 控制台输出 -->
	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<!--格式化输出:%d表示日期,%X{traceId}表示mdc全局id,%ip 表示ip , %thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}] [%X{ip}] [%X{url}] [%thread] %-5level %logger{50} - %msg%n</pattern>
		</encoder>
	</appender>

	<!-- 按照每天生成日志文件 -->
	<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
			<!--日志文件输出的文件名 -->
			<FileNamePattern>${LOG_HOME}/console.log.%d{yyyy-MM-dd}.%i.log
			</FileNamePattern>
			<maxFileSize>50MB</maxFileSize>
			<maxHistory>30</maxHistory>
		</rollingPolicy>
		<!--&lt;!&ndash;过滤 INFO&ndash;&gt;-->
		<!--<level>INFO</level>-->
		<!--&lt;!&ndash;匹配到就禁止&ndash;&gt;-->
		<!--<onMatch>ACCEPT</onMatch>-->
		<!--&lt;!&ndash;没有匹配到就允许&ndash;&gt;-->
		<!--<onMismatch>DENY</onMismatch>-->
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<!--格式化输出:%d表示日期,%X{traceId}表示mdc全局id,%ip 表示ip , %thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}] [%X{ip}] [%X{url}] [%thread] %-5level %logger{50} - %msg%n</pattern>
		</encoder>
	</appender>


	<!-- 日志输出级别 -->
	<root level="info">
		<appender-ref ref="CONSOLE" />
		<appender-ref ref="FILE" />
	</root>
</configuration>