errorapp_notifier 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/MIT-LICENSE +25 -0
  6. data/README.md +60 -0
  7. data/Rakefile +8 -0
  8. data/errorapp_notifier.gemspec +29 -0
  9. data/lib/errorapp_notifier/action_controller_methods.rb +35 -0
  10. data/lib/errorapp_notifier/application_environment_data.rb +77 -0
  11. data/lib/errorapp_notifier/config.rb +67 -0
  12. data/lib/errorapp_notifier/controller_failure_data.rb +96 -0
  13. data/lib/errorapp_notifier/exception_data.rb +19 -0
  14. data/lib/errorapp_notifier/failure_data.rb +87 -0
  15. data/lib/errorapp_notifier/monkeypatches.rb +11 -0
  16. data/lib/errorapp_notifier/notifier.rb +80 -0
  17. data/lib/errorapp_notifier/notifiers/rack_rails.rb +32 -0
  18. data/lib/errorapp_notifier/notifiers/rails.rb +34 -0
  19. data/lib/errorapp_notifier/notifiers/tester.rb +16 -0
  20. data/lib/errorapp_notifier/notify.rb +59 -0
  21. data/lib/errorapp_notifier/rack_failure_data.rb +29 -0
  22. data/lib/errorapp_notifier/railtie.rb +48 -0
  23. data/lib/errorapp_notifier/sanitizer.rb +56 -0
  24. data/lib/errorapp_notifier/tasks/errorapp_notifier.rake +6 -0
  25. data/lib/errorapp_notifier/version.rb +3 -0
  26. data/lib/errorapp_notifier.rb +74 -0
  27. data/lib/generators/errorapp_notifier/errorapp_notifier_generator.rb +54 -0
  28. data/lib/generators/errorapp_notifier/templates/errorapp_notifier.rb +2 -0
  29. data/spec/errorapp_notifier/config_spec.rb +76 -0
  30. data/spec/errorapp_notifier/controller_failure_data_spec.rb +277 -0
  31. data/spec/errorapp_notifier/failure_data_spec.rb +32 -0
  32. data/spec/errorapp_notifier/notifier_spec.rb +31 -0
  33. data/spec/errorapp_notifier/notify_spec.rb +128 -0
  34. data/spec/errorapp_notifier/rack_failure_data_spec.rb +87 -0
  35. data/spec/errorapp_notifier/sanitizer_spec.rb +57 -0
  36. data/spec/helper.rb +12 -0
  37. data/spec/spec_helper.rb +16 -0
  38. metadata +188 -0
