Class: Google::Cloud::ErrorReporting::Middleware

Inherits:
Object
  • Object
show all
Defined in:
lib/google/cloud/error_reporting/middleware.rb

Overview

Middleware

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, error_reporting: V1beta1::ReportErrorsServiceClient.new, project_id: nil, service_name: nil, service_version: nil, ignore_classes: nil) ⇒ Google::Cloud::ErrorReporting::Middleware

Construct a new instance of Middleware

Parameters:

  • app (Rack Application)

    The Rack application

  • error_reporting (Google::Cloud::ErrorReporting::V1beta1::ReportErrorsServiceClient)

    A ErrorReporting::V1beta1::ReportErrorsServiceClient object to for reporting exceptions

  • project_id (String)

    Name of GCP project. Default to ENV["ERROR_REPORTING_PROJECT"] then ENV["GOOGLE_CLOUD_PROJECT"]. Automatically discovered if on GAE

  • service_name (String)

    Name of the service. Default to ENV["ERROR_REPORTING_SERVICE"] then "ruby". Automatically discovered if on GAE

  • service_version (String)

    Version of the service. Optional. ENV["ERROR_REPORTING_VERSION"]. Automatically discovered if on GAE

  • ignore_classes (Array<Class>)

    A single or an array of Exception classes to ignore



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/google/cloud/error_reporting/middleware.rb', line 57

def initialize app,
               error_reporting: V1beta1::ReportErrorsServiceClient.new,
               project_id: nil,
               service_name: nil,
               service_version: nil,
               ignore_classes: nil
  @app = app
  @error_reporting = error_reporting
  @service_name = service_name ||
                  ENV["ERROR_REPORTING_SERVICE"] ||
                  Google::Cloud.env.app_engine_service_id ||
                  "ruby"
  @service_version = service_version ||
                     ENV["ERROR_REPORTING_VERSION"] ||
                     Google::Cloud.env.app_engine_service_version
  @ignore_classes = Array(ignore_classes)
  @project_id = project_id ||
                ENV["ERROR_REPORTING_PROJECT"] ||
                ENV["GOOGLE_CLOUD_PROJECT"] ||
                Google::Cloud.env.project_id

  fail ArgumentError, "project_id is required" if @project_id.nil?
end

Instance Attribute Details

#error_reportingObject (readonly)

Returns the value of attribute error_reporting



32
33
34
# File 'lib/google/cloud/error_reporting/middleware.rb', line 32

def error_reporting
  @error_reporting
end

#ignore_classesObject (readonly)

Returns the value of attribute ignore_classes



32
33
34
# File 'lib/google/cloud/error_reporting/middleware.rb', line 32

def ignore_classes
  @ignore_classes
end

#project_idObject (readonly)

Returns the value of attribute project_id



32
33
34
# File 'lib/google/cloud/error_reporting/middleware.rb', line 32

def project_id
  @project_id
end

#service_nameObject (readonly)

Returns the value of attribute service_name



32
33
34
# File 'lib/google/cloud/error_reporting/middleware.rb', line 32

def service_name
  @service_name
end

#service_versionObject (readonly)

Returns the value of attribute service_version



32
33
34
# File 'lib/google/cloud/error_reporting/middleware.rb', line 32

def service_version
  @service_version
end

Instance Method Details

#build_error_event_from_exception(env, exception) ⇒ Google::Devtools::Clouderrorreporting::V1beta1::ReportedErrorEvent

Creates a GRPC 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.

Parameters:

  • env (Hash)

    Rack environment hash

  • exception (Exception)

    Exception to convert from

Returns:



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/google/cloud/error_reporting/middleware.rb', line 149

def build_error_event_from_exception env, exception
  error_event = ErrorEvent.from_exception exception

  # Inject service_context info into error_event object
  error_event[:service_context] = {
    service: service_name,
    version: service_version
  }.delete_if { |_, v| v.nil? }

  # Inject http_request_context info into error_event object
  rack_request = Rack::Request.new env
  error_event[:context][:http_request] = {
    method: rack_request.request_method,
    url: rack_request.url,
    user_agent: rack_request.user_agent,
    referrer: rack_request.referrer,
    response_status_code: get_http_status(exception),
    remote_ip: rack_request.ip
  }.delete_if { |_, v| v.nil? }

  error_event.to_grpc
end

#call(env) ⇒ Object

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.

Parameters:

  • env (Hash)

    Rack environment hash



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/google/cloud/error_reporting/middleware.rb', line 90

def call env
  response = @app.call env

  # sinatra doesn't always raise the Exception, but it saves it in
  # env['sinatra.error']
  if env["sinatra.error"].is_a? Exception
    report_exception env, env["sinatra.error"]
  end

  response
rescue Exception => exception
  report_exception env, exception

  # Always raise exception backup
  raise exception
end

#full_project_idString

Build full ReportErrorsServiceClient project_path from project_id, which is in "projects/##project_id" format.

Returns:

  • (String)

    fully qualified project id in "projects/##project_id" format



178
179
180
# File 'lib/google/cloud/error_reporting/middleware.rb', line 178

def full_project_id
  V1beta1::ReportErrorsServiceClient.project_path project_id
end

#report_exception(env, exception) ⇒ Object

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.

Parameters:

  • env (Hash)

    Rack environment hash

  • exception (Exception)

    The Ruby exception to report.



117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/google/cloud/error_reporting/middleware.rb', line 117

def report_exception env, exception
  # Do not any exceptions that's specified by the ignore_classes list.
  return if ignore_classes.include? exception.class

  error_event_grpc = build_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_grpc.context.http_request.response_status_code.to_i
  return if status_code > 0 && status_code < 500

  error_reporting.report_error_event full_project_id, error_event_grpc
end