为了记录日志,我创建了一个方法logTitle(),它打印出TestNG测试的调用方法名称。示例代码如下。
@Test
public void test1() throws Exception {
method1();
}
public static void method1() throws Exception {
Utils.logTitle(2);
}
...
public static void logTitle(Integer level) throws Exception {
// Gets calling method name
String method = Thread.currentThread().getStackTrace()[2].getMethodName();
// This would get current method name
switch (level) {
case 1:
logger.info("=======================================================");
logger.info(method);
logger.info("=======================================================");
break;
case 2:
logger.info("------------------------------------");
logger.info(method);
logger.info("------------------------------------");
break;
case 3:
logger.info("---------------------");
logger.info(method);
logger.info("---------------------");
break;
case 4:
logger.info("--------- " + method + " ------------");
break;
default:
logger.info(method);
}
}
问题是我在两台不同的机器上获得了不同的logTitle()结果。
每个人的笔记本电脑都正确返回:
2016-06-20 14:22:06 INFO - ------------------------------------
2016-06-20 14:22:06 INFO - method1
2016-06-20 14:22:06 INFO - ------------------------------------
我们的dev unix框以不同的方式返回:
2016-06-20 14:42:26 INFO - ------------------------------------
2016-06-20 14:42:26 INFO - logTitle
2016-06-20 14:42:26 INFO - ------------------------------------
这可以在其他人的笔记本电脑上正常工作,而不是dev unix框。我认为dev unix框使用的是IBM的Java版本,而其他所有人都在使用Oracle的Java版本,但不确定这是否是罪魁祸首。
有什么想法吗?
答案 0 :(得分:4)
获得测试方法名称的简单方法是使用@BeforeMethod
并注入Method
。请参阅TestNG的文档here。
只需将名称存储在某处并在日志中使用(为什么不在@AfterMethod
中?)
答案 1 :(得分:4)
来自Javadoc:
在某些情况下,某些虚拟机可能会从堆栈跟踪中省略一个或多个堆栈帧。在极端情况下,允许没有关于此throwable的堆栈跟踪信息的虚拟机从此方法返回零长度数组。
因此,唯一有保证的方法是使用方面,或者使用其他自定义方式收集堆栈跟踪。但是,您可以将此方法与回退结合起来,以获取当前方法的名称(例如,在您的logTitle
方法将被内联的情况下)。例如,它可以找到here。再一次,不能保证,但更好的机会。
答案 2 :(得分:2)
我的猜测,正如MeBigFatGuy所提到的那样。这可能是因为在进行内联优化方法时,IBM / Oracle JVM的JIT编译器的实现/默认值不同。
我建议使用
在dev unix框中运行代码-Xjit:disableInlining
并查看问题是否消失。
如果这对您有用,那么测试可能没问题,但正如Alexey Adamovskiy中提到的那样,我们不能相信java包含在堆栈帧中。
另见:
答案 3 :(得分:1)
我猜这种行为是特定于JVM的。在过去,我提出了这个解决方案:
// find first stack trace entry that is not in this class
Optional<StackTraceElement> ste = Iterables.tryFind(
Arrays.asList(new RuntimeException().getStackTrace()),
new Predicate<StackTraceElement>() {
@Override
public boolean apply(StackTraceElement input) {
return !input.getClassName().equals(PutYourClassHere.class.getName());
}
});
if (ste.isPresent()) {
LOG.trace("Method called by: {}.{}", ste.get().getClassName(), ste.get().getMethodName());
}
该片段使用的是Google Guava,因为它适用于Java 7.如果您使用的是Java 8,则可以使用Streams API和lambdas。我进行了ste.isPresent()
检查,因为我遇到了一次空堆栈跟踪。据我记得,当反复抛出相同的异常时,Oracle JVM正在跳过堆栈跟踪。
编辑: Java 8方式
Optional<StackTraceElement> ste = Arrays.stream(new RuntimeException().getStackTrace())
.filter(x -> !x.getClassName().equals(Utils.class.getName()))
.findFirst();
答案 4 :(得分:1)
我认为它导致问题的具体深度在您的方案中为2。
所以,而不是写
String method = Thread.currentThread().getStackTrace()[2].getMethodName();
如果你写
StackTraceElement[] ste = Thread.currentThread().getStackTrace();
String method = null;
boolean doNext = false;
for (StackTraceElement s : ste) {
if (doNext) {
method = s.getMethodName();
return;
}
doNext = s.getMethodName().equals("getStackTrace");
}
它仅适用于JDK 1.5 +
另一个选项如下:
String method = new Object(){}.getClass().getEnclosingMethod().getName();
或者较慢的选项是:
String method = new Exception().getStackTrace()[0].getMethodName();
因为这会每次都创建一个Exception实例。
希望能帮到你。
答案 5 :(得分:0)
issue 23177通过搜索堆栈跟踪直到找到必须传入的目标类名,然后读取方法名称。
在你的代码中,你可以使用类似的技术 - 而不是静态方法Utils你可以在你的测试中创建一个实例,传入测试的类:
Utils utils = new Utils(MyTest.class);
然后在Utils.logTitle()
方法中使用前面提到的搜索技术。
Utils.logTitle()
将搜索新创建的Throwable的堆栈跟踪元素,直到找到具有所需目标类的第一个元素。
答案 6 :(得分:0)
Log4j 2使用Logger的完全限定类名来查找调用Logger的类和方法。找到位置的代码如下。随意使用它。
请注意,循环从stacktrace的底部开始;这对于检测以递归方式调用记录器的异常情况(可能来自记录的对象的toString()
方法)是必要的。在这种情况下,我们想要报告调用Logger的第一个类/方法,而不是最后一个,所以我们别无选择,只能从下往上走堆栈跟踪。
public static StackTraceElement calcLocation(final String fqcnOfLogger) {
if (fqcnOfLogger == null) {
return null;
}
// LOG4J2-1029 new Throwable().getStackTrace is faster
// than Thread.currentThread().getStackTrace().
final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
StackTraceElement last = null;
for (int i = stackTrace.length - 1; i > 0; i--) {
final String className = stackTrace[i].getClassName();
if (fqcnOfLogger.equals(className)) {
return last;
}
last = stackTrace[i];
}
return null;
}