class Google::Cloud::ErrorReporting::Middleware

# Middleware

Google::Cloud::ErrorReporting::Middleware defines a Rack Middleware that can automatically catch upstream exceptions and report them to Stackdriver Error Reporting.

Constants

EXCEPTION_KEYS

Attributes

error_reporting[R]

A Google::Cloud::ErrorReporting::Project client used to report error events.

Public Class Methods

new(app, error_reporting: nil, **kwargs) click to toggle source

Construct a new instance of Middleware.

@param [Rack::Application] app The Rack application @param [Google::Cloud::ErrorReporting::Project] error_reporting A

Google::Cloud::ErrorReporting::Project client for reporting
exceptions

@param [Hash] kwargs Hash of configuration settings. Used for backward

API compatibility. See the [Configuration
Guide](https://googleapis.dev/ruby/stackdriver/latest/file.INSTRUMENTATION_CONFIGURATION.html)
for the prefered way to set configuration parameters.

@return [Google::Cloud::ErrorReporting::Middleware] A new instance of

Middleware
# File lib/google/cloud/error_reporting/middleware.rb, line 48
def initialize app, error_reporting: nil, **kwargs
  require "rack"
  require "rack/request"
  @app = app

  load_config(**kwargs)

  @error_reporting =
    error_reporting ||
    ErrorReporting.default_reporter do
      ErrorReporting::AsyncErrorReporter.new(
        ErrorReporting.new(project: configuration.project_id,
                           credentials: configuration.credentials)
      )
    end
end

Public Instance Methods

call(env) click to toggle source

Implements the mandatory Rack Middleware call method.

Catch all Exceptions from upstream and report them to Stackdriver Error Reporting. Unless the exception's class is defined to be ignored by this Middleware.

@param [Hash] env Rack environment hash

# File lib/google/cloud/error_reporting/middleware.rb, line 74
def call env
  response = @app.call env

  # sinatra doesn't always raise the Exception, but it saves it in
  # env['sinatra.error']
  #
  # some frameworks (i.e. hanami) will save a rendering exception in
  # env['rack.exception']
  EXCEPTION_KEYS.each do |exception_key|
    next unless env[exception_key].is_a? Exception

    report_exception env, env[exception_key]
  end

  response
rescue Exception => e
  report_exception env, e

  # Always raise exception backup
  raise e
end
error_event_from_exception(env, exception) click to toggle source

Creates a {Google::Cloud::ErrorReporting::ErrorEvent} based on the exception. Fill in the HttpRequestContext section of the ErrorEvent based on the HTTP Request headers.

When used in Rails environment. It replies on ActionDispatch::ExceptionWrapper class to derive a HTTP status code based on the exception's class.

@param [Hash] env Rack environment hash @param [Exception] exception Exception to convert from

@return [Google::Cloud::ErrorReporting::ErrorEvent] The gRPC

ErrorEvent object that's based on given env and exception
# File lib/google/cloud/error_reporting/middleware.rb, line 135
def error_event_from_exception env, exception
  error_event = ErrorReporting::ErrorEvent.from_exception exception

  # Inject service_context info into error_event object
  error_event.service_name = configuration.service_name
  error_event.service_version = configuration.service_version

  # Inject http_request_context info into error_event object
  rack_request = Rack::Request.new env
  error_event.http_method = rack_request.request_method
  error_event.http_url = rack_request.url
  error_event.http_user_agent = rack_request.user_agent
  error_event.http_referrer = rack_request.referrer
  error_event.http_status = http_status exception
  error_event.http_remote_ip = rack_request.ip

  error_event
end
report_exception(env, exception) click to toggle source

Report an given exception to Stackdriver Error Reporting.

While it reports most of the exceptions. Certain Rails exceptions that maps to a HTTP status code less than 500 will be treated as not the app fault and ignored.

@param [Hash] env Rack environment hash @param [Exception] exception The Ruby exception to report.

# File lib/google/cloud/error_reporting/middleware.rb, line 106
def report_exception env, exception
  # Do not any exceptions that's specified by the ignore_classes list.
  return if configuration.ignore_classes.include? exception.class

  error_event = error_event_from_exception env, exception

  # If this exception maps to a HTTP status code less than 500, do
  # not report it.
  status_code = error_event.http_status.to_i
  return if status_code.positive? && status_code < 500

  error_reporting.report error_event
end

Private Instance Methods

configuration() click to toggle source

@private Get Google::Cloud::ErrorReporting.configure

# File lib/google/cloud/error_reporting/middleware.rb, line 216
def configuration
  Google::Cloud::ErrorReporting.configure
end
http_status(exception) click to toggle source

Helper method to derive HTTP status code base on exception class in Rails. Returns nil if not in Rails environment.

@param [Exception] exception An Ruby exception

@return [Integer] A number that represents HTTP status code or nil if

status code can't be determined
# File lib/google/cloud/error_reporting/middleware.rb, line 199
def http_status exception
  http_status = nil
  if defined?(ActionDispatch::ExceptionWrapper) &&
     ActionDispatch::ExceptionWrapper.respond_to?(
       :status_code_for_exception
     )
    http_status =
      ActionDispatch::ExceptionWrapper.status_code_for_exception(
        exception.class.name
      )
  end

  http_status
end
init_default_config() click to toggle source

Fallback to default configuration values if not defined already

# File lib/google/cloud/error_reporting/middleware.rb, line 182
def init_default_config
  configuration.project_id ||= (Cloud.configure.project_id || ErrorReporting::Project.default_project_id)
  configuration.credentials ||= Cloud.configure.credentials
  configuration.service_name ||= ErrorReporting::Project.default_service_name
  configuration.service_version ||= ErrorReporting::Project.default_service_version
  configuration.ignore_classes = Array(configuration.ignore_classes)
end
load_config(**kwargs) click to toggle source

Consolidate configurations from various sources. Also set instrumentation config parameters to default values if not set already.

# File lib/google/cloud/error_reporting/middleware.rb, line 161
def load_config **kwargs
  project_id = kwargs[:project] || kwargs[:project_id]
  configuration.project_id = project_id unless project_id.nil?

  creds = kwargs[:credentials] || kwargs[:keyfile]
  configuration.credentials = creds unless creds.nil?

  service_name = kwargs[:service_name]
  configuration.service_name = service_name unless service_name.nil?

  service_vers = kwargs[:service_version]
  configuration.service_version = service_vers unless service_vers.nil?

  ignores = kwargs[:ignore_classes]
  configuration.ignore_classes = ignores unless ignores.nil?

  init_default_config
end