@@ -0,0 +1,16 @@
1
+ module ErrorappNotifier
2
+ module Integration
3
+ class OmgTestException < StandardError;
4
+ end
5
+
6
+ def self.test
7
+ begin
8
+ raise OmgTestException.new, 'Test exception'
9
+ rescue Exception => e
10
+ ErrorappNotifier::Notifier.notify_error(
11
+ ErrorappNotifier::FailureData.new(e, "Test Exception")
12
+ )
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,59 @@
1
+ module ErrorappNotifier
2
+ class Notify
3
+ class << self
4
+ def notify_with_controller(exception, controller = nil, request = nil)
5
+ notify_exception(exception) do
6
+ if !ignore?(exception, request)
7
+ ControllerFailureData.new(exception, controller, request)
8
+ end
9
+ end
10
+ end
11
+
12
+ def notify_with_rack(exception, environment, request)
13
+ notify_exception(exception) do
14
+ RackFailureData.new(exception, environment, request)
15
+ end
16
+ end
17
+
18
+ def notify(exception, name = nil)
19
+ notify_exception(exception) do
20
+ FailureData.new(exception, name)
21
+ end
22
+ end
23
+
24
+ def ignore?(exception, request)
25
+ ignore_class?(exception) || ignore_user_agent?(request)
26
+ end
27
+
28
+ def ignore_class?(exception)
29
+ config.ignore_exceptions.flatten.any? do |exception_class|
30
+ exception_class === exception.class.to_s
31
+ end
32
+ end
33
+
34
+ def ignore_user_agent?(request)
35
+ config.ignore_user_agents.flatten.any? do |user_agent|
36
+ user_agent === request.user_agent.to_s
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def notify_exception(exception, &block)
43
+ if config.should_send_to_api?
44
+ notify!(yield)
45
+ else
46
+ raise exception
47
+ end
48
+ end
49
+
50
+ def notify!(data)
51
+ Notifier.notify_error(data)
52
+ end
53
+
54
+ def config
55
+ ErrorappNotifier.configuration
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,29 @@
1
+ module ErrorappNotifier
2
+ class RackFailureData < FailureData
3
+ def initialize(exception, environment, request)
4
+ super(exception)
5
+ @environment = environment
6
+ @request = request
7
+ end
8
+
9
+ private
10
+
11
+ def framework
12
+ 'rack'
13
+ end
14
+
15
+ def extra_stuff
16
+ return {} if @request.nil?
17
+ {
18
+ :request => {
19
+ :url => "#{@request.url}",
20
+ :parameters => @request.params,
21
+ :request_method => @request.request_method.to_s,
22
+ :remote_ip => @request.ip,
23
+ :headers => extract_http_headers(@environment),
24
+ :session => Sanitizer.sanitize_session(@request)
25
+ }
26
+ }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,48 @@
1
+ require 'rails'
2
+ require 'errorapp_notifier'
3
+ require 'errorapp_notifier/notifiers/rack_rails'
4
+
5
+ module ErrorappNotifier
6
+ class Railtie < Rails::Railtie
7
+ rake_tasks do
8
+ load "errorapp_notifier/tasks/errorapp_notifier.rake"
9
+ end
10
+
11
+ config.after_initialize do
12
+ ErrorappNotifier.configure do |config|
13
+ config.logger ||= ::Rails.logger
14
+ config.environment_name ||= ::Rails.env
15
+ config.project_root ||= ::Rails.root
16
+ config.params_filters += Rails.configuration.filter_parameters.map do |filter|
17
+ case filter
18
+ when String, Symbol
19
+ /\A#{filter}\z/
20
+ else
21
+ filter
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ initializer "errorapp.middleware" do |app|
28
+ if defined?(ActionController::Base)
29
+ ActionController::Base.send(:include, ErrorappNotifier::ActionControllerMethods)
30
+ end
31
+
32
+ ErrorappNotifier.logger.info("Loading ErrorappNotifier #{ErrorappNotifier::VERSION} for #{Rails::VERSION::STRING}")
33
+
34
+ middleware = if defined?(ActionDispatch::DebugExceptions)
35
+ # rails 3.2.x
36
+ "ActionDispatch::DebugExceptions"
37
+ elsif defined?(ActionDispatch::ShowExceptions)
38
+ # rails 3.0.x && 3.1.x
39
+ "ActionDispatch::ShowExceptions"
40
+ end
41
+ begin
42
+ app.config.middleware.insert_after middleware, "Rack::RailsErrorappNotifier"
43
+ rescue
44
+ app.config.middleware.use "Rack::RailsErrorappNotifier"
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,56 @@
1
+ module ErrorappNotifier
2
+ class Sanitizer
3
+ def self.sanitize_hash(hash)
4
+ case hash
5
+ when Hash
6
+ hash.inject({}) do |result, (key, value)|
7
+ result.update(key => sanitize_hash(value))
8
+ end
9
+ when Array
10
+ hash.collect{|value| sanitize_hash(value)}
11
+ when Fixnum, String, Bignum
12
+ hash
13
+ else
14
+ hash.to_s
15
+ end
16
+ end
17
+
18
+ def self.filter_hash(keys_to_filter, hash)
19
+ keys_to_filter.map!{ |key| key.to_s }
20
+ if keys_to_filter.is_a?(Array) && !keys_to_filter.empty?
21
+ hash.each do |key, value|
22
+ if key_match?(key, keys_to_filter)
23
+ hash[key] = '[FILTERED]'
24
+ elsif value.respond_to?(:to_hash)
25
+ filter_hash(keys_to_filter, hash[key])
26
+ end
27
+ end
28
+ end
29
+ hash
30
+ end
31
+
32
+ def self.sanitize_session(request)
33
+ session_hash = {'session_id' => "", 'data' => {}}
34
+
35
+ if request.respond_to?(:session)
36
+ session = request.session
37
+ session_hash['session_id'] = request.session_options ? request.session_options[:id] : nil
38
+ session_hash['session_id'] ||= session.respond_to?(:session_id) ? session.session_id : session.instance_variable_get("@session_id")
39
+ session_hash['data'] = session.respond_to?(:to_hash) ? session.to_hash : session.instance_variable_get("@data") || {}
40
+ session_hash['session_id'] ||= session_hash['data'][:session_id]
41
+ session_hash['data'].delete(:session_id)
42
+ end
43
+
44
+ sanitize_hash(session_hash)
45
+ end
46
+
47
+ private
48
+
49
+ def self.key_match?(key, keys_to_filter)
50
+ keys_to_filter.any? do |k|
51
+ regexp = k.is_a?(Regexp)? k : Regexp.new(k.to_s, true)
52
+ key =~ regexp
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,6 @@
1
+ namespace :errorapp_notifier do
2
+ desc "Send a test exception to Errorapp."
3
+ task :test_exception => :environment do
4
+ ErrorappNotifier::Integration.test
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ module ErrorappNotifier
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,74 @@
1
+ require 'logger'
2
+
3
+ require 'errorapp_notifier/config'
4
+ require 'errorapp_notifier/failure_data'
5
+ require 'errorapp_notifier/notify'
6
+ require 'errorapp_notifier/controller_failure_data'
7
+ require 'errorapp_notifier/rack_failure_data'
8
+ require 'errorapp_notifier/notifier'
9
+ require 'errorapp_notifier/sanitizer'
10
+ require 'errorapp_notifier/version'
11
+ require 'errorapp_notifier/action_controller_methods'
12
+ require 'errorapp_notifier/notifiers/tester'
13
+
14
+ require 'errorapp_notifier/railtie' if defined?(Rails::Railtie)
15
+
16
+ module ErrorappNotifier
17
+ PROTOCOL_VERSION = 1
18
+ CLIENT_NAME = 'errorapp_notifier-gem'
19
+ ENVIRONMENT_FILTER = []
20
+ ENVIRONMENT_WHITELIST = %w(HOME PATH PWD RUBYOPT GEM_HOME RACK_ENV
21
+ RAILS_ENV BUNDLE_GEMFILE BUNDLE_BIN_PATH)
22
+
23
+
24
+ class << self
25
+ def configure
26
+ yield(configuration)
27
+ end
28
+
29
+ def configuration
30
+ @configuration ||= ErrorappNotifier::Config.new
31
+ end
32
+
33
+ def logger
34
+ configuration.logger
35
+ end
36
+
37
+ def notify(exception, name=nil)
38
+ ErrorappNotifier::Notify.notify(exception, name)
39
+ end
40
+
41
+ def rescue(name=nil, context=nil, &block)
42
+ begin
43
+ context(context) unless context.nil?
44
+ block.call
45
+ rescue Exception => e
46
+ ErrorappNotifier::Notify.notify(e, name)
47
+ ensure
48
+ clear!
49
+ end
50
+ end
51
+
52
+ def rescue_and_reraise(name=nil, context=nil, &block)
53
+ begin
54
+ context(context) unless context.nil?
55
+ block.call
56
+ rescue Exception => e
57
+ ErrorappNotifier::Notify.notify(e, name)
58
+ raise(e)
59
+ ensure
60
+ clear!
61
+ end
62
+ end
63
+
64
+ def clear!
65
+ Thread.current[:notifier_context] = nil
66
+ end
67
+
68
+ def context(hash = {})
69
+ Thread.current[:notifier_context] ||= {}
70
+ Thread.current[:notifier_context].merge!(hash)
71
+ self
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,54 @@
1
+ require 'rails/generators'
2
+
3
+ class ErrorappNotifierGenerator < Rails::Generators::Base
4
+ desc "Creates the ErrorApp initializer file at config/errorapp_notifier.rb"
5
+
6
+ class_option :api_key, :aliases => "-k", :type => :string,
7
+ :desc => "Your ErrorApp API key"
8
+
9
+ def self.source_root
10
+ @_errorapp_source_root ||= File.expand_path("../templates", __FILE__)
11
+ end
12
+
13
+ def install
14
+ ensure_api_key_was_configured
15
+ generate_initializer unless api_key_configured?
16
+ test_errorapp
17
+ end
18
+
19
+ private
20
+
21
+ def ensure_api_key_was_configured
22
+ if !options[:api_key] && !api_key_configured?
23
+ puts "Must pass --api_key or create config/initializers/errorapp_notifier.rb"
24
+ exit
25
+ end
26
+ end
27
+
28
+ def api_key
29
+ if options[:api_key]
30
+ "'#{options[:api_key]}'"
31
+ end
32
+ end
33
+
34
+ def generate_initializer
35
+ template 'errorapp_notifier.rb', 'config/initializers/errorapp_notifier.rb'
36
+ end
37
+
38
+ def api_key_configured?
39
+ File.exists?('config/initializers/errorapp_notifier.rb')
40
+ end
41
+
42
+ def test_errorapp
43
+ puts run("rails runner ErrorappNotifier::Integration.test")
44
+ end
45
+
46
+ def configuration_output
47
+ output = <<-eos
48
+ ErrorappNotifier.configure do|config|
49
+ config.api_key = '#{options[:api_key]}'
50
+ end
51
+ eos
52
+ output
53
+ end
54
+ end
@@ -0,0 +1,2 @@
1
+ <%= configuration_output %>
2
+
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe ErrorappNotifier::Config, 'defaults' do
4
+ it "have sensible defaults" do
5
+ default_config.ssl.should == true
6
+ default_config.remote_host.should == 'errorapp.com'
7
+ default_config.remote_port.should == 443
8
+ default_config.application_root.should == Dir.pwd
9
+ default_config.http_proxy_host.should be_nil
10
+ default_config.http_proxy_port.should be_nil
11
+ default_config.http_proxy_username.should be_nil
12
+ default_config.http_proxy_password.should be_nil
13
+ default_config.http_open_timeout.should == 2
14
+ default_config.http_read_timeout.should == 4
15
+ end
16
+
17
+ it "have correct defaults when ssl" do
18
+ default_config.ssl = true
19
+ default_config.remote_host.should == 'errorapp.com'
20
+ default_config.remote_port.should == 443
21
+ end
22
+
23
+ it "be disabled based on environment by default" do
24
+ %w(development test).each do |env|
25
+ default_config.stub(:application_environment).and_return(env)
26
+ default_config.should_send_to_api?.should == false
27
+ end
28
+ end
29
+
30
+ it "be enabled based on environment by default" do
31
+ %w(production staging).each do |env|
32
+ default_config.stub(:application_environment).and_return(env)
33
+ default_config.should_send_to_api?.should == true
34
+ end
35
+ end
36
+
37
+ it "load api_key from environment variable" do
38
+ ENV.stub(:[]).with('ERRORAPP_API_KEY').and_return('env-api-key')
39
+ default_config.api_key.should == 'env-api-key'
40
+ end
41
+
42
+ context 'production environment' do
43
+ before :each do
44
+ default_config.stub(:application_environment).and_return('production')
45
+ end
46
+
47
+ it 'loads a errorapp_notifier config' do
48
+ override_default_value :api_key
49
+ override_default_value :ssl, true
50
+ override_default_value :remote_host, 'example.com'
51
+ override_default_value :remote_port, 3000
52
+ override_default_value :http_proxy_host, 'annoying-proxy.example.com'
53
+ override_default_value :http_proxy_port, 1066
54
+ override_default_value :http_proxy_username, 'username'
55
+ override_default_value :http_proxy_password, 'password'
56
+ override_default_value :http_open_timeout, 5
57
+ override_default_value :http_read_timeout, 10
58
+ end
59
+
60
+ it 'disable' do
61
+ default_config.api_key = "new-key"
62
+ default_config.disabled_by_default = %(production)
63
+ default_config.api_key.should == 'new-key'
64
+ default_config.should_send_to_api?.should == false
65
+ end
66
+ end
67
+ end
68
+
69
+ def default_config
70
+ @config||= ErrorappNotifier::Config.new
71
+ end
72
+
73
+ def override_default_value(option, value ="value")
74
+ default_config.send(:"#{option}=", value)
75
+ expect(default_config.send(option)).to eq(value)
76
+ end