log4j 动态改变日志的输出路径

时间:2021-08-08 21:56:01

 

实际背景

  客户有客户端多台,每个客户端有自己的唯一编号。输出的日志要根据每个客户端的编号生成,例如10001_demo.log,10002_demo.log

方法

  1.网上给出的第一种方法是:

    在log4j的配置文件中log4j.appender.file.File=${log.dir}/${log.file}中,使用${}形式定义变量,在后台使用

    System.setProperty("log.dir","/home/..."),来设置变量值。但我测试了多次,这样是不起作用的,可能是我自己配置的问题。

    2.第二种方法,亲测可用:  

1    Logger log = Logger.getLogger(ZhzhcxCtl.class);//获取log对象
2   FileAppender fileAppender = (FileAppender) Logger.getRootLogger().getAppender("file");//获取FileAppender对象
3   fileAppender.setFile("/home/log/gcds.log");//重新设置输出日志的路径和文件名
4   fileAppender.activateOptions();//使设置的FileAppender起作用
5   log.info("index.........");

          但是这样的话,每次调用log.info()方法之前都要添加第2--4行这几段代码,这样写代码冗余比较大。或许这时可以想到用代理的方法,但是如果代理了,这时log.info()出现的地方就不是原来的那个类里面了,而是现在这个代理的类了。这样出现的问题就是log4j.properties配置文件中定义的输出格式%c打印的类的名字永远都是代理的那个类了,就永远看不到到底是哪个类调用的log.info方法。所以这种方法也是不可行的。

  3.第三种方法:重写FileAppender的subAppend方法。

    通过看log4j的FileAppender源码,我们可以发现,FileAppender是继承WriterAppender类的,而WriterAppender有一个subAppend方法,我们看一下subAppend方法源码:

protected void subAppend(LoggingEvent event)
  {
    this.qw.write(this.layout.format(event));
    if (this.layout.ignoresThrowable())
    {
      String[] s = event.getThrowableStrRep();
      if (s != null)
      {
        int len = s.length;
        for (int i = 0; i < len; i++)
        {
          this.qw.write(s[i]);
          this.qw.write(Layout.LINE_SEP);
        }
      }
    }
    if (shouldFlush(event)) {
      this.qw.flush();
    }
  }

 

   对,没看错,这个方法就是往日志文件里写东西的。因为FileAppender是继承WriterAppender类的,所以subAppend也是FileAppender的。这时我们只要在subAppend之前,吧文件名改成我们想要的,就可以了。

FileAppender有2个setFile方法,是用来设置输出日志文件的路径的。我们可以subAppend之前调用一次setFile就可以了。我们知道,每一次http请求,到后台都是对应一个线程。如果要做到每个终端对应一个日志的话,我们就要每一个线程都要带一个终端号,然后把输出的文件名改成其终端对应的。我们可以这样想一下,线程A调用log.info()-->调用FileAppender的对象FA--->FA.subAppend写日志。我们要改成:线程A调用log.info()-->调用FileAppender的对象FA--->FA.setFile(线程A对应终端的日志路径)-->FA.subAppend写日志。而现在的问题是我们用什么来存放线程A对应终端的日志路径,如何根据每个线程来找其对应的日志路径。这里我么可以想象一个对象,他里面有无数个线程,这个线程对应的路径,这里ThreadLocal了解一下。当然现在每个终端的日志都存放到一个文件里,如果再加上一个每个终端的日志都存放到一个文件里,第二天这个文件名字要改成前一天的,要如何做呢?