errorapp_notifier 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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