Class: ErrorReporting

Inherits:
Object
  • Object
show all
Defined in:
app/services/error_reporting.rb

Overview

ErrorReporting - Unified error reporting abstraction

Provides a consistent interface for error reporting that can route to
different providers (AppSignal, Rollbar, etc.) without changing call sites.

Namespace Structure (9 namespaces total):
Aligned with AppSignal's default namespaces (web, background, frontend)

Source Severity Namespace Notification
Rails Error web First in Deploy
Rails Warning web_warning First Occurrence
Rails Info web_info Never Notify
JavaScript Error frontend First in Deploy
JavaScript Warning frontend_warning First Occurrence
JavaScript Info frontend_info Never Notify
Sidekiq Error background First in Deploy
Sidekiq Warning background_warning First Occurrence
Sidekiq Info background_info Never Notify

Usage:

From a controller

ErrorReporting.from_controller(self).error(exception)
ErrorReporting.from_controller(self).warning(exception)
ErrorReporting.from_controller(self).informational(exception)

From a worker (uses 'background' namespace family)

ErrorReporting.error(exception, source: :background)
ErrorReporting.warning(exception, source: :background)

Direct calls default to 'web' namespace family

ErrorReporting.error(exception)
ErrorReporting.warning("Unusual condition", { value: x })
ErrorReporting.informational("Expected condition", { reason: 'bot' })

Info/debug (logs only, not sent to AppSignal)

ErrorReporting.info("Process completed", { count: 100 })

See: https://docs.appsignal.com/guides/namespaces.html

Defined Under Namespace

Classes: ControllerReporter

Class Method Summary collapse

Class Method Details

.critical(exception_or_message, context = {}) ⇒ Object

Report a critical error (more severe than error)
Goes to base namespace with critical severity tag

Parameters:

  • exception_or_message (Exception, String)

    The critical error

  • context (Hash) (defaults to: {})

    Additional context/metadata



81
82
83
84
85
86
87
88
# File 'app/services/error_reporting.rb', line 81

def critical(exception_or_message, context = {})
  context = normalize_context(context)
  context[:severity] = 'critical'
  exception = normalize_exception(exception_or_message)
  namespace = build_namespace(context)
  send_to_provider(exception, context: context, namespace: namespace)
  log_error(exception, context, :critical)
end

.debug(message, context = {}) ⇒ Object

Report debug level

Parameters:

  • message (String)

    The debug message

  • context (Hash) (defaults to: {})

    Additional context/metadata



115
116
117
118
# File 'app/services/error_reporting.rb', line 115

def debug(message, context = {})
  context = normalize_context(context)
  Rails.logger.debug { "[ErrorReporting] #{message}#{context_suffix(context)}" }
end

.enabled?Boolean

Check if error reporting is enabled

Returns:

  • (Boolean)


148
149
150
151
152
# File 'app/services/error_reporting.rb', line 148

def enabled?
  return false if Rails.env.test?

  appsignal_available?
end

.error(exception_or_message, context = {}) ⇒ Object

Report an error (exception or message string)
Goes to namespace based on source (e.g., web, frontend, background)

Parameters:

  • exception_or_message (Exception, String)

    The error to report

  • context (Hash) (defaults to: {})

    Additional context/metadata

Options Hash (context):

  • :source (Symbol)

    One of :web, :frontend, :background (default: :web)



57
58
59
60
61
62
63
# File 'app/services/error_reporting.rb', line 57

def error(exception_or_message, context = {})
  context = normalize_context(context)
  exception = normalize_exception(exception_or_message)
  namespace = build_namespace(context)
  send_to_provider(exception, context: context, namespace: namespace)
  log_error(exception, context, :error)
end

.from_controller(controller) ⇒ ControllerReporter

Create a reporter with controller context for richer error data

Parameters:

  • controller (ActionController::Base)

    The controller instance

Returns:



48
49
50
# File 'app/services/error_reporting.rb', line 48

def from_controller(controller)
  ControllerReporter.new(controller)
end

.info(message, context = {}) ⇒ Object

Report info level (logs only, does not send to AppSignal)
For operational messages that don't need tracking in error monitoring

Parameters:

  • message (String)

    The info message

  • context (Hash) (defaults to: {})

    Additional context/metadata



107
108
109
110
# File 'app/services/error_reporting.rb', line 107

def info(message, context = {})
  context = normalize_context(context)
  Rails.logger.info("[ErrorReporting] #{message}#{context_suffix(context)}")
end

.informational(message_or_exception, context = {}) ⇒ Object

Report an informational event (expected conditions worth tracking)
Goes to namespace_info - configure "Never Notify" in AppSignal
Use for: bot detection, CSRF failures, record not found, routing errors

Parameters:

  • message_or_exception (String, Exception)

    The event to report

  • context (Hash) (defaults to: {})

    Additional context/metadata



95
96
97
98
99
100
101
# File 'app/services/error_reporting.rb', line 95

def informational(message_or_exception, context = {})
  context = normalize_context(context)
  exception = normalize_exception(message_or_exception)
  namespace = build_namespace(context, suffix: 'info')
  send_to_provider(exception, context: context, namespace: namespace)
  log_info(exception, context)
end

.providerObject

Get the current provider name



155
156
157
158
159
160
161
# File 'app/services/error_reporting.rb', line 155

def provider
  if appsignal_available?
    :appsignal
  else
    :none
  end
end

.scoped(context = {}) ⇒ Object Also known as: with_context

Execute block with additional context for error reports
Context is thread-local and automatically cleared after the block

Parameters:

  • context (Hash) (defaults to: {})

    Context to add to any errors reported in the block



136
137
138
139
140
141
142
# File 'app/services/error_reporting.rb', line 136

def scoped(context = {}, &)
  previous_context = Thread.current[:error_reporting_context] || {}
  Thread.current[:error_reporting_context] = previous_context.merge(context)
  yield
ensure
  Thread.current[:error_reporting_context] = previous_context
end

.track(name, context = {}) ⇒ Object

Track a custom event/metric

Parameters:

  • name (String)

    Event name

  • context (Hash) (defaults to: {})

    Event metadata



123
124
125
126
127
128
129
130
131
# File 'app/services/error_reporting.rb', line 123

def track(name, context = {})
  context = normalize_context(context)
  if appsignal_available?
    Appsignal.instrument(name) do
      # No-op block, just for tracking
    end
  end
  Rails.logger.info("[ErrorReporting.track] #{name}#{context_suffix(context)}")
end

.warning(message_or_exception, context = {}) ⇒ Object

Report a warning (less severe than error)
Goes to namespace_warning (e.g., web_warning, background_warning)

Parameters:

  • message_or_exception (String, Exception)

    The warning to report

  • context (Hash) (defaults to: {})

    Additional context/metadata



69
70
71
72
73
74
75
# File 'app/services/error_reporting.rb', line 69

def warning(message_or_exception, context = {})
  context = normalize_context(context)
  exception = normalize_exception(message_or_exception)
  namespace = build_namespace(context, suffix: 'warning')
  send_to_provider(exception, context: context, namespace: namespace)
  log_warning(exception, context)
end