JAVA编程技巧之 MDC实现日志追踪
白羽 2018-07-09 来源 :网络 阅读 122 评论 0

摘要:本文将带你了解JAVA编程技巧之 MDC实现日志追踪,希望本文对大家学JAVA有所帮助。


MDC 中包含的可以被同一线程中执行的代码所访问内容。当前线程的子线程会继承其父线程中的 MDC 的内容。记录日志时,只需要从 MDC 中获取所需的信息即可。
作用:
使用MDC来记录日志,可以规范多开发下日志格式。

一:新建线程处理类 ThreadContext

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

/**

 * 线程上下文

 *

 * @date 2017年3月1日

 * @since 1.0.0

 */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>());

    }

 

 

    /**

     * traceID

     */

    private final static String TRACE_ID_KEY = "traceId";

 

    /**

     * 会话ID

     */

    private final static String SESSION_KEY = "token";

 

    /**

     * 操作用户ID

     */

    private final static String VISITOR_ID_KEY = "userId";

    /**

     * 操作用户名

     */

    private final static String VISITOR_NAME_KEY = "userName";

 

    /**

     * 客户端IP

     */

    private static final String CLIENT_IP_KEY = "clientIp";

 

    /**

     * 添加内容到线程上下文中

     *

     * @param key

     * @param value

     */

    public final static void putContext(String key, Object value) {

        Map<String, Object> ctx = CTX_HOLDER.get();

        if (ctx == null) {

            return;

        }

        ctx.put(key, value);

    }

 

    /**

     * 从线程上下文中获取内容

     *

     * @param key

     */

    @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;

    }

 

    /**

     * 删除上下文中的key

     *

     * @param key

     */

    public final static void remove(String key) {

        Map<String, Object> ctx = CTX_HOLDER.get();

        if (ctx != null) {

            ctx.remove(key);

        }

    }

 

    /**

     * 上下文中是否包含此key

     *

     * @param key

     * @return

     */

    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>());

    }

 

 

    /**

     * 设置traceID数据

     */

    public final static void putTraceId(String traceId) {

        putContext(TRACE_ID_KEY, traceId);

    }

 

    /**

     * 获取traceID数据

     */

    public final static String getTraceId() {

        return getContext(TRACE_ID_KEY);

    }

 

    /**

     * 设置会话的用户ID

     */

    public final static void putUserId(Integer userId) {

        putContext(VISITOR_ID_KEY, userId);

    }

 

    /**

     * 设置会话的用户ID

     */

    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("");

    }

 

    /**

     * 取出IP

     *

     * @return

     */

    public static final String getClientIp() {

        return getContext(CLIENT_IP_KEY);

    }

 

    /**

     * 设置IP

     *

     * @param ip

     */

    public static final void putClientIp(String ip) {

        putContext(CLIENT_IP_KEY, ip);

    }

 

    /**

     * 设置会话ID

     *

     * @param token

     */

    public static void putSessionId(String token) {

        putContext(SESSION_KEY, token);

    }

 

    /**

     * 获取会话ID

     *

     * @param token

     */

    public static String getSessionId(String token) {

        return getContext(SESSION_KEY);

    }

 

    /**

     * 清空会话数据

     */

    public final static void removeSessionId() {

        remove(SESSION_KEY);

    }

}

二:添加工具类TraceUtil

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

/**

 * trace工具

 *

 * @date 2017年3月10日

 * @since 1.0.0

 */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();

    }

 

    /**

     * 生成跟踪ID

     *

     * @return

     */

    private static String generateTraceId() {

        return UUID.randomUUID().toString();

    }

}

三:添加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;

/**

 * 上下文Filter

 *

 * @date 2017/3/10

 * @since 1.0.0

 *///@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();

        }

    }

}

四:在webConfiguriation注册filter

    /**

     * 请求上下文,应该在最外层

     *

     * @return

     */

    @Bean

    public FilterRegistrationBean requestContextRepositoryFilterRegistrationBean() {

        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();

        filterRegistrationBean.setFilter(new ContextFilter());

        filterRegistrationBean.addUrlPatterns("/*");

        return filterRegistrationBean;

    }

五:修改log4j日志配置文件,设置日志traceId

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

 

    <jmxConfigurator/>

 

    <property name="LOG_LEVEL_PATTERN" value="%X{traceId:-} %5p"/>

    <property name="LOG_FILE" value="${LOG_PATH}/web.logx"/>

    <property name="LOG_FILE_SUFFIX" value=".logx"/>

 

    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>

 

    <appender name="FILE"

              class="ch.qos.logback.core.rolling.RollingFileAppender">

        <encoder>

            <pattern>${FILE_LOG_PATTERN}</pattern>

        </encoder>

        <file>${LOG_FILE}${LOG_FILE_SUFFIX}</file>

        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}${LOG_FILE_SUFFIX}</fileNamePattern>

        </rollingPolicy>

    </appender>

 

以上就介绍了JAVA的相关知识,希望对JAVA有兴趣的朋友有所帮助。了解更多内容,请关注职坐标编程语言JAVA频道!


本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论
X
免费获取海同IT培训资料
验证码手机号,获得海同独家IT培训资料
获取验证码
提交

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号