Skip to content

Commit

Permalink
EG-65 - Improved error reporting on stdout (corda#5962)
Browse files Browse the repository at this point in the history
I modified the `ErrorCodeRewritePolicy` to concatenate all the error messages of the exception's cause chain.
The code will start from the throwable being passed to the logger and concatenate its error message with the one in its cause and proceed recursively until it finds an exception with no cause or it detects a loop (an exception already encountered in the traversal).

This should provide customers with more information about errors without having to look at the logs (that should still remain the primary source of information)
  • Loading branch information
woggioni authored Feb 17, 2020
1 parent 4c492a7 commit 98e9b1c
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,37 @@ import org.apache.logging.log4j.Level
import org.apache.logging.log4j.message.Message
import org.apache.logging.log4j.message.SimpleMessage
import java.util.*
//Returns an iterator that traverses all the exception's cause chain stopping in case of loops (an exception caused by itself)
fun Throwable.walkExceptionCausedByList() : Iterator<Throwable> {
val self = this
return object : Iterator<Throwable> {
private var cursor : Throwable? = self
private val knownThrowables = mutableSetOf<Throwable>()

override fun hasNext(): Boolean {
return cursor != null
}

override fun next(): Throwable {
val result = cursor
val cause = cursor?.cause
cursor = if(cause != null && knownThrowables.add(cause)) {
cause
} else {
null
}
return result!!
}
}
}

fun Message.withErrorCodeFor(error: Throwable?, level: Level): Message {

return when {
error != null && level.isInRange(Level.FATAL, Level.WARN) -> CompositeMessage("$formattedMessage [errorCode=${error.errorCode()}, moreInformationAt=${error.errorCodeLocationUrl()}]", format, parameters, throwable)
error != null && level.isInRange(Level.FATAL, Level.WARN) -> {
val message = error.walkExceptionCausedByList().asSequence().mapNotNull(Throwable::message).joinToString(" - ")
CompositeMessage("$message [errorCode=${error.errorCode()}, moreInformationAt=${error.errorCodeLocationUrl()}]", format, parameters, throwable)
}
else -> this
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package net.corda.commmon.logging

import net.corda.common.logging.walkExceptionCausedByList
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized

@RunWith(Parameterized::class)
class WalkExceptionCausedByListTest(@Suppress("UNUSED_PARAMETER") testTitle: String, private val e: Throwable, private val expectedExceptionSequence: List<Throwable>) {

private class TestThrowable(val id : Int, cause : Throwable?) : Throwable(cause) {
override fun toString(): String {
return "${this.javaClass.simpleName}(${this.id})"
}
}

companion object {
@JvmStatic
@Parameterized.Parameters(name = "{0}")
fun data(): Collection<Array<Any>> {
val field = Throwable::class.java.getDeclaredField("cause")
field.isAccessible = true
return listOf(
run {
val e = TestThrowable(0, null)
arrayOf("Simple exception with no cause", e, listOf(e))
},
run {
var e: TestThrowable? = null
val exceptions = (0 until 10).map {
e = TestThrowable(it, e)
e
}
arrayOf("Exception with cause list", e!!, exceptions.asReversed())
},
run {
val e = TestThrowable(0, null)
field.set(e, e)
arrayOf("Exception caused by itself", e, listOf(e))
},
run {
val stack = mutableListOf<TestThrowable>()
var e: TestThrowable? = null
for(i in 0 until 10) {
e = TestThrowable(i, stack.lastOrNull())
stack.add(e!!)
}
field.set(stack[0], stack[4])
arrayOf("Exception with loop in cause list", e!!, stack.asReversed())
})
}
}

@Test(timeout = 1000)
fun test() {
Assert.assertEquals(expectedExceptionSequence, e.walkExceptionCausedByList().asSequence().toList())
}
}

0 comments on commit 98e9b1c

Please sign in to comment.