精华内容
下载资源
问答
  • 日志Java Webapp 从浏览器实时跟踪日志
  • Java 实现全链路日志跟踪唯一ID 日志痛点: 使用Spring-Aop切面的时候,只能切控制层或者服务层的开始位置与结束位置的数据(也就是请求出入参),对于逻辑日志无法定位跟踪 普通打印日志的时候是这样子的 1.如果参数里面...

    Java 实现全链路日志跟踪唯一ID

    日志痛点:
    使用Spring-Aop切面的时候,只能切控制层或者服务层的开始位置与结束位置的数据(也就是请求出入参),对于逻辑日志无法定位跟踪

    普通打印日志的时候是这样子的

    1.如果参数里面没有seq传递过来

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

    2.参数里面有seq传递过来

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

    第一种更简洁,第二种入侵了业务逻辑,并且每次都要拼接

    解决方案:
    1.简单的配置(异步线程会有点问题)
    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>
    

    springboot版

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

    日志常量(常量,不可以new , 其实可以使用接口类定义)

    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("/**");
    	}
    
    
    
    }
    

    最终效果
    在这里插入图片描述

    展开全文
  • 怎么准确的跟踪一次请求到底经过了哪些方法? 本文基于log4j日志提供一个解决思路(如有相同,纯属巧合,^_^)。 实现方案: 1. 首先要实现能区分每次请求的唯一标识(记作:traceId), 可以通过如下...

    背景描述:

    在用java语言开发经典的MVC服务端应用时,一个请求从controller进入,会经过N个service层,N个Dao层。怎么准确的跟踪一次请求到底经过了哪些方法? 本文基于log4j日志提供一个解决思路(如有相同,纯属巧合,^_^)。

    实现方案:

    1. 首先要实现能区分每次请求的唯一标识(记作:traceId), 可以通过如下TraceContext .ctx.getTraceId() 得到traceId。

    
    public class TraceContext {
    
    	public static ThreadLocal ctx = new InheritableThreadLocal(){ //此处用InheritableThreadLocal保证可以在子线程得到相同的traceId
    		@Override
    		protected TraceContext initialValue() {
    			return new TraceContext();
    		}
    	};
    	private String traceId;
    
    	public String getTraceId() {
    		if(traceId == null || "".equals(traceId)){
    			traceId = System.nanoTime() + "";
    		}
    		return traceId;
    	}
    }
    

    2. 在每次请求进入controller业务逻辑之前,比如可以是springmvc的HandlerInterceptor.preHandle方法,也可以是servlet规范中的javax.servlet.Filter.doFilter方法,重置traceId的值,并把新traceId的值put到MDC(org.slf4j.MDC, 此处推荐使用slf4j的MDC对象,这样底层的实际日志引擎可以自由切换,比如由log4j切换到log4j2或者logback)上下文中。

    
    //clear and reset trace context
    TraceContext.ctx.get().clear();
    //org.slf4j.MDC
    MDC.put("traceId", TraceContext.ctx.get().getTraceId());
    
    //Then, do your business...
    

    3. 在底层日志系统的配置文件中增加traceId标识(此处以log4j配置为例)。

    
    
    log4j.appender.stdout.layout.ConversionPattern=[%5p] [%d{yyyy-MM-dd HH:mm:ss}] [%t] [traceId:%X{traceId}] (%F:%L) %m%n
    
    

    4. 打印日志中即可显示traceId,再结合日志检索(不是本文重点,比如可以使用ELK,输入traceId作为查询条件)功能,即可定位一次请求都经历了哪些过程。

    
    [DEBUG] [2017-09-29 18:03:38] [http-80-1] [traceId:] (AppInterceptor.java:42) AppInterceptor: [traceId:175161640446888]
    [DEBUG] [2017-09-29 18:06:09] [LocalCache-Timer] [traceId:175161640446888] (LocalCache.java:28) LocalCache clearExpired start...
    [DEBUG] [2017-09-29 18:06:09] [LocalCache-Timer] [traceId:175161640446888] (LocalCache.java:53) LocalCache clearExpired count=1 , remain count=2,  time_cost=3ms
    

     

    转载于:https://my.oschina.net/fdhay/blog/1545529

    展开全文
  • JAVA日志MDC追踪快速定位问题源头

    千次阅读 2019-09-27 11:01:03
      MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能,也可以说是一种轻量级的日志跟踪工具。 MDC能做什么   那么通过MDC的概念,我们可以...

    一、了解MDC

    • MDC是什么
        MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能,也可以说是一种轻量级的日志跟踪工具。
    • MDC能做什么
        那么通过MDC的概念,我们可以知道,MDC是应用内的线程级别,不是分布式的应用层级别,所以仅靠它无法做到分布式应用调用链路跟踪的需求。它要解决的问题主要是让我们可以在海量日志数据中快速捞到可用的日志信息。
    • 场景分析
        既然我们知道MDC可以让我们快速的捞到可用的日志信息,那具体怎么捞呢?我们先来看这样的一个场景:很多时候,我们一个程序调用链可能会很复杂,并且在调用链的各个环节中,会对一些关键的操作做日志埋点,比如说入参出参、复杂计算后的结果等等信息,但在线上环境是很多用户使用我们功能的,比如说A程序,每个用户都在使用了A程序后,打印了A程序方法调用链内的所以日志,那我怎么就知道这一堆相同日志中,哪些是同一次请求所打印的呢?可能大家会说:可以看它的线程名啊,HTTP在同一请求中会用同一个线程。一定程度上看线程是可以的,但我们也知道,web服务器不可能无限创建线程的,它内部有个线程池,用于HTTP线程的创建、回收等管理,如果该程序使用频率是很高,那完全有可能短时间内的几次请求用的都是同一个线程,这样的话就解决不了上述所说的:“把一次请求中调用链内的所以日志找出来”的需求了。
    • 解决方案 
        针对以上的场景,我们可以在一次请求进来的时候,创建一个全局唯一的标识符,该标识符可以没有业务含义,我们就叫它做“traceId”吧,因为这仅仅只是为了区分每次请求打印了什么信息,接下来,我们知道ThreadLocal这个类是可以共享线程内的数据的,所以我们就可以利用它来实现这个需求了。把traceId放入到ThreadLocal中,然后在我们程序调用链中输出日志时,就可以带上这个traceId了,比如以下代码:
    String traceId = threadLocal.get();
    log.info("这里是打印信息{}", traceId);
    

      以上方法虽然解决了我们的问题,但是我们每次打印日志都要自己拿一下traceId,这无形增加了我们的工作量和降低了代码的美观度,所以我们肯定得想办法封装这部分重复的代码了。而这个封装的事情MDC就帮我们做了,我们只管在请求最开始时,生成一个traceId,然后放到MDC中就可以了,之后的事情就是按照我们原来的方式打印日志,不用新增其他额外的重复代码,这个traceId也一直跟随这个线程的执行完所有的任务。

    二、具体实现

    实现前效果

      我们先来看看实现MDC前的日志打印效果。我们以logback为例,在配置文件中,定义的日志格式为:
    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %thread | [%X{X-B3-TraceId}] | %-5level %logger{50} %msg%n</pattern> 
    代码我们打印的日志要包含以下信息:日志打印的时间、线程、TraceId、级别、哪个类打印的和具体打印信息吗,其中%X{X-B3-TraceId}就是我们接下来要讲的内容。跑一下应用看看当前的日志长啥样:

    #第一次请求
    2019-08-10 15:34:36.428 http-nio-9191-exec-1 | [] | INFO  com.lhp.aspects.RequestLogAspect 请求IP:0:0:0:0:0:0:0:1
    2019-08-10 15:34:36.428 http-nio-9191-exec-1 | [] | INFO  com.lhp.aspects.RequestLogAspect 请求路径:http://localhost:9191/mdcTest
    2019-08-10 15:34:36.428 http-nio-9191-exec-1 | [] | INFO  com.lhp.aspects.RequestLogAspect 请求方式:GET
    2019-08-10 15:34:36.430 http-nio-9191-exec-1 | [] | INFO  com.lhp.aspects.RequestLogAspect 方法描述:
    2019-08-10 15:34:36.434 http-nio-9191-exec-1 | [] | INFO  com.lhp.aspects.RequestLogAspect 请求参数:{}
    2019-08-10 15:34:36.438 http-nio-9191-exec-1 | [] | INFO  com.lhp.pmj.controller.BackDoorController invoke controller method=mdcControllerTest param=罗海鹏
    2019-08-10 15:34:36.511 http-nio-9191-exec-1 | [] | INFO  c.s.p.o.l.facade.impl.XxxFacade invoke facade method=mdcFacadeTest param=罗海鹏trace
    2019-08-10 15:34:36.631 http-nio-9191-exec-1 | [] | INFO  com.lhp.aspects.RequestLogAspect 请求出参: "MDC测试"
    2019-08-10 15:34:36.631 http-nio-9191-exec-1 | [] | INFO  com.lhp.aspects.RequestLogAspect 执行时间: 203
    
    #第二次请求
    2019-08-10 15:39:40.966 http-nio-9191-exec-1 | [] | INFO  com.lhp.aspects.RequestLogAspect 请求IP:0:0:0:0:0:0:0:1
    2019-08-10 15:39:40.966 http-nio-9191-exec-1 | [] | INFO  com.lhp.aspects.RequestLogAspect 请求路径:http://localhost:9191/mdcTest
    2019-08-10 15:39:40.966 http-nio-9191-exec-1 | [] | INFO  com.lhp.aspects.RequestLogAspect 请求方式:GET
    2019-08-10 15:39:41.025 http-nio-9191-exec-1 | [] | INFO  com.lhp.aspects.RequestLogAspect 方法描述:
    2019-08-10 15:39:41.144 http-nio-9191-exec-1 | [] | INFO  com.lhp.aspects.RequestLogAspect 请求参数:{}
    2019-08-10 15:39:41.145 http-nio-9191-exec-1 | [] | INFO  com.lhp.pmj.controller.BackDoorController invoke controller method=mdcControllerTest param=罗海鹏
    2019-08-10 15:39:41.145 http-nio-9191-exec-1 | [] | INFO  c.s.p.o.l.facade.impl.XxxFacade invoke facade method=mdcFacadeTest param=罗海鹏trace
    2019-08-10 15:39:41.855 http-nio-9191-exec-1 | [] | INFO  com.lhp.aspects.RequestLogAspect 请求出参: "MDC测试"
    2019-08-10 15:39:41.857 http-nio-9191-exec-1 | [] | INFO  com.lhp.aspects.RequestLogAspect 执行时间: 203
    

    通过上述打印的日志可以看出问题了,两次请求,刚好分配了同一个线程http-nio-9191-exec-1处理,这样我们在海量日志数据的情况下,就很难区分每次请求分别打印了哪一些日志了。

    实现后效果

      在上面演示中,我们看到输出的日志中,有个“[ ]”的字符串,这一块信息是我们在定义日志格式中的“[%X{X-B3-TraceId}]”,%X{ }是取值的意思,告诉日志框架,需要去MDC获取key为X-B3-TraceId的值,很明显,我们并没有给MDC设置一个key为X-B3-TraceId的值,所以当我们打印日志的时候,这一块就打印成空字符串了。下面我们来看看给MDC设置了值之后的效果。

    • 创建并设置traceId 
      我们以SpringMVC为例,在请求最开始时创建traceId,并把该traceId放到MDC中:这一步我们可以使用SpringMVC的拦截器或者AOP来实现。而这里的例子我就使用AOP来实现。
      1、定义切点
        /**
         * controller的切点
         */
        @Pointcut("execution(public * com.lhp.*.controller..*.*(..))")
        public void controllerTraceId() {
        }
    

    2、环绕切入

        /**
         * 所有controller环绕切点
         *
         * @param proceedingJoinPoint 切入点
         * @return Object
         * @throws Throwable 异常
         */
        @Around("controllerTraceId()")
        public Object doControllerAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            MDC.put("X-B3-TraceId", UUID.randomUUID().toString());
            Object result = proceedingJoinPoint.proceed();
            MDC.clear();
            return result;
        }
    

      需要操作MDC很简单,使用的工具类就叫做MDC,它是slf4j提供的日志标准包下的一个类,log4j和logback都有实现,然后往MDC设置一个key为X-B3-TraceId的值,X-B3-TraceId就是我们上述日志格式定义的%X{X-B3-TraceId}。value需要唯一,并且不需要有业务含义,所以我这里直接使用UUID。接着AOP的proceedingJoinPoint.proceed()执行完后,我们的方法也就执行完了,要调用MDC.clear()把报错到当前线程的MDC数据清空。
    3、查看效果

    #第一次请求
    2019-08-10 16:29:51.494 http-nio-9191-exec-2 | [807d770b-b44b-4cc3-80d0-91e47b0baf34] | INFO  com.lhp.aspects.RequestLogAspect 请求IP:0:0:0:0:0:0:0:1
    2019-08-10 16:29:51.494 http-nio-9191-exec-2 | [807d770b-b44b-4cc3-80d0-91e47b0baf34] | INFO  com.lhp.aspects.RequestLogAspect 请求路径:http://localhost:9191/mdcTest
    2019-08-10 16:29:51.494 http-nio-9191-exec-2 | [807d770b-b44b-4cc3-80d0-91e47b0baf34] | INFO  com.lhp.aspects.RequestLogAspect 请求方式:GET
    2019-08-10 16:29:51.496 http-nio-9191-exec-2 | [807d770b-b44b-4cc3-80d0-91e47b0baf34] | INFO  com.lhp.aspects.RequestLogAspect 方法描述:
    2019-08-10 16:29:51.498 http-nio-9191-exec-2 | [807d770b-b44b-4cc3-80d0-91e47b0baf34] | INFO  com.lhp.aspects.RequestLogAspect 请求参数:{}
    2019-08-10 16:29:51.501 http-nio-9191-exec-2 | [807d770b-b44b-4cc3-80d0-91e47b0baf34] | INFO  com.lhp.controller.BackDoorController invoke controller method=mdcControllerTest param=罗海鹏
    2019-08-10 16:29:51.560 http-nio-9191-exec-2 | [807d770b-b44b-4cc3-80d0-91e47b0baf34] | INFO  c.s.p.o.l.facade.impl.XxxFacade invoke facade method=mdcFacadeTest param=罗海鹏trace
    2019-08-10 16:29:51.677 http-nio-9191-exec-2 | [807d770b-b44b-4cc3-80d0-91e47b0baf34] | INFO  com.lhp.aspects.RequestLogAspect 请求出参: "MDC测试"
    2019-08-10 16:29:51.677 http-nio-9191-exec-2 | [807d770b-b44b-4cc3-80d0-91e47b0baf34] | INFO  com.lhp.aspects.RequestLogAspect 执行时间: 183
    
    #第二次请求
    2019-08-10 16:34:04.709 http-nio-9191-exec-2 | [59123bc7-a03e-41b6-89fc-cf984369896c] | INFO  com.lhp.aspects.RequestLogAspect 请求IP:0:0:0:0:0:0:0:1
    2019-08-10 16:34:04.709 http-nio-9191-exec-2 | [59123bc7-a03e-41b6-89fc-cf984369896c] | INFO  com.lhp.aspects.RequestLogAspect 请求路径:http://localhost:9191/mdcTest
    2019-08-10 16:34:04.709 http-nio-9191-exec-2 | [59123bc7-a03e-41b6-89fc-cf984369896c] | INFO  com.lhp.aspects.RequestLogAspect 请求方式:GET
    2019-08-10 16:34:04.709 http-nio-9191-exec-2 | [59123bc7-a03e-41b6-89fc-cf984369896c] | INFO  com.lhp.aspects.RequestLogAspect 方法描述:
    2019-08-10 16:34:04.709 http-nio-9191-exec-2 | [59123bc7-a03e-41b6-89fc-cf984369896c] | INFO  com.lhp.aspects.RequestLogAspect 请求参数:{}
    2019-08-10 16:34:04.709 http-nio-9191-exec-2 | [59123bc7-a03e-41b6-89fc-cf984369896c] | INFO  com.lhp.controller.BackDoorController invoke controller method=mdcControllerTest param=罗海鹏
    2019-08-10 16:34:04.766 http-nio-9191-exec-2 | [59123bc7-a03e-41b6-89fc-cf984369896c] | INFO  c.s.p.o.l.facade.impl.XxxFacade invoke facade method=mdcFacadeTest param=罗海鹏trace
    2019-08-10 16:34:04.883 http-nio-9191-exec-2 | [59123bc7-a03e-41b6-89fc-cf984369896c] | INFO  com.lhp.aspects.RequestLogAspect 请求出参: "MDC测试"
    2019-08-10 16:34:04.883 http-nio-9191-exec-2 | [59123bc7-a03e-41b6-89fc-cf984369896c] | INFO  com.lhp.aspects.RequestLogAspect 执行时间: 174
    

      可以看到,这次日志输出%X{X-B3-TraceId}的位置就不在是空了,而是一串UUID,并且在一次请求之内,UUID都是一样的,这样我们在排查问题时,首先找到了问题的入口日志,在搜索该日志的UUID,就整个请求内的所有日志找出来了,提高了我们排查问题的效率。

    三、MDC原理

    通过以上的实现,我们发现MDC使用起来非常简单,就只有两个步骤:

    • 1、定义日志格式,其中%X{}代表去MDC取值
    • 2、通过拦截器或者AOP在方法调用链最开始,设置MDC的值

    接下来我们看看它的实现:

    四、子线程MDC传递

      既然我们知道MDC底层使用TreadLocal来实现,那根据TreadLocal的特点,它是可以让我们在同一个线程中共享数据的,但是往往我们在业务方法中,会开启多线程来执行程序,这样的话MDC就无法传递到其他子线程了。这时,我们需要使用额外的方法来传递存在TreadLocal里的值。MDC提供了一个叫getCopyOfContextMap的方法,很显然,该方法就是把当前线程TreadLocal绑定的Map获取出来,之后就是把该Map绑定到子线程中的ThreadLocal中了,具体代码如下:

            Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
            new Thread(() -> {
                if (copyOfContextMap != null) {
                    MDC.setContextMap(copyOfContextMap);
                }
                log.info("这个是子线程的信息");
            }).start();
    

    也就是说,我们在主线程中获取MDC的值,然后在子线程中设置进去,这样,子线程打印的信息也会带有整个调用链共同的traceId了。

    展开全文
  • 主要介绍了Java异常处理 如何跟踪异常的传播路径,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 通过Xshell 查看跟踪日志java

    千次阅读 2021-01-12 05:55:38
    根据项目发布的环境配置XShell ,前两个命令用的会频繁#查看所有的服务列表, -n 后跟 namespace, 查看指定的命名空间kubectl get podkubectl get pod -n kube#查看容器的日志kubectl logs kubectl logs --tail=100 ...

    根据项目发布的环境配置XShell ,前两个命令用的会频繁

    #查看所有的服务列表,  -n 后跟 namespace, 查看指定的命名空间

    kubectl get pod

    kubectl get pod -n kube

    #查看容器的日志

    kubectl logs

    kubectl logs --tail=100    查找前100的日志

    kubectl logs  crmc-service-deployment-757b449dd5-hgsgg | grep error       搜索这个服务crmc-service-deployment-757b449dd5-hgsgg中的关键字errorkubectl logs -f # 实时查看日志

    #查看 RC 和 service 列表, -o wide 查看详细信息

    kubectl get rc,svc

    kubectl get pod,svc -o wide

    kubectl get pod -o yaml

    #显示 Node 的详细信息

    kubectl describe node 192.168.0.212

    #显示 Pod 的详细信息, 特别是查看 pod 无法创建的时候的日志

    kubectl describe pod

    eg:

    kubectl describe pod redis-master-tqds9

    #根据 yaml 创建资源, apply 可以重复执行,create 不行

    kubectl create -f pod.yaml

    kubectl apply -f pod.yaml

    #基于 pod.yaml 定义的名称删除 pod

    kubectl delete -f pod.yaml#删除所有包含某个 label 的pod 和 service

    kubectl delete pod,svc -l name=

    #删除所有 Pod

    kubectl delete pod --all

    #查看 endpoint 列表

    kubectl get endpoints

    #执行 pod 的 date 命令

    kubectl exec -- date

    kubectl exec -- bash

    kubectl exec -- ping 10.24.51.9

    #通过bash获得 pod 中某个容器的TTY,相当于登录容器

    kubectl exec -it -c -- bash

    eg:

    kubectl exec -it redis-master-cln81 -- bash

    展开全文
  • Java日志记录-我的日志文件在哪里?

    千次阅读 2021-02-12 13:19:00
    我java.util.logging在Windows XP的Eclipse 3.7.1中使用Java日志记录-。我logging.properties文件的相关行是:handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler.level=INFOjava.util....
  • java日志使用

    万次阅读 多人点赞 2019-06-08 09:56:58
    java日志:日志就是记录程序的运行轨迹,方便查找关键信息,也方便快速定位解决问题。 常用的日志框架:Log4j、Slf4j 、Logback 。 在JDK 1.3及以前,Java打日志依赖System.out.println(), System.err.println()...
  • Java跟踪代理 一个轻量级且快速的运行时注入工具,用于日志记录和跟踪,还可以跟踪OSGi应用程序。 将日志记录注入到正在运行的应用程序中的任何位置。 设置 将具有路径的VM参数添加到跟踪jar -javaagent:dakaraphi....
  • java种菜源码
  • 日志规范总结

    2019-04-26 15:40:43
    生产和测试环境中需要日志来记录、跟踪和分析系统的运行状态,但是有太多带有杂讯的日志又会影响跟踪,甚至可能对系统的运行带来影响。如果生产环境里运行的程序没有日志,会让问题定位变得异常艰难。但冗余的日志...
  • Java日志体系日志实现 JUL、logback、log4j、log4j2JUL日志级别 java.util.logging.Level示例讲解功能JULTest 测试类 test方法 不同日志输出方法testLogConsoleConfig方法 控制台输出测试testLogFileConfig方法 输出...
  • java 日志打印规范

    千次阅读 2019-08-16 11:43:00
    日志要求: 重要日志一定要打印到日志文件 日志文件应该每天滚动一次,日志多的可以每个小时滚动一次 日期必须精确到毫秒,而不是秒 确保日志是按事件顺序输出 【推荐】最好能打印调用方信息,比如访问者ip等信息 ...
  • importjava.io.BufferedOutputStream;importjava.io.BufferedReader;importjava.io.BufferedWriter;importjava.io.File;importjava.io.FileOutputStream;importjava.io.FileReader;importjava.io...
  • 该项目提供了在 Web 浏览器中跟踪日志文件的功能。 它使用新兴的技术将新的日志行流式传输到浏览器,以便在可滚动的文本区域中显示。 跟踪日志文件可能位于本地安装在 Web 应用程序服务器上的文件系统上,也可能...
  • Java最详细常用日志框架介绍

    千次阅读 2020-07-09 23:32:44
    Java日志概述 对于一个应用程序来说日志记录是必不可少的一部分。线上问题追踪,基于日志的业务逻辑统计分析等都离不日志。java领域存在多种日志框架,目前常用的日志框架包括Log4j1,Log4j2,Commons Logging,Slf4...
  • java错误日志

    千次阅读 2018-08-27 15:08:31
    #配置根Logger log4j.rootLogger = [ level ] , appenderName1 , ...#配置日志信息输出目的地Appender log4j.appender.appenderName = fully.qualified.name.of.appender.class  log4j.appender.appende...
  • Java日志规范

    千次阅读 2016-10-13 10:23:55
    Overview ...一个在生产环境里运行的程序如果没有日志是很让维护者提心吊胆的,有太多杂乱又无意义的日志也是令人伤神。...本文想讨论的是如何在Java程序里写好日志。 一般来说日志分为两种:业务日志和异
  • 分布式系统日志跟踪

    千次阅读 2017-05-08 11:23:25
     先介绍一个概念:分布式跟踪,或分布式追踪。  电商平台由数以百计的分布式服务构成,每一个请求路由过来后,会经过多个业务系统并留下足迹,并产生对各种Cache或DB的访问,但是这些分散的数据对于问题排查,...
  • HarmonyOS之HiTrace日志跟踪定位分析

    千次阅读 2021-07-25 13:34:19
    DevEco Studio 提供了 HiTrace 日志跟踪的能力,可以分析和梳理跨设备分布式应用之间的调用关系,通过分析调用链,方便开发者定位调用异常、性能瓶颈等问题。 开发者可以通过 HarmonyOS Interface Definition ...
  • java日志系统框架整理(转载)

    万次阅读 2018-02-06 11:45:38
    参考:(方便记录,直接将内容贴过来了。如有侵权,请留言删除,此致敬意!) 首先,在日志系统的森林里面理理头绪,修炼内功。参考文章如下: 1.... ...Java日志系统确实比较丰富,常用的
  • Java常用日志框架介绍

    千次阅读 2017-12-29 23:19:15
    java日志概述 对于一个应用程序来说日志记录是必不可少的一部分。线上问题追踪,基于日志的业务逻辑统计分析等都离不日志。java领域存在多种日志框架,目前常用的日志框架包括Log4j,Log4j 2,Commons Logging,Slf...
  • 受和启发,用 Java 编写的通用日志跟踪器和解析器工具。 它可以通过新的解析器和处理器轻松扩展。 主要目的是构建一个简单的工具来从日志文件中获取指标值和错误并向 (和 )报告,但每个人都可以在几分钟内编写...
  • 技术 Tracing 链路跟踪、生态圈现状 技术 Logging 本身,生态圈现状 技术选型 比较 实战 第一个问题:所有请求的日志明细 第二个问题: 将 Logging 收集到 ELK 第三个问题:我们在我们的每个请求 Header 上...
  • 图形异常跟踪视图(日志详细信息视图) (+ 点击放大) 使用图形流程图查看异常跟踪 显示或隐藏库包 查看原文日志 使用谷歌搜索搜索库包信息 安慰 (+ 点击放大) 使用 WebSocket 实时提供所有日志日志查看页面相同的...
  • 提供三种接入方式:javaagent完全无侵入接入,字节码一行代码接入,基于配置文件的接入 对业务代码无侵入式设计,使用简单,10分钟即可接入 支持常见的log4j,log4j2,logback三大日志框架,并提供自动检测,完成...
  • Java封装错误日志

    2019-04-25 11:07:07
    Java封装错误日志 作者:谢景,撰写:2019-4-25 在实际项目中,我们项目中多多少少会出现bug,这时候写一个错误日志可以方便我们以后查找并优化。 下面是本人封装的错误日志: //声明一个继承异常类的泛型类作为形参...
  • 你已经看到了它们,它只是荒谬的...这个堆栈跟踪:Exception in thread "main" java.lang.NoClassDefFoundError: aa/bb/DDat SOMEWHERE(unknown source)Caused by: java.lang.ClassNotFoundException: aaa.bbb.CCat...
  • 例如,这包括用于Servlet的跟踪过滤器和用于Apache Log4J的日志相关性。 您可以查看我们的,了解如何跟踪简单的Web应用程序。 包含什么 Brave的无依赖可用于JRE6 +。 这是工具用于计时操作并添加描述它们的标签的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 85,431
精华内容 34,172
关键字:

java日志跟踪

java 订阅