使用log4j MDC实现日志追踪

网友投稿 1449 2022-12-06

使用log4j MDC实现日志追踪

使用log4j MDC实现日志追踪

目录log4j MDC实现日志追踪1、新建线程处理类 ThreadContext2、添加工具类TraceUtil3、添加ContextFilter4、在webConfiguriation注册filter5、修改log4j日志配置文件,设置日志traceIdlog4j2实现日志跟踪日志跟踪我们可以通过过滤器实现以上的功能

log4j MDC实现日志追踪

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

作用:

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

1、新建线程处理类 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> CTX_HOLDER = new ThreadLocal>();

static {

CTX_HOLDER.set(new HashMap());

}

/**

* 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 ctx = CTX_HOLDER.get();

if (ctx == null) {

return;

}

ctx.put(key, value);

}

/**

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

*

* @param key

*/

@SuppressWarnings("unchecked")

public final static T getContext(String key) {

Map ctx = CTX_HOLDER.get();

if (ctx == null) {

return null;

}

return (T) ctx.get(key);

}

/**

* 获取线程上下文

*/

public final static Map getContext() {

Map ctx = CTX_HOLDER.get();

if (ctx == null) {

return null;

}

return ctx;

}

/**

* 删除上下文中的key

*

* @param key

*/

public final static void remove(String key) {

Map ctx = CTX_HOLDER.get();

if (ctx != null) {

ctx.remove(key);

}

}

/**

* 上下文中是否包含此key

*

* @param key

* @return

*/

public final static boolean contains(String key) {

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

}

/**

* 设置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);

}

}

2、添加工具类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();

}

}

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;

/**

* 上下文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();

}

}

}

4、在webConfiguriation注册filter

/**

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

*

* @return

*/

@Bean

public FilterRegistrationBean requestContextRepositoryFilterRegistrationBean() {

http:// FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();

filterRegistrationBean.setFilter(new ContextFilter());

filterRegistrationBean.addUrlPatterns("/*");

return filterRegistrationBean;

}

5、修改log4j日志配置文件,设置日志traceId

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

${FILE_LOG_PATTERN}

${LOG_FILE}${LOG_FILE_SUFFIX}

${LOG_FILE}.%d{yyyy-MM-dd}${LOG_FILE_SUFFIX}

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

${FILE_LOG_PATTERN}

${LOG_FILE}${LOG_FILE_SUFFIX}

${LOG_FILE}.%d{yyyy-MM-dd}${LOG_FILE_SUFFIX}

127.0.0.1

local6

514

${FILE_LOG_PATTERN}

log4j2实现日志跟踪

日志跟踪

在每条日志前添加一个随机字符串并且确保同一个请求的字符串相同。如下:c6019df137174d2b98631474db4156b7为此次请求的标识。通过次标识可以查询到所有该请求的日志信息

[traceID:c6019df137174d2b98631474db4156b7]-[2020-08-11 19:56:58:204]-[http-nio-8803-exec-4]-

[traceID:c6019df137174d2b98631474db4156b7]-[2020-08-11 19:56:58:204]-[http-nio-8803-exec-4]-

[traceID:c6019df137174d2b98631474db4156b7]-[2020-08-11 19:56:58:205]-[http-nio-8803-exec-4]-

[traceID:c6019df137174d2b98631474db4156b7]-[2020-08-11 19:56:58:205]-[http-nio-8803-exec-4]-

[traceID:c6019df137174d2b98631474db4156b7]-[2020-08-11 19:56:58:209]-[http-nio-8803-exec-4]-

[traceID:c6019df137174d2b98631474db4156b7]-[2020-08-11 19:56:58:214]-[http-nio-8803-exec-4]-

[traceID:c6019df137174d2b98631474db4156b7]-[2020-08-11 19:56:58:223]-[http-nio-8803-exec-4]-

[traceID:c6019df137174d2b98631474db4156b7]-[2020-08-11 19:56:58:224]-[http-nio-8803-exec-4]-

同时可以将此标识返回给前端,便于问题查询。traceID: c6019df137174d2b98631474db4156b7

Access-Control-Allow-Credentials: true

Access-Control-Allow-Origin: http://test.page.qingin-

Cache-Control: max-age=30

Connection: keep-alive

Content-Type: application/json;charset=UTF-8

Date: Tue, 11 Aug 2020 12:02:19 GMT

Expires: Tue, 11 Aug 2020 12:02:49 GMT

Server: nginx/1.16.1

traceID: c6019df137174d2b98631474db4156b7

Transfer-Encoding: chunked

Vary: Origin

Vary: Access-Control-Request-Method

Vary: Access-Control-Request-Headers

我们可以通过过滤器实现以上的功能

Log4j2Filter.java

package com.generator.admin.filter;

import org.apache.logging.log4j.ThreadContext;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import javax.servlet.*;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.io.IOException;

import java.util.UUID;

/**

* @calssName Log4j2Filter

* @Description 对用户的请求添加日志编号,并将此编号返回给前端,便于日志查询

*/

@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

logs

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Servlet方法生命周期及执行原理详解
下一篇:SpringBoot整合Mybatis自定义拦截器不起作用的处理方案
相关文章

 发表评论

暂时没有评论,来抢沙发吧~