Skip to content
Browse files

Now depend on safely for safely running code and logging backtraces

  • Loading branch information...
1 parent 0961fdd commit b189804ef596c5313b283a38953d60b870a58fbc @kennethkalmer committed May 8, 2011
View
7 History.txt
@@ -1,3 +1,10 @@
+== 0.1.9 (WIP)
+
+* Depend on the 'safely' gem for the safely method and logging backtraces
+* Fixed compatibility with ruote-amqp 2.2.0
+* AMQP fixes by @marcbowes
+* Updated to rspec 2 (various contributors)
+
== 0.1.18.1 2010-08-04
* Fixed issue with our own amqp.rb file causing havoc on Ruby 1.8.7
View
1 lib/daemon_kit.rb
@@ -19,7 +19,6 @@ module DaemonKit
autoload :Application, 'daemon_kit/application'
autoload :Arguments, 'daemon_kit/arguments'
autoload :Config, 'daemon_kit/config'
- autoload :Safety, 'daemon_kit/safety'
autoload :PidFile, 'daemon_kit/pid_file'
autoload :AbstractLogger, 'daemon_kit/abstract_logger'
autoload :EM, 'daemon_kit/em'
View
32 lib/daemon_kit/error_handlers/base.rb
@@ -1,32 +0,0 @@
-module DaemonKit
- module ErrorHandlers
- # Error handlers in DaemonKit are used by the #Safety class. Any
- # error handler has to support the interface provided by this
- # class. It's also required that safety handlers implement a
- # singleton approach (handled by default by #Base).
- class Base
-
- class << self
-
- @instance = nil
-
- def instance
- @instance ||= new
- end
- private :new
-
- # When we're inherited, immediately register the handler with
- # the safety net
- def inherited( child ) #:nodoc:
- Safety.register_error_handler( child )
- end
- end
-
- # Error handlers should overwrite this method and implement
- # their own reporting method.
- def handle_exception( exception )
- raise NoMethodError, "Error handler doesn't support #handle_exception"
- end
- end
- end
-end
View
180 lib/daemon_kit/error_handlers/hoptoad.rb
@@ -1,180 +0,0 @@
-require 'net/http'
-
-module DaemonKit
- module ErrorHandlers
- # Error reporting via Hoptoad.
- class Hoptoad < Base
-
- # Front end to parsing the backtrace for each notice
- # (Graciously borrowed from http://github.com/thoughtbot/hoptoad_notifier)
- class Backtrace
-
- # Handles backtrace parsing line by line
- # (Graciously borrowed from http://github.com/thoughtbot/hoptoad_notifier)
- class Line
-
- INPUT_FORMAT = %r{^([^:]+):(\d+)(?::in `([^']+)')?$}.freeze
-
- # The file portion of the line (such as app/models/user.rb)
- attr_reader :file
-
- # The line number portion of the line
- attr_reader :number
-
- # The method of the line (such as index)
- attr_reader :method
-
- # Parses a single line of a given backtrace
- # @param [String] unparsed_line The raw line from +caller+ or some backtrace
- # @return [Line] The parsed backtrace line
- def self.parse(unparsed_line)
- _, file, number, method = unparsed_line.match(INPUT_FORMAT).to_a
- new(file, number, method)
- end
-
- def initialize(file, number, method)
- self.file = file
- self.number = number
- self.method = method
- end
-
- # Reconstructs the line in a readable fashion
- def to_s
- "#{file}:#{number}:in `#{method}'"
- end
-
- def ==(other)
- to_s == other.to_s
- end
-
- def inspect
- "<Line:#{to_s}>"
- end
-
- def to_xml
- data = [ method, file, number ].map { |s| URI.escape( s || 'unknown', %q{"'<>&} ) }
- %q{<line method="%s" file="%s" number="%s" />} % data
- end
-
- private
-
- attr_writer :file, :number, :method
- end
-
- # holder for an Array of Backtrace::Line instances
- attr_reader :lines
-
- def self.parse(ruby_backtrace, opts = {})
- ruby_lines = split_multiline_backtrace(ruby_backtrace)
-
- filters = opts[:filters] || []
- filtered_lines = ruby_lines.to_a.map do |line|
- filters.inject(line) do |line, proc|
- proc.call(line)
- end
- end.compact
-
- lines = filtered_lines.collect do |unparsed_line|
- Line.parse(unparsed_line)
- end
-
- instance = new(lines)
- end
-
- def initialize(lines)
- self.lines = lines
- end
-
- def inspect
- "<Backtrace: " + lines.collect { |line| line.inspect }.join(", ") + ">"
- end
-
- def ==(other)
- if other.respond_to?(:lines)
- lines == other.lines
- else
- false
- end
- end
-
- private
-
- attr_writer :lines
-
- def self.split_multiline_backtrace(backtrace)
- if backtrace.to_a.size == 1
- backtrace.to_a.first.split(/\n\s*/)
- else
- backtrace
- end
- end
- end
-
- # Your hoptoad API key
- @api_key = nil
- attr_accessor :api_key
-
- def handle_exception( exception )
- headers = {
- 'Content-type' => 'text/xml',
- 'Accept' => 'text/xml, application/xml'
- }
-
- http = Net::HTTP.new( url.host, url.port )
- data = format_exception( exception )
- DaemonKit.logger.debug("Sending to Hoptoad: #{data}")
-
- response = begin
- http.post( url.path, data, headers )
- rescue TimeoutError => e
- DaemonKit.logger.error("Timeout while contacting the Hoptoad server.")
- nil
- end
- case response
- when Net::HTTPSuccess then
- DaemonKit.logger.info "Hoptoad Success: #{response.class}"
- else
- DaemonKit.logger.error "Hoptoad Failure: #{response.class}\n#{response.body if response.respond_to? :body}"
- end
- end
-
- def url
- URI.parse("http://hoptoadapp.com/notifier_api/v2/notices")
- end
-
- def format_exception( exception )
- lines = Backtrace.parse( exception.backtrace )
- exception_message= exception.message
- exception_message.gsub!("\"","&quot;")
- exception_message.gsub!("'","&apos;")
- exception_message.gsub!("&","&amp;")
- exception_message.gsub!("<","&lt;")
- exception_message.gsub!(">","&gt;")
-
- <<-EOF
-<?xml version="1.0" encoding="UTF-8"?>
-<notice version="2.0">
- <api-key>#{self.api_key}</api-key>
- <notifier>
- <name>daemon-kit</name>
- <version>#{DaemonKit::VERSION}</version>
- <url>http://github.com/kennethkalmer/daemon-kit</url>
- </notifier>
- <error>
- <class>#{exception.class.name}</class>
- <message>#{exception_message}</message>
- <backtrace>
- #{Backtrace.parse( exception.backtrace ).lines.inject('') { |string,line| string << line.to_xml }}
- </backtrace>
- </error>
- <server-environment>
- <project-root>#{DaemonKit.root}</project-root>
- <environment-name>#{DaemonKit.env}</environment-name>
- </server-environment>
-</notice>
- EOF
- end
- end
-
- end
-end
View
41 lib/daemon_kit/initializer.rb
@@ -8,6 +8,7 @@
$LOAD_PATH.include?( File.expand_path('../', __FILE__).to_absolute_path )
require 'daemon_kit'
+require 'safely'
module DaemonKit
@@ -74,7 +75,7 @@ def self.shutdown( clean = false, do_exit = false )
end
end
- log_exceptions if DaemonKit.configuration.backtraces && !clean
+ Safely::Backtrace.safe_shutdown! if DaemonKit.configuration.backtraces && clean
DaemonKit.logger.warn "Shutting down #{DaemonKit.configuration.daemon_name}"
@@ -203,37 +204,16 @@ def include_core_lib
end
def configure_backtraces
- Thread.abort_on_exception = configuration.backtraces
+ Thread.abort_on_exception = true
+
+ Safely::Backtrace.trace_directory = File.join( DAEMON_ROOT, "log" )
+ Safely::Backtrace.enable!
end
def set_process_name
$0 = configuration.daemon_name
end
- def self.log_exceptions
- trace_file = File.join( DaemonKit.root, 'log', "backtrace-#{Time.now.strftime('%Y%m%d%H%M%S')}-#{Process.pid}.log" )
- trace_log = Logger.new( trace_file )
-
- # Find the last exception
- e = nil
- ObjectSpace.each_object {|o|
- if ::Exception === o
- e = o
- end
- }
-
- trace_log.info "*** Below you'll find the most recent exception thrown, this will likely (but not certainly) be the exception that made #{DaemonKit.configuration.daemon_name} exit abnormally ***"
- trace_log.error e
-
- trace_log.info "*** Below you'll find all the exception objects in memory, some of them may have been thrown in your application, others may just be in memory because they are standard exceptions ***"
- ObjectSpace.each_object {|o|
- if ::Exception === o
- trace_log.error o
- end
- }
-
- trace_log.close
- end
end
# Holds our various configuration values
@@ -268,8 +248,8 @@ class Configuration
# Use the force kill patch? Give the number of seconds
configurable :force_kill_wait
- # Should be log backtraces
- configurable :backtraces, false
+ # Should we log backtraces
+ configurable :backtraces, true
# Configurable umask
configurable :umask, 0022
@@ -283,9 +263,6 @@ class Configuration
# Collection of signal traps
attr_reader :signal_traps
- # Our safety net (#Safety) instance
- attr_accessor :safety_net
-
# :nodoc: Shutdown hooks
attr_reader :shutdown_hooks
@@ -301,8 +278,6 @@ def initialize
self.force_kill_wait = false
- self.safety_net = DaemonKit::Safety.instance
-
@signal_traps = {}
@shutdown_hooks = []
end
View
84 lib/daemon_kit/safety.rb
@@ -1,84 +0,0 @@
-module DaemonKit
- # Provides a wrapper for running code inside a 'safety net' Any
- # exceptions raised inside a safety net is handled and reported via
- # loggers, email or Hoptoad.
- #
- # The safety net can be configured via DaemonKit.config.safety,
- # which holds the only instance of the safety net.
- class Safety
-
- # Who get's notified.
- @handler = nil
- attr_accessor :handler
-
- # Registered error handlers
- @error_handlers = {}
- attr_reader :error_handlers
-
- class << self
-
- # Singleton
- @instance = nil
-
- def instance
- @instance ||= new
- end
- private :new
-
- # Run the provided block inside a safety net.
- def run(&block)
- self.instance.run(&block)
- end
-
- def register_error_handler( klass )
- name = klass.to_s.split('::').last.downcase
-
- DaemonKit.logger.debug( "Registering error handler '#{name}' (#{klass})" ) if DaemonKit.logger
-
- instance.instance_eval( <<-EOF, __FILE__, __LINE__ )
- def #{name}
- @#{name} ||= #{klass}.instance
- end
- EOF
- end
- end
-
- # Run the provided block inside a safety net.
- def run(&block)
- begin
- block.call
- rescue => e
- # Log
- DaemonKit.logger.fatal "Safety net caught exception: #{e.message}"
- DaemonKit.logger.fatal "Backtrace: #{e.backtrace.join("\n ")}"
-
- get_handler.handle_exception( e ) if get_handler
- end
- end
-
- def get_handler
- if @handler && self.respond_to?( @handler )
- h = send( @handler )
- return h if h.class.ancestors.include?( DaemonKit::ErrorHandlers::Base )
- end
-
- return nil
- end
- end
-end
-
-class Object
- class << self
- def safely(&block)
- DaemonKit::Safety.run(&block)
- end
- end
-
- def safely(&block)
- DaemonKit::Safety.run(&block)
- end
-end
-
-# Load our error handlers
-require 'daemon_kit/error_handlers/base'
-require 'daemon_kit/error_handlers/hoptoad'
View
3 lib/generators/daemon_kit/app/templates/Gemfile
@@ -9,4 +9,7 @@ source :gemcutter
# daemon-kit
gem 'daemon-kit'
+# safely (http://github.com/kennethkalmer/safely)
+gem 'safely'
+
# For more information on bundler, please visit http://gembundler.com
View
4 lib/generators/daemon_kit/app/templates/config/environment.rb.tt
@@ -19,8 +19,4 @@ DaemonKit::Initializer.run do |config|
# Log backraces when a thread/daemon dies (Recommended)
# config.backtraces = true
-
- # Configure the safety net (see DaemonKit::Safety)
- # config.safety_net.handler = :hoptoad
- # config.safety_net.hoptoad.api_key = ''
end
View
23 spec/error_handlers_spec.rb
@@ -1,23 +0,0 @@
-require File.dirname(__FILE__) + '/spec_helper'
-
-describe DaemonKit::Safety do
-end
-
-describe "DaemonKit::ErrorHandlers::Mail" do
- it "should send an email report" do
- conf = Object.new
- conf.stubs(:daemon_name).returns('test')
- DaemonKit.stubs(:configuration).returns(conf)
-
- fake_smtp = Object.new
- fake_smtp.expects(:start).with('localhost.localdomain', nil, nil, nil)
- Net::SMTP.expects(:new).with('localhost', 25).returns(fake_smtp)
-
- begin
- raise RuntimeError, "specs don't fail :)"
- rescue => e
- handler = DaemonKit::ErrorHandlers::Mail.instance
- handler.handle_exception( e )
- end
- end
-end

0 comments on commit b189804

Please sign in to comment.
Something went wrong with that request. Please try again.