阿里TTl使用管理日志

在管理日志的时候我们需要查看生成日志都是那些人干了那些事,那么怎么在日志上查看这些事情呢,首先呢可以直接使用Slf4j,然后再配置文件里配置一下

#日志文件最大上限
logging.file.max-size=100MB
#日志文件存储位置
logging.file.path=./logs
#日志文件输出格式	(5level:线程等级,traceID:线程名,PIDL:当前线程ID, logger{50}:日志名称最多50字符 ,%m:日志内容 ,%n:换行)
logging.pattern.console= "%d{yyyy-MM-dd HH:mm:ss.SSS} %clr(%-5level) %clr([%X{traceId}]) %clr(${PID:-}) --- %clr(%logger{50}) - %m%n"

这里我们的线程名可以使用用户信息里具有唯一标识的来标记,我们直接在过滤器里获取用户信息

package com.sunflower.filter;

import cn.hutool.core.util.IdUtil;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Order(1)
@WebFilter(urlPatterns = "/*",filterName = "traceIdFilter")
public class ThreadFilter implements Filter {

    public final static String MDC_TRACE_ID = "traceId";

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String traceId = httpRequest.getHeader(MDC_TRACE_ID);
        if (StringUtils.isEmpty(traceId)) {
            traceId = IdUtil.fastSimpleUUID();;
        }
        MDC.put(MDC_TRACE_ID, traceId);

        chain.doFilter(request, response);
    }
}

配置完成后输出端日志就是这样的
在这里插入图片描述
但是这样只能在同一个日志里操作,如果开启一个子线程那么子线程就没法获取到thradId 因为Slf4j默认只用的是Threadlocal线程,普通 ThreadLocal 类允许每个线程拥有自己的独立变量副本,这些变量只对该线程可见,且不会影响其他线程。然而,当使用 InheritableThreadLocal 时,如果一个线程创建了另一个线程(例如,在调用 Thread.start() 方法时),父线程中 InheritableThreadLocal 绑定的值会被自动复制(或通过 childValue 方法进行转换)给新创建的子线程。
这意味着,如果你在一个线程中设置了一个 InheritableThreadLocal 的值,那么这个值不仅在这个线程中可用,而且在任何由这个线程派生出来的子线程中也将是初始的有效值。这对于需要跨线程层级传递特定上下文信息的场景非常有用。
在内部实现上,InheritableThreadLocal 同样依赖于 ThreadLocalMap 结构来为每个线程存储本地变量的值,不过在子线程初始化时,会额外处理父线程的 InheritableThreadLocal 值的传递。
但是当你new一个新线程后还是一样子线程没有内容,那怎么办呢
在这里插入图片描述
这时我们就用到了阿里提供的TTL(TransmittableThreadLoca)
引入依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.11.5</version>
    <scope>compile</scope>
</dependency>

重写(LogbackMDCAdapter)类(必须写在org.slf4j包下)

在这里插入图片描述

package org.slf4j;
 
import ch.qos.logback.classic.util.LogbackMDCAdapter;
import org.slf4j.spi.MDCAdapter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
 
/**
 * 描述:
 * 重写{@link LogbackMDCAdapter}类,搭配TransmittableThreadLocal实现父子线程之间的数据传递
 * 内容直接从{@link LogbackMDCAdapter}类中copy。把copyOnThreadLocal实例化对象更改为TransmittableThreadLocal即可
 */
public class TtlMDCAdapter implements MDCAdapter {
    final ThreadLocal<Map<String, String>> copyOnThreadLocal = new InheritableThreadLocal();
    private static final int WRITE_OPERATION = 1;
    private static final int MAP_COPY_OPERATION = 2;
    final ThreadLocal<Integer> lastOperation = new ThreadLocal();
    private static TtlMDCAdapter mtcMDCAdapter;
    static {
        mtcMDCAdapter = new TtlMDCAdapter();
        MDC.mdcAdapter = mtcMDCAdapter;
    }
 
    public TtlMDCAdapter() {
    }
    public static MDCAdapter getInstance() {
        return mtcMDCAdapter;
    }
 
    private Integer getAndSetLastOperation(int op) {
        Integer lastOp = (Integer)this.lastOperation.get();
        this.lastOperation.set(op);
        return lastOp;
    }
 
    private boolean wasLastOpReadOrNull(Integer lastOp) {
        return lastOp == null || lastOp == MAP_COPY_OPERATION;
    }
 
    private Map<String, String> duplicateAndInsertNewMap(Map<String, String> oldMap) {
        Map<String, String> newMap = Collections.synchronizedMap(new HashMap());
        if (oldMap != null) {
            synchronized(oldMap) {
                newMap.putAll(oldMap);
            }
        }
 
        this.copyOnThreadLocal.set(newMap);
        return newMap;
    }
 
    public void put(String key, String val) throws IllegalArgumentException {
        if (key == null) {
            throw new IllegalArgumentException("key cannot be null");
        } else {
            Map<String, String> oldMap = (Map)this.copyOnThreadLocal.get();
            Integer lastOp = this.getAndSetLastOperation(WRITE_OPERATION);
            if (!this.wasLastOpReadOrNull(lastOp) && oldMap != null) {
                oldMap.put(key, val);
            } else {
                Map<String, String> newMap = this.duplicateAndInsertNewMap(oldMap);
                newMap.put(key, val);
            }
 
        }
    }
 
    public void remove(String key) {
        if (key != null) {
            Map<String, String> oldMap = (Map)this.copyOnThreadLocal.get();
            if (oldMap != null) {
                Integer lastOp = this.getAndSetLastOperation(WRITE_OPERATION);
                if (this.wasLastOpReadOrNull(lastOp)) {
                    Map<String, String> newMap = this.duplicateAndInsertNewMap(oldMap);
                    newMap.remove(key);
                } else {
                    oldMap.remove(key);
                }
 
            }
        }
    }
 
    public void clear() {
        this.lastOperation.set(WRITE_OPERATION);
        this.copyOnThreadLocal.remove();
    }
 
    public String get(String key) {
        Map<String, String> map = (Map)this.copyOnThreadLocal.get();
        return map != null && key != null ? (String)map.get(key) : null;
    }
 
    public Map<String, String> getPropertyMap() {
        this.lastOperation.set(MAP_COPY_OPERATION);
        return (Map)this.copyOnThreadLocal.get();
    }
 
    public Set<String> getKeys() {
        Map<String, String> map = this.getPropertyMap();
        return map != null ? map.keySet() : null;
    }
 
    public Map<String, String> getCopyOfContextMap() {
        Map<String, String> hashMap = copyOnThreadLocal.get();
        return hashMap == null ? null : new HashMap(hashMap);
    }
 
    public void setContextMap(Map<String, String> contextMap) {
        this.lastOperation.set(WRITE_OPERATION);
        Map<String, String> newMap = Collections.synchronizedMap(new HashMap());
        newMap.putAll(contextMap);
        this.copyOnThreadLocal.set(newMap);
    }
}

更改spring.factories
在resource/META-INF/spring.factories中进行配置。

org.springframework.context.ApplicationContextInitializer=\
com.jarvan.demo.config.TtlMDCAdapterInitializer

TtlMDCAdapterInitializer类如下

package com.by.config;
 
import org.slf4j.TtlMDCAdapter;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
 
public class TtlMDCAdapterInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        TtlMDCAdapter.getInstance();
    }
}

结果展示
在这里插入图片描述

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值