log4j MDC实现日志追踪
介绍:
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> <appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender"> <syslogHost>127.0.0.1</syslogHost> <facility>local6</facility> <port>514</port> <suffixPattern>${FILE_LOG_PATTERN}</suffixPattern> </appender> <logger name="druid.sql" level="DEBUG" /> <root level="INFO"> <appender-ref ref="CONSOLE"/> <appender-ref ref="FILE"/> <appender-ref ref="SYSLOG"/> </root> </configuration>
相关推荐
零 2020-09-18
choupiaoyi 2020-07-28
Lzs 2020-10-23
聚合室 2020-11-16
Justhavefun 2020-10-22
jacktangj 2020-10-14
ChaITSimpleLove 2020-10-06
Andrea0 2020-09-18
周游列国之仕子 2020-09-15
afanti 2020-09-16
88234852 2020-09-15
YClimb 2020-09-15
风雨断肠人 2020-09-04
卖口粥湛蓝的天空 2020-09-15
stulen 2020-09-15
pythonxuexi 2020-09-06
abfdada 2020-08-26