Module: Failbot
- Included in:
- Failbot
- Defined in:
- lib/failbot.rb,
lib/failbot/compat.rb,
lib/failbot/version.rb,
lib/failbot/haystack.rb,
lib/failbot/exit_hook.rb,
lib/failbot/middleware.rb,
lib/failbot/file_backend.rb,
lib/failbot/http_backend.rb,
lib/failbot/json_backend.rb,
lib/failbot/heroku_backend.rb,
lib/failbot/memory_backend.rb
Overview
This file exists so that the unhandled exception hook may easily be injected into programs that don’t register it themselves. It also provides a lightweight failbot interface that doesn’t bring in any other libraries until a report is made, which is useful for environments where boot time is important.
To use, set RUBYOPT or pass an -r argument to ruby:
RUBYOPT=rfailbot/exit_hook some-program.rb
Or:
ruby -rfailbot/exit_hook some-program.rb
Your program can also require this library instead of ‘failbot’ to minimize the amount of up-front processing required and automatically install the exit hook.
require 'failbot/exit_hook'
The ‘failbot’ lib is loaded in full the first time an actual report is made.
Defined Under Namespace
Modules: Compat Classes: FileBackend, HTTPBackend, Haystack, HerokuBackend, JSONBackend, MemoryBackend, Rescuer
Constant Summary collapse
- VERSION =
"1.1.2"
Instance Attribute Summary collapse
-
#already_reporting ⇒ Object
prevent recursive calls to Failbot.report!.
-
#instrumenter ⇒ Object
Public: Set an instrumenter to be called when exceptions are reported.
Attributes included from Compat
Instance Method Summary collapse
-
#context ⇒ Object
Stack of context information to include in the next failbot report.
-
#disable(&block) ⇒ Object
Public: Disable exception reporting.
-
#enable ⇒ Object
Public: Enable exception reporting.
-
#exception_info(e) ⇒ Object
Extract exception info into a simple Hash.
- #hostname ⇒ Object
-
#install_unhandled_exception_hook! ⇒ Object
Installs an at_exit hook to report exceptions that raise all the way out of the stack and halt the interpreter.
- #logger ⇒ Object
- #logger=(logger) ⇒ Object
-
#method_missing(method, *args, &block) ⇒ Object
Tap into any other method invocation on the Failbot module (especially report) and lazy load and configure everything the first time.
-
#pop ⇒ Object
Remove the last info hash from the context stack.
-
#push(info = {}) ⇒ Object
Add info to be sent in the next failbot report, should one occur.
-
#report(e, other = {}) ⇒ Object
Public: Sends an exception to the exception tracking service along with a hash of custom attributes to be included with the report.
- #report!(e, other = {}) ⇒ Object
-
#reports ⇒ Object
Public: exceptions that were reported.
-
#reset! ⇒ Object
Reset the context stack to a pristine state.
-
#setup(settings = {}, default_context = {}) ⇒ Object
Shim into Failbot.setup and store config information off for the first time a real method is invoked.
-
#squash_context(*other) ⇒ Object
Combines all context hashes into a single hash converting non-standard data types in values to strings, then combines the result with a custom info hash provided in the other argument.
Methods included from Compat
backend!, backend_name, cast, config, config_file, default_options, default_options=, environment, fail, haystack, raise_errors=, raise_errors?, report_errors=, report_errors?, setup_deprecated
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
Tap into any other method invocation on the Failbot module (especially report) and lazy load and configure everything the first time.
75 76 77 78 79 |
# File 'lib/failbot/exit_hook.rb', line 75 def method_missing(method, *args, &block) return super if @failbot_loaded require 'failbot' send(method, *args, &block) end |
Instance Attribute Details
#already_reporting ⇒ Object
prevent recursive calls to Failbot.report!
44 45 46 |
# File 'lib/failbot.rb', line 44 def already_reporting @already_reporting end |
#instrumenter ⇒ Object
Public: Set an instrumenter to be called when exceptions are reported.
class CustomInstrumenter
def instrument(name, payload = {})
warn "Exception: #{payload["class"]}\n#{payload.inspect}"
end
end
Failbot.instrumenter = CustomInstrumenter
The instrumenter must conform to the ‘ActiveSupport::Notifications` interface, which defines `#instrument` and accepts:
name - the String name of the event (e.g. “report.failbot”) payload - a Hash of the exception context.
41 42 43 |
# File 'lib/failbot.rb', line 41 def instrumenter @instrumenter end |
Instance Method Details
#context ⇒ Object
Stack of context information to include in the next failbot report. These hashes are condensed down into one and included in the next report. Don’t mess with this structure directly - use the #push and #pop methods.
101 102 103 |
# File 'lib/failbot.rb', line 101 def context @context ||= [{'server' => hostname}] end |
#disable(&block) ⇒ Object
Public: Disable exception reporting. This is equivalent to calling ‘Failbot.setup(“FAILBOT_REPORT” => 0)`, but can be called after setup.
Failbot.disable do
something_that_might_go_kaboom
end
block - an optional block to perform while reporting is disabled. If a block
is passed, reporting will be re-enabled after the block is called.
189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/failbot.rb', line 189 def disable(&block) original_report_errors = @report_errors @report_errors = false if block begin block.call ensure @report_errors = original_report_errors end end end |
#enable ⇒ Object
Public: Enable exception reporting. Reporting is enabled by default, but this can be called if it is explicitly disabled by calling ‘Failbot.disable` or setting `FAILBOT_REPORTING => “0”` in `Failbot.setup`.
205 206 207 |
# File 'lib/failbot.rb', line 205 def enable @report_errors = true end |
#exception_info(e) ⇒ Object
Extract exception info into a simple Hash.
e - The exception object to turn into a Hash.
Returns a Hash including a ‘class’, ‘message’, ‘backtrace’, and ‘rollup’
keys. The rollup value is a MD5 hash of the exception class, file, and line
number and is used to group exceptions.
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/failbot.rb', line 256 def exception_info(e) backtrace = Array(e.backtrace)[0, 500] res = { 'class' => e.class.to_s, 'message' => e., 'backtrace' => backtrace.join("\n"), 'ruby' => RUBY_DESCRIPTION, 'rollup' => Digest::MD5.hexdigest("#{e.class}#{backtrace[0]}"), 'created_at' => Time.now.utc.iso8601(6) } if exception_context = (e.respond_to?(:failbot_context) && e.failbot_context) res.merge!(exception_context) end if original = (e.respond_to?(:original_exception) && e.original_exception) remote_backtrace = [] remote_backtrace << original. if original.backtrace remote_backtrace.concat(Array(original.backtrace)[0,500]) end res['remote_backtrace'] = remote_backtrace.join("\n") end res end |
#hostname ⇒ Object
292 293 294 |
# File 'lib/failbot.rb', line 292 def hostname @hostname ||= Socket.gethostname end |
#install_unhandled_exception_hook! ⇒ Object
Installs an at_exit hook to report exceptions that raise all the way out of the stack and halt the interpreter. This is useful for catching boot time errors as well and even signal kills.
To use, call this method very early during the program’s boot to cover as much code as possible:
require 'failbot'
Failbot.install_unhandled_exception_hook!
Returns true when the hook was installed, nil when the hook had previously been installed by another component.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/failbot/exit_hook.rb', line 51 def install_unhandled_exception_hook! # only install the hook once, even when called from multiple locations return if @unhandled_exception_hook_installed # the $! is set when the interpreter is exiting due to an exception at_exit do boom = $! if boom && !@raise_errors && !boom.is_a?(SystemExit) report(boom, 'argv' => ([$0]+ARGV).join(" "), 'halting' => true) end end @unhandled_exception_hook_installed = true end |
#logger ⇒ Object
284 285 286 |
# File 'lib/failbot.rb', line 284 def logger @logger ||= Logger.new($stderr) end |
#logger=(logger) ⇒ Object
288 289 290 |
# File 'lib/failbot.rb', line 288 def logger=(logger) @logger = logger end |
#pop ⇒ Object
Remove the last info hash from the context stack.
120 121 122 |
# File 'lib/failbot.rb', line 120 def pop context.pop if context.size > 1 end |
#push(info = {}) ⇒ Object
Add info to be sent in the next failbot report, should one occur.
info - Hash of name => value pairs to include in the exception report. block - When given, the info is removed from the current context after the
block is executed.
Returns the value returned by the block when given; otherwise, returns nil.
112 113 114 115 116 117 |
# File 'lib/failbot.rb', line 112 def push(info={}) context.push(info) yield if block_given? ensure pop if block_given? end |
#report(e, other = {}) ⇒ Object
Public: Sends an exception to the exception tracking service along with a hash of custom attributes to be included with the report. When the raise_errors option is set, this method raises the exception instead of reporting to the exception tracking service.
e - The Exception object. Must respond to #message and #backtrace. other - Hash of additional attributes to include with the report.
Examples
begin
my_code
rescue => e
Failbot.report(e, :user => current_user)
end
Returns nothing.
146 147 148 149 150 151 152 153 |
# File 'lib/failbot.rb', line 146 def report(e, other = {}) if @raise_errors squash_context(exception_info(e), other) # surface problems squashing raise e else report!(e, other) end end |
#report!(e, other = {}) ⇒ Object
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/failbot.rb', line 155 def report!(e, other = {}) return unless @report_errors data = squash_context(exception_info(e), other) if already_reporting logger.warn "FAILBOT: asked to report while reporting! #{data.inspect}" rescue nil logger.warn e. rescue nil logger.warn e.backtrace.join("\n") rescue nil return end self.already_reporting = true backend.report(data) instrument("report.failbot", data) rescue Object => i # don't fail for any reason logger.debug "FAILBOT: #{data.inspect}" rescue nil logger.debug e. rescue nil logger.debug e.backtrace.join("\n") rescue nil logger.debug i. rescue nil logger.debug i.backtrace.join("\n") rescue nil ensure self.already_reporting = false end |
#reports ⇒ Object
Public: exceptions that were reported. Only available when using the memory and file backends.
Returns an Array of exceptions data Hash.
213 214 215 |
# File 'lib/failbot.rb', line 213 def reports backend.reports end |
#reset! ⇒ Object
Reset the context stack to a pristine state.
125 126 127 |
# File 'lib/failbot.rb', line 125 def reset! @context = [context[0]] end |
#setup(settings = {}, default_context = {}) ⇒ Object
Shim into Failbot.setup and store config information off for the first time a real method is invoked.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/failbot.rb', line 55 def setup(settings={}, default_context={}) deprecated_settings = %w[ backend host port haystack raise_errors ] if settings.empty? || settings.keys.any? { |key| deprecated_settings.include?(key) } warn "%s Deprecated Failbot.setup usage. See %s for details." % [ caller[0], "https://github.com/github/failbot" ] return setup_deprecated(settings) end if default_context.respond_to?(:to_hash) && !default_context.to_hash.empty? context[0] = default_context.to_hash end self.backend = case (name = settings["FAILBOT_BACKEND"]) when "memory" Failbot::MemoryBackend.new when "file" Failbot::FileBackend.new(settings["FAILBOT_BACKEND_FILE_PATH"]) when "http" Failbot::HTTPBackend.new(URI(settings["FAILBOT_HAYSTACK_URL"])) when 'json' Failbot::JSONBackend.new(settings["FAILBOT_BACKEND_JSON_HOST"], settings["FAILBOT_BACKEND_JSON_PORT"]) else raise ArgumentError, "Unknown backend: #{name.inspect}" end @raise_errors = !settings["FAILBOT_RAISE"].to_s.empty? @report_errors = settings["FAILBOT_REPORT"] != "0" # allows overriding the 'app' value to send to single haystack bucket. # used primarily on ghe.io. @app_override = settings["FAILBOT_APP_OVERRIDE"] end |
#squash_context(*other) ⇒ Object
Combines all context hashes into a single hash converting non-standard data types in values to strings, then combines the result with a custom info hash provided in the other argument.
other - Optional array of hashes to also squash in on top of the context
stack hashes.
Returns a Hash with all keys and values.
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/failbot.rb', line 225 def squash_context(*other) merged = {} (context + other).each do |hash| hash.each do |key, value| value = (value.call rescue nil) if value.kind_of?(Proc) merged[key.to_s] = case value when Time value.iso8601 when Date value.strftime("%F") # equivalent to %Y-%m-%d when Numeric value when String, true, false value.to_s else value.inspect end end end merged["app"] = @app_override if @app_override merged end |