欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用log4j MDC實(shí)現(xiàn)日志追蹤

 更新時(shí)間:2021年09月23日 12:46:02   作者:houshiqun689  
這篇文章主要介紹了使用log4j MDC實(shí)現(xiàn)日志追蹤方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

log4j MDC實(shí)現(xiàn)日志追蹤

MDC 中包含的可以被同一線程中執(zhí)行的代碼所訪問(wèn)內(nèi)容。當(dāng)前線程的子線程會(huì)繼承其父線程中的 MDC 的內(nèi)容。記錄日志時(shí),只需要從 MDC 中獲取所需的信息即可。

作用:

使用MDC來(lái)記錄日志,可以規(guī)范多開(kāi)發(fā)下日志格式。

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<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";
    /**
     * 會(huì)話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";
    /**
     * 添加內(nèi)容到線程上下文中
     *
     * @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);
    }
    /**
     * 從線程上下文中獲取內(nèi)容
     *
     * @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>());
    }
    /**
     * 設(shè)置traceID數(shù)據(jù)
     */
    public final static void putTraceId(String traceId) {
        putContext(TRACE_ID_KEY, traceId);
    }
    /**
     * 獲取traceID數(shù)據(jù)
     */
    public final static String getTraceId() {
        return getContext(TRACE_ID_KEY);
    }
    /**
     * 設(shè)置會(huì)話的用戶ID
     */
    public final static void putUserId(Integer userId) {
        putContext(VISITOR_ID_KEY, userId);
    }
    /**
     * 設(shè)置會(huì)話的用戶ID
     */
    public final static int getUserId() {
        Integer val = getContext(VISITOR_ID_KEY);
        return val == null ? 0 : val;
    }
    /**
     * 設(shè)置會(huì)話的用戶名
     */
    public final static void putUserName(String userName) {
        putContext(VISITOR_NAME_KEY, userName);
    }
    /**
     * 獲取會(huì)話的用戶名稱
     */
    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);
    }
    /**
     * 設(shè)置IP
     *
     * @param ip
     */
    public static final void putClientIp(String ip) {
        putContext(CLIENT_IP_KEY, ip);
    }
    /**
     * 設(shè)置會(huì)話ID
     *
     * @param token
     */
    public static void putSessionId(String token) {
        putContext(SESSION_KEY, token);
    }
    /**
     * 獲取會(huì)話ID
     *
     * @param token
     */
    public static String getSessionId(String token) {
        return getContext(SESSION_KEY);
    }
    /**
     * 清空會(huì)話數(shù)據(jù)
     */
    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

對(duì)于每個(gè)請(qǐng)求隨機(jī)生成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 標(biāo)記組件的加載順序
@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注冊(cè)filter

    /**
     * 請(qǐng)求上下文,應(yīng)該在最外層
     *
     * @return
     */
    @Bean
    public FilterRegistrationBean requestContextRepositoryFilterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new ContextFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        return filterRegistrationBean;
    }

5、修改log4j日志配置文件,設(shè)置日志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>

log4j2實(shí)現(xiàn)日志跟蹤

日志跟蹤

在每條日志前添加一個(gè)隨機(jī)字符串并且確保同一個(gè)請(qǐng)求的字符串相同。如下:c6019df137174d2b98631474db4156b7為此次請(qǐng)求的標(biāo)識(shí)。通過(guò)次標(biāo)識(shí)可以查詢到所有該請(qǐng)求的日志信息

[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]-

同時(shí)可以將此標(biāo)識(shí)返回給前端,便于問(wèn)題查詢。traceID: c6019df137174d2b98631474db4156b7

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://test.page.qingin.cn
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

我們可以通過(guò)過(guò)濾器實(shí)現(xiàn)以上的功能

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 對(duì)用戶的請(qǐng)求添加日志編號(hào),并將此編號(hào)返回給前端,便于日志查詢
 */
