How do people handle wanting to add contextual inf...
# codingconventions
j
How do people handle wanting to add contextual information to an exception that is thrown? Say I have a function getUser() that can throw exceptions of various types for various (unknown) reasons. This function can be called multiple places, for example in getAdmins() and getCustomers() and I'd like to append contextual information in the callplace, to say what I was trying to do when the function failed
Copy code
fun getAdmins(adminId: String) {
    try {
        getUsers(adminId)
    } catch (e: Exception) {
        // "Failed fetching admin by $adminId"
    }
}
Now I could wrap the existing exception in a new custom exception MyUserException("Failed ...", e) However this means I cannot catch the underlying exception specifically, and in most logging tools shows my wrapper rather than the often usefull underlying Exception. I could rethrow the exception, with a log statement
Copy code
catch (e: Exception){
   log.error("Failed fetching admin...", e)
   throw e
}
However I dislike having multiple log entries from a single error, and puts the responsibility on me for "connecting the dots" of the different log statements.
y
You can add a
cause
exception or a
suppressedException
j
> However this means I cannot catch the underlying exception specifically If you want to add metadata to an exception, why would you want to catch the original one? Most likely your high-level exception wrapper makes more sense and should be the one to catch
most logging tools shows my wrapper rather than the often usefull underlying Exception
That is strange. Most logging tools should show the full stacktrace including the cause.
j
If you want to add metadata to an exception, why would you want to catch the original one? Most likely your high-level exception wrapper makes more sense and should be the one to catch
Usually I have metadata at a particular call-site I won't have at a top-level exception handler unless it's somehow thread context or something similar
That is strange. Most logging tools should show the full stacktrace including the cause.
This is true, but it will be buried in the stack-trace, and not catcheable based on the original type, And slightly harder to find in logs since it won't be top-level exception
j
Usually I have metadata at a particular call-site I won't have at a top-level exception handler unless it's somehow thread context or something similar
I think you missed my point. At the particular call site where you have metadata, you create a wrapper exception with properties that contain this metadata. Later at a high-level catch-site, you want to catch the high-level exception and access the metadata.
Copy code
class AdminNotFoundException(val adminId: String, cause: Throwable) : Exception("Admin not found with ID $adminId", cause)

fun getAdmins(adminId: String) {
    try {
        getUsers(adminId)
    } catch (e: Exception) {
        throw AdminNotFoundException(adminId, e)
    }
}

// in some place
try {
    someFunctionThatEventuallyCallsGetAdmins()
} catch(e: AdminNotFoundException) {
    // do something with e.adminId
}
> This is true, but it will be buried in the stack-trace This is kinda the point. You're usually interested in the high-level exception primarily for reporting/general logging. If you want to find the root cause / investigate, then you are interested in the cause and you can access it.