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
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值