@WebFilter(filterName = "Log4j2Filter", urlPatterns = "/*", initParams = {@WebInitParam(name = "DESCRIPTION", value = "這是Log4j2Filter過(guò)濾器")})
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("過(guò)濾器初始化:"+ description);
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException,ServletException {
            
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;
        
        // 生成一個(gè)隨機(jī)數(shù)給到前端
        String traceId = UUID.randomUUID().toString().replace("-", "");
        // 隨機(jī)數(shù)放到此線程的上下文中,可以在每條日志前加入。具體看下面log4j2.xml
        ThreadContext.put(TRACE_ID, traceId);
        // 隨機(jī)數(shù)放到Header中,在Response Headers中可查看到此數(shù)據(jù)
        resp.addHeader(TRACE_ID, traceId);  
        filterChain.doFilter(req, resp);
        ThreadContext.clearAll();
    }
    @Override
    public void destroy() {
        System.out.println("過(guò)濾器,被銷毀:"+ 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"?>
<!--設(shè)置log4j2的自身log級(jí)別為warn-->
<!--日志級(jí)別以及優(yōu)先級(jí)排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,這個(gè)用于設(shè)置log4j2自身內(nèi)部的信息輸出,可以不設(shè)置,當(dāng)設(shè)置成trace時(shí),你會(huì)看到log4j2內(nèi)部各種詳細(xì)輸出-->
<!--monitorInterval:Log4j能夠自動(dòng)檢測(cè)修改配置 文件和重新配置本身,設(shè)置間隔秒數(shù)-->
<configuration status="warn" monitorInterval="30">
    
    <!--全局參數(shù)-->
    <Properties>
        <Property name="logPath">logs</Property>
    </Properties>
    
    <!--先定義所有的appender-->
    <appenders>
        <!--這個(gè)輸出控制臺(tái)的配置-->
        <console name="Console" target="SYSTEM_OUT">
            <!-- traceID:就是在過(guò)濾器中生成的隨機(jī)數(shù) -->
            <PatternLayout pattern="[traceID:%X{traceID}]-[%d{yyyy-MM-dd HH:mm:ss:SSS}]-[%t]-[%p]-[%l]-%m%n"/>
        </console>
    </appenders>
    <!--然后定義logger,只有定義了logger并引入的appender,appender才會(huì)生效-->
    <loggers>
        <!--過(guò)濾掉spring和mybatis的一些無(wú)用的debug信息-->
        <logger name="org.springframework" level="INFO"></logger>
        <logger name="org.mybatis" level="INFO"></logger>
        <logger name="com.zaxxer" level="WARN"></logger>
        <!-- com.generator開(kāi)發(fā)/測(cè)試環(huán)境用DEBUG,并用控制臺(tái)輸出即可 -->
        <logger name="com.generator" level="DEBUG" additivity="false">
            <appender-ref ref="Console"/>
        </logger>
        <!-- 未指定的包都按此 level 打印日志 -->
        <root level="DEBUG">
            <appender-ref ref="Console"/>
        </root>
    </loggers>
</configuration>

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • java爬蟲(chóng)Jsoup主要類及功能使用詳解

    java爬蟲(chóng)Jsoup主要類及功能使用詳解

    這篇文章主要為大家介紹了java爬蟲(chóng)Jsoup主要類及功能使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • java中實(shí)體類和JSON對(duì)象之間相互轉(zhuǎn)化

    java中實(shí)體類和JSON對(duì)象之間相互轉(zhuǎn)化

    Java中關(guān)于Json格式轉(zhuǎn)化Object,Map,Collection類型和String類型之間的轉(zhuǎn)化在我們實(shí)際項(xiàng)目中應(yīng)用的很是普遍和廣泛。最近工作的過(guò)程中也是經(jīng)常有,因此,自己封裝了一個(gè)類分享給大家。
    2015-05-05
  • Java從ftp服務(wù)器上傳與下載文件的實(shí)現(xiàn)

    Java從ftp服務(wù)器上傳與下載文件的實(shí)現(xiàn)

    這篇文章主要給大家介紹了關(guān)于Java從ftp服務(wù)器上傳與下載文件的實(shí)現(xiàn)方法,最近項(xiàng)目中需要實(shí)現(xiàn)將文件先存放到ftp上,需要的時(shí)候再?gòu)膄tp上下載,做的過(guò)程中碰到了問(wèn)題,所以這里總結(jié)下,需要的朋友可以參考下
    2023-08-08
  • Cookie在Java中的使用

    Cookie在Java中的使用

    Cookie又稱“小甜餅”,類型為“小型文本文件”,指某些網(wǎng)站為了辨別用戶身份而儲(chǔ)存在用戶本地終端(Client Side)上的數(shù)據(jù)(通常經(jīng)過(guò)加密)。由用戶客戶端計(jì)算機(jī)暫時(shí)或永久保存的信息。本文將講解Cookie在Java中的使用,感興趣的朋友可以了解下
    2021-05-05
  • java中this的用法示例(關(guān)鍵字this)

    java中this的用法示例(關(guān)鍵字this)

    這篇文章主要介紹了java中this的用法示例(關(guān)鍵字this),需要的朋友可以參考下
    2014-03-03
  • java socket接收保證能讀完數(shù)據(jù)的實(shí)例

    java socket接收保證能讀完數(shù)據(jù)的實(shí)例

    這篇文章主要介紹了java socket接收保證能讀完數(shù)據(jù)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java開(kāi)發(fā)深入分析講解二叉樹(shù)的遞歸和非遞歸遍歷方法

    Java開(kāi)發(fā)深入分析講解二叉樹(shù)的遞歸和非遞歸遍歷方法

    樹(shù)是一種重要的非線性數(shù)據(jù)結(jié)構(gòu),直觀地看,它是數(shù)據(jù)元素(在樹(shù)中稱為結(jié)點(diǎn))按分支關(guān)系組織起來(lái)的結(jié)構(gòu),很象自然界中的樹(shù)那樣。樹(shù)結(jié)構(gòu)在客觀世界中廣泛存在,如人類社會(huì)的族譜和各種社會(huì)組織機(jī)構(gòu)都可用樹(shù)形象表示,本篇介紹二叉樹(shù)的遞歸與非遞歸遍歷的方法
    2022-05-05
  • springBoot使用openfeign來(lái)遠(yuǎn)程調(diào)用的實(shí)現(xiàn)

    springBoot使用openfeign來(lái)遠(yuǎn)程調(diào)用的實(shí)現(xiàn)

    這篇文章主要介紹了springBoot使用openfeign來(lái)遠(yuǎn)程調(diào)用的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • IDEA項(xiàng)目如何取消git版本管控并添加svn版本控制

    IDEA項(xiàng)目如何取消git版本管控并添加svn版本控制

    在公司內(nèi)部服務(wù)器環(huán)境下,將代碼倉(cāng)庫(kù)從Gitee的Git遷移到SVN可以避免外部版本控制的風(fēng)險(xiǎn),遷移過(guò)程中,先刪除項(xiàng)目的.git文件夾,再通過(guò)Eclipse的設(shè)置界面刪除原Git配置并添加SVN配置,之后,將項(xiàng)目提交到SVN倉(cāng)庫(kù),確保使用ignore列表過(guò)濾不必要的文件
    2024-10-10
  • Java實(shí)現(xiàn)動(dòng)態(tài)生成GIF圖像詳解

    Java實(shí)現(xiàn)動(dòng)態(tài)生成GIF圖像詳解

    在互聯(lián)網(wǎng)上有許多有趣的場(chǎng)景,其中的一種就是動(dòng)圖。這不是視頻,而是一種GIF圖像信息。本文將利用Java實(shí)現(xiàn)動(dòng)態(tài)生成GIF圖像功能,需要的可以參考一下
    2022-09-09

最新評(píng)論