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.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +25 -0
- data/README.md +60 -0
- data/Rakefile +8 -0
- data/errorapp_notifier.gemspec +29 -0
- data/lib/errorapp_notifier/action_controller_methods.rb +35 -0
- data/lib/errorapp_notifier/application_environment_data.rb +77 -0
- data/lib/errorapp_notifier/config.rb +67 -0
- data/lib/errorapp_notifier/controller_failure_data.rb +96 -0
- data/lib/errorapp_notifier/exception_data.rb +19 -0
- data/lib/errorapp_notifier/failure_data.rb +87 -0
- data/lib/errorapp_notifier/monkeypatches.rb +11 -0
- data/lib/errorapp_notifier/notifier.rb +80 -0
- data/lib/errorapp_notifier/notifiers/rack_rails.rb +32 -0
- data/lib/errorapp_notifier/notifiers/rails.rb +34 -0
- data/lib/errorapp_notifier/notifiers/tester.rb +16 -0
- data/lib/errorapp_notifier/notify.rb +59 -0
- data/lib/errorapp_notifier/rack_failure_data.rb +29 -0
- data/lib/errorapp_notifier/railtie.rb +48 -0
- data/lib/errorapp_notifier/sanitizer.rb +56 -0
- data/lib/errorapp_notifier/tasks/errorapp_notifier.rake +6 -0
- data/lib/errorapp_notifier/version.rb +3 -0
- data/lib/errorapp_notifier.rb +74 -0
- data/lib/generators/errorapp_notifier/errorapp_notifier_generator.rb +54 -0
- data/lib/generators/errorapp_notifier/templates/errorapp_notifier.rb +2 -0
- data/spec/errorapp_notifier/config_spec.rb +76 -0
- data/spec/errorapp_notifier/controller_failure_data_spec.rb +277 -0
- data/spec/errorapp_notifier/failure_data_spec.rb +32 -0
- data/spec/errorapp_notifier/notifier_spec.rb +31 -0
- data/spec/errorapp_notifier/notify_spec.rb +128 -0
- data/spec/errorapp_notifier/rack_failure_data_spec.rb +87 -0
- data/spec/errorapp_notifier/sanitizer_spec.rb +57 -0
- data/spec/helper.rb +12 -0
- data/spec/spec_helper.rb +16 -0
- 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,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,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
|