Improve ActiveSupport::ErrorReporter API doc [ci-skip]

This moves some of the documentation from the `ErrorReporter` class
directly to the `handle` and `record` methods, and fleshes those out.
This also tweaks the documentation of the other `ErrorReporter` methods.
This commit is contained in:
Jonathan Hefner 2022-08-10 16:26:43 -05:00
parent 4b784710c8
commit e8f189b1cb
1 changed files with 65 additions and 33 deletions

View File

@ -3,7 +3,7 @@
module ActiveSupport module ActiveSupport
# +ActiveSupport::ErrorReporter+ is a common interface for error reporting services. # +ActiveSupport::ErrorReporter+ is a common interface for error reporting services.
# #
# To rescue and report any unhandled error, you can use the +handle+ method: # To rescue and report any unhandled error, you can use the #handle method:
# #
# Rails.error.handle do # Rails.error.handle do
# do_something! # do_something!
@ -11,38 +11,16 @@ module ActiveSupport
# #
# If an error is raised, it will be reported and swallowed. # If an error is raised, it will be reported and swallowed.
# #
# Alternatively if you want to report the error but not swallow it, you can use +record+ # Alternatively, if you want to report the error but not swallow it, you can use #record:
# #
# Rails.error.record do # Rails.error.record do
# do_something! # do_something!
# end # end
# #
# Both methods can be restricted to only handle a specific exception class # Both methods can be restricted to handle only a specific error class:
# #
# maybe_tags = Rails.error.handle(Redis::BaseError) { redis.get("tags") } # maybe_tags = Rails.error.handle(Redis::BaseError) { redis.get("tags") }
# #
# You can also pass some extra context information that may be used by the error subscribers:
#
# Rails.error.handle(context: { section: "admin" }) do
# # ...
# end
#
# Additionally a +severity+ can be passed along to communicate how important the error report is.
# +severity+ can be one of +:error+, +:warning+, or +:info+. Handled errors default to the +:warning+
# severity, and unhandled ones to +:error+.
#
# A +source+ can also be specified, describing where the error originates from. Error subscribers can
# use this to ignore certain errors. For instance, ActiveSupport may report internal errors
# such as cache failures with a source like "redis_cache_store.active_support".
# The default +source+ is "application".
#
# Both +handle+ and +record+ pass through the return value from the block. In the case of +handle+
# rescuing an error, a fallback can be provided. The fallback must be a callable whose result will
# be returned when the block raises and is handled:
#
# user = Rails.error.handle(fallback: -> { User.anonymous }) do
# User.find_by(params)
# end
class ErrorReporter class ErrorReporter
SEVERITIES = %i(error warning info) SEVERITIES = %i(error warning info)
DEFAULT_SOURCE = "application" DEFAULT_SOURCE = "application"
@ -54,12 +32,43 @@ module ActiveSupport
@logger = logger @logger = logger
end end
# Report any unhandled exception, and swallow it. # Evaluates the given block, reporting and swallowing any unhandled error.
# If no error is raised, returns the return value of the block. Otherwise,
# returns the result of +fallback.call+, or +nil+ if +fallback+ is not
# specified.
# #
# # Will report a TypeError to all subscribers and return nil.
# Rails.error.handle do # Rails.error.handle do
# 1 + '1' # 1 + '1'
# end # end
# #
# Can be restricted to handle only a specific error class:
#
# maybe_tags = Rails.error.handle(Redis::BaseError) { redis.get("tags") }
#
# ==== Options
#
# * +:severity+ - This value is passed along to subscribers to indicate how
# important the error report is. Can be +:error+, +:warning+, or +:info+.
# Defaults to +:warning+.
#
# * +:context+ - Extra information that is passed along to subscribers. For
# example:
#
# Rails.error.handle(context: { section: "admin" }) do
# # ...
# end
#
# * +:fallback+ - A callable that provides +handle+'s return value when an
# unhandled error is raised. For example:
#
# user = Rails.error.handle(fallback: -> { User.anonymous }) do
# User.find_by(params)
# end
#
# * +:source+ - This value is passed along to subscribers to indicate the
# source of the error. Subscribers can use this value to ignore certain
# errors. Defaults to <tt>"application"</tt>.
def handle(error_class = StandardError, severity: :warning, context: {}, fallback: nil, source: DEFAULT_SOURCE) def handle(error_class = StandardError, severity: :warning, context: {}, fallback: nil, source: DEFAULT_SOURCE)
yield yield
rescue error_class => error rescue error_class => error
@ -67,13 +76,34 @@ module ActiveSupport
fallback.call if fallback fallback.call if fallback
end end
# Report any unhandled exception, but do not swallow it. # Evaluates the given block, reporting and re-raising any unhandled error.
# If no error is raised, returns the return value of the block.
# #
# # Will report a TypeError to all subscribers and re-raise it.
# Rails.error.record do # Rails.error.record do
# # Will report the TypeError to all subscribers and then raise it.
# 1 + '1' # 1 + '1'
# end # end
# #
# Can be restricted to handle only a specific error class:
#
# tags = Rails.error.record(Redis::BaseError) { redis.get("tags") }
#
# ==== Options
#
# * +:severity+ - This value is passed along to subscribers to indicate how
# important the error report is. Can be +:error+, +:warning+, or +:info+.
# Defaults to +:error+.
#
# * +:context+ - Extra information that is passed along to subscribers. For
# example:
#
# Rails.error.record(context: { section: "admin" }) do
# # ...
# end
#
# * +:source+ - This value is passed along to subscribers to indicate the
# source of the error. Subscribers can use this value to ignore certain
# errors. Defaults to <tt>"application"</tt>.
def record(error_class = StandardError, severity: :error, context: {}, source: DEFAULT_SOURCE) def record(error_class = StandardError, severity: :error, context: {}, source: DEFAULT_SOURCE)
yield yield
rescue error_class => error rescue error_class => error
@ -85,7 +115,7 @@ module ActiveSupport
# #
# report(Exception, handled: Boolean, severity: (:error OR :warning OR :info), context: Hash, source: String) # report(Exception, handled: Boolean, severity: (:error OR :warning OR :info), context: Hash, source: String)
# #
# The +report+ method +should+ never raise an error. # The +report+ method <b>should never</b> raise an error.
def subscribe(subscriber) def subscribe(subscriber)
unless subscriber.respond_to?(:report) unless subscriber.respond_to?(:report)
raise ArgumentError, "Error subscribers must respond to #report" raise ArgumentError, "Error subscribers must respond to #report"
@ -108,19 +138,21 @@ module ActiveSupport
end end
end end
# Update the execution context that is accessible to error subscribers # Update the execution context that is accessible to error subscribers. Any
# context passed to #handle, #record, or #report will be merged with the
# context set here.
# #
# Rails.error.set_context(section: "checkout", user_id: @user.id) # Rails.error.set_context(section: "checkout", user_id: @user.id)
# #
# Any context passed to +handle+, +record+, or +report+ will be merged with the context set here.
# See +ActiveSupport::ExecutionContext.set+
def set_context(...) def set_context(...)
ActiveSupport::ExecutionContext.set(...) ActiveSupport::ExecutionContext.set(...)
end end
# When the block based +handle+ and +record+ methods are not suitable, you can directly use +report+ # Report an error directly to subscribers. You can use this method when the
# block-based #handle and #record methods are not suitable.
# #
# Rails.error.report(error) # Rails.error.report(error)
#
def report(error, handled: true, severity: handled ? :warning : :error, context: {}, source: DEFAULT_SOURCE) def report(error, handled: true, severity: handled ? :warning : :error, context: {}, source: DEFAULT_SOURCE)
unless SEVERITIES.include?(severity) unless SEVERITIES.include?(severity)
raise ArgumentError, "severity must be one of #{SEVERITIES.map(&:inspect).join(", ")}, got: #{severity.inspect}" raise ArgumentError, "severity must be one of #{SEVERITIES.map(&:inspect).join(", ")}, got: #{severity.inspect}"