java堆栈日志打印错误,如何验证(使用单元测试)错误堆栈已打印在日志文件中?...

In continuing to this answer I wrote a unit test to verify that in case of error, the stack will be printed in the log file.

The tested method:

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

private final Logger logger = LoggerFactory.getLogger(getClass());

public long getFq(String fi) {

try {

return calcSomeThing(fi.toLowerCase());

} catch (Exception e) {

logger.error("unable to calculate SomeThing. Error: "

, e);

return -1;

}

}

The unit test:

import ch.qos.logback.classic.Level;

import ch.qos.logback.classic.Logger;

import ch.qos.logback.classic.spi.ILoggingEvent;

import ch.qos.logback.core.read.ListAppender;

import org.slf4j.LoggerFactory;

@Test

public void getFileQualifier() {

// get Logback Logger

Logger logger = (Logger) LoggerFactory.getLogger(QService.class);

// create and start a ListAppender

ListAppender listAppender = new ListAppender<>();

listAppender.start();

// add the appender to the logger

// addAppender is outdated now

logger.addAppender(listAppender);

// call method under test

QService.getFq(null);

// JUnit assertions

List logsList = listAppender.list;

Assert.assertEquals("unable to calculate SomeThing. Error: ", logsList.get(0)

.getFormattedMessage());

Assert.assertEquals(Level.ERROR, logsList.get(0)

.getLevel());

Assert.assertEquals("java.lang.NullPointerException: null", logsList.get(1)

.getMessage());

Assert.assertEquals(Level.ERROR, logsList.get(1)

.getLevel());

Assert.assertThat("(QService.java", containsString(logsList.get(2)

.getMessage()));

Assert.assertEquals(Level.ERROR, logsList.get(2)

.getLevel());

}

Well, although I can see the stack is indeed printed in the log file, the unit test failed because of the

logsList contains only one item (the first printed line only [unable to calculate SomeThing. Error: ]).

java.lang.IndexOutOfBoundsException: Index 1 out of bounds for length 1

Why does it happen and how it can be tested?

EDIT

Answers:

the answers (all are quotes from @Gavin's answer and comment, thank you):

On the first question (Why does it happen) the answer is:

It looks to me that exceptions is stored separately from the message in the log event

On the second question (how it can be tested) the answer is:

to find what you are looking for in the list of log events and can be expressed in a manner suitable to your domain, e.g checking the that a Throwable was logged, perhaps looking in org.apache.log4j.spi.LoggingEvent for appropriate methods

Finally, my code to verify it was:

Assert.assertEquals(logsList.get(0).getThrowableProxy().getClassName(), "java.lang.NullPointerException");

解决方案

This is how I have captured log messages in the past, this is based on an old blog (not written by me) that no longer seems to be available.

It is quite old code written for Java 7/8 and Junit4.

I will try to keep this short :)

First you need an Appender it is probably best to extend the AppenderSkeleton, something like:

public class RuleAppender extends AppenderSkeleton {

private final List loggingEvents = new ArrayList<>();

protected RuleAppender() {

super(true);

this.setThreshold(Level.TRACE);

setName("Rule Appender");

}

@Override

public void close() {

// No op in this case

}

@Override

public boolean requiresLayout() {

return false;

}

@Override

protected void append(final LoggingEvent event) {

loggingEvents.add(event);

}

public boolean hasEventsMeeting(LogExpectation logExpectation) {

// Use the LogExpectation to determine if the list of log events contains what you want.

}

@Override

public String toString() {

return "RuleAppender";

}

}

The LogExpectation is simply somewhere too define an expectation/criteria to match against the stored log events.

This is then wrapped up in a Junit Rule to make adding the the Appender to the Log4J a little easier, I did this by implementing TestRule and extending Statement, ensuring the first thing Statements evaluate method does is:

LogManager.getRootLogger().addAppender(ruleAppender);

LogManager.getRootLogger().setLevel(Level.ALL);

Notes:

This can be done without a JUnit rule, so long as the above two lines are executed before the test in order to ensure the appending is added to Log4J (The custom appender is still required).

