文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

使用log4j MDC实现日志追踪

2024-04-02 19:55

关注

log4j MDC实现日志追踪

MDC 中包含的可以被同一线程中执行的代码所访问内容。当前线程的子线程会继承其父线程中的 MDC 的内容。记录日志时,只需要从 MDC 中获取所需的信息即可。

作用:

使用MDC来记录日志,可以规范多开发下日志格式。

1、新建线程处理类 ThreadContext


import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

public class ThreadContext {
    
    private final static ThreadLocal<Map<String, Object>> CTX_HOLDER = new ThreadLocal<Map<String, Object>>();
    static {
        CTX_HOLDER.set(new HashMap<String, Object>());
    }
 
    
    private final static String TRACE_ID_KEY = "traceId";
    
    private final static String SESSION_KEY = "token";
    
    private final static String VISITOR_ID_KEY = "userId";
    
    private final static String VISITOR_NAME_KEY = "userName";
    
    private static final String CLIENT_IP_KEY = "clientIp";
    
    public final static void putContext(String key, Object value) {
        Map<String, Object> ctx = CTX_HOLDER.get();
        if (ctx == null) {
            return;
        }
        ctx.put(key, value);
    }
    
    @SuppressWarnings("unchecked")
    public final static <T extends Object> T getContext(String key) {
        Map<String, Object> ctx = CTX_HOLDER.get();
        if (ctx == null) {
            return null;
        }
        return (T) ctx.get(key);
    }
    
    public final static Map<String, Object> getContext() {
        Map<String, Object> ctx = CTX_HOLDER.get();
        if (ctx == null) {
            return null;
        }
        return ctx;
    }
    
    public final static void remove(String key) {
        Map<String, Object> ctx = CTX_HOLDER.get();
        if (ctx != null) {
            ctx.remove(key);
        }
    }
    
    public final static boolean contains(String key) {
        Map<String, Object> ctx = CTX_HOLDER.get();
        if (ctx != null) {
            return ctx.containsKey(key);
        }
        return false;
    }
    
    public final static void clean() {
        CTX_HOLDER.remove();
    }
    
    public final static void init() {
        CTX_HOLDER.set(new HashMap<String, Object>());
    }
    
    public final static void putTraceId(String traceId) {
        putContext(TRACE_ID_KEY, traceId);
    }
    
    public final static String getTraceId() {
        return getContext(TRACE_ID_KEY);
    }
    
    public final static void putUserId(Integer userId) {
        putContext(VISITOR_ID_KEY, userId);
    }
    
    public final static int getUserId() {
        Integer val = getContext(VISITOR_ID_KEY);
        return val == null ? 0 : val;
    }
    
    public final static void putUserName(String userName) {
        putContext(VISITOR_NAME_KEY, userName);
    }
    
    public final static String getUserName() {
        return Optional.ofNullable(getContext(VISITOR_NAME_KEY))
                .map(name -> String.valueOf(name))
                .orElse("");
    }
    
    public static final String getClientIp() {
        return getContext(CLIENT_IP_KEY);
    }
    
    public static final void putClientIp(String ip) {
        putContext(CLIENT_IP_KEY, ip);
    }
    
    public static void putSessionId(String token) {
        putContext(SESSION_KEY, token);
    }
    
    public static String getSessionId(String token) {
        return getContext(SESSION_KEY);
    }
    
    public final static void removeSessionId() {
        remove(SESSION_KEY);
    }
}

2、添加工具类TraceUtil


import java.util.UUID;
import org.slf4j.MDC;
import ThreadContext;

public class TraceUtil {
    public static void traceStart() {
        ThreadContext.init();
        String traceId = generateTraceId();
        MDC.put('traceId', traceId);
        ThreadContext.putTraceId(traceId);
    }
    public static void traceEnd() {
        MDC.clear();
        ThreadContext.clean();
    }
    
    private static String generateTraceId() {
        return UUID.randomUUID().toString();
    }
}

3、添加ContextFilter

对于每个请求随机生成RequestID并放入MDC


import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.OncePerRequestFilter;
import com.pingan.manpan.common.util.TraceUtil;
import com.pingan.manpan.user.dto.ThreadContext;
import com.pingan.manpan.web.common.surpport.IpUtils;

//@Order 标记组件的加载顺序
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ContextFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        try {
            ThreadContext.putClientIp(IpUtils.getClientIp(request));
            TraceUtil.traceStart();
            filterChain.doFilter(request, response);
        } finally {
            TraceUtil.traceEnd();
        }
    }
}

4、在webConfiguriation注册filter


    
    @Bean
    public FilterRegistrationBean requestContextRepositoryFilterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new ContextFilter());
        filterRegistrationBean.addUrlPatterns("
@WebFilter(filterName = "Log4j2Filter", urlPatterns = "/*", initParams = {@WebInitParam(name = "DESCRIPTION", value = "这是Log4j2Filter过滤器")})
public class Log4j2Filter implements Filter {
    private String description;
    public static final String TRACE_ID = "traceID";
    private static final Logger logger = LoggerFactory.getLogger(Log4j2Filter.class);
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        description = filterConfig.getInitParameter("DESCRIPTION");
        System.out.println("过滤器初始化:"+ description);
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException,ServletException {
            
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;
        
        // 生成一个随机数给到前端
        String traceId = UUID.randomUUID().toString().replace("-", "");
        // 随机数放到此线程的上下文中,可以在每条日志前加入。具体看下面log4j2.xml
        ThreadContext.put(TRACE_ID, traceId);
        // 随机数放到Header中,在Response Headers中可查看到此数据
        resp.addHeader(TRACE_ID, traceId);  
        filterChain.doFilter(req, resp);
        ThreadContext.clearAll();
    }
    @Override
    public void destroy() {
        System.out.println("过滤器,被销毁:"+ description);
    }
}

log4j2.xml <PatternLayout pattern="[traceID:%X{traceID}]-[%d{yyyy-MM-dd HH:mm:ss:SSS}]-[%t]-[%p]-[%l]-%m%n"/>


<?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="logPath">logs</Property>
    </Properties>
    
    <!--先定义所有的appender-->
    <appenders>
        <!--这个输出控制台的配置-->
        <console name="Console" target="SYSTEM_OUT">
            <!-- traceID:就是在过滤器中生成的随机数 -->
            <PatternLayout pattern="[traceID:%X{traceID}]-[%d{yyyy-MM-dd HH:mm:ss:SSS}]-[%t]-[%p]-[%l]-%m%n"/>
        </console>
    </appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>
        <!--过滤掉spring和mybatis的一些无用的debug信息-->
        <logger name="org.springframework" level="INFO"></logger>
        <logger name="org.mybatis" level="INFO"></logger>
        <logger name="com.zaxxer" level="WARN"></logger>
        <!-- com.generator开发/测试环境用DEBUG,并用控制台输出即可 -->
        <logger name="com.generator" level="DEBUG" additivity="false">
            <appender-ref ref="Console"/>
        </logger>
        <!-- 未指定的包都按此 level 打印日志 -->
        <root level="DEBUG">
            <appender-ref ref="Console"/>
        </root>
    </loggers>
</configuration>

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