Introduce Throwable.toStringWithTrace

#KT-37603
This commit is contained in:
Ilya Gorbunov
2020-03-18 07:49:26 +03:00
parent 7b7263c5bf
commit 552bcdb31b
7 changed files with 154 additions and 4 deletions
@@ -5,6 +5,18 @@
package kotlin
/**
* Returns the detailed description of this throwable with its stack trace.
*
* The detailed description includes:
* - the short description (see [Throwable.toString]) of this throwable;
* - the complete stack trace;
* - detailed descriptions of the exceptions that were [suppressed][suppressedExceptions] in order to deliver this exception;
* - the detailed description of each throwable in the [Throwable.cause] chain.
*/
@SinceKotlin("1.4")
public actual fun Throwable.toStringWithTrace(): String = buildString { dumpStackTraceTo("", "", this) }
/**
* Adds the specified exception to the list of exceptions that were
* suppressed in order to deliver this exception.
@@ -29,3 +41,42 @@ public actual val Throwable.suppressedExceptions: List<Throwable>
get() {
return this.asDynamic()._suppressed?.unsafeCast<List<Throwable>>() ?: emptyList()
}
private fun Throwable.dumpStackTraceTo(indent: String, qualifier: String, target: StringBuilder) {
this.dumpSelfTrace(indent, qualifier, target)
var cause = this.cause
while (cause != null) {
// TODO: should skip common stack frames
cause.dumpSelfTrace(indent, "Caused by: ", target)
cause = cause.cause
}
}
private fun Throwable.dumpSelfTrace(indent: String, qualifier: String, target: StringBuilder) {
target.append(indent).append(qualifier)
val stack = this.asDynamic().stack as String?
if (stack != null) {
if (indent.isNotEmpty()) {
// indent stack, but avoid indenting exception message lines
val messageLines = 1 + (message?.count { c -> c == '\n' } ?: 0)
stack.lineSequence().forEachIndexed { index: Int, line: String ->
if (index >= messageLines) target.append(indent)
target.append(line).append("\n")
}
} else {
target.append(stack).append("\n")
}
} else {
target.append(this.toString()).append("\n")
}
val suppressed = suppressedExceptions
if (suppressed.isNotEmpty()) {
val suppressedIndent = indent + '\t'
for (s in suppressed) {
s.dumpStackTraceTo(suppressedIndent, "Suppressed: ", target)
}
}
}