java log detail - landon30/Bulls GitHub Wiki

关于Java log库、console、stdout、stderr、nohup.out之间的关系

为什么讨论这个问题

  • 因为项目会引用一些第三方库,而第三方库本身会用一些log库,而这些log库可能不是slf4j,如jedis用的是jul(java.util.logging)
  • 而实际线上部署的比如会用nohup会将console的输出包括stdout/stderr都会给清空(重定向到/dev/null),这样对于第三方库的控制台输出就看不到日志了
  • 如果第三方库用了slf4j,那么我们就可以通过logback配置第三方的logger输出了
  • 即不同的脚本启动等会影响三方日志的输出,有可能会吞掉第三方非slf4j的控制台日志

相互之间的关系

  1. console
    • 控制台,可标准输入(stdin),标准输出(stdout),标准错误(stderr)
  2. Java log库和标准输出,标准错误的关系
    • Java的标准输出是System.out,标准错误是System.err
    • 都会输出到控制台(Eclipse控制台标准输出是正常颜色,标准错误是红色)
    • logback
      • 通常logback.xml都会配置一个ConsoleAppender,即控制台输出源
      • ch.qos.logback.core.ConsoleAppender
      • protected ConsoleTarget target = ConsoleTarget.SystemOut
      • 可以看到logback的控制台输出源是System.out,即标准输出
      • 这个也解释了比如我们在eclipse运行项目的时候,eclipse的控制台会标准输出logback所有的日志输出,因为其配置了ConsoleAppender,即相当于用System.out输出
    • jul
      • jul默认的输出源是ConsoleHandler
      • java.util.logging.ConsoleHandler
      • setOutputStream(System.err)
      • 可以看到jul的控制台输出源是System.err,即标准错误
      • 这个也解释了为什么我们用jul的log进行输出的时候,eclipse控制台输出的都是红色,所以是标准错误
  3. 关于nohup.out
    • 实际项目在操作过程中,通常会用nohup启动程序
    • 默认nohup会将控制的标准输出和标准错误都会被重定向到nohup.out
    • 而我们项目如用logback,其配置了ConsoleAppender,即相当于所有的logback输出都会被重定向到nohup.out(当然可以配置logger等级来减少输出);包括如第三方库用到的日志库jul,其控制台输出源是标准错误,也会被重定向到nohup.out
    • 这样nohup.out会越来越大
    • 注:nohup通常结合&,程序后台运行,所以不会有标准输入的问题
    • 如何解决nohup.out过大问题
      • /dev/null 2>&1 &

      • 这种方式是将标准输出和标准错误全部重定向到/dev/null,即什么信息都不输出,也不会生成nohup.out
      • /dev/null 2>nohup_err.log &

      • 这种方式是只将标准错误重定向到指定的文件中
    • 目前线上是用第一种方式,原因如下
      • 目前项目中所有日志的输出,都走logback, 会配置xxFileAppender,即输出到文件中,所以nohup.out中的控制台的相关输出确实可以都不要
      • 另外线上的logback.xml通常可以关闭掉ConsoleAppender
      • 但是这种方式有一种问题就是第三方的非slf4j的日志输出会丢失,有一些可能的错误输出等.那么如果采用第二种方式呢,将标准错误保留呢?也有问题,因为有的日志库的输出全部是system.err,即如果三方库调用很频繁或者日志很大,那么也可能会导致nohup相关日志很大

总结1

  • 对于标准输出和标准错误的重定向完全要看日志库的console输出流实现方式是什么
  • 对于第三方库的日志是否需要输出
    • 个人建议第一步确认是否是slf4j实现,如果是,那么可以自己控制输出
    • 如果不是 那么建议在上线前,即开发环境下保留nohup.out,即保留所有的控制台输出(包括第三方输出和项目输出)然后可以去看一下是否有第三方库自己的异常产生等
    • 当然这个仅是个人保留意见 值得商榷 因为实际上好的第三方库第一基本是用slf4j的,第二是即使他出现了异常,通常自己记录日志,也会继续向上抛出到项目层的
    • 所以第三方的库日志其实无所谓

总结2

  • 回归到一个实际案例,项目中对于引用的jedis库,其用到了jul,其启动的时候可能会抛出一个异常,其用jul记录了,默认输出到了控制台(stderr)
  • 如果我们nohup启动的时候将stdout和stderr全部重定向/dev/null,那么关于jedis的这个日志我们是无法看到的
  • 所以正因为如jedis#jul的控制台输出是stderr,我们可以nohup启动的时候只重定向stderr到一个日志文件,只要我们就可以在这个日志文件看到如jedis的输出了

总结3

  • 如果上面说的两种方式都不想用,那么可以引入第三方库
  • jul-to-slf4j
  • sysout-over-slf4j
  • 这两个第三方库可以引入后增加一些代码可以实现to slf4j

总结4

  • 关于e.printstacktrace
  • printStackTrace(System.err)
  • 该方法是标准错误,很多人可能习惯这样输出异常,但是线上项目千万不要
  • 因为如之前说的,这种标准错误会被重定向清空的
  • 所以一定要用slf4j记录日志异常
  • 需要检查项目代码,是否有类似的代码
  • 同理System.out的代码也不能有