Subclass this method to create a custom class used to contact external exception monitoring / reporting engine. Examples include:

Hoodoo includes some classes for integration which you can choose from if you want to use the integrated service. Alternatively write your own. When you do this, name your class outside the Hoodoo namespace (the class's name can be anything you like). Otherwise you will trample upon Hoodoo' reserved namespace and may cause a naming collision in future Hoodoo versions.

“Under the hood” the Hoodoo::Communicators::Pool mechanism is used. All reporters are assumed to be (comparatively) slow communicators so are descendants of Hoodoo::Communicators::Slow.

Add a reporter class to the middleware from any service application by calling Hoodoo::Services::Middleware::ExceptionReporting.add.

Included Modules
Instance Public methods
communicate( object )

Subclasses MUST NOT override this method, which is part of the base class implementation and implements Hoodoo::Communicators::Slow#communicate. It calls through to the report method which subclasses do implement, unpacking a payload used for the internal communicators into the parameters that report expects.


Hoodoo::Services::Middleware::ExceptionReporting::Payload instance.

# File lib/hoodoo/services/middleware/exception_reporting/base_reporter.rb, line 121
def communicate( object )

  env = object.rack_env || ( object.context.owning_interaction.rack_request.env rescue nil )

  # The 'instance_methods( false )' call pulls only instance methods
  # strictly defined in 'self' instance, not in any superclasses.
  # Thus we don't see the base implementation of 'contextual_report'
  # in this source file; only an overriding implementation in a real
  # reporter subclass.
  subclass_methods = self.class.instance_methods( false )

  if object.context && subclass_methods.include?( :contextual_report )
    self.contextual_report( object.exception, object.context )
  else object.exception, env )
contextual_report( e, context )

Similar to report, with the same caveats; but has more information available.

Subclasses report an exception for errors that occur within a fully handled Rack request context, with a high level processed Hoodoo representation available.

Through the protected user_data_for method, subclasses can, if the exception reporting backend supports it, include detailed request information with their contextual exception reports.

Implementation is optional. If not available, the system falls back to the less detailed report method. If called, all parameters must be provided; none are optional.


Exception (or subclass) instance to be reported.


Hoodoo::Services::Context instance describing an in-flight request/response cycle.

# File lib/hoodoo/services/middleware/exception_reporting/base_reporter.rb, line 107
def contextual_report( e, context )
  raise( 'Subclasses may implement #contextual_report' )
report( e, env = nil )

Subclasses implement this method. The middleware creates the singleton instance of the subclass, then calls your implementation of the method when it catches an exception in its top level handler. The subclass implementation sends details of the exception to the desired exception monitoring or reporting service. The return value is ignored.

The middleware wraps calls to your subclass in a nested exception handler. If you raise an exception, the middleware logs details with a :debug level through its logger instance if possible (see Hoodoo::Services::Middleware.logger) along with printing details to $stderr, then continues processing.

If service applications are expecting potential exceptions to occur and they catch them without re-raising for the middleware to catch, this reporting method will not be called. If a service author thinks such an exception ought to be logged, they must re-raise it.

The middleware runs calls here in a processing Thread to avoid delays to the calling client. This means your implementation of this method can use blocking network calls should you so wish; but beware, you are running in your own Thread on every call and more than one call might be running concurrently. If your implementation is not threadsafe, use a Mutex. For example, add a mutex class variable to your class:

@@mutex =

…then use it inside report with something like:

def report( e )
  @@mutex.synchronize do
    # Your reporting service's custom code goes here

Exception (or subclass) instance to be reported.


Optional Rack environment hash for the inbound request, for exception reports made in the context of Rack request handling.

# File lib/hoodoo/services/middleware/exception_reporting/base_reporter.rb, line 83
def report( e, env = nil )
  raise( 'Subclasses must implement #report' )
Instance Protected methods
user_data_for( context )

When passed a request context, extracts information that can be given as “user data” (or similar) to an exception reporting endpoint, if it supports such a concept.


Hoodoo::Services::Context instance describing an in-flight request/response cycle.

# File lib/hoodoo/services/middleware/exception_reporting/base_reporter.rb, line 151
def user_data_for( context )
    hash = {
      :interaction_id =>   context.owning_interaction.interaction_id,
      :action         => ( context.owning_interaction.requested_action          || '(unknown)' ).to_s,
      :resource       => ( context.owning_interaction.target_interface.resource || '(unknown)' ).to_s,
      :version        =>   context.owning_interaction.target_interface.version,
      :request        => {
        :locale              => context.request.locale,
        :uri_path_components => context.request.uri_path_components,
        :uri_path_extension  => context.request.uri_path_extension,
        :embeds              => context.request.embeds,
        :references          => context.request.references,
        :headers             => context.request.headers,
        :list                => {
          :offset      => context.request.list.offset,
          :limit       => context.request.list.limit,
          :sort_data   => context.request.list.sort_data,
          :search_data => context.request.list.search_data,
          :filter_data => context.request.list.filter_data

    hash[ :session ] = context.session.to_h unless context.session.nil?
    return hash

    return nil