仅获取堆栈跟踪的前 N 行
Posted
技术标签:
【中文标题】仅获取堆栈跟踪的前 N 行【英文标题】:Fetch only first N lines of a Stack Trace 【发布时间】:2014-03-09 12:38:10 【问题描述】:我有一个从 ID 调用返回对象的工厂方法。
模拟代码:
public static Object getById(String id)
Object o = CRUD.doRecovery(Class, id);
if(o == null)
printLogMessage("recovery by ID returned Null: " + id);
// would really like to show only a few lines of stack trace.
return o;
如何只显示堆栈跟踪的前 N 行(这样我就知道该方法的调用者),而无需将整个堆栈跟踪转储到日志上或不必依赖外部库?
【问题讨论】:
请不要使用堆栈跟踪来了解方法的调用者。它非常昂贵(比较this question,它也有一些分析数据。)。而是使用第二个参数,并通过合同要求您的调用者使用一些有意义的自我识别 ID。 【参考方案1】:以下链接中的片段适用于 N 行。
低于 sn-p 有助于剥离异常堆栈跟踪。
public class LoggerHelper
private static final String SEPARATOR = "\r\n";
private static final String CAUSE_CAPTION = "Caused by: ";
private static final String SUPPRESSED_CAPTION = "Suppressed: ";
/**
* The first 10 lines of exception stack information are returned by default
*
* @param e
* @return
*/
public static String printTop10StackTrace(Throwable e)
if (e == null)
return "";
return printStackTrace(e, 20);
public static String printStackTrace(Throwable e, int maxLineCount)
if (e == null || maxLineCount <= 0)
return "";
StringBuilder sb = new StringBuilder(maxLineCount * 10);
sb.append(e.toString()).append(SEPARATOR);
StackTraceElement[] trace = e.getStackTrace();
if (trace == null)
return e.toString();
int count = maxLineCount > trace.length ? trace.length : maxLineCount;
int framesInCommon = trace.length - count;
for (int i = 0; i < count; i++)
sb.append("\tat ").append(trace[i]).append(SEPARATOR);
if (framesInCommon != 0)
sb.append("\t... ").append(framesInCommon).append(" more").append(SEPARATOR);
// Print suppressed exceptions, if any
Throwable[] suppressedExceptions = e.getSuppressed();
if (ArrayUtils.isNotEmpty(suppressedExceptions))
for (Throwable suppressedException : suppressedExceptions)
sb.append(printEnclosedStackTrace(suppressedException, maxLineCount, trace, SUPPRESSED_CAPTION, "\t"));
// Print cause, if any
Throwable cause = e.getCause();
if (cause != null)
sb.append(printEnclosedStackTrace(cause, maxLineCount, trace, CAUSE_CAPTION, ""));
return sb.toString();
private static String printEnclosedStackTrace(Throwable e, int maxLineCount, StackTraceElement[] enclosingTrace,
String caption, String prefix)
StringBuilder sb = new StringBuilder(maxLineCount * 5);
StackTraceElement[] trace = e.getStackTrace();
int m = trace.length - 1;
int n = enclosingTrace.length - 1;
while (m >= 0 && n >= 0 && trace[m].equals(enclosingTrace[n]))
m--;
n--;
int count = maxLineCount > (m + 1) ? (m + 1) : maxLineCount;
int framesInCommon = trace.length - count;
// Print our stack trace
sb.append(prefix).append(caption).append(e.toString()).append(SEPARATOR);
for (int i = 0; i < count; i++)
sb.append(prefix).append("\tat ").append(trace[i]).append(SEPARATOR);
if (framesInCommon != 0)
sb.append(prefix).append("\t... ").append(framesInCommon).append(" more").append(SEPARATOR);
// Print suppressed exceptions, if any
Throwable[] suppressedExceptions = e.getSuppressed();
if (ArrayUtils.isNotEmpty(suppressedExceptions))
for (Throwable suppressedException : suppressedExceptions)
sb.append(printEnclosedStackTrace(suppressedException, maxLineCount, trace, SUPPRESSED_CAPTION, prefix + "\t"));
// Print cause, if any
Throwable cause = e.getCause();
if (cause != null)
sb.append(printEnclosedStackTrace(cause, maxLineCount, trace, CAUSE_CAPTION, prefix));
return sb.toString();
【讨论】:
欢迎提供指向解决方案的链接,但请确保您的答案在没有它的情况下有用:add context around the link 这样您的其他用户就会知道它是什么以及为什么存在,然后引用最多您链接到的页面的相关部分,以防目标页面不可用。 Answers that are little more than a link may be deleted.【参考方案2】:显示第五行的示例:
final int nbLinesToShow = 5;
try
/* your code here */
catch (final NullPointerException e)
// catch error
final StackTraceElement[] elements = e.getStackTrace();
System.err.println(
"===================================== \n" + "[ERROR] lorem ipsum");
for (int i = 0; i < nbLinesToShow; i++)
System.err.println(elements[i]);
【讨论】:
提出的问题没有抛出异常;因此,这不是给定问题的答案。【参考方案3】:对于 e.printStackTrace() 的缩写版本:
Exception e = ...
System.out.println(e.toString());
StackTraceElement[] elements = e.getStackTrace();
for(int i = 0; i<elements.length && i < STACK_TRACE_LIMIT; i++)
System.out.println("\tat "+elements[i]);
将 STACK_TRACE_LIMIT
替换为您想要的限制或删除 && i < STACK_TRACE_LIMIT
以重现简单堆栈跟踪的输出(例如,没有嵌套异常)
最内层的方法调用位于索引 0,main 位于索引长度 1。
【讨论】:
【参考方案4】:番石榴可以提供帮助。例如,我们只想查看前十行:
log.error("Error:", Joiner.on("\n").join(Iterables.limit(asList(ex.getStackTrace()), 10)));
【讨论】:
它应该是Joiner.on("\n").join(Iterables.limit(Arrays.asList(ex.getStackTrace()), 10))
【参考方案5】:
此方法显示堆栈跟踪的i
行,跳过前两行。
public static String traceCaller(Exception ex, int i)
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
StringBuilder sb = new StringBuilder();
ex.printStackTrace(pw);
String ss = sw.toString();
String[] splitted = ss.split("\n");
sb.append("\n");
if(splitted.length > 2 + i)
for(int x = 2; x < i+2; x++)
sb.append(splitted[x].trim());
sb.append("\n");
return sb.toString();
return "Trace too Short.";
前两行是异常名称和调用traceCaller()
的方法。如果要显示这些行,请对其进行调整。
感谢 @BrianAgnew (***.com/a/1149712/1532705) 提供 StringWriter PrintWriter 的想法
【讨论】:
感谢@BrianAgnew (***.com/a/1149712/1532705) 提出StringWriter
PrintWriter
的想法。
IMO,您应该将此评论作为您答案的一部分。【参考方案6】:
如果您只想截断堆栈跟踪,您可以将整个堆栈跟踪打印到 StringWriter,然后删除您不想要的:
public static void main(String[] args) throws ParseException
try
throw new Exception("Argh!");
catch (Exception e)
System.err.println(shortenedStackTrace(e, 1));
public static String shortenedStackTrace(Exception e, int maxLines)
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
String[] lines = writer.toString().split("\n");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < Math.min(lines.length, maxLines); i++)
sb.append(lines[i]).append("\n");
return sb.toString();
或者,使用e.getStackTrace()
获取StackTraceElement[]
数组。这为您提供调用者堆栈(从内部到外部),但不提供错误消息。您必须使用 e.getMessage()
来获取错误消息。
一些日志框架可以配置为自动截断堆栈跟踪。例如。有关 log4j 配置,请参阅this question and answer。
如果您只想查看代码中任意位置的堆栈跟踪,可以从Thread.currentThread()
对象中获取元素:
Thread.currentThread().getStackTrace();
【讨论】:
"无需依赖外部库"【参考方案7】:根据您的要求,我假设您没有要处理的异常。在这种情况下,您可以从以下位置获取当前堆栈跟踪:
StackTraceElement[] elements = Thread.currentThread().getStackTrace()
这将告诉你几乎所有你需要知道的关于你在代码中来自哪里的信息。
【讨论】:
问题中要求的非异常用例的最全面答案。只需以您方便的格式输出“元素”数组的前几个元素。提醒一句,Stacktrace 的计算成本异常。您正在查看 4 个数量级的性能恶化。不要将其用于高性能功能!进行分析,然后将这个想法放入 bin 并传递一些标识符作为参数。然后,每个调用者都必须使用某种自识别参数来通过合约调用该方法。不那么方便,但要快得多。 性能不是问题,因为我们正在处理逻辑异常,例如当应用程序的某些部分引用不存在的实体 ID 时。然后我们可以确定罪犯。我认为让调用者标识自己是一个额外的耦合层,我宁愿用性能来付出代价。 但是mikea的回答很优雅,我在绿化它。 最内层的方法调用位于索引 0,main 位于索引长度 1。请参阅my answer 以获取重现堆栈跟踪的示例(如果您碰巧拥有完整的异常对象)。【参考方案8】:您可以使用ex.getStackTrace()
获取堆栈元素,StackTraceElement
包含一行完整堆栈,然后 print print 你想要什么。
StackTraceElement[] elements = ex.getStackTrace();
print(elements[0]);
【讨论】:
以上是关于仅获取堆栈跟踪的前 N 行的主要内容,如果未能解决你的问题,请参考以下文章