Module: Realuptime::Errors::Rails

Defined in:
lib/realuptime/errors/rails.rb

Overview

Rails integration (REA-255). Feature-detected: this file never requires "rails"; the Railtie at the bottom is defined only when ::Rails::Railtie already exists, so requiring this file outside Rails is harmless and the helpers here are testable without it.

# Gemfile
gem "realuptime-errors", require: "realuptime/errors/rails"

# config/initializers/realuptime_errors.rb (or ENV REALUPTIME_ERRORS_DSN)
Rails.application.config.realuptime_errors.dsn = "https://realuptime.io/api/errors/v1/ingest/rue_..."
Rails.application.config.realuptime_errors.release = ENV["GIT_SHA"]

What the Railtie installs, all through the ONE capture/scrub/transport path a plain init gives:

- RailsMiddleware at the outermost position of the Rack stack: an
exception that escapes, or one Rails swallowed into its own error
page (action_dispatch.exception), is reported ONCE with method,
path and the matched controller#action as the route template.
- an ActiveJob around_perform hook that reports a job's unhandled
exception with the job class as the fingerprint key and re-raises,
so retry_on / discard_on keep working exactly as before. A job that
retries reports each failed attempt; that is the honest count.
- init itself from config.realuptime_errors / ENV when the app did
not call Realuptime::Errors.init in an initializer.

Defined Under Namespace

Modules: ActiveJobExtension Classes: RailsMiddleware, Railtie

Constant Summary collapse

FRAMEWORK_TAG =
{ "framework" => "rails" }.freeze

Class Method Summary collapse

Class Method Details

.options_from(config, env = ENV, rails_env = nil) ⇒ Object

Resolves init options from config.realuptime_errors (any object answering [] or the dotted accessors) and ENV. Returns nil when no DSN is configured anywhere, so the Railtie stays inert rather than logging on every boot of an app that has not opted in.



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/realuptime/errors/rails.rb', line 97

def self.options_from(config, env = ENV, rails_env = nil)
  read = lambda do |key|
    value = nil
    if config.respond_to?(:[])
      value = begin
        config[key]
      rescue StandardError
        nil
      end
    end
    value = config.public_send(key) if value.nil? && config.respond_to?(key)
    value
  end
  dsn = read.call(:dsn) || env["REALUPTIME_ERRORS_DSN"]
  return nil if dsn.nil? || dsn.to_s.empty?

  {
    dsn: dsn.to_s,
    release: read.call(:release) || env["REALUPTIME_RELEASE"],
    environment: read.call(:environment) || rails_env,
    allow_fields: read.call(:allow_fields),
    send_device_info: read.call(:send_device_info).nil? ? true : read.call(:send_device_info)
  }
end

.perform_reporting(job) ⇒ Object

Reports a job's failure and re-raises. Usable without ActiveJob (tests, or a hand-rolled job runner): yield the work.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/realuptime/errors/rails.rb', line 66

def self.perform_reporting(job)
  yield
rescue Exception => e # rubocop:disable Lint/RescueException
  begin
    job_name = job.class.name.to_s
    Realuptime::Errors.capture_exception(
      e,
      fingerprint: ["active_job", job_name, e.class.name.to_s],
      tags: { "framework" => "active_job", "job" => job_name }
    )
  rescue StandardError
    nil
  end
  raise
end

.request_context(env) ⇒ Object

Rack-level context plus the matched controller#action, the grouping key Rails exposes without any user-supplied bytes. path_parameters VALUES (the :id etc.) are never forwarded; only the controller and action names are.



38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/realuptime/errors/rails.rb', line 38

def self.request_context(env)
  request = RackMiddleware.request_context(env) || {}
  params = env.is_a?(Hash) ? env["action_dispatch.request.path_parameters"] : nil
  if params.is_a?(Hash)
    controller = params[:controller] || params["controller"]
    action = params[:action] || params["action"]
    request["route"] = "#{controller}##{action}" if controller && action
  end
  request.empty? ? nil : request
rescue StandardError
  nil
end