多应用(进程)使用同一个log4j配置文件被覆盖问题

1,649 阅读3分钟

问题描述

今天在排查一个bug的时候发现日志被覆盖了,我们应用以jar包的方式集成了一个运维工具到项目中,应用需要启动两次,每次启动的时候根据参数来区分启动哪一个主程序。

类似如下:

public class App {
    public static void main(String[] args){
        if(args!=null && Objects.equals(args[0],"devOps")){
            // 启动运维模式
            startDevOps(args)
            return ;
        }
        // 启动主应用
        SpringApplication.run(App .class, args);
    }    


}

在使用这个应用的时候,会在启动脚本中,执行两次,分别是启动主应用和运维应用,运维是主应用的代理,可以操作主应用

如:

nohup java -jar xxxApp.jar  # 启动主应用
nohup java -jar xxxApp.jar devOps # 启动运维模式

应用从设计和运行了一段时间都没啥问题,但是今天在查日志的时候,发现主日志文件只有运维工具的日志信息,而没有主应用的日志信息。而且查看其它节点,有的节点日志文件中是主应用的日志,有的是运维日志。让我很纳闷,

最后排查出来,在主应用和运维应用(jar)都用了log4j2打印日志,并且因为运维应用是通过jar继承的,启动入口又是从打包好的主应用jar启动,因此使用的是主应用的log4j2.xml。而在log4j2.xml中配置文件名称一致,它们是不同进程,在日志文件滚动(我们设置的一天一个日志文件),谁先满足新生成文件规则就先占用了这个文件,另一个进程就写不进去了。

2023-05-13-20-43-28-image.png

问题的根源就是两个进程使用了同一个日志配置,导致两个进程操作同一个日志文件,从而造成被覆盖的问题。

问题解决

知道了问题原因要解决就不难了,毕竟办法总比困难多。

第一种,将其中一个应用的在启动的时候使用命令 nohup xxx.jar > xxx.log &指定输出,但是这种指标不治本,当日志发生文件归档的时候,还是会以输出到日志文件中,并且这种无法归档日志文件,导致一个日志文件变得很大。(PASS)

第二种,使用两个日志文件来记录,dev的使用dev日志文件,main的使用main的日志文件。

现在又提出了一个问题,日志文件在打包的时候就已经打包到了jar包中,它们都在同一个jar包中,那怎么来区分呢?

又有两种思路:

1、在启动的时候指定日志文件,在启动的时候加载JAVA_OPT参数:-Dlog4j.configuration="file:/opt/config/log4j2.xml"。如:

    java -Dlog4j.configuration="file:/opt/config/log4j2.xml" -jar xxxApp.jar 我没有使用这种方式,因为我们不想在启动的时候还要带个日志文件,有点繁琐了,而且也不好统一管理,改一个另一个也得改。

2、根据参数输出到不同日志中去,我们使用的是这种

先来看下我们原来的有关日志文件的配置

<RollingFile name="RollingFile" fileName="logs/xxxApp.log"  filePattern="logs/xxxApp-%d{MM-dd-yyyy}-%i.log" append="false">
  <PatternLayout>
      <Pattern>%d{yyyy-MM-dd HH:mm:ss} %p [%t] %m%n</Pattern>
  </PatternLayout>
  <Policies>
     <TimeBasedTriggeringPolicy   interval="1" />
     <SizeBasedTriggeringPolicy size="10 MB"/>
  </Policies>
  <DefaultRolloverStrategy max="5"/>
</RollingFile>

以前运行都会将日志输出当logs目录中,并且名称都是xxxApp.log,会导致两个进程使用一个日志文件。

修改后

<RollingFile name="RollingFile" fileName="logs/xxxApp${env:log4j.logSuffix:-}.log"  filePattern="logs/xxxApp${env:log4j.logSuffix:-}-%d{MM-dd-yyyy}-%i.log" append="false">
  <PatternLayout>
      <Pattern>%d{yyyy-MM-dd HH:mm:ss} %p [%t] %m%n</Pattern>
  </PatternLayout>
  <Policies>
     <TimeBasedTriggeringPolicy />
     <SizeBasedTriggeringPolicy size="10 MB"/>
  </Policies>
  <DefaultRolloverStrategy max="5"/>
</RollingFile>

修改后 ${env:log4j.logSuffix:-} 参数说明:

    ${前缀:变量:-默认值}

  • env:标识引用变量作用域,使用环境变量
  • log4j.logSuffix:指定变量名
  • -:如果没有环境变量则使用默认值,-后面没接任何内容标识默认为空字符串 

修改后的启动命令为:

nohup java -jar xxxApp.jar  # 启动主应用
nohup java -Dlog4j.logSuffix="-devops" -jar xxxApp.jar devOps # 启动运维模式

修改后它们就能够使用同一个日志配置文件,并且分别输出到xxxApp.log和xxxApp-devops.log文件中,互不干扰。