Java は完全なリンク ログ追跡の一意の ID を実現します

Java は完全なリンク ログ追跡の一意の ID を実現します

ログの問題点:
Spring-Aop アスペクトを使用すると、コントロール層またはサービス層の開始位置と終了位置のデータ (つまり、リクエストの入出力パラメーター) のみを切り取ることができ、論理ログを見つけることができません。そして追跡された

ログを印刷するとこんな感じ

1.パラメータにseqが渡されない場合

LOGGER.error("xxx不能为空" );

2.パラメータにseqが渡されています

LOGGER.error("【" + seq + "】,xxx不能为空" );

1 つ目はより簡潔ですが、2 つ目はビジネス ロジックに侵入するため、毎回ステッチする必要があります。

解決策:
1. 単純な構成 (非同期スレッドにはいくつかの問題が発生します。log4j ログ)
1) これらの構成はプリインターセプターに配置でき、制御層は到着するとすぐに割り当てられます。

//前置拦截器
String logUid = UUID.randomUUID().toString();
//org.apache.logging.log4j.ThreadContext
ThreadContext.put("logId", logId);

//后置拦截器
//在请求结束时需要清理logId
ThreadContext.clearMap();

2) ログ印刷キー [logId::%X{logId}]
XML 設定バージョン

 <console name="Console" target="SYSTEM_OUT">
            <!--输出日志的格式-->
            <PatternLayout pattern="[logId::%X{logId}][%d{yyyy-MM-dd HH:mm:ss.SSS}] [%p] - %l - %m%n"/>
            <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY" />
</console>

スプリングブートバージョン

logging:
  pattern:
    #配置日志全链路跟踪 logId
    console: "[logId::%X{logId}] [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%p] - %l - %m%n"


2. 非同期スレッド (ログバック ログ) のキーポイントを含むリンク全体を追跡します
1). MDC (org.slf4j.MDC)
2). インターセプター (主に logId を挿入)
3). スレッド処理

一意の logId を生成するインターセプター コード

@Component
public class LogInterceptor extends HandlerInterceptorAdapter {
    
    

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        //如果有上层调用就用上层的ID
        String traceId = request.getHeader(LogConstant.TRACE_ID);
        if (traceId == null) {
    
    
            traceId = TraceIdUtil.getTraceId();
        }

        MDC.put(LogConstant.TRACE_ID, traceId);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
    

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
    
        //调用结束后删除
        MDC.remove(LogConstant.TRACE_ID);
    }
}

ログリンク ID ツールクラスの取得 (UUID を使用してシリアル番号を生成)

public class TraceIdUtil {
    
    

    private TraceIdUtil() {
    
    
        throw new UnsupportedOperationException("Utility class");
    }

    /**
     * 获取traceId
     * @return
     */
    public static String getTraceId() {
    
    
        return UUID.randomUUID().toString().replace("-", "").toUpperCase();
    }


}

ログ定数 (定数であり、新しいものではありません。実際には、インターフェイス クラスによって定義できます)

public class LogConstant {
    
    
    private LogConstant(){
    
    
        throw new UnsupportedOperationException();
    }

    /**
     * 日志追踪ID
     */
    public static final String TRACE_ID = "traceId";
}

mdcスレッドプロセッサ

public class ThreadMdcUtil {
    
    

    public static void setTraceIdIfAbsent() {
    
    
        if (MDC.get(LogConstant.TRACE_ID) == null) {
    
    
            //插入唯一日志ID
            MDC.put(LogConstant.TRACE_ID, TraceIdUtil.getTraceId());
        }
    }

    public static <T> Callable<T> wrap(final Callable<T> callable, final Map<String, String> context) {
    
    
        return () -> {
    
    
            if (context == null) {
    
    
                MDC.clear();
            } else {
    
    
                MDC.setContextMap(context);
            }
            setTraceIdIfAbsent();
            try {
    
    
                return callable.call();
            } finally {
    
    
                MDC.clear();
            }
        };
    }

    public static Runnable wrap(final Runnable runnable, final Map<String, String> context) {
    
    
        return () -> {
    
    
            if (context == null) {
    
    
                MDC.clear();
            } else {
    
    
                MDC.setContextMap(context);
            }
            setTraceIdIfAbsent();
            try {
    
    
                runnable.run();
            } finally {
    
    
                MDC.clear();
            }
        };
    }

}

スレッド構成クラス

@Slf4j
@Configuration
public class ExecutorConfig {
    
    

    @Bean
    @Primary
    public Executor asyncServiceExecutor() {
    
    
        log.info("start asyncServiceExecutor");
        ThreadPoolExecutorMdcWrapper executor = new ThreadPoolExecutorMdcWrapper();
        //配置核心线程数
        executor.setCorePoolSize(10);
        //配置最大线程数
        executor.setMaxPoolSize(200);
        //配置队列大小
        executor.setQueueCapacity(99999);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix("async-service-");

        // 设置拒绝策略:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }


}

スレッドプールの構成

@Slf4j
public class ThreadPoolExecutorMdcWrapper extends ThreadPoolTaskExecutor {
    
    

    private void showThreadPoolInfo(String prefix){
    
    
        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

        if(null==threadPoolExecutor){
    
    
            return;
        }

        log.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                this.getThreadNamePrefix(),
                prefix,
                threadPoolExecutor.getTaskCount(),
                threadPoolExecutor.getCompletedTaskCount(),
                threadPoolExecutor.getActiveCount(),
                threadPoolExecutor.getQueue().size());
    }

    @Override
    public void execute(Runnable task) {
    
    
        showThreadPoolInfo("1. do execute");
        super.execute(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));

    }

    @Override
    public void execute(Runnable task, long startTimeout) {
    
    
        showThreadPoolInfo("2. do execute");
        super.execute(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()), startTimeout);

    }

    @Override
    public Future<?> submit(Runnable task) {
    
    
        showThreadPoolInfo("1. do submit");
        return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
    
    
        showThreadPoolInfo("2. do submit");
        return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
    }

    @Override
    public ListenableFuture<?> submitListenable(Runnable task) {
    
    
        showThreadPoolInfo("1. do submitListenable");
        return super.submitListenable(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
    }

    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
    
    
        showThreadPoolInfo("2. do submitListenable");
        return super.submitListenable(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
    }

}

インターセプター

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
    
    

	@Autowired
	private PreventRepeatSubmitInterceptor preventRepeatSubmitInterceptor;

	@Autowired
	private LogInterceptor logInterceptor;
	
    @Override
    public void addCorsMappings(CorsRegistry registry) {
    
    
        //设置允许跨域的路径
    	registry.addMapping("/**") //映射地址
		.allowedOrigins("*")//允许跨域地址
		.allowedHeaders("*")
		.allowCredentials(true)
	    .allowedMethods("GET", "POST")
	    .maxAge(3600);
    }


	@Override
	public void addInterceptors(InterceptorRegistry registry) {
    
    
    	//.excludePathPatterns("/wechatwork/**")  .addPathPatterns("/order/**")
		//防重复提交拦截器
		registry.addInterceptor(preventRepeatSubmitInterceptor);
		//日志拦截器
		registry.addInterceptor(logInterceptor);//.addPathPatterns("/**");
	}



}

最終効果
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/HX0326CSDN/article/details/121281618