资讯专栏INFORMATION COLUMN

Log4j2 在 Windows 下导致 System.out 失效

Harpsichord1207 / 3725人阅读

摘要:此问题已经在里面修复,详见今天在下调试这几天写的一个命令行程序,发现在在一种情况下会在下会出现无法输出到终端的情况,花了几个小时去排查这个问题,这里分享一下。

</>复制代码

  1. 此问题已经在 Log4j2 2.3 里面修复,详见 https://issues.apache.org/jira/browse/LOG4J2-965

今天在 Windows 下调试这几天写的一个命令行程序,发现在 Log4j2 在一种情况下会在 Windows 下会出现 System.out.println("XXXX") 无法输出到终端的情况,花了几个小时去排查这个问题,这里分享一下。

1. 问题还原

为了简化问题,我尽量用少的代码来重现出这个 Bug,首先是工程的 build.gradle 文件:

</>复制代码

  1. apply plugin: "java"
  2. version = "1.0"
  3. repositories {
  4. mavenCentral()
  5. }
  6. def log4j2Version = "2.2"
  7. def log4j2GroupId = "org.apache.logging.log4j"
  8. dependencies {
  9. compile log4j2GroupId + ":log4j-core:" + log4j2Version
  10. compile log4j2GroupId + ":log4j-jcl:" + log4j2Version
  11. compile log4j2GroupId + ":log4j-slf4j-impl:" + log4j2Version
  12. compile "org.fusesource.jansi:jansi:1.11"
  13. }

一个位于 src/main/resources 目录下的 log4j2.xml

</>复制代码

  1. %d %p %c{1.} [%t] %m%n

然后是重现问题的代码:

</>复制代码

  1. import org.slf4j.LoggerFactory;
  2. /**
  3. * @author khotyn 15/3/2 下午8:17
  4. */
  5. public class Log4j2WindowsBug {
  6. public static void main(String[] args) {
  7. System.out.println("Able to print on Windows");
  8. LoggerFactory.getLogger(Log4j2WindowsBug.class);
  9. System.out.println("Unable to print on Windows");
  10. }
  11. }

这段代码在 Windows 下的运行结果是:

</>复制代码

  1. Able to print on Windows

getLogger 后面的那一句 System.out 并没有输出。

2. 问题原因

刚开始遇到这个问题的时候非常震惊,因为觉得 System.out.println 应该是 Java 最基本的功能了,遇到这样的问题,瞬间让我觉得人生完整了。在经过一阵 Debug 以后,发现执行第三行代码的时候,System.out 这个 PrintWriter里面的 out 成员变量为 null 了,然后就导致了 println 方法在检查 out 是否为 null 的时候抛了一个异常:

</>复制代码

  1. /** Checks to make sure that the stream has not been closed */
  2. private void ensureOpen() throws IOException {
  3. if (out == null)
  4. throw new IOException("Stream closed");
  5. }

那么到底是什么把 out 给置为 null 了呢。经过了一段时间的 Debug,发现了在 Windows 下,如果 ClassPath 下有 org.fusesource.jansi.WindowsAnsiOutputStream 这个类的话,Log4j2 会将用这个类将 System.out 包装起来(按照 Log4j2 的说明,这是是为了在 Windows 下的 Console 上支持彩色字符):

然后,在 log4j2 里面,不管在 ClassPath 下有没有 log4j2.xml 或者方式的配置,它都会先初始化一个 ConsoleAppender,如果后面发现有诸如 log4j2.xml 这样的配置,那么就进行 reconfigure,我们看下 log4j2 里面的 LoggerContext 类的 reconfigure 方法:

主要看它所调用到的 setConfiguration 方法:

在这个方法里面,如果发现之前有了配置(就是默认的 ConsoleAppender),就会尝试关闭它,然后继续跟踪 prev.stop 这段代码,发现它下面会走到 OutPutStreamManager 的这段代码:

只有当 outputStream 是 System.out 或者 System.err 的时候,才不会关闭,但是如果是 System.out 的封装,就比如我们这个场景中的 WindowsAnsiOutputStream,就被关闭了,进而导致后续的 System.out.println 都无效。

3. 解决方法

其实细心的话,在上面的截图的代码中就可以看到解决方法了,要解决这个问题,只需要在 log4j2 初始化之前执行下面这段代码

</>复制代码

  1. System.setProperty("log4j.skipJansi", true)

不过,这个方法只有像我这样其实对于 log4j 时候采用 Jansi 的封装无所谓的人才算有用。如果有所谓的话,那么似乎只能坐等官方修 Bug 了(https://issues.apache.org/jira/browse/LOG4J2-965)。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64252.html

相关文章

  • log4j2搭建并使用

    摘要:作为一个实用主义者,我喜欢在理解基本原理后快速的搭建系统,当系统运行起的时候有那种愉悦和兴奋。,着手搭建,我用的是进行的。要使用日志系统,就需要进行相关配置,这个不用我多说了叁。 作为一个实用主义者,我喜欢在理解基本原理后快速的搭建系统,当系统运行起的时候有那种愉悦和兴奋。最近在完善公司框架,从最基本的日志系统开始。 java日志系统比较流行的是log4j,slf4j和logbac...

    lauren_liuling 评论0 收藏0
  • java判断百度云分享链接是否失效

    我不知道现在有多少人在用网盘搜索引擎,但就去转盘网来说本人倾注了很多的心血,现在使用的人数也还可以,网盘资源都有个通病,那就是资源可能失效,但很多引擎都没有做失效判断,尤其是一些google自定义的引擎,技术含量不高,站长也就花心思赚钱,很少考虑用户体验。这篇文章是本人又一篇技术公开博客,之前本人已经公开了去转盘 网的几乎所有的技术细节,这一篇继续补充: 首先做个回顾:百度网盘爬虫 java分词...

    kid143 评论0 收藏0
  • java判断百度云分享链接是否失效

    我不知道现在有多少人在用网盘搜索引擎,但就去转盘网来说本人倾注了很多的心血,现在使用的人数也还可以,网盘资源都有个通病,那就是资源可能失效,但很多引擎都没有做失效判断,尤其是一些google自定义的引擎,技术含量不高,站长也就花心思赚钱,很少考虑用户体验。这篇文章是本人又一篇技术公开博客,之前本人已经公开了去转盘 网的几乎所有的技术细节,这一篇继续补充: 首先做个回顾:百度网盘爬虫 java分词...

    chanthuang 评论0 收藏0
  • SpringBoot(三)日志

    摘要:日志消息,是换行符如果使用作为日志配置文件,还要使用功能,会有以下错误切换日志框架可以按照的日志适配图,进行相关的切换的方式切换为 三、日志 1、日志框架 小张;开发一个大型系统; 1、System.out.println();将关键数据打印在控制台;去掉?写在一个文件? 2、框架来记录系统的一些运行时信息;日志框架 ; zhanglogging.jar; 3、高大上的几个功能?异步...

    arashicage 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<