精华内容
下载资源
问答
  • java thread 日志分析工具这里用的是tda(Thread Dump Analyzer),可以到网上下载到,是个免费的软件。这个软件可以分析导出的java线程日志,至于如何获取java线程日志,可以看”获取JAVA线程和堆信息“。\各种JAVA...

    java thread 日志分析工具

    这里用的是tda(Thread Dump Analyzer),可以到网上下载到,是个免费的软件。

    这个软件可以分析导出的java线程日志,至于如何获取java线程日志,可以看”获取JAVA线程和堆信息“。

    d6d28c0f0aefe0fe43a83011e2b84b1c.gif\

    各种JAVA线程状态的说明

    先看一下线程的各种状态的状态图:

    3fa6be324964d9e3ec55159354cf1622.gif\

    dtatools

    下面是日志里线程状态的一些解释。

    waiting for monitor entry

    意味着它 在等待进入一个临界区 ,所以它在”Entry

    Set”队列中等待。此时线程状态一般都是 Blocked

    如果大量线程在 “waiting for monitor entry”:

    可能是一个全局锁阻塞住了大量线程。

    如果短时间内打印的 thread dump 文件反映,随着时间流逝,waiting for

    monitor entry 的线程越来越多,没有减少的趋势,可能意味着

    某些线程在临界区里呆的时间太长了,以至于越来越多新线程迟迟无法进入临界区。

    java.lang.Thread.State: BLOCKED (on object monitor)

    waiting on condition

    说明它

    在等待另一个条件的发生,来把自己唤醒,或者干脆它是调用了sleep(N)。此时线程状态大致为以下几种

    java.lang.Thread.State: WAITING (parking):一直等那个条件发生;

    java.lang.Thread.State: TIMED_WAITING

    (parking或sleeping):定时的,那个条件不到来,也将定时唤醒自己。

    如果大量线程在 “ waiting on condition “:

    可能是它们又跑去获取第三方资源,

    尤其是第三方网络资源,迟迟获取不到Response,导致大量线程进入等待状态。

    所以如果你发现有大量的线程都处在 Wait on

    condition,从线程堆栈看,正等待网络读写

    ,这可能是一个网络瓶颈的征兆,因为网络阻塞导致线程无法执行。

    in Object.wait()

    说明它 获得了监视器之后,又调用了 java.lang.Object.wait() 方法。

    每个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active

    Thread”,而其它线程都是 “Waiting Thread”,分别在两个队列 “ Entry Set”和

    “Wait Set”里面等候。在 “Entry Set”中等待的线程状态是 “Waiting for

    monitor entry”,而在 “Wait Set”中等待的线程状态是 “in Object.wait()”。

    当线程获得了

    Monitor,如果发现线程继续运行的条件没有满足,它则调用对象(一般就是被

    synchronized 的对象)的 wait() 方法,放弃了 Monitor,进入 “Wait

    Set”队列。此时线程状态大致为以下几种:

    java.lang.Thread.State: TIMED_WAITING (on object monitor);

    java.lang.Thread.State: WAITING (on object monitor);

    示范一

    下面这个线程在等待这个锁 0x00000000fe7e3b50,等待进入临界区:

    "RMI TCP Connection(64896)-172.16.52.118" daemon prio=10 tid=0x00000000405a6000 nid=0x68fe waiting for monitor entry [0x00007f2be65a3000]

    java.lang.Thread.State: BLOCKED (on object monitor)

    at com.xyz.goods.service.impl.GoodsServiceImpl. findChanellGoodsCountWithCache(GoodsServiceImpl.java:1734)

    - waiting to lock <0x00000000fe7e3b50> (a java.lang.String)

    那么谁持有这个锁呢?

    是另一个先调用了 findChanellGoodsCountWithCache 函数的线程:

    "RMI TCP Connection(64878)-172.16.52.117" daemon prio=10 tid=0x0000000040822000 nid=0x6841 runnable [0x00007f2be76b3000]

    java.lang.Thread.State: RUNNABLE

    at java.net.SocketInputStream.socketRead0(Native Method)

    at java.net.SocketInputStream.read(SocketInputStream.java:129)

    at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)

    at java.io.BufferedInputStream.read1(BufferedInputStream.java:258)

    at java.io.BufferedInputStream.read(BufferedInputStream.java:317)

    - locked <0x00000000af4ed638> (a java.io.BufferedInputStream)

    at org.bson.io.Bits.readFully(Bits.java:35)

    at org.bson.io.Bits.readFully(Bits.java:28)

    at com.mongodb.Response.(Response.java:35)

    at com.mongodb.DBPort.go(DBPort.java:110)

    - locked <0x00000000af442d48> (a com.mongodb.DBPort)

    at com.mongodb.DBPort.go(DBPort.java:75)

    - locked <0x00000000af442d48> (a com.mongodb.DBPort)

    at com.mongodb.DBPort.call(DBPort.java:65)

    at com.mongodb.DBTCPConnector.call(DBTCPConnector.java:202)

    at com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:296)

    at com.mongodb.DB.command(DB.java:152)

    at com.mongodb.DBCollection.getCount(DBCollection.java:760)

    at com.mongodb.DBCollection.getCount(DBCollection.java:731)

    at com.mongodb.DBCollection.count(DBCollection.java:697)

    at com.xyz.goods.manager.MongodbManager.count(MongodbManager.java:202)

    at com.xyz.goods.service.impl.GoodsServiceImpl.findChanellGoodsCount(GoodsServiceImpl.java:1787)

    at com.xyz.goods.service.impl.GoodsServiceImpl. findChanellGoodsCountWithCache(GoodsServiceImpl.java:1739)

    - locked <0x00000000fe7e3b50> (a java.lang.String)

    示范二

    等待另一个条件发生来将自己唤醒:

    "RMI TCP Connection(idle)" daemon prio=10 tid=0x00007fd50834e800 nid=0x56b2 waiting on condition [0x00007fd4f1a59000]

    java.lang.Thread.State: TIMED_WAITING (parking)

    at sun.misc.Unsafe.park(Native Method)

    - parking to wait for <0x00000000acd84de8> (a java.util.concurrent.SynchronousQueue$TransferStack)

    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:198)

    at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:424)

    at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:323)

    at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:874)

    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:945)

    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)

    at java.lang.Thread.run(Thread.java:662)

    "TIMED_WAITING (parking)"中的 timed_waiting

    指等待状态,但这里指定了时间,到达指定的时间后自动退出等待状态;parking指线程处于挂起中。

    "waiting on condition"需要与堆栈中的"parking to wait for

    <0x00000000acd84de8> (a

    java.util.concurrent.SynchronousQueue$TransferStack)"

    结合来看。首先,本线程肯定是在等待某个条件的发生,来把自己唤醒。其次,SynchronousQueue

    并不是一个队列,只是线程之间移交信息的机制,当我们把一个元素放入到

    SynchronousQueue

    中时必须有另一个线程正在等待接受移交的任务,因此这就是本线程在等待的条件。

    示范三

    "RMI RenewClean-[172.16.50.182:4888]" daemon prio=10 tid=0x0000000040d2c800 nid=0x97e in Object.wait() [0x00007f9ccafd0000]

    java.lang.Thread.State: TIMED_WAITING (on object monitor)

    at java.lang.Object.wait(Native Method)

    - waiting on <0x0000000799b032d8> (a java.lang.ref.ReferenceQueue$Lock)

    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118)

    - locked <0x0000000799b032d8> (a java.lang.ref.ReferenceQueue$Lock)

    at sun.rmi.transport.DGCClient$EndpointEntry$RenewCleanThread.run(DGCClient.java:516)

    at java.lang.Thread.run(Thread.java:662)

    展开全文
  • 除了JDK本身自带的简单的日志工具,java还有如log4j,commons-loggin,slf4j,logback等众多第三方工具框架可供开发者使用,其中,上述几个框架,虽说都实现了日志记录的功能,但相互之间各分伯仲,同时,也存在着一定...
      接触过Java的朋友应该都会知道,java的开源框架百花齐放,实现同样的功能,总能找到几个强大的开源框架来进行选择。在日志方面,Java同样不逊色。除了JDK本身自带的简单的日志工具,java还有如log4j,commons-loggin,slf4j,logback等众多第三方工具框架可供开发者使用,其中,上述几个框架,虽说都实现了日志记录的功能,但相互之间各分伯仲,同时,也存在着一定的联系。比如,log4j,slf4j,logback就是出自同一个人之手。
     
      在了解各个框架之前,首先过过大部分日志框架的使用时会遇到的一些概念。
    1、日志工具类(logger):通常,大部分的日志框架都会定义一个主要的日志输出的工具类,开发者只需要声明该类,即可实现大部分的日志输出操作
    2、日志输出级别(Level):日志输出的级别,意味着该日志的重要程度,大部分的日志框架的级别会分为:Debug(调试模式),Info,warn,error,fatal,其中,根据级别的大小排序,debug>info>warn>error>fatal。级别的大小,将会影响日志的输出,将级别设置的越高,输出的信息将会越多
    3、日志输出格式(format):日志输出的格式,通常可以进行自定义,通过自定义的格式,输出的详细效果将不同
    4、日志输出的方式:即将日志输出到哪?常见的输出方式包括如:控制台,文本文件,数据库等
    5、日志分割:日志文件若以文件的方式进行输出的话,会存在文件大小问题,所以需要对日志进行分割,分割的方式可以分为按大小,按日期等。
     
      如图,常见的日志框架的工作流程大约都会围绕着几个对象展开。首先,通过日志工厂产生一个日志工具类,通过该类来进行日志的输出和日志输出级别的设置,该类的输出,其实是通过另外的框架中定义的不同类型输出类进行输出,常见的包括文件输出,数据库输出,都有与之相对应的日志输出类。同时,该类接收来自配置文件或者开发者自定义的日志输出格式,会根据该日志格式进行日志的输出。
     
      了解过大部分日志框架的工作方式,接下来了解一下,java的一些日志框架。
     
      首先,java日志框架嫡长子,jdk自带的log工具,该工具自JDK1.4版本开始就包含在java.util.logging包下。该工具由于太过粗糙,所以并没有得到广泛的使用,但是log框架应有的日志处理功能还是能够实现的:
    以下是通过JDK自带的日志工具实现输出的实例代码:
     1 public class SimpleLog {
     2 
     3 
     4     public static void main(String[] args) throws SecurityException, IOException {
     5         Logger log=Logger.getLogger("Test");
     6         //添加log处理,默认是console
     7         ConsoleHandler console=new ConsoleHandler();
     8         console.setLevel(Level.ALL);
     9         log.addHandler(console);
    10         FileHandler file=new FileHandler("log.log");
    11         file.setLevel(Level.WARNING);
    12         log.addHandler(file);
    13         //设置日志输出格式
    14         file.setFormatter(new Formatter() {
    15 
    16             @Override
    17             public String format(LogRecord record) {
    18                 // TODO Auto-generated method stub
    19                 return new SimpleDateFormat("yyyy-MM-dd hh-mm-ss").format(new Date(record.getMillis())) +"------"+record.getLevel()+"-------"+record.getClass()+"-------"+record.getLoggerName()+"----"+record.getMessage()+"\t\n";
    20             }
    21         });
    22         console.setFormatter(new Formatter() {
    23 
    24             @Override
    25             public String format(LogRecord record) {
    26                 return new SimpleDateFormat("yyyy-MM-dd hh-mm-ss").format(new Date(record.getMillis())) +"------"+record.getLevel()+"-------"+record.getClass()+"---"+record.getSourceMethodName()+"-------"+record.getLoggerName()+"----"+record.getMessage()+"\t\n";
    27             }
    28         });
    29         //日志控制
    30         log.log(Level.SEVERE, "测试日志");
    31         log.log(Level.INFO, "测试日志");
    32         log.log(Level.WARNING, "测试日志");
    33         log.log(Level.OFF, "测试日志");
    34 
    35     }

     

      接下来,看一下常见的日志框架,common-loggin,log4j,slf4j,logback。其实,个人认为,真正其中真正算是日志框架的只有log4j和logback而已。因为common-logging和slf4j都是进行日志框架整合的工具,本身并没有独特的日志处理功能。commons-logging其实是一个日志集合api,它通过检测classpath环境下存在怎么样的log工具,然后通过调用该工具来进行日志输出操作 。默认情况下,common-logging是通过jdk自带的log工具来实现日志输出的,当检测到项目中存在log4j此类的日志框架及其配置文件的时候,common-logging就会去调用该框架来进行日志的输出。
     
      接着,看下log4j,一个强大的日志框架。它支持文件俺输出,复杂的自定义格式输出,支持输出成html,数据库等,一系列的强大功能让它成为了许多开源框架首选的日志工具。具体的关于log4j的配置,后续继续更新。
     
      再说说slf4j,其实它和common-logging有相似的功能,只不过,它并不是通过自己实现日志的整合,而是通过定义接口的,定义标准的方式进行的。Common-logging是通过自身来实现日志框架的调用,而slf4j并不同,它自身其实并没有实现功能,它定义了接口,使用它进行日志输出,通常需要导入slf4j的标准包,日志框架的包,还有就是继承了slf4j定义的接口的操作对应日志框架的实现类包。表面上,我们是在使用slf4j进行日志输出,其实,我们使用的是对应的实现类包中的方法进行日志输出。
     
      最后一个说到的日志框架,是logback,他和slf4j,log4j是同一个作者。后期,log4j停止了更新后,他又开源了一个新的日志框架就是logback,目前,国外的许多框架也已经开始使用logback了。因为是同出一人,所以logback的一些api其实与log4j有些相似之处,同时,logback也符合slf4j定义的标准,所以如果在项目中使用了slf4j和log4j,后期要改成logback,需要做的调整会比较少。

    转载于:https://www.cnblogs.com/Seanit/p/5126019.html

    展开全文
  • 作者:朱乐陶,软件架构师,具备多年Java开发及架构设计经验,擅长微服务领域作者博客:https://blog.csdn.net/zlt2000背景开发排查系统问题用得最多的手段就是查看系统日志,在分布式环境中一般使用ELK来统一收集...

    作者:朱乐陶,软件架构师,具备多年Java开发及架构设计经验,擅长微服务领域

    作者博客:https://blog.csdn.net/zlt2000

    背景

    开发排查系统问题用得最多的手段就是查看系统日志,在分布式环境中一般使用ELK来统一收集日志,但是在并发大时使用日志定位问题还是比较麻烦,由于大量的其他用户/其他线程的日志也一起输出穿行其中导致很难筛选出指定请求的全部相关日志,以及下游线程/服务对应的日志。

    解决思路

    每个请求都使用一个唯一标识来追踪全部的链路显示在日志中,并且不修改原有的打印方式(代码无入侵)

    使用Logback的MDC机制日志模板中加入traceId标识,取值方式为%X{traceId}

    MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的Map,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据。

    方案实现

    由于MDC内部使用的是ThreadLocal所以只有本线程才有效,子线程和下游的服务MDC里的值会丢失;所以方案主要的难点是解决值的传递问题,主要包括以几下部分:

    API网关中的MDC数据如何传递给下游服务

    服务如何接收数据,并且调用其他远程服务时如何继续传递

    异步的情况下(线程池)如何传给子线程

    修改日志模板

    logback配置文件模板格式添加标识%X{traceId}

    8b460a8329919d556a12d7d9591ba12c.png

    网关添加过滤器

    生成traceId并通过header传递给下游服务

    @Component

    public class TraceFilter extends ZuulFilter {

    @Autowired

    private TraceProperties traceProperties;

    @Override

    public String filterType() {

    return FilterConstants.PRE_TYPE;

    }

    @Override

    public int filterOrder() {

    return FORM_BODY_WRAPPER_FILTER_ORDER - 1;

    }

    @Override

    public boolean shouldFilter() {

    //根据配置控制是否开启过滤器

    return traceProperties.getEnable();

    }

    @Override

    public Object run() {

    //链路追踪id

    String traceId = IdUtil.fastSimpleUUID();

    MDC.put(CommonConstant.LOG_TRACE_ID, traceId);

    RequestContext ctx = RequestContext.getCurrentContext();

    ctx.addZuulRequestHeader(CommonConstant.TRACE_ID_HEADER, traceId);

    return null;

    }

    }

    下游服务增加spring拦截器

    接收并保存traceId的值

    拦截器

    public class TraceInterceptor implements HandlerInterceptor {

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

    String traceId = request.getHeader(CommonConstant.TRACE_ID_HEADER);

    if (StrUtil.isNotEmpty(traceId)) {

    MDC.put(CommonConstant.LOG_TRACE_ID, traceId);

    }

    return true;

    }

    }

    注册拦截器

    public class DefaultWebMvcConfig extends WebMvcConfigurationSupport {

    @Override

    protected void addInterceptors(InterceptorRegistry registry) {

    //日志链路追踪拦截器

    registry.addInterceptor(new TraceInterceptor()).addPathPatterns("/**");

    super.addInterceptors(registry);

    }

    }

    下游服务增加feign拦截器

    继续把当前服务的traceId值传递给下游服务

    public class FeignInterceptorConfig {

    @Bean

    public RequestInterceptor requestInterceptor() {

    RequestInterceptor requestInterceptor = template -> {

    //传递日志traceId

    String traceId = MDC.get(CommonConstant.LOG_TRACE_ID);

    if (StrUtil.isNotEmpty(traceId)) {

    template.header(CommonConstant.TRACE_ID_HEADER, traceId);

    }

    };

    return requestInterceptor;

    }

    }

    解决父子线程传递问题

    主要针对业务会使用线程池(异步、并行处理),并且spring自己也有@Async注解来使用线程池,要解决这个问题需要以下两个步骤

    重写logback的LogbackMDCAdapter

    由于logback的MDC实现内部使用的是ThreadLocal不能传递子线程,所以需要重写替换为阿里的TransmittableThreadLocal

    TransmittableThreadLocal 是Alibaba开源的、用于解决 “在使用线程池等会缓存线程的组件情况下传递ThreadLocal” 问题的 InheritableThreadLocal 扩展。若希望 TransmittableThreadLocal 在线程池与主线程间传递,需配合 TtlRunnable 和 TtlCallable 使用。

    TtlMDCAdapter类

    package org.slf4j;

    import com.alibaba.ttl.TransmittableThreadLocal;

    import org.slf4j.spi.MDCAdapter;

    public class TtlMDCAdapter implements MDCAdapter {

    /**

    * 此处是关键

    */

    private final ThreadLocal> copyOnInheritThreadLocal = new TransmittableThreadLocal<>();

    private static TtlMDCAdapter mtcMDCAdapter;

    static {

    mtcMDCAdapter = new TtlMDCAdapter();

    MDC.mdcAdapter = mtcMDCAdapter;

    }

    public static MDCAdapter getInstance() {

    return mtcMDCAdapter;

    }

    其他代码与ch.qos.logback.classic.util.LogbackMDCAdapter一样,只需改为调用copyOnInheritThreadLocal变量

    TtlMDCAdapterInitializer类用于程序启动时加载自己的mdcAdapter实现

    public class TtlMDCAdapterInitializer implements ApplicationContextInitializer {

    @Override

    public void initialize(ConfigurableApplicationContext applicationContext) {

    //加载TtlMDCAdapter实例

    TtlMDCAdapter.getInstance();

    }

    }

    扩展线程池实现

    增加TtlRunnable和TtlCallable扩展实现TTL

    public class CustomThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {

    @Override

    public void execute(Runnable runnable) {

    Runnable ttlRunnable = TtlRunnable.get(runnable);

    super.execute(ttlRunnable);

    }

    @Override

    public Future submit(Callable task) {

    Callable ttlCallable = TtlCallable.get(task);

    return super.submit(ttlCallable);

    }

    @Override

    public Future> submit(Runnable task) {

    Runnable ttlRunnable = TtlRunnable.get(task);

    return super.submit(ttlRunnable);

    }

    @Override

    public ListenableFuture> submitListenable(Runnable task) {

    Runnable ttlRunnable = TtlRunnable.get(task);

    return super.submitListenable(ttlRunnable);

    }

    @Override

    public ListenableFuture submitListenable(Callable task) {

    Callable ttlCallable = TtlCallable.get(task);

    return super.submitListenable(ttlCallable);

    }

    }

    场景测试

    测试代码如下

    a1e12be85380b288c0d42702906db83b.png

    api网关打印的日志

    网关生成traceId值为13d9800c8c7944c78a06ce28c36de670

    7235e94c02431230e144756e72c87b3a.png

    请求跳转到文件服务时打印的日志

    显示的traceId与网关相同,这里特意模拟发生异常的场景

    b637ede47bb084d5b31236764a5072a7.png

    ELK聚合日志通过traceId查询整条链路日志

    当系统出现异常时,可直接通过该异常日志的traceId​的值,在日志中心中询该请求的所有日志信息

    d469dbf1abdd52e99a9d79b23f23838c.png

    源码下载

    附上我的开源微服务框架(包含本文中的代码),欢迎 star 关注

    扫一扫,支持下作者吧

    (转载本站文章请注明作者和出处 方志朋的博客)

    展开全文
  • 日志跟踪,不管对于怎么样的项目来说,都是非常重要的一部分,它关系到项目后期的维护和排错,起着举足轻重的作用。项目开发过程中,对日志的记录规则,也将影响到改项目后期维护的难度。在开发过程中,我们经常...

    日志,是软件运行过程中,对各类操作中重要信息的记录。 日志跟踪,不管对于怎么样的项目来说,都是非常重要的一部分,它关系到项目后期的维护和排错,起着举足轻重的作用。项目开发过程中,对日志的记录规则,也将影响到改项目后期维护的难度。

    在开发过程中,我们经常遇到的日志记录方式包括有系统输出(System.out,最基础方式),日志框架输出(log4J 等)和分布式日志框架输出等。作为项目的开发者,和后期的管理者来说,对于日志的管理,除了需要知道如何进行日志输出,还需要关注的是,项目后期对日志文件的管理。

    日志追踪第一步(web容器日志)

    在此分享下自己在学习过程中的经历,本人虽然说是学计算机的,可是学校的Java课程并没怎么学习,大部分的Java知识都是在课后自学的。在最先开始,本人并没有接触到所谓的日志框架,哪怕是在搭建SSH项目的过程中,知道有这样的框架,知道三大框架都采用了这样的日子框架进行日志记录,可是还是没有主动的是去使用,所以在最先开始的时候,本人还是使用最原始的System.out,来进行日志的追踪。在项目开发的工程中,遇到了问题,就System.out一下,看看输出的是啥,然后再排错,我觉得应该不止是我一个人,采用的是这样的老办法吧。这样的问题来了,出了问题去哪找?

    其实,不论我们有没有日志输出,只要我们采用的是成型的web容器,web容器就会对一些必要的信息进行日志的记录。在此以最常用的tomcat为例。

    tomcat服务器的日志文件配置保存在$CATALINA_HOME/conf目录下的loggin.properties中(在此不做详解),而对应的日志文件,则保留在$CATALINA_HOME/logs目录下。Tomcat容器默认按照日期进行日志的保存,每天都会产生对应于当天的日志文件,其中包括:容器输出、管理日志、访问日志等。我们常见的tomcat启动输出,就对应的在容器日志文件中。对于一些tomcat的错误信息,可以通过这些日志文件进行排查

    5084488.html

    0acfa95218d0ea67c7db80c838c22903.png

    89c94aa1e5f8c7c173b57f9b12060716.png

    5084488.html

    对于日志输出的规则和保留的目录,不同的web容器采用的方式不同,因此需要根据对应的web容器进行日志查找,在此不再举例了

    日志追踪第二步(System.out在哪里)

    在此说一下,编程过程中,除了System.out以外,当异常抛出或者是说异常处理的e.printstrack,也都采用的是out输出。从编程角度上看,提出尽量规范化编程,减少System.out和e.printstrack的输出,可是,当这一部分不可避免的时候,就得好好的利用它

    作为常用的web容器,tomcat对系统输出有自己的一套管理方法,根据不同管理系统,有不同的配置。

    在Linux下,tomcat会为对应的实例创建一个.out文件,放在$CATALINA_HOME/logs目录下,方便后期的排错使用。然而,该文件默认不能自动分割,在没有配置的情况下,该文件会不断的增大,日久天长,或许等到需要排错的时候,该文件的大小可以以G为单位了。所以,在配置完tomcat实例之后,建议修改一下启动文件,让.out文件随着日期的变化自增。

    具体的配置如下:

    vim catalina.sh

    找到catalina.out所在处,将其改为catalina.$(date +%Y-%m-%d) .out(注意date之后的空格)

    c1c342a4d98c94865e07b90602854691.png

    5084488.html

    改完之后,重启tomcat,会发现,tomcat会根据日期的不同,动态的创建.out文件

    5084488.html

    b9b0dd3d8c2466a1451e8eb35b373e29.png

    在Windows下,启动tomcat通常需要启动一个控制台,同时该控制台需要一直开着,tomcat的SYstem.out 也默认为控制台输出。也就是说,开发者所写的System.out,还有异常输出,都会打印在控制台上面。所以如果出了问题,找控制台就行了。。

    但是,这一种方式显然非常麻烦,Windows控制台显示查看是有限制的,同时也没有持久化的文件保存下来。所以,需要通过配置tomcat的配置文件,来实现与Linux同样的效果。

    1、打开startup.bat文件,找到call "%EXECUTABLE%" start %CMD_LINE_ARGS%, 改为call "%EXECUTABLE%" run %CMD_LINE_ARGS% 。

    f3d638d76dac088fb813d6ba23f42a0a.png

    5084488.html

    2、打开catalina.bat文件,在4处 %ACTION% 后面加上     >> %CATALINA_HOME%\logs\catalina.out 。重启tomcat,tomcat的logs文件夹下就会出现catalina.out文件了。

    908dd9f55496cceb62a8cbe8b2b450ce.png

    5084488.html

    按照上面的方法,同样存在着.out无限变大的问题。如果想要其按照日期进行配置可以通过将catalina.out改为catalina.%date:~0,4%-%date:~5,2%-%date:~8,2%.out,重启即可

    5084488.html

    6617151efb6180e69c8013f5992864fb.png

    至此,关于tomcat的日志追踪,整理完毕。其中部分资源来自网络。

    展开全文
  • 之前说过关于java日志跟踪的几大主要用的框架,也说到了,其实在其中,Log4J充当着一个相当重要的角色。目前,大部分框架也都是采用的是Log4J,虽然说它已经停止了更新,作者也重新起了LogBack的项目,但是,LogBack...
  • 背景多线程情况下,子线程的sl4j打印日志缺少traceId等信息,导致定位问题不方便解决方案打印日志时添加用户ID、trackId等信息,缺点是每个日志都要手动添加使用mdc直接拷贝父线程值实现// 新建线程时:Map ...
  • 此时省略部分日志 at java.lang.Thread.run(Unknown Source) 好! 到这里 , 一个基础的基于AOP设置MDC自定义参数的方式已经实现好了 , 以上日志 , 当出现问题的时候 , 可以通过 b655875c-7052-427e-a5ce-b5d4183450d...
  • 日志跟踪,不管对于怎么样的项目来说,都是非常重要的一部分,它关系到项目后期的维护和排错,起着举足轻重的作用。项目开发过程中,对日志的记录规则,也将影响到改项目后期维护的难度。   在开发过程中,我们...
  • 分布式系统中日志追踪需要考虑的几个点?需要一个全服务唯一的id,即traceId,如何保证?traceId如何在服务间传递?traceId如何在服务内部传递?traceId如何在多线程中传递?我们一一来解答:全服务唯一的traceId,...
  • Java日志

    2019-10-08 21:22:40
    经过不断的改善,这个API终于成为一个十分受欢迎的Java日志软件包,即Log4j。后来,Log4j成为Apache基金会项目中的一员。 期间Log4j近乎成了Java社区的日志标准。据说Apache基金会还曾经建议Sun引入Log4j到Java的...
  • java日志

    2019-08-02 14:46:48
    经过不断的完善,这个API终于成为一个十分受欢迎的Java日志软件包,即log4j。后来log4j成为Apache基金会项目中的一员。 期间log4j近乎成了Java社区的日志标准。据说Apache基金会还曾经建议Sun引入log4j到java的标准...
  • 举例:Testlog4.main(TestLog4.java:10) %x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。 %%: 输出一个"%"字符 %F: 输出日志消息产生时所在的文件名称 %L: 输出...
  • 当我们微服务之间调用的时候可能会出错,但是我们不知道是哪个服务的问题,这时候就可以通过日志链路跟踪发现哪个服务出错。它还有一个好处:当我们在企业中,可能每个人都负责一个服务,我们可以通过日志来检查自己...
  • 通常,Java程序员在开发项目时都是依赖Eclipse/IDEA等集成开发工具的Debug 调试功能来跟踪解决Bug,但项目发布到了测试、生产环境怎么办?你有可能会说可以使用远程调试,但实际并不能允许让你这么做。所以,日志的...
  • 二、自定义日志跟踪器 这是由Thread的返回的,其实Throw也可以返回StackTraceElement,下面使用Throw制作一个简单的日志 protected void logStackChain(Object obj) { synchronized (activity) { ...
  • Java日志框架

    2019-11-05 19:23:59
    Java日志框架简介及其应用 Java日志简介 ​ 简单的说,日志就是记录程序的运行轨迹,方便查找关键信息,也方便快速定位解决问题。 ​ 我们Java程序员在开发项目时都是依赖 Eclipse/ Idea 等开发工具的 Debug 调试...
  • Java 日志系统

    2019-10-03 16:45:40
    Java 日志系统 1. 创建日志记录器 private final Logger logger = LoggerFactory.getLogger(LoggerTest.class); 2. 打印日志信息 // 跟踪轨迹,记录跟踪代码运行过程种的信息 logger.trace("这是 trace 日志"); // ...
  • 目录什么是日志常用日志框架日志...我们 Java 程序员在开发项目时都是依赖 Eclipse/ Idea 等开发工具的 Debug 调试功能来跟踪解决 Bug,在开发环境可以这么做,但项目发布到了测试、生产环境呢?你有可能会说可以使...
  • java日志框架

    2019-03-17 23:35:32
    java日志框架日志体系历史如何使用bridge只引入slf4j-api.jar包slf4j和logback组合slf4j和log4j组合slf4j和jdk组合slf4j和simple组合slf4j和nop组合引入jcl包jcl和log4j组合重定向jcl转slf4j死循环归纳后记 java日志...
  • Apache’s HTTP client, Hystrix, JUnit, AspectJ and (via its elegant API) several other frameworks Convenient Spring Boot Auto Configuration Sensible defaults Dependencies Java 8 Any build tool using ...
  • NDC、MDC以及ThreadContext当处理多线程应用程序,特别是web服务时,跟踪事件可能会变得困难。当针对多个同时存在的多个用户生成日志记录时,你如何区分哪个行为和哪个日志事件有关呢?如何两个用户没有成功打开一个...
  • 并在日志配置文件里加上trade_id的信息,如下 [%X{trade_id}]%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 到此完成日志跟踪功能, 效果如下 [16fbd9304e23492cab3ccabb6a85d0c0]2018-07-...
  • 背景开发排查系统问题用得最多的手段就是查看系统日志,在分布式环境中一般使用ELK来统一收集日志,但是在并发大时使用日志定位问题还是比较麻烦,我们来看下面的图:上图一个用户请求一个url,整个链路如图,每个...
  • java日志架构

    2020-03-06 20:52:05
    跟踪用户对系统访问 统计 Debug 1.2 日志框架 市场上存在非常多的日志框架。 JUL(java.util.logging),JCL(Jakarta Commons Logging),Log4j,Log4j2,Logback(具体框架,springboot使用)、SLF4j、jboss-logging...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,192
精华内容 476
关键字:

java日志跟踪

java 订阅