摘要:此问题已经在里面修复,详见今天在下调试这几天写的一个命令行程序,发现在在一种情况下会在下会出现无法输出到终端的情况,花了几个小时去排查这个问题,这里分享一下。
此问题已经在 Log4j2 2.3 里面修复,详见 https://issues.apache.org/jira/browse/LOG4J2-965
今天在 Windows 下调试这几天写的一个命令行程序,发现在 Log4j2 在一种情况下会在 Windows 下会出现 System.out.println("XXXX") 无法输出到终端的情况,花了几个小时去排查这个问题,这里分享一下。
1. 问题还原为了简化问题,我尽量用少的代码来重现出这个 Bug,首先是工程的 build.gradle 文件:
apply plugin: "java" version = "1.0" repositories { mavenCentral() } def log4j2Version = "2.2" def log4j2GroupId = "org.apache.logging.log4j" dependencies { compile log4j2GroupId + ":log4j-core:" + log4j2Version compile log4j2GroupId + ":log4j-jcl:" + log4j2Version compile log4j2GroupId + ":log4j-slf4j-impl:" + log4j2Version compile "org.fusesource.jansi:jansi:1.11" }
一个位于 src/main/resources 目录下的 log4j2.xml
%d %p %c{1.} [%t] %m%n
然后是重现问题的代码:
import org.slf4j.LoggerFactory; /** * @author khotyn 15/3/2 下午8:17 */ public class Log4j2WindowsBug { public static void main(String[] args) { System.out.println("Able to print on Windows"); LoggerFactory.getLogger(Log4j2WindowsBug.class); System.out.println("Unable to print on Windows"); } }
这段代码在 Windows 下的运行结果是:
Able to print on Windows
在 getLogger 后面的那一句 System.out 并没有输出。
2. 问题原因刚开始遇到这个问题的时候非常震惊,因为觉得 System.out.println 应该是 Java 最基本的功能了,遇到这样的问题,瞬间让我觉得人生完整了。在经过一阵 Debug 以后,发现执行第三行代码的时候,System.out 这个 PrintWriter里面的 out 成员变量为 null 了,然后就导致了 println 方法在检查 out 是否为 null 的时候抛了一个异常:
/** Checks to make sure that the stream has not been closed */ private void ensureOpen() throws IOException { if (out == null) throw new IOException("Stream closed"); }
那么到底是什么把 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 初始化之前执行下面这段代码
System.setProperty("log4j.skipJansi", true)
不过,这个方法只有像我这样其实对于 log4j 时候采用 Jansi 的封装无所谓的人才算有用。如果有所谓的话,那么似乎只能坐等官方修 Bug 了(https://issues.apache.org/jira/browse/LOG4J2-965)。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/64252.html
摘要:作为一个实用主义者,我喜欢在理解基本原理后快速的搭建系统,当系统运行起的时候有那种愉悦和兴奋。,着手搭建,我用的是进行的。要使用日志系统,就需要进行相关配置,这个不用我多说了叁。 作为一个实用主义者,我喜欢在理解基本原理后快速的搭建系统,当系统运行起的时候有那种愉悦和兴奋。最近在完善公司框架,从最基本的日志系统开始。 java日志系统比较流行的是log4j,slf4j和logbac...
我不知道现在有多少人在用网盘搜索引擎,但就去转盘网来说本人倾注了很多的心血,现在使用的人数也还可以,网盘资源都有个通病,那就是资源可能失效,但很多引擎都没有做失效判断,尤其是一些google自定义的引擎,技术含量不高,站长也就花心思赚钱,很少考虑用户体验。这篇文章是本人又一篇技术公开博客,之前本人已经公开了去转盘 网的几乎所有的技术细节,这一篇继续补充: 首先做个回顾:百度网盘爬虫 java分词...
我不知道现在有多少人在用网盘搜索引擎,但就去转盘网来说本人倾注了很多的心血,现在使用的人数也还可以,网盘资源都有个通病,那就是资源可能失效,但很多引擎都没有做失效判断,尤其是一些google自定义的引擎,技术含量不高,站长也就花心思赚钱,很少考虑用户体验。这篇文章是本人又一篇技术公开博客,之前本人已经公开了去转盘 网的几乎所有的技术细节,这一篇继续补充: 首先做个回顾:百度网盘爬虫 java分词...
摘要:日志消息,是换行符如果使用作为日志配置文件,还要使用功能,会有以下错误切换日志框架可以按照的日志适配图,进行相关的切换的方式切换为 三、日志 1、日志框架 小张;开发一个大型系统; 1、System.out.println();将关键数据打印在控制台;去掉?写在一个文件? 2、框架来记录系统的一些运行时信息;日志框架 ; zhanglogging.jar; 3、高大上的几个功能?异步...
阅读 3467·2021-10-18 13:30
阅读 2902·2021-10-09 09:44
阅读 1952·2019-08-30 11:26
阅读 2271·2019-08-29 13:17
阅读 744·2019-08-29 12:17
阅读 2226·2019-08-26 18:42
阅读 457·2019-08-26 13:24
阅读 2892·2019-08-26 11:39