I have not gone into the JUnit rule code, as we probably should be moving to JUnit 5 which doesnt support Rules and I have not gone into LogExpecation as this is simply away to find what you are looking for in the list of log events and can be expressed in a manner suitable to your domain, e.g checking the that a Throwable was logged, perhaps looking in org.apache.log4j.spi.LoggingEvent for appropriate methods

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
anylog 是一个可以在代码的任意区域无入侵地加入日志的工具,适用于线上问题排查。 anylog 为开发人员提供一个易于使用的平台,帮助开发人员在正在运行的系统随时加入自己想要的日志,而免于修改代码和重启。 使用场景举例     1、一些同学在写代码时,把异常吃掉了,使得问题难以查找,可以使用这个工具,动态打印出被吃掉         的异常,而不用停机。     2、一些项目依赖第三方jar包,如果发生问题,但第三方包日志打印,以往可能需要重新编译第         三方包,加上日志,重启服务,然后排查问题。但使用这个工具,就可以直接动态加入日志,而不用         修改第三方jar包,也不用重启。 已有功能     1、让系统打印某个exception的堆栈,无论此exception是否已经被吃掉都可打印     2、在某个指定类的某个方法的某一行,输出日志。     3、在某个指定类的某个方法的开始,输出日志。     4、在某个指定类的某个方法的结束,输出日志。       5、打印方法耗时,支持方法嵌套。     如果需要扩展新的功能(例如输出jvm的cpu占用,内存大小等),只需要实现spi的     com.github.jobop.anylog.spi.TransformDescriptor      和com.github.jobop.anylog.spi.TransformHandler接口,     然后把实现的jar包放到providers目录即可识别。 使用方法     1、获取运行程序:         1)可以到以下地址获取正式发行版:https://github.com/jobop/release/tree/master/anylog         2)你也可以clone下源码后,执行如下命令,生成运行程序,生成的运行程序将在dist目录下             生成windows版本:  mvn install             生成linux版本:  mvn install -Plinux     2、直接执行startup.bat或者startup.sh即可运行起来     3、访问 http://127.0.0.1:52808 即可使用 功能扩展     anylog利用spi机制实现其扩展,如果你想要对anylog增加新的功能(例如添加返回值打印的功能)可以按照如下步骤操作:     1、使用如下命令,生成一个spi实现工程,并导入eclipse     mvn archetype:generate -DarchetypeGroupId=com.github.jobop -DarchetypeArtifactId=anylogspi-archetype -DarchetypeVersion=1.0.4     2、参照该工程已有的两个例子(一个是在方法开始插入日志,一个是在方法结束插入日志),实现TransformDescriptor和TransformHandler接口     3、把两个接口实现类的全路径,分别加到以下两个文件         src/main/resources/META-INF/services/com.github.jobop.anylog.spi.TransformDescriptor         src/main/resources/META-INF/services/com.github.jobop.anylog.spi.TransformHandler     4、执行mvn install打包,在dist下会生成你的扩展实现jar。     5、把扩展实现jar拷贝到anylog的providers目录下,重启即可生效。     tips:在实现spi时,我们提供了SpiDesc注解,该注解作用在你实现的TransformDescriptor上,可以用来生成功能描述文字。          如果要深入了解spi机制,请自行google:java spi 标签:anylog
开发工具在软件开发生命周期扮演着至关重要的角色,它们旨在简化和加速从概念设计到产品部署的各个环节。以下是开发工具的主要作用: 代码编写与编辑: 提供集成开发环境(IDE),如Visual Studio、Eclipse、Android Studio和Sublime Text等,这些工具集成了文本编辑器,支持语法高亮、自动补全、代码片段管理和版本控制等功能,有助于开发者高效编写和维护代码。 项目管理: 支持项目创建、组织、构建自动化以及依赖管理,确保不同模块和组件之间的协调一致。 编译与构建: 包括编译器、构建工具(如Make、Gradle、Maven)等,用于将源代码转换为可执行文件或库,并进行资源打包、优化等处理。 调试与测试: 集成调试器允许开发者逐行执行代码,设置断点、查看变量值、跟踪调用堆栈等,帮助定位并修复代码错误。 测试框架和工具则协助开发者编写和运行单元测试、集成测试及性能测试,确保软件质量。 版本控制与协作: 通过集成Git、SVN等版本控制系统,支持团队成员间的代码共享、分支管理、合并请求和冲突解决。 可视化设计与原型制作: 对于UI/UX设计,有界面设计工具,如Sketch、Adobe XD,可以帮助设计师快速构建应用程序界面模型,并生成规范的设计稿供开发人员参考实现。 跨平台支持: 跨平台开发工具如Xamarin、React Native和Flutter,让开发者使用一种语言或框架编写可以在多个操作系统上运行的应用程序。 文档编写与API管理: 文档生成工具可以自动生成代码注释文档,便于团队内外理解和使用项目代码。 API管理工具则方便开发者创建、测试、发布和维护API接口。 持续集成与持续部署(CI/CD): Jenkins、Travis CI、GitHub Actions等工具负责自动化构建、测试和部署流程,提高交付效率和可靠性。 数据库管理与ORM工具: 数据库客户端工具用于连接、查询、更新数据库,ORM(对象关系映射)工具简化了数据操作和持久化层的开发工作。 总之,开发工具极大地提升了软件工程师的工作效率,保证了开发过程的准确性与一致性,同时也促进了团队合作,使得软件开发更系统化、规范化和工业化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值