exceptional 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/exceptional +6 -6
- data/exceptional.gemspec +3 -3
- data/init.rb +17 -2
- data/install.rb +3 -3
- data/lib/exceptional/catcher.rb +9 -2
- data/lib/exceptional/config.rb +9 -12
- data/lib/exceptional/controller_exception_data.rb +60 -0
- data/lib/exceptional/exception_data.rb +13 -50
- data/lib/exceptional/integration/rails.rb +3 -2
- data/lib/exceptional/integration/tester.rb +3 -3
- data/lib/exceptional/remote.rb +2 -1
- data/lib/exceptional.rb +30 -4
- data/spec/exceptional/catcher_spec.rb +2 -2
- data/spec/exceptional/config_spec.rb +7 -7
- data/spec/exceptional/exception_data_spec.rb +22 -12
- data/spec/exceptional_rescue_spec.rb +53 -6
- data/spec/rails_integration_spec.rb +24 -7
- data/spec/spec_helper.rb +2 -1
- data/tasks/exceptional_tasks.rake +1 -1
- metadata +7 -16
- data/lib/exceptional/integration/rack.rb +0 -14
data/bin/exceptional
CHANGED
@@ -7,9 +7,9 @@ command = args.shift.strip rescue 'help'
|
|
7
7
|
case command
|
8
8
|
when 'help'
|
9
9
|
puts <<USAGE
|
10
|
-
help #
|
11
|
-
test #
|
12
|
-
install <api_key> #
|
10
|
+
help # Show this usage.
|
11
|
+
test # Send a test exception to Exceptional.
|
12
|
+
install <api_key> # Create config/exceptional.yml with your api_key. Overrites existing one.
|
13
13
|
USAGE
|
14
14
|
when 'test'
|
15
15
|
puts "Loading Rails environment."
|
@@ -22,15 +22,15 @@ USAGE
|
|
22
22
|
when 'install'
|
23
23
|
api_key = args[0]
|
24
24
|
if (api_key.nil?)
|
25
|
-
puts '
|
25
|
+
puts 'Missing required paramater <api-key>. Check your app configuration at http://getexceptional.com.'
|
26
26
|
else
|
27
27
|
unless File.file?('config/environment.rb')
|
28
|
-
puts "Run this command from the root of your rails application"
|
28
|
+
puts "Run this command from the root of your rails application."
|
29
29
|
else
|
30
30
|
config_file = File.open('config/exceptional.yml', 'w')
|
31
31
|
config_file.puts("api-key: #{api_key}\n")
|
32
32
|
config_file.close
|
33
|
-
puts "Config file written as config/exceptional.yml"
|
33
|
+
puts "Config file written as config/exceptional.yml."
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
data/exceptional.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
Gem::Specification.new do |s|
|
3
3
|
s.name = %q{exceptional}
|
4
|
-
s.version = "2.0.
|
4
|
+
s.version = "2.0.1"
|
5
5
|
s.authors = ["Contrast"]
|
6
6
|
s.summary = %q{Exceptional is the core Ruby library for communicating with http://getexceptional.com (hosted error tracking service)}
|
7
7
|
s.description = %q{Exceptional is the core Ruby library for communicating with http://getexceptional.com (hosted error tracking service). Use it to find out about errors that happen in your live app. It captures lots of helpful information to help you fix the errors.}
|
@@ -11,5 +11,5 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.require_paths = ["lib"]
|
12
12
|
s.executables << 'exceptional'
|
13
13
|
s.rubyforge_project = %q{exceptional}
|
14
|
-
s.
|
15
|
-
end
|
14
|
+
s.requirements << "json_pure, json-jruby or json gem required"
|
15
|
+
end
|
data/init.rb
CHANGED
@@ -1,12 +1,27 @@
|
|
1
1
|
require 'exceptional'
|
2
2
|
|
3
|
+
unless Object.const_defined?(:JSON)
|
4
|
+
begin
|
5
|
+
require 'json'
|
6
|
+
rescue LoadError
|
7
|
+
begin
|
8
|
+
require 'json-ruby'
|
9
|
+
rescue LoadError
|
10
|
+
require 'json_pure'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
unless Object.const_defined?(:JSON)
|
15
|
+
raise "Could not load json gem; make sure you install one of json_pure, json-ruby, or the C-based json gem."
|
16
|
+
end
|
17
|
+
|
3
18
|
# If old plugin still installed then we don't want to install this one.
|
4
19
|
# In production environments we should continue to work as before, but in development/test we should
|
5
20
|
# advise how to correct the problem and exit
|
6
|
-
if defined?(Exceptional::VERSION::STRING) && %w(development test).include?(RAILS_ENV)
|
21
|
+
if (defined?(Exceptional::VERSION::STRING) rescue nil) && %w(development test).include?(RAILS_ENV)
|
7
22
|
message = %Q(
|
8
23
|
***********************************************************************
|
9
|
-
You seem to still have an old version of the
|
24
|
+
You seem to still have an old version of the Exceptional plugin installed.
|
10
25
|
Remove it from /vendor/plugins and try again.
|
11
26
|
***********************************************************************
|
12
27
|
)
|
data/install.rb
CHANGED
@@ -10,10 +10,10 @@ if File::exists? config_file
|
|
10
10
|
puts "Exceptional config file already exists. Please ensure it is up-to-date with the current format."
|
11
11
|
puts "See #{example_config_file}"
|
12
12
|
else
|
13
|
-
puts "Installing default Exceptional config"
|
13
|
+
puts "Installing default Exceptional config."
|
14
14
|
puts " From #{example_config_file}"
|
15
|
-
puts "For exceptional to work you need to configure your API
|
15
|
+
puts "For exceptional to work you need to configure your API key."
|
16
16
|
puts " See #{example_config_file}"
|
17
|
-
puts "If you don't have an API
|
17
|
+
puts "If you don't have an API key, get one at http://getexceptional.com/."
|
18
18
|
File.copy example_config_file, config_file
|
19
19
|
end
|
data/lib/exceptional/catcher.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
module Exceptional
|
2
2
|
class Catcher
|
3
3
|
class << self
|
4
|
-
def
|
4
|
+
def handle_with_controller(exception, controller=nil, request=nil)
|
5
5
|
if Config.should_send_to_api?
|
6
|
-
data =
|
6
|
+
data = ControllerExceptionData.new(exception, controller, request)
|
7
|
+
Remote.error(data)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def handle(exception, name=nil)
|
12
|
+
if Config.should_send_to_api?
|
13
|
+
data = ExceptionData.new(exception, name)
|
7
14
|
Remote.error(data)
|
8
15
|
end
|
9
16
|
end
|
data/lib/exceptional/config.rb
CHANGED
@@ -2,11 +2,8 @@ module Exceptional
|
|
2
2
|
class Config
|
3
3
|
class << self
|
4
4
|
DEFAULTS = {
|
5
|
-
:
|
6
|
-
:remote_host_http => '
|
7
|
-
:remote_port_http => 80,
|
8
|
-
:remote_host_https => 'getexceptional.appspot.com',
|
9
|
-
:remote_port_https => 443,
|
5
|
+
:ssl => false,
|
6
|
+
:remote_host_http => 'plugin.getexceptional.com',
|
10
7
|
:http_open_timeout => 2,
|
11
8
|
:http_read_timeout => 4,
|
12
9
|
:disabled_by_default => %w(development test)
|
@@ -14,7 +11,7 @@ module Exceptional
|
|
14
11
|
|
15
12
|
attr_accessor :api_key
|
16
13
|
attr_accessor :http_proxy_host, :http_proxy_port, :http_proxy_username, :http_proxy_password
|
17
|
-
attr_writer :
|
14
|
+
attr_writer :ssl
|
18
15
|
|
19
16
|
def load(config_file=nil)
|
20
17
|
if (config_file && File.file?(config_file))
|
@@ -30,7 +27,7 @@ module Exceptional
|
|
30
27
|
@http_open_timeout = config['http-open-timeout']
|
31
28
|
@http_read_timeout = config['http-read-timeout']
|
32
29
|
|
33
|
-
@
|
30
|
+
@ssl = config['ssl'] || env_config['ssl']
|
34
31
|
@enabled = env_config['enabled']
|
35
32
|
@remote_port = config['remote-port'].to_i unless config['remote-port'].nil?
|
36
33
|
@remote_host = config['remote-host'] unless config['remote-host'].nil?
|
@@ -57,20 +54,20 @@ module Exceptional
|
|
57
54
|
defined?(RAILS_ROOT) ? RAILS_ROOT : Dir.pwd
|
58
55
|
end
|
59
56
|
|
60
|
-
def
|
61
|
-
@
|
57
|
+
def ssl?
|
58
|
+
@ssl ||= DEFAULTS[:ssl]
|
62
59
|
end
|
63
60
|
|
64
61
|
def remote_host
|
65
|
-
@remote_host ||=
|
62
|
+
@remote_host ||= DEFAULTS[:remote_host_http]
|
66
63
|
end
|
67
64
|
|
68
65
|
def remote_port
|
69
|
-
@remote_port ||=
|
66
|
+
@remote_port ||= ssl? ? 443 : 80
|
70
67
|
end
|
71
68
|
|
72
69
|
def reset
|
73
|
-
@enabled = @
|
70
|
+
@enabled = @ssl = @remote_host = @remote_port = @api_key = nil
|
74
71
|
end
|
75
72
|
|
76
73
|
def http_open_timeout
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
|
3
|
+
module Exceptional
|
4
|
+
class ControllerExceptionData < ExceptionData
|
5
|
+
def initialize(exception, controller=nil, request=nil)
|
6
|
+
super(exception)
|
7
|
+
@request = request
|
8
|
+
@controller = controller
|
9
|
+
end
|
10
|
+
|
11
|
+
def extra_stuff
|
12
|
+
return {} if @request.nil?
|
13
|
+
{
|
14
|
+
'request' => {
|
15
|
+
'url' => "#{@request.protocol}#{@request.host}#{@request.request_uri}",
|
16
|
+
'controller' => @controller.class.to_s,
|
17
|
+
'action' => @request.parameters['action'],
|
18
|
+
'parameters' => filter_paramaters(@request.parameters),
|
19
|
+
'request_method' => @request.request_method.to_s,
|
20
|
+
'remote_ip' => @request.remote_ip,
|
21
|
+
'headers' => extract_http_headers(@request.env),
|
22
|
+
'session' => Exceptional::ControllerExceptionData.sanitize_session(@request)
|
23
|
+
}
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def filter_paramaters(hash)
|
28
|
+
if @controller.respond_to?(:filter_parameters)
|
29
|
+
@controller.send(:filter_parameters, hash)
|
30
|
+
else
|
31
|
+
hash
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def extract_http_headers(env)
|
36
|
+
headers = {}
|
37
|
+
env.select{|k, v| k =~ /^HTTP_/}.each do |name, value|
|
38
|
+
proper_name = name.sub(/^HTTP_/, '').split('_').map{|upper_case| upper_case.capitalize}.join('-')
|
39
|
+
headers[proper_name] = value
|
40
|
+
end
|
41
|
+
unless headers['Cookie'].nil?
|
42
|
+
headers['Cookie'] = headers['Cookie'].sub(/_session=\S+/, '_session=[FILTERED]')
|
43
|
+
end
|
44
|
+
headers
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.sanitize_session(request)
|
48
|
+
session = request.session
|
49
|
+
session_hash = {}
|
50
|
+
session_hash['session_id'] = request.session_options ? request.session_options[:id] : nil
|
51
|
+
session_hash['session_id'] ||= session.respond_to?(:session_id) ? session.session_id : session.instance_variable_get("@session_id")
|
52
|
+
session_hash['data'] = session.respond_to?(:to_hash) ? session.to_hash : session.instance_variable_get("@data") || {}
|
53
|
+
session_hash['session_id'] ||= session_hash['data'][:session_id]
|
54
|
+
session_hash['data'].delete(:session_id)
|
55
|
+
ControllerExceptionData.sanitize_hash(session_hash)
|
56
|
+
rescue
|
57
|
+
{}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -2,10 +2,9 @@ require 'digest/md5'
|
|
2
2
|
|
3
3
|
module Exceptional
|
4
4
|
class ExceptionData
|
5
|
-
def initialize(exception,
|
5
|
+
def initialize(exception, name=nil)
|
6
6
|
@exception = exception
|
7
|
-
@
|
8
|
-
@controller = controller
|
7
|
+
@name = name
|
9
8
|
end
|
10
9
|
|
11
10
|
def to_hash
|
@@ -18,23 +17,20 @@ module Exceptional
|
|
18
17
|
'occurred_at' => Time.now.strftime("%Y%m%d %H:%M:%S %Z")
|
19
18
|
}
|
20
19
|
})
|
21
|
-
|
22
|
-
|
23
|
-
'request' => {
|
24
|
-
'url' => "#{@request.protocol}#{@request.host}#{@request.request_uri}",
|
25
|
-
'controller' => @controller.class.to_s,
|
26
|
-
'action' => @request.parameters['action'],
|
27
|
-
'parameters' => filter_paramaters(@request.parameters),
|
28
|
-
'request_method' => @request.request_method.to_s,
|
29
|
-
'remote_ip' => @request.remote_ip,
|
30
|
-
'headers' => extract_http_headers(@request.env),
|
31
|
-
'session' => Exceptional::ExceptionData.sanitize_session(@request)
|
32
|
-
}
|
33
|
-
})
|
34
|
-
end
|
20
|
+
hash.merge!(extra_stuff)
|
21
|
+
hash.merge!(context_stuff)
|
35
22
|
hash
|
36
23
|
end
|
37
24
|
|
25
|
+
def extra_stuff
|
26
|
+
{ 'rescue_block' => { 'name' => @name} }
|
27
|
+
end
|
28
|
+
|
29
|
+
def context_stuff
|
30
|
+
context = Thread.current[:exceptional_context]
|
31
|
+
context.blank? ? {} : {'context' => context}
|
32
|
+
end
|
33
|
+
|
38
34
|
def to_json
|
39
35
|
to_hash.to_json
|
40
36
|
end
|
@@ -44,26 +40,6 @@ module Exceptional
|
|
44
40
|
Digest::MD5.hexdigest(@exception.backtrace.join)
|
45
41
|
end
|
46
42
|
|
47
|
-
def filter_paramaters(hash)
|
48
|
-
if @controller.respond_to?(:filter_parameters)
|
49
|
-
@controller.send(:filter_parameters, hash)
|
50
|
-
else
|
51
|
-
hash
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def extract_http_headers(env)
|
56
|
-
headers = {}
|
57
|
-
env.select{|k, v| k =~ /^HTTP_/}.each do |name, value|
|
58
|
-
proper_name = name.sub(/^HTTP_/, '').split('_').map{|upper_case| upper_case.capitalize}.join('-')
|
59
|
-
headers[proper_name] = value
|
60
|
-
end
|
61
|
-
unless headers['Cookie'].nil?
|
62
|
-
headers['Cookie'] = headers['Cookie'].sub(/_session=\S+/, '_session=[FILTERED]')
|
63
|
-
end
|
64
|
-
headers
|
65
|
-
end
|
66
|
-
|
67
43
|
def self.sanitize_hash(hash)
|
68
44
|
case hash
|
69
45
|
when Hash
|
@@ -78,18 +54,5 @@ module Exceptional
|
|
78
54
|
rescue
|
79
55
|
{}
|
80
56
|
end
|
81
|
-
|
82
|
-
def self.sanitize_session(request)
|
83
|
-
session = request.session
|
84
|
-
session_hash = {}
|
85
|
-
session_hash['session_id'] = request.session_options ? request.session_options[:id] : nil
|
86
|
-
session_hash['session_id'] ||= session.respond_to?(:session_id) ? session.session_id : session.instance_variable_get("@session_id")
|
87
|
-
session_hash['data'] = session.respond_to?(:to_hash) ? session.to_hash : session.instance_variable_get("@data") || {}
|
88
|
-
session_hash['session_id'] ||= session_hash['data'][:session_id]
|
89
|
-
session_hash['data'].delete(:session_id)
|
90
|
-
ExceptionData.sanitize_hash(session_hash)
|
91
|
-
rescue
|
92
|
-
{}
|
93
|
-
end
|
94
57
|
end
|
95
58
|
end
|
@@ -8,7 +8,8 @@ if defined? ActionController
|
|
8
8
|
class Base
|
9
9
|
def rescue_action_with_exceptional(exception)
|
10
10
|
unless exception_handled_by_rescue_from?(exception)
|
11
|
-
Exceptional::Catcher.
|
11
|
+
Exceptional::Catcher.handle_with_controller(exception, self, request)
|
12
|
+
Exceptional.context.clear!
|
12
13
|
end
|
13
14
|
rescue_action_without_exceptional exception
|
14
15
|
end
|
@@ -23,4 +24,4 @@ if defined? ActionController
|
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
26
|
-
end
|
27
|
+
end
|
@@ -4,11 +4,11 @@ module Exceptional
|
|
4
4
|
end
|
5
5
|
|
6
6
|
def self.test
|
7
|
-
data = Exceptional::ExceptionData.new(ExceptionalTestException.new)
|
7
|
+
data = Exceptional::ExceptionData.new(ExceptionalTestException.new, 'Test exception')
|
8
8
|
unless Exceptional::Remote.error(data)
|
9
|
-
puts "Problem sending
|
9
|
+
puts "Problem sending exception to Exceptional. Check your API key."
|
10
10
|
else
|
11
|
-
puts "Exception sent successfully"
|
11
|
+
puts "Exception sent successfully."
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
data/lib/exceptional/remote.rb
CHANGED
@@ -30,7 +30,8 @@ module Exceptional
|
|
30
30
|
client = optional_proxy.new(config.remote_host, config.remote_port)
|
31
31
|
client.open_timeout = config.http_open_timeout
|
32
32
|
client.read_timeout = config.http_read_timeout
|
33
|
-
client.use_ssl = config.
|
33
|
+
client.use_ssl = config.ssl?
|
34
|
+
client.verify_mode = OpenSSL::SSL::VERIFY_NONE if config.ssl?
|
34
35
|
begin
|
35
36
|
response = client.post(url, data)
|
36
37
|
case response
|
data/lib/exceptional.rb
CHANGED
@@ -6,12 +6,12 @@ require 'exceptional/log_factory'
|
|
6
6
|
require 'exceptional/config'
|
7
7
|
require 'exceptional/application_environment'
|
8
8
|
require 'exceptional/exception_data'
|
9
|
+
require 'exceptional/controller_exception_data'
|
9
10
|
require 'exceptional/remote'
|
10
|
-
require 'exceptional/integration/rack'
|
11
11
|
|
12
12
|
module Exceptional
|
13
13
|
PROTOCOL_VERSION = 5
|
14
|
-
VERSION = '0.2.
|
14
|
+
VERSION = '0.2.1'
|
15
15
|
CLIENT_NAME = 'getexceptional-rails-plugin'
|
16
16
|
|
17
17
|
def self.logger
|
@@ -22,12 +22,38 @@ module Exceptional
|
|
22
22
|
Exceptional::Config.api_key = api_key
|
23
23
|
end
|
24
24
|
|
25
|
-
def self.
|
25
|
+
def self.handle(exception, name=nil)
|
26
|
+
Exceptional::Catcher.handle(exception, name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.rescue(name=nil, context=nil, &block)
|
26
30
|
begin
|
31
|
+
self.context(context) unless context.nil?
|
27
32
|
block.call
|
28
33
|
rescue Exception => e
|
29
|
-
Exceptional::Catcher.handle(e)
|
34
|
+
Exceptional::Catcher.handle(e,name)
|
35
|
+
self.clear!
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.rescue_and_reraise(name=nil, context=nil, &block)
|
40
|
+
begin
|
41
|
+
self.context(context) unless context.nil?
|
42
|
+
block.call
|
43
|
+
rescue Exception => e
|
44
|
+
Exceptional::Catcher.handle(e,name)
|
45
|
+
self.clear!
|
30
46
|
raise(e)
|
31
47
|
end
|
32
48
|
end
|
49
|
+
|
50
|
+
def self.clear!
|
51
|
+
Thread.current[:exceptional_context] = nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.context(hash = {})
|
55
|
+
Thread.current[:exceptional_context] ||= {}
|
56
|
+
Thread.current[:exceptional_context].merge!(hash)
|
57
|
+
self
|
58
|
+
end
|
33
59
|
end
|
@@ -6,8 +6,8 @@ describe Exceptional::Catcher do
|
|
6
6
|
exception = mock('exception')
|
7
7
|
controller = mock('controller')
|
8
8
|
request = mock('request')
|
9
|
-
Exceptional::
|
9
|
+
Exceptional::ControllerExceptionData.should_receive(:new).with(exception,controller,request).and_return(data = mock('exception_data'))
|
10
10
|
Exceptional::Remote.should_receive(:error).with(data)
|
11
|
-
Exceptional::Catcher.
|
11
|
+
Exceptional::Catcher.handle_with_controller(exception,controller,request)
|
12
12
|
end
|
13
13
|
end
|
@@ -5,8 +5,8 @@ describe Exceptional::Config, 'defaults' do
|
|
5
5
|
Exceptional::Config.reset
|
6
6
|
end
|
7
7
|
it "have sensible defaults" do
|
8
|
-
Exceptional::Config.
|
9
|
-
Exceptional::Config.remote_host.should == '
|
8
|
+
Exceptional::Config.ssl?.should == false
|
9
|
+
Exceptional::Config.remote_host.should == 'plugin.getexceptional.com'
|
10
10
|
Exceptional::Config.remote_port.should == 80
|
11
11
|
Exceptional::Config.application_root.should == Dir.pwd
|
12
12
|
Exceptional::Config.http_proxy_host.should be_nil
|
@@ -16,9 +16,9 @@ describe Exceptional::Config, 'defaults' do
|
|
16
16
|
Exceptional::Config.http_open_timeout.should == 2
|
17
17
|
Exceptional::Config.http_read_timeout.should == 4
|
18
18
|
end
|
19
|
-
it "have correct defaults when
|
20
|
-
Exceptional::Config.
|
21
|
-
Exceptional::Config.remote_host.should == 'getexceptional.
|
19
|
+
it "have correct defaults when ssl" do
|
20
|
+
Exceptional::Config.ssl = true
|
21
|
+
Exceptional::Config.remote_host.should == 'plugin.getexceptional.com'
|
22
22
|
Exceptional::Config.remote_port.should == 443
|
23
23
|
end
|
24
24
|
it "be enabled based on environment by default" do
|
@@ -38,7 +38,7 @@ describe Exceptional::Config, 'defaults' do
|
|
38
38
|
it "allow a new simpler format for exception.yml" do
|
39
39
|
Exceptional::Config.load('spec/fixtures/exceptional.yml')
|
40
40
|
Exceptional::Config.api_key.should == 'abc123'
|
41
|
-
Exceptional::Config.
|
41
|
+
Exceptional::Config.ssl?.should == true
|
42
42
|
Exceptional::Config.remote_host.should == 'example.com'
|
43
43
|
Exceptional::Config.remote_port.should == 123
|
44
44
|
Exceptional::Config.should_send_to_api?.should == true
|
@@ -52,7 +52,7 @@ describe Exceptional::Config, 'defaults' do
|
|
52
52
|
it "allow olded format for exception.yml" do
|
53
53
|
Exceptional::Config.load('spec/fixtures/exceptional_old.yml')
|
54
54
|
Exceptional::Config.api_key.should == 'abc123'
|
55
|
-
Exceptional::Config.
|
55
|
+
Exceptional::Config.ssl?.should == true
|
56
56
|
Exceptional::Config.should_send_to_api?.should == true
|
57
57
|
end
|
58
58
|
it "load api_key from environment variable" do
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper'
|
2
2
|
require 'digest/md5'
|
3
|
+
require 'json'
|
3
4
|
|
4
5
|
class Exceptional::FunkyError < StandardError
|
5
6
|
def backtrace
|
@@ -7,7 +8,7 @@ class Exceptional::FunkyError < StandardError
|
|
7
8
|
end
|
8
9
|
end
|
9
10
|
|
10
|
-
describe Exceptional::
|
11
|
+
describe Exceptional::ControllerExceptionData, 'when no request/controller/params' do
|
11
12
|
before :each do
|
12
13
|
ENV['LOGNAME'] = 'bob'
|
13
14
|
ENV['SOMEVAR'] = 'something'
|
@@ -15,8 +16,8 @@ describe Exceptional::ExceptionData, 'when no request/controller/params' do
|
|
15
16
|
RAILS_ENV = 'test' unless defined?(RAILS_ENV)
|
16
17
|
Time.stub!(:now).and_return(Time.mktime(1970,1,1))
|
17
18
|
error = Exceptional::FunkyError.new('some message')
|
18
|
-
data = Exceptional::
|
19
|
-
@hash = data.to_hash
|
19
|
+
@data = Exceptional::ControllerExceptionData.new(error)
|
20
|
+
@hash = @data.to_hash
|
20
21
|
end
|
21
22
|
|
22
23
|
it "capture exception details" do
|
@@ -31,6 +32,10 @@ describe Exceptional::ExceptionData, 'when no request/controller/params' do
|
|
31
32
|
client_hash['protocol_version'].should == Exceptional::PROTOCOL_VERSION
|
32
33
|
end
|
33
34
|
|
35
|
+
it "generates json" do
|
36
|
+
JSON.parse(@data.to_json)
|
37
|
+
end
|
38
|
+
|
34
39
|
it "capture application_environment" do
|
35
40
|
application_env_hash = @hash['application_environment']
|
36
41
|
application_env_hash['environment'].should == 'test'
|
@@ -46,7 +51,7 @@ describe Exceptional::ExceptionData, 'when no request/controller/params' do
|
|
46
51
|
end
|
47
52
|
end
|
48
53
|
|
49
|
-
describe Exceptional::
|
54
|
+
describe Exceptional::ControllerExceptionData, 'with request/controller/params' do
|
50
55
|
class Exceptional::SomeController < ActionController::Base
|
51
56
|
filter_parameter_logging :filter_me
|
52
57
|
end
|
@@ -60,7 +65,7 @@ describe Exceptional::ExceptionData, 'with request/controller/params' do
|
|
60
65
|
@request.stub!(:remote_ip).and_return('1.2.3.4')
|
61
66
|
@request.stub!(:env).and_return({'SOME_VAR' => 'abc', 'HTTP_CONTENT_TYPE' => 'text/html'})
|
62
67
|
error = Exceptional::FunkyError.new('some message')
|
63
|
-
data = Exceptional::
|
68
|
+
data = Exceptional::ControllerExceptionData.new(error, @controller, @request)
|
64
69
|
@hash = data.to_hash
|
65
70
|
end
|
66
71
|
|
@@ -83,7 +88,12 @@ describe Exceptional::ExceptionData, 'with request/controller/params' do
|
|
83
88
|
end
|
84
89
|
crazy = Crazy.new
|
85
90
|
input = {'crazy' => crazy, :simple => '123', :some_hash => {'1' => '2'}, :array => ['1','2']}
|
86
|
-
Exceptional::
|
91
|
+
Exceptional::ControllerExceptionData.sanitize_hash(input).should == {'crazy' => crazy.to_s, :simple => '123', :some_hash => {'1' => '2'}, :array => ['1','2']}
|
92
|
+
end
|
93
|
+
|
94
|
+
it "to_strings regex because JSON.parse(/aa/.to_json) doesn't work" do
|
95
|
+
input = {'crazy' => /abc.*/}
|
96
|
+
Exceptional::ExceptionData.sanitize_hash(input).should == {'crazy' => /abc.*/.to_s}
|
87
97
|
end
|
88
98
|
|
89
99
|
it "handles session objects with various interfaces" do
|
@@ -97,27 +107,27 @@ describe Exceptional::ExceptionData, 'with request/controller/params' do
|
|
97
107
|
session = SessionWithInstanceVariables.new
|
98
108
|
request.stub!(:session).and_return(session)
|
99
109
|
request.stub!(:session_options).and_return({})
|
100
|
-
Exceptional::
|
110
|
+
Exceptional::ControllerExceptionData.sanitize_session(request).should == {'session_id' => '123', 'data' => {'a' => '1'}}
|
101
111
|
session = mock('session', :session_id => '123', :instance_variable_get => {'a' => '1'})
|
102
112
|
request.stub!(:session).and_return(session)
|
103
|
-
Exceptional::
|
113
|
+
Exceptional::ControllerExceptionData.sanitize_session(request).should == {'session_id' => '123', 'data' => {'a' => '1'}}
|
104
114
|
session = mock('session', :session_id => nil, :to_hash => {:session_id => '123', 'a' => '1'})
|
105
115
|
request.stub!(:session).and_return(session)
|
106
|
-
Exceptional::
|
116
|
+
Exceptional::ControllerExceptionData.sanitize_session(request).should == {'session_id' => '123', 'data' => {'a' => '1'}}
|
107
117
|
request.stub!(:session_options).and_return({:id => 'xyz'})
|
108
|
-
Exceptional::
|
118
|
+
Exceptional::ControllerExceptionData.sanitize_session(request).should == {'session_id' => 'xyz', 'data' => {'a' => '1'}}
|
109
119
|
end
|
110
120
|
|
111
121
|
it "filter session cookies from headers" do
|
112
122
|
@request.stub!(:env).and_return({'SOME_VAR' => 'abc', 'HTTP_COOKIE' => '_something_else=faafsafafasfa; _myapp-lick-nation_session=BAh7DDoMbnVtYmVyc1sJaQZpB2kIaQk6FnNvbWVfY3Jhenlfb2JqZWN0bzobU3Bpa2VDb250cm9sbGVyOjpDcmF6eQY6CUBiYXJABzoTc29tZXRoaW5nX2Vsc2UiCGNjYzoKYXBwbGUiDUJyYWVidXJuOgloYXNoewdpBmkHaQhpCToPc2Vzc2lvbl9pZCIlMmJjZTM4MjVjMThkNzYxOWEyZDA4NTJhNWY1NGQzMmU6C3RvbWF0byIJQmVlZg%3D%3D--66fb4606851f06bf409b8bc4ba7aea47a0259bf7'})
|
113
|
-
@hash = Exceptional::
|
123
|
+
@hash = Exceptional::ControllerExceptionData.new(Exceptional::FunkyError.new('some message'), @controller, @request).to_hash
|
114
124
|
@hash['request']['headers'].should == {'Cookie' => '_something_else=faafsafafasfa; _myapp-lick-nation_session=[FILTERED]'}
|
115
125
|
end
|
116
126
|
|
117
127
|
it "creates a uniqueness_hash from backtrace" do
|
118
128
|
myException = Exception.new
|
119
129
|
myException.stub!(:backtrace).and_return(['123'])
|
120
|
-
data = Exceptional::
|
130
|
+
data = Exceptional::ControllerExceptionData.new(myException)
|
121
131
|
data.uniqueness_hash.should == Digest::MD5.hexdigest('123')
|
122
132
|
end
|
123
133
|
end
|
@@ -1,17 +1,64 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/spec_helper'
|
2
2
|
|
3
3
|
context 'resuce errors from within a block' do
|
4
|
-
class FunkyException < StandardError;
|
5
|
-
|
4
|
+
class FunkyException < StandardError;
|
5
|
+
end
|
6
|
+
it "Exceptional.rescue should send to exceptional and swallow error " do
|
7
|
+
to_raise = FunkyException.new
|
8
|
+
Exceptional::Catcher.should_receive(:handle).with(to_raise, 'my rescue name')
|
9
|
+
Exceptional.rescue('my rescue name') do
|
10
|
+
raise to_raise
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it "reraises error with rescue_and_reraise" do
|
6
15
|
to_raise = FunkyException.new
|
7
|
-
Exceptional::Catcher.should_receive(:handle).with(to_raise)
|
16
|
+
Exceptional::Catcher.should_receive(:handle).with(to_raise, 'my rescue name')
|
8
17
|
begin
|
9
|
-
Exceptional.rescue do
|
18
|
+
Exceptional.rescue_and_reraise('my rescue name') do
|
10
19
|
raise to_raise
|
11
20
|
end
|
12
|
-
fail
|
21
|
+
fail 'expected a reraise'
|
13
22
|
rescue FunkyException => e
|
14
|
-
e.should == to_raise
|
15
23
|
end
|
16
24
|
end
|
25
|
+
|
26
|
+
it "Exceptional.handle calls Exceptional::Catcher.handle" do
|
27
|
+
to_raise = FunkyException.new
|
28
|
+
Exceptional::Catcher.should_receive(:handle).with(to_raise, 'optional name for where it has occurred')
|
29
|
+
begin
|
30
|
+
raise to_raise
|
31
|
+
rescue => e
|
32
|
+
Exceptional.handle(e, 'optional name for where it has occurred')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it "collects context information but leaves thread local empty after block" do
|
37
|
+
to_raise = FunkyException.new
|
38
|
+
Exceptional::Config.should_receive(:should_send_to_api?).and_return(true)
|
39
|
+
Exceptional::Remote.should_receive(:error) {|exception_data|
|
40
|
+
exception_data.to_hash['context']['foo'] == 'bar'
|
41
|
+
exception_data.to_hash['context']['baz'] == 42
|
42
|
+
exception_data.to_hash['context']['cats'] == {'lol' => 'bot'}
|
43
|
+
}
|
44
|
+
Exceptional.rescue('my rescue name') do
|
45
|
+
Exceptional.context('foo' => 'bar')
|
46
|
+
Exceptional.context('baz' => 42)
|
47
|
+
Exceptional.context('cats' => {'lol' => 'bot'})
|
48
|
+
raise to_raise
|
49
|
+
end
|
50
|
+
Thread.current[:exceptional_context].should == nil
|
51
|
+
end
|
52
|
+
|
53
|
+
it "clearr context with Exceptional.context.clear!" do
|
54
|
+
Exceptional.context('foo' => 'bar')
|
55
|
+
Thread.current[:exceptional_context].should_not == nil
|
56
|
+
Exceptional.context.clear!
|
57
|
+
Thread.current[:exceptional_context].should == nil
|
58
|
+
end
|
59
|
+
|
60
|
+
it "allows optional second paramater hash to get added to context" do
|
61
|
+
Exceptional.should_receive(:context).with(context_hash = {'foo' => 'bar', 'baz' => 42})
|
62
|
+
Exceptional.rescue('my resccue', context_hash) {}
|
63
|
+
end
|
17
64
|
end
|
@@ -3,13 +3,13 @@ require File.join(File.dirname(__FILE__), '..', 'lib', 'exceptional', 'integrati
|
|
3
3
|
|
4
4
|
describe Exceptional, 'version number' do
|
5
5
|
it "be available proramatically" do
|
6
|
-
Exceptional::VERSION.should == '0.2.
|
6
|
+
Exceptional::VERSION.should == '0.2.1'
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
10
|
describe ActiveSupport::JSON, 'standards compliant json' do
|
11
11
|
it "quote keys" do
|
12
|
-
{:a => '123'}.to_json.gsub(/ /,'').should == '{"a":"123"}'
|
12
|
+
{:a => '123'}.to_json.gsub(/ /, '').should == '{"a":"123"}'
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -25,27 +25,36 @@ describe TestingController do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'handle exception with Exceptional::Catcher' do
|
28
|
-
Exceptional::Catcher.should_receive(:
|
28
|
+
Exceptional::Catcher.should_receive(:handle_with_controller).with(an_instance_of(StandardError), @controller, an_instance_of(ActionController::TestRequest))
|
29
29
|
send_request(:raises_something)
|
30
30
|
end
|
31
31
|
|
32
32
|
it "still return an error response to the user" do
|
33
|
-
Exceptional::Catcher.stub!(:
|
33
|
+
Exceptional::Catcher.stub!(:handle_with_controller)
|
34
34
|
send_request(:raises_something)
|
35
35
|
@response.code.should == '500'
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
39
|
if ActionController::Base.respond_to?(:rescue_from)
|
40
|
-
class CustomError < StandardError;
|
40
|
+
class CustomError < StandardError;
|
41
|
+
end
|
41
42
|
class TestingWithRescueFromController < ActionController::Base
|
42
43
|
rescue_from CustomError, :with => :custom_handler
|
44
|
+
|
43
45
|
def raises_custom_error
|
44
46
|
raise CustomError.new
|
45
47
|
end
|
48
|
+
|
46
49
|
def raises_other_error
|
47
50
|
raise StandardError.new
|
48
51
|
end
|
52
|
+
|
53
|
+
def raises_with_context
|
54
|
+
Exceptional.context('foo' => 'bar')
|
55
|
+
raise StandardError.new
|
56
|
+
end
|
57
|
+
|
49
58
|
def custom_handler
|
50
59
|
head :ok
|
51
60
|
end
|
@@ -57,12 +66,20 @@ if ActionController::Base.respond_to?(:rescue_from)
|
|
57
66
|
end
|
58
67
|
|
59
68
|
it 'not handle exception with Exceptional that is dealt with by rescue_from' do
|
60
|
-
Exceptional::Catcher.should_not_receive(:
|
69
|
+
Exceptional::Catcher.should_not_receive(:handle_with_controller)
|
61
70
|
send_request(:raises_custom_error)
|
62
71
|
end
|
63
72
|
it 'handle exception with Exceptional that is not dealt with by rescue_from' do
|
64
|
-
Exceptional::Catcher.should_receive(:
|
73
|
+
Exceptional::Catcher.should_receive(:handle_with_controller)
|
65
74
|
send_request(:raises_other_error)
|
66
75
|
end
|
76
|
+
it "has context and clears context after request" do
|
77
|
+
Exceptional::Config.should_receive(:should_send_to_api?).and_return(true)
|
78
|
+
Exceptional::Remote.should_receive(:error) {|exception_data|
|
79
|
+
exception_data.to_hash['context']['foo'] == 'bar'
|
80
|
+
}
|
81
|
+
send_request(:raises_with_context)
|
82
|
+
Thread.current[:exceptional_context].should == nil
|
83
|
+
end
|
67
84
|
end
|
68
85
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,6 +4,7 @@ begin
|
|
4
4
|
rescue LoadError
|
5
5
|
raise "cant load ginger"
|
6
6
|
end
|
7
|
+
|
7
8
|
gem 'rails'
|
8
9
|
require File.dirname(__FILE__) + '/../lib/exceptional' unless defined?(Exceptional)
|
9
10
|
|
@@ -17,4 +18,4 @@ def send_request(action = nil)
|
|
17
18
|
@request.action = action ? action.to_s : ""
|
18
19
|
@response = ActionController::TestResponse.new
|
19
20
|
@controller.process(@request, @response)
|
20
|
-
end
|
21
|
+
end
|
@@ -2,7 +2,7 @@ namespace :exceptional do
|
|
2
2
|
desc 'Send a test exception to Exceptional.'
|
3
3
|
task :test => :environment do
|
4
4
|
unless Exceptional::Config.api_key.blank?
|
5
|
-
puts "Sending test exception to Exceptional"
|
5
|
+
puts "Sending test exception to Exceptional."
|
6
6
|
require "exceptional/integration/tester"
|
7
7
|
Exceptional::Integration.test
|
8
8
|
puts "Done."
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: exceptional
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Contrast
|
@@ -9,19 +9,10 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-18 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
16
|
-
name: json
|
17
|
-
type: :runtime
|
18
|
-
version_requirement:
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 1.0.0
|
24
|
-
version:
|
14
|
+
dependencies: []
|
15
|
+
|
25
16
|
description: Exceptional is the core Ruby library for communicating with http://getexceptional.com (hosted error tracking service). Use it to find out about errors that happen in your live app. It captures lots of helpful information to help you fix the errors.
|
26
17
|
email: hello@contrast.ie
|
27
18
|
executables:
|
@@ -34,8 +25,8 @@ files:
|
|
34
25
|
- lib/exceptional/application_environment.rb
|
35
26
|
- lib/exceptional/catcher.rb
|
36
27
|
- lib/exceptional/config.rb
|
28
|
+
- lib/exceptional/controller_exception_data.rb
|
37
29
|
- lib/exceptional/exception_data.rb
|
38
|
-
- lib/exceptional/integration/rack.rb
|
39
30
|
- lib/exceptional/integration/rails.rb
|
40
31
|
- lib/exceptional/integration/tester.rb
|
41
32
|
- lib/exceptional/log_factory.rb
|
@@ -81,8 +72,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
72
|
- !ruby/object:Gem::Version
|
82
73
|
version: "0"
|
83
74
|
version:
|
84
|
-
requirements:
|
85
|
-
|
75
|
+
requirements:
|
76
|
+
- json_pure, json-jruby or json gem required
|
86
77
|
rubyforge_project: exceptional
|
87
78
|
rubygems_version: 1.3.5
|
88
79
|
signing_key:
|