Interface EventExceptionHandler


public interface EventExceptionHandler
A functional interface for handling exceptions that occur during event handler execution.

When a handler throws an exception, the event bus will invoke this handler with full context information, allowing for detailed logging, monitoring, or recovery actions. The exception handler prevents one failing handler from breaking the entire event pipeline.

The exception handler is invoked synchronously in the same thread where the exception occurred, regardless of whether the failing handler was executing synchronously or asynchronously.

Key Characteristics

  • Invoked for every handler exception, providing full error context
  • Executed synchronously in the same thread as the failing handler
  • Other handlers continue to execute even after one fails
  • Exception handler failures are logged but do not propagate

Common Use Cases

  • Logging handler failures with detailed context information
  • Sending error metrics to monitoring systems
  • Alerting administrators of critical handler failures
  • Recording handler reliability statistics
  • Implementing retry logic for transient failures

Example: Basic Logging


 EventExceptionHandler handler = (event, exception, handlerId) -> {
     logger.error("Handler {} failed while processing event {}: {}",
         handlerId,
         event.getClass().getSimpleName(),
         exception.getMessage(),
         exception);
 };
 
 EventBus eventBus = EventBus.builder()
     .exceptionHandler(handler)
     .build();
 

Example: Metrics and Monitoring


 EventExceptionHandler handler = (event, exception, handlerId) -> {
     // Record exception metrics
     metrics.incrementCounter("event.handler.errors",
         "event_type", event.getClass().getSimpleName(),
         "exception_type", exception.getClass().getSimpleName()
     );

     // Send to monitoring system
     monitoring.recordException(handlerId, event, exception);

     // Log with context
     logger.error("Handler failure", exception);
 };
 

Example: Critical Error Alerting


 EventExceptionHandler handler = (event, exception, handlerId) -> {
     logger.error("Handler {} failed processing {}", handlerId, event, exception);

     // Alert for critical events
     if (event instanceof CommandPreRegistrationEvent) {
         alertService.sendAlert(
             "Critical handler failure in command registration",
             "Handler ID: " + handlerId + "\nException: " + exception.getMessage()
         );
     }

     // Store for later analysis
     errorRepository.save(new HandlerError(handlerId, event, exception));
 };
 

Example: Conditional Retry Logic


 EventExceptionHandler handler = (event, exception, handlerId) -> {
     logger.warn("Handler {} failed, checking for retry", handlerId, exception);

     // Retry for transient failures
     if (exception instanceof TransientException) {
         EventSubscription<?> subscription = findSubscription(handlerId);
         if (subscription != null && shouldRetry(handlerId)) {
             logger.info("Retrying handler {}", handlerId);
             scheduler.schedule(() -> {
                 try {
                     subscription.handler().accept(event);
                 } catch (Exception e) {
                     logger.error("Retry failed for handler {}", handlerId, e);
                 }
             }, 1, TimeUnit.SECONDS);
         }
     }
 };
 

Example: Handler Reliability Tracking


 Map<UUID, HandlerStats> handlerStats = new ConcurrentHashMap<>();

 EventExceptionHandler handler = (event, exception, handlerId) -> {
     // Track failure statistics
     handlerStats.computeIfAbsent(handlerId, id -> new HandlerStats())
                 .recordFailure(exception);

     HandlerStats stats = handlerStats.get(handlerId);
     logger.error("Handler {} failed (failures: {}, success rate: {}%)",
         handlerId, stats.getFailureCount(), stats.getSuccessRate());

     // Disable problematic handlers
     if (stats.getSuccessRate() < 50.0) {
         logger.warn("Disabling unreliable handler {}", handlerId);
         eventBus.unregister(handlerId);
     }
 };
 

Best Practices

  • Always log exceptions with sufficient context for debugging
  • Keep exception handler logic fast and simple
  • Avoid throwing exceptions from the exception handler itself
  • Consider async logging/metrics to avoid blocking
  • Use structured logging for better searchability

Isolation Guarantee: When an exception occurs, the exception handler is called, but the event pipeline continues. Other handlers will still execute even if one handler fails.

Since:
1.0
See Also:
  • Method Summary

    Modifier and Type
    Method
    Description
    <E extends Event>
    void
    handle(E event, Throwable exception, EventSubscription<E> subscription)
    Handles an exception that occurred during event handler execution.
  • Method Details

    • handle

      <E extends Event> void handle(E event, Throwable exception, EventSubscription<E> subscription)
      Handles an exception that occurred during event handler execution.

      This method is called synchronously whenever a handler throws an exception. Implementations should be fast and should not throw exceptions themselves.

      Parameters:
      event - the event that was being processed when the exception occurred
      exception - the exception that was thrown by the handler
      subscription - the unique identifier of the handler that threw the exception