canvas-lms/lib/canvas/errors.rb

94 lines
3.4 KiB
Ruby

#
# Copyright (C) 2015 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
module Canvas
# The central message bus for errors in canvas.
#
# This class is injected both in ApplicationController for capturing
# exceptions that happen in request/response cycles, and in our
# Delayed::Job callback for failed jobs. We also call out to it
# from several points throughout the codebase directly to register
# an unexpected occurance that doesn't necessarily bubble up to
# that point.
#
# There's a sentry connector built into canvas, but anything one
# wants to do with errors can be hooked into this path with the
# .register! method.
class Errors
# register something to happen on every exception that occurs.
#
# The parameter is a unique key for this callback, which is
# used when assembling return values from ".capture" and it's friends.
#
# The block should accept two parameters, one for the exception/message
# and one for contextual info in the form of a hash. The hash *will*
# have an ":extra" key, and *may* have a ":tags" key. tags would
# be things it might be useful to aggreate errors around (job queue), extras
# are things that would be useful for tracking down why or in what
# circumstance an error might occur (request_context_id)
#
# Canvas::Errors.register!(:my_key) do |ex, data|
# # do something with the exception
# end
def self.register!(key, &block)
registry[key] = block
end
# "capture" is the thing to call if you want to tell Canvas::Errors
# that something bad happened. You can pass in an exception, or
# just a message. If you don't build your data hash
# with a "tags" key and an "extra" key, it will just group all contextual
# information under "extra"
def self.capture(exception, data={})
run_callbacks(exception, wrap_in_extra(data))
end
def self.capture_exception(type, exception)
self.capture(exception, {tags: {type: type.to_s}})
end
# This is really just for clearing out the registry during tests,
# if you call it in production it will dump all registered callbacks
# that got fired in initializers and such until the process restarts.
def self.clear_callback_registry!
@registry = {}
end
def self.run_callbacks(exception, extra)
registry.each_with_object({}) do |(key, callback), outputs|
outputs[key] = callback.call(exception, extra)
end
end
private_class_method :run_callbacks
def self.registry
@registry ||= {}
end
private_class_method :registry
def self.wrap_in_extra(data)
if data.key?(:tags) || data.key?(:extra)
data
else
{extra: data}
end
end
private_class_method :wrap_in_extra
end
end