Module: Dispatch::Rails

Defined in:
lib/dispatch-rails.rb,
lib/dispatch/rails/engine.rb,
lib/dispatch/rails/version.rb,
lib/dispatch/rails/reporter.rb,
lib/dispatch/rails/transport.rb,
lib/dispatch/rails/middleware.rb,
lib/dispatch/rails/rake_handler.rb,
lib/dispatch/rails/configuration.rb,
lib/dispatch/rails/event_builder.rb,
lib/dispatch/rails/error_subscriber.rb,
lib/dispatch/rails/response_annotator.rb,
lib/dispatch/rails/heartbeat_aggregator.rb,
lib/dispatch/rails/heartbeat_middleware.rb

Defined Under Namespace

Modules: RakeHandler, Reporter Classes: Configuration, Engine, ErrorSubscriber, EventBuilder, HeartbeatAggregator, HeartbeatMiddleware, Middleware, ResponseAnnotator, Transport

Constant Summary collapse

VERSION =
"0.7.0".freeze

Class Method Summary collapse

Class Method Details

.capture_exception(exception, env: nil, context: {}, level: "error") ⇒ Object

Manually report a handled exception, e.g. inside a rescue:

rescue => e
  Dispatch::Rails.capture_exception(e, context: { tags: { area: "import" } })
end


33
34
35
# File 'lib/dispatch-rails.rb', line 33

def capture_exception(exception, env: nil, context: {}, level: "error")
  Reporter.capture(exception, handled: true, env: env, context: context, level: level)
end

.configurationObject



17
18
19
# File 'lib/dispatch-rails.rb', line 17

def configuration
  @configuration ||= Configuration.new
end

.configure {|configuration| ... } ⇒ Object

Yields:



21
22
23
# File 'lib/dispatch-rails.rb', line 21

def configure
  yield(configuration)
end

.handle_at_exit(exception = nil) ⇒ Object

The at_exit body, extracted so it can be exercised in tests.



76
77
78
79
80
81
82
83
84
85
86
# File 'lib/dispatch-rails.rb', line 76

def handle_at_exit(exception = nil)
  if exception && !ignored_exit_exception?(exception) && configuration.capture_at_exit
    Reporter.capture(exception, handled: false, context: { tags: { source: "at_exit" } })
  end

  timeout = configuration.shutdown_timeout.to_f
  return unless timeout.positive?

  HeartbeatAggregator.instance.deliver_all
  Transport.instance.flush(timeout: timeout)
end

.ignored_exit_exception?(exception) ⇒ Boolean

Normal exits and SIGTERM-driven graceful shutdowns (deploys, scale-downs) are not crashes.

Returns:

  • (Boolean)


90
91
92
93
94
95
96
# File 'lib/dispatch-rails.rb', line 90

def ignored_exit_exception?(exception)
  return true if exception.is_a?(SystemExit)
  return false unless exception.is_a?(SignalException)

  name = exception.respond_to?(:signm) ? exception.signm.to_s : exception.to_s
  name.end_with?("TERM")
end

.install_at_exit_callbackObject

Process-exit safety net: report the exception that is killing the process (a crash during boot, a dying runner/script), then ship the in-progress heartbeat window and drain the transport queue so already-captured events survive the shutdown. Exceptions captured upstream (middleware, rake handler) carry the Reporter dedup marker and aren’t re-sent.



68
69
70
71
72
73
# File 'lib/dispatch-rails.rb', line 68

def install_at_exit_callback
  return if @at_exit_installed

  @at_exit_installed = true
  at_exit { handle_at_exit($!) }
end

.report(description:, severity: nil, source: "api", metadata: {}, reporter: nil, correlation_id: nil) ⇒ Object

File a curated bug report (a ticket) programmatically — the API-only analogue of a human clicking the widget. Ideal for an AI agent or a rescue block that wants to turn a failure into a tracked, human-readable report. Pass a correlation_id (e.g. request.request_id) to link the report to an already-captured error. Synchronous — returns the created ticket’s { “id”, “status”, “url” } Hash, or nil on failure. Never raises.

Dispatch::Rails.report(
  description: "Nightly import aborted: upstream returned 502",
  severity: "high",
  correlation_id: request.request_id,
  metadata: { job: "ImportJob" }
)


50
51
52
53
54
55
56
57
58
59
60
# File 'lib/dispatch-rails.rb', line 50

def report(description:, severity: nil, source: "api", metadata: {}, reporter: nil, correlation_id: nil)
  return nil unless configuration.configured?

  meta = ( || {}).dup
  meta[:correlation_id] = correlation_id if correlation_id
  ticket = {
    description: description, source: source, severity: severity,
    metadata: meta, reporter: reporter
  }.compact
  Transport.instance.post_ticket(ticket: ticket)
end

.reset!Object



25
26
27
# File 'lib/dispatch-rails.rb', line 25

def reset!
  @configuration = Configuration.new
end