ExceptionWithLoggingContext

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 to cause.message. This lets you:

      • Wrap a cause exception with log fields, and use the cause exception's message

      • Extend ExceptionWithLoggingContext and override message, without having to pass it through the constructor

  • (vararg logFields: LogField, cause: Throwable?)

    • Combines the two previous constructors, to let you extend ExceptionWithLoggingContext and override message while also passing log fields as varargs

Constructors

Link copied to clipboard
constructor(message: String?, logFields: Collection<LogField> = emptyList(), cause: Throwable? = null)
constructor(message: String?, vararg logFields: LogField, cause: Throwable? = null)
constructor(logFields: Collection<LogField> = emptyList(), cause: Throwable? = null)
constructor(vararg logFields: LogField, cause: Throwable? = null)

Properties

Link copied to clipboard
open val cause: Throwable?
Link copied to clipboard
open override val message: String?

The exception message.