Exception With Logging Context
Exception that carries log fields, to provide structured logging context when the exception is logged. When passing a cause
exception to one of the methods on Logger, it will check if the given exception is an instance of this class, and if it is, these fields will be added to the log.
Use the field/rawJsonField functions to construct log fields.
This class is useful when you are throwing an exception from somewhere down in the stack, but do logging further up the stack, and you have structured data that you want to attach to the exception log. In this case, one may typically resort to string concatenation, but this class allows you to have the benefits of structured logging for exceptions as well.
You can extend this class for your own custom exception types. If you'd rather implement an interface than extend a class, use HasLoggingContext.
Example
import dev.hermannm.devlog.ExceptionWithLoggingContext
import dev.hermannm.devlog.field
import dev.hermannm.devlog.getLogger
private val log = getLogger()
fun example(event: OrderUpdateEvent) {
try {
processOrderUpdate(event)
} catch (e: Exception) {
log.error(e) { "Failed to process order update event" }
}
}
fun processOrderUpdate(event: OrderUpdateEvent) {
withLoggingContext(field("event", event)) {
val order = getOrder(event.orderId)
if (!order.canBeUpdated()) {
throw ExceptionWithLoggingContext(
"Received update event for finalized order",
field("order", order),
)
}
}
}
The log.error
would then give the following log output (using logstash-logback-encoder
), with both the order
field from the exception and the event
field from the logging context:
{
"message": "Failed to process order update event",
"stack_trace": "...ExceptionWithLoggingContext: Received update event for finalized order...",
"order": { ... },
"event": { ... },
// ...timestamp etc.
}
Constructors
ExceptionWithLoggingContext
provides 4 constructor overloads:
(message: String?, logFields: Collection<LogField>, cause: Throwable?)
Primary constructor taking an exception message, a collection of log fields and an optional cause exception
(message: String?, vararg logFields: LogField, cause: Throwable?)
Takes log fields as varargs, so you don't have to wrap them in a list
To pass
cause
, use a named argument(logFields: Collection<LogField>, cause: Throwable?)
Defaults
message
tocause.message
. This lets you:Wrap a cause exception with log fields, and use the cause exception's message
Extend
ExceptionWithLoggingContext
and overridemessage
, without having to pass it through the constructor(vararg logFields: LogField, cause: Throwable?)
Combines the two previous constructors, to let you extend
ExceptionWithLoggingContext
and overridemessage
while also passing log fields as varargs