ratchetio 0.5.5 → 0.6.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.
- data/.travis.yml +2 -1
- data/CHANGELOG.md +9 -0
- data/THANKS +2 -0
- data/lib/ratchetio/exception_reporter.rb +24 -0
- data/lib/ratchetio/middleware/rack/builder.rb +21 -0
- data/lib/ratchetio/middleware/rack/test_session.rb +31 -0
- data/lib/ratchetio/middleware/rails/show_exceptions.rb +26 -0
- data/lib/ratchetio/rack.rb +9 -0
- data/lib/ratchetio/rails/controller_methods.rb +7 -85
- data/lib/ratchetio/railtie.rb +8 -9
- data/lib/ratchetio/request_data_extractor.rb +115 -0
- data/lib/ratchetio/sidekiq.rb +25 -0
- data/lib/ratchetio/version.rb +1 -1
- data/lib/ratchetio.rb +13 -10
- data/ratchetio.gemspec +2 -2
- data/spec/controllers/home_controller_spec.rb +23 -29
- data/spec/dummyapp/app/controllers/home_controller.rb +4 -0
- data/spec/dummyapp/app/controllers/users_controller.rb +5 -0
- data/spec/dummyapp/app/models/user.rb +0 -1
- data/spec/dummyapp/config/routes.rb +4 -1
- data/spec/ratchetio_spec.rb +5 -5
- data/spec/requests/home_spec.rb +48 -0
- data/spec/spec_helper.rb +3 -3
- metadata +16 -8
- data/lib/ratchetio/rails/middleware/exception_catcher.rb +0 -37
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
**0.6.0**
|
|
4
|
+
- BREAKING CHANGE: Ratchetio.report_exception now returns 'ignored', 'disabled', or 'error' instead of nil when the exception is not reported for one of those reasons. It still returns the payload upon success.
|
|
5
|
+
- Request data is now parsed from the rack environment instead of from within the controller, addressing issue #10.
|
|
6
|
+
- Add Sidekiq middleware for catching workers' exceptions
|
|
7
|
+
- Replaced activesupport dependency with multi_json
|
|
8
|
+
|
|
9
|
+
**0.5.5**
|
|
10
|
+
- Added activesupport dependency for use without Rails
|
|
11
|
+
|
|
3
12
|
**0.5.4**
|
|
4
13
|
- Added new default scrub params
|
|
5
14
|
|
data/THANKS
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Ratchetio
|
|
2
|
+
module ExceptionReporter
|
|
3
|
+
include RequestDataExtractor
|
|
4
|
+
|
|
5
|
+
def report_exception_to_ratchetio(env, exception)
|
|
6
|
+
ratchetio_debug "[Ratchet.io] Reporting exception: #{exception.try(:message)}", :error
|
|
7
|
+
request_data = extract_request_data_from_rack(env)
|
|
8
|
+
person_data = extract_person_data_from_controller(env)
|
|
9
|
+
exception_data = Ratchetio.report_exception(exception, request_data, person_data)
|
|
10
|
+
env['ratchetio.exception_uuid'] = exception_data[:uuid]
|
|
11
|
+
ratchetio_debug "[Ratchet.io] Exception uuid saved in env: #{exception_data[:uuid]}"
|
|
12
|
+
rescue => e
|
|
13
|
+
ratchetio_debug "[Ratchet.io] Exception while reporting exception to Ratchet.io: #{e.try(:message)}"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def ratchetio_debug(message, level = :debug)
|
|
17
|
+
if defined?(Rails)
|
|
18
|
+
::Rails.logger.send(level, message)
|
|
19
|
+
else
|
|
20
|
+
puts message
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Ratchetio
|
|
2
|
+
module Middleware
|
|
3
|
+
module Rack
|
|
4
|
+
module Builder
|
|
5
|
+
include ExceptionReporter
|
|
6
|
+
|
|
7
|
+
def call_with_ratchetio(env)
|
|
8
|
+
call_without_ratchetio(env)
|
|
9
|
+
rescue => exception
|
|
10
|
+
report_exception_to_ratchetio(env, exception)
|
|
11
|
+
raise exception
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.included(base)
|
|
15
|
+
base.send(:alias_method, :call_without_ratchetio, :call)
|
|
16
|
+
base.send(:alias_method, :call, :call_with_ratchetio)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Ratchetio
|
|
2
|
+
module Middleware
|
|
3
|
+
module Rack
|
|
4
|
+
module TestSession
|
|
5
|
+
include ExceptionReporter
|
|
6
|
+
|
|
7
|
+
def process_request_with_ratchetio(uri, env, &block)
|
|
8
|
+
process_request_without_ratchetio(uri, env, &block)
|
|
9
|
+
rescue => exception
|
|
10
|
+
report_exception_to_ratchetio(env, exception)
|
|
11
|
+
raise exception
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def env_for_with_ratchetio(path, env)
|
|
15
|
+
env_for_without_ratchetio(path, env)
|
|
16
|
+
rescue => exception
|
|
17
|
+
report_exception_to_ratchetio(env, exception)
|
|
18
|
+
raise exception
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.included(base)
|
|
22
|
+
base.send(:alias_method, :process_request_without_ratchetio, :process_request)
|
|
23
|
+
base.send(:alias_method, :process_request, :process_request_with_ratchetio)
|
|
24
|
+
|
|
25
|
+
base.send(:alias_method, :env_for_without_ratchetio, :env_for)
|
|
26
|
+
base.send(:alias_method, :env_for, :env_for_with_ratchetio)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Ratchetio
|
|
2
|
+
module Middleware
|
|
3
|
+
module Rails
|
|
4
|
+
module ShowExceptions
|
|
5
|
+
include ExceptionReporter
|
|
6
|
+
|
|
7
|
+
def render_exception_with_ratchetio(env, exception)
|
|
8
|
+
report_exception_to_ratchetio(env, exception)
|
|
9
|
+
render_exception_without_ratchetio(env, exception)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def call_with_ratchetio(env)
|
|
13
|
+
call_without_ratchetio(env)
|
|
14
|
+
rescue => exception
|
|
15
|
+
report_exception_to_ratchetio(env, exception)
|
|
16
|
+
raise exception
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.included(base)
|
|
20
|
+
base.send(:alias_method_chain, :render_exception, :ratchetio)
|
|
21
|
+
base.send(:alias_method_chain, :call, :ratchetio)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
if defined?(Rack::Builder)
|
|
2
|
+
require 'ratchetio/middleware/rack/builder'
|
|
3
|
+
Rack::Builder.send(:include, Ratchetio::Middleware::Rack::Builder)
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
if defined?(Rack::Test::Session)
|
|
7
|
+
require 'ratchetio/middleware/rack/test_session'
|
|
8
|
+
Rack::Test::Session.send(:include, Ratchetio::Middleware::Rack::TestSession)
|
|
9
|
+
end
|
|
@@ -1,28 +1,16 @@
|
|
|
1
1
|
module Ratchetio
|
|
2
2
|
module Rails
|
|
3
3
|
module ControllerMethods
|
|
4
|
-
|
|
5
|
-
def ratchetio_request_data
|
|
6
|
-
{
|
|
7
|
-
:params => ratchetio_filter_params(params),
|
|
8
|
-
:url => ratchetio_request_url,
|
|
9
|
-
:user_ip => ratchetio_user_ip,
|
|
10
|
-
:headers => ratchetio_request_headers,
|
|
11
|
-
:GET => request.GET.to_hash,
|
|
12
|
-
# leaving out POST for now
|
|
13
|
-
:session => ratchetio_session_data,
|
|
14
|
-
:method => request.method,
|
|
15
|
-
}
|
|
16
|
-
end
|
|
4
|
+
include RequestDataExtractor
|
|
17
5
|
|
|
18
6
|
def ratchetio_person_data
|
|
19
7
|
user = send(Ratchetio.configuration.person_method)
|
|
20
8
|
# include id, username, email if non-empty
|
|
21
9
|
if user
|
|
22
|
-
{
|
|
23
|
-
:id =>
|
|
24
|
-
:username =>
|
|
25
|
-
:email =>
|
|
10
|
+
{
|
|
11
|
+
:id => (user.send(Ratchetio.configuration.person_id_method) rescue nil),
|
|
12
|
+
:username => (user.send(Ratchetio.configuration.person_username_method) rescue nil),
|
|
13
|
+
:email => (user.send(Ratchetio.configuration.person_email_method) rescue nil)
|
|
26
14
|
}
|
|
27
15
|
else
|
|
28
16
|
{}
|
|
@@ -31,74 +19,8 @@ module Ratchetio
|
|
|
31
19
|
{}
|
|
32
20
|
end
|
|
33
21
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def ratchetio_filter_params(params)
|
|
37
|
-
filtered = {}
|
|
38
|
-
|
|
39
|
-
params.to_hash.each_pair do |k,v|
|
|
40
|
-
if v.is_a? ActionDispatch::Http::UploadedFile
|
|
41
|
-
# only save content_type, original_filename, and length
|
|
42
|
-
begin
|
|
43
|
-
filtered[k] = {
|
|
44
|
-
:content_type => v.content_type,
|
|
45
|
-
:original_filename => v.original_filename,
|
|
46
|
-
:size => v.tempfile.size
|
|
47
|
-
}
|
|
48
|
-
rescue
|
|
49
|
-
filtered[k] = 'Uploaded file'
|
|
50
|
-
end
|
|
51
|
-
elsif v.is_a? Hash
|
|
52
|
-
filtered[k] = ratchetio_filter_params v
|
|
53
|
-
elsif Ratchetio.configuration.scrub_fields.include? k.to_sym
|
|
54
|
-
filtered[k] = "*" * v.length
|
|
55
|
-
else
|
|
56
|
-
filtered[k] = v
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
filtered
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def ratchetio_request_url
|
|
63
|
-
url = "#{request.protocol}#{request.host}"
|
|
64
|
-
unless [80, 443].include?(request.port)
|
|
65
|
-
url << ":#{request.port}"
|
|
66
|
-
end
|
|
67
|
-
url << request.fullpath
|
|
68
|
-
url
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def ratchetio_user_ip
|
|
72
|
-
# priority: X-Real-Ip, then X-Forwarded-For, then request.remote_ip
|
|
73
|
-
real_ip = request.env['HTTP_X_REAL_IP']
|
|
74
|
-
if real_ip
|
|
75
|
-
return real_ip
|
|
76
|
-
end
|
|
77
|
-
forwarded_for = request.env['HTTP_X_FORWARDED_FOR']
|
|
78
|
-
if forwarded_for
|
|
79
|
-
return forwarded_for
|
|
80
|
-
end
|
|
81
|
-
request.remote_ip
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def ratchetio_request_headers
|
|
85
|
-
headers = {}
|
|
86
|
-
request.env.each_pair do |k,v|
|
|
87
|
-
if k.match(/^HTTP_/)
|
|
88
|
-
# convert HTTP_CONTENT_TYPE to Content-Type, etc.
|
|
89
|
-
name = k.split("_", 2)[1].sub("_", "-").split(/(\W)/).map(&:capitalize).join
|
|
90
|
-
headers[name] = v
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
headers
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def ratchetio_session_data
|
|
97
|
-
if session.respond_to?(:to_hash)
|
|
98
|
-
ratchetio_filter_params(session.to_hash)
|
|
99
|
-
else
|
|
100
|
-
session.data
|
|
101
|
-
end
|
|
22
|
+
def ratchetio_request_data
|
|
23
|
+
extract_request_data_from_rack(request.env)
|
|
102
24
|
end
|
|
103
25
|
|
|
104
26
|
end
|
data/lib/ratchetio/railtie.rb
CHANGED
|
@@ -22,16 +22,15 @@ module Ratchetio
|
|
|
22
22
|
include Ratchetio::Rails::ControllerMethods
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
if defined?(
|
|
26
|
-
#
|
|
27
|
-
require 'ratchetio/rails/
|
|
28
|
-
|
|
29
|
-
elsif defined?(
|
|
30
|
-
#
|
|
31
|
-
require 'ratchetio/rails/
|
|
32
|
-
|
|
25
|
+
if defined?(ActionDispatch::DebugExceptions)
|
|
26
|
+
# Rails 3.2.x
|
|
27
|
+
require 'ratchetio/middleware/rails/show_exceptions'
|
|
28
|
+
ActionDispatch::DebugExceptions.send(:include, Ratchetio::Middleware::Rails::ShowExceptions)
|
|
29
|
+
elsif defined?(ActionDispatch::ShowExceptions)
|
|
30
|
+
# Rails 3.0.x and 3.1.x
|
|
31
|
+
require 'ratchetio/middleware/rails/show_exceptions'
|
|
32
|
+
ActionDispatch::ShowExceptions.send(:include, Ratchetio::Middleware::Rails::ShowExceptions)
|
|
33
33
|
end
|
|
34
|
-
|
|
35
34
|
end
|
|
36
35
|
end
|
|
37
36
|
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
module Ratchetio
|
|
2
|
+
module RequestDataExtractor
|
|
3
|
+
ATTACHMENT_CLASSES = %w[
|
|
4
|
+
ActionDispatch::Http::UploadedFile
|
|
5
|
+
Rack::Multipart::UploadedFile
|
|
6
|
+
].freeze
|
|
7
|
+
|
|
8
|
+
def extract_person_data_from_controller(env)
|
|
9
|
+
controller = env['action_controller.instance']
|
|
10
|
+
person_data = controller ? controller.try(:ratchetio_person_data) : {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def extract_request_data_from_rack(env)
|
|
14
|
+
sensitive_params = sensitive_params_list(env)
|
|
15
|
+
request_params = ratchetio_request_params(env)
|
|
16
|
+
cookies = ratchetio_filtered_params(sensitive_params, ratchetio_request_cookies(env))
|
|
17
|
+
get_params = ratchetio_filtered_params(sensitive_params, ratchetio_get_params(env))
|
|
18
|
+
post_params = ratchetio_filtered_params(sensitive_params, ratchetio_post_params(env))
|
|
19
|
+
|
|
20
|
+
{
|
|
21
|
+
:params => get_params.merge(post_params).merge(request_params),
|
|
22
|
+
:url => ratchetio_url(env),
|
|
23
|
+
:user_ip => ratchetio_user_ip(env),
|
|
24
|
+
:headers => ratchetio_headers(env),
|
|
25
|
+
:GET => get_params,
|
|
26
|
+
:POST => post_params,
|
|
27
|
+
:cookies => cookies,
|
|
28
|
+
:session => env['rack.session.options'],
|
|
29
|
+
:method => ratchetio_request_method(env)
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def ratchetio_request_method(env)
|
|
36
|
+
env['REQUEST_METHOD'] || env[:method]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def ratchetio_headers(env)
|
|
40
|
+
env.keys.grep(/^HTTP_/).map do |header|
|
|
41
|
+
name = header.gsub(/^HTTP_/, '').split('_').map(&:capitalize).join('-')
|
|
42
|
+
{ name => env[header] }
|
|
43
|
+
end.inject(:merge)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def ratchetio_url(env)
|
|
47
|
+
scheme = env['rack.url_scheme']
|
|
48
|
+
host = env['HTTP_HOST'] || env['SERVER_NAME']
|
|
49
|
+
path = env['ORIGINAL_FULLPATH'] || env['REQUEST_URI']
|
|
50
|
+
unless path.nil? || path.empty?
|
|
51
|
+
path = '/' + path.to_s if path.to_s.slice(0, 1) != '/'
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
[scheme, '://', host, path].join
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def ratchetio_user_ip(env)
|
|
58
|
+
(env['action_dispatch.remote_ip'] || env['HTTP_X_REAL_IP'] || env['HTTP_X_FORWARDED_FOR'] || env['REMOTE_ADDR']).to_s
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def ratchetio_request_params(env)
|
|
62
|
+
route = ::Rails.application.routes.recognize_path(env['PATH_INFO']) rescue {}
|
|
63
|
+
{
|
|
64
|
+
:controller => route[:controller],
|
|
65
|
+
:action => route[:action],
|
|
66
|
+
:format => route[:format],
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def ratchetio_get_params(env)
|
|
71
|
+
rack_request(env).GET
|
|
72
|
+
rescue
|
|
73
|
+
{}
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def ratchetio_post_params(env)
|
|
77
|
+
rack_request(env).POST
|
|
78
|
+
rescue
|
|
79
|
+
{}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def ratchetio_request_cookies(env)
|
|
83
|
+
rack_request(env).cookie
|
|
84
|
+
rescue
|
|
85
|
+
{}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def rack_request(env)
|
|
89
|
+
@rack_request ||= Rack::Request.new(env)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def ratchetio_filtered_params(sensitive_params, params)
|
|
93
|
+
params.inject({}) do |result, (key, value)|
|
|
94
|
+
if key.to_sym.in?(sensitive_params)
|
|
95
|
+
result[key] = '*' * (value.length rescue 8)
|
|
96
|
+
elsif value.is_a?(Hash)
|
|
97
|
+
result[key] = ratchetio_filtered_params(sensitive_params, value)
|
|
98
|
+
elsif value.class.name.in?(ATTACHMENT_CLASSES)
|
|
99
|
+
result[key] = {
|
|
100
|
+
:content_type => value.content_type,
|
|
101
|
+
:original_filename => value.original_filename,
|
|
102
|
+
:size => value.tempfile.size
|
|
103
|
+
} rescue 'Uploaded file'
|
|
104
|
+
else
|
|
105
|
+
result[key] = value
|
|
106
|
+
end
|
|
107
|
+
result
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def sensitive_params_list(env)
|
|
112
|
+
Ratchetio.configuration.scrub_fields |= Array(env['action_dispatch.parameter_filter'])
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Ratchetio
|
|
4
|
+
class Sidekiq
|
|
5
|
+
def call(worker, msg, queue)
|
|
6
|
+
begin
|
|
7
|
+
yield
|
|
8
|
+
rescue => e
|
|
9
|
+
msg.delete('backtrace')
|
|
10
|
+
msg.delete('error_backtrace')
|
|
11
|
+
msg.delete('error_message')
|
|
12
|
+
msg.delete('error_class')
|
|
13
|
+
|
|
14
|
+
Ratchetio.report_exception(e, :params => msg)
|
|
15
|
+
raise
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Sidekiq.configure_server do |config|
|
|
22
|
+
config.server_middleware do |chain|
|
|
23
|
+
chain.add Ratchetio::Sidekiq
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/ratchetio/version.rb
CHANGED
data/lib/ratchetio.rb
CHANGED
|
@@ -6,15 +6,21 @@ require 'uri'
|
|
|
6
6
|
|
|
7
7
|
require "girl_friday" if defined?(GirlFriday)
|
|
8
8
|
|
|
9
|
+
require 'ratchetio/version'
|
|
9
10
|
require 'ratchetio/configuration'
|
|
11
|
+
require 'ratchetio/request_data_extractor'
|
|
12
|
+
require 'ratchetio/exception_reporter'
|
|
13
|
+
|
|
10
14
|
require 'ratchetio/delayed_job' if defined?(Delayed) && defined?(Delayed::Plugins)
|
|
15
|
+
require 'ratchetio/sidekiq' if defined?(Sidekiq)
|
|
11
16
|
require 'ratchetio/goalie' if defined?(Goalie)
|
|
17
|
+
require 'ratchetio/rack' if defined?(Rack)
|
|
12
18
|
require 'ratchetio/railtie' if defined?(Rails)
|
|
13
|
-
require 'ratchetio/version'
|
|
14
19
|
|
|
15
20
|
module Ratchetio
|
|
16
21
|
class << self
|
|
17
22
|
attr_writer :configuration
|
|
23
|
+
attr_reader :last_report
|
|
18
24
|
|
|
19
25
|
# Configures the gem.
|
|
20
26
|
#
|
|
@@ -57,24 +63,21 @@ module Ratchetio
|
|
|
57
63
|
# @param person_data [Hash] Data describing the affected person. Should be the result of calling
|
|
58
64
|
# `ratchetio_person_data`
|
|
59
65
|
def report_exception(exception, request_data = nil, person_data = nil)
|
|
60
|
-
unless configuration.enabled
|
|
61
|
-
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
if ignored?(exception)
|
|
65
|
-
return
|
|
66
|
-
end
|
|
66
|
+
return 'disabled' unless configuration.enabled
|
|
67
|
+
return 'ignored' if ignored?(exception)
|
|
67
68
|
|
|
68
69
|
data = exception_data(exception, filtered_level(exception))
|
|
69
70
|
data[:request] = request_data if request_data
|
|
70
71
|
data[:person] = person_data if person_data
|
|
71
72
|
|
|
73
|
+
@last_report = data
|
|
74
|
+
|
|
72
75
|
payload = build_payload(data)
|
|
73
76
|
schedule_payload(payload)
|
|
74
77
|
data
|
|
75
78
|
rescue => e
|
|
76
79
|
logger.error "[Ratchet.io] Error reporting exception to Ratchet.io: #{e}"
|
|
77
|
-
|
|
80
|
+
'error'
|
|
78
81
|
end
|
|
79
82
|
|
|
80
83
|
# Reports an arbitrary message to Ratchet.io
|
|
@@ -274,7 +277,7 @@ module Ratchetio
|
|
|
274
277
|
:access_token => configuration.access_token,
|
|
275
278
|
:data => data
|
|
276
279
|
}
|
|
277
|
-
|
|
280
|
+
MultiJson.dump(payload)
|
|
278
281
|
end
|
|
279
282
|
|
|
280
283
|
def base_data(level = 'error')
|
data/ratchetio.gemspec
CHANGED
|
@@ -14,9 +14,9 @@ Gem::Specification.new do |gem|
|
|
|
14
14
|
gem.require_paths = ["lib"]
|
|
15
15
|
gem.version = Ratchetio::VERSION
|
|
16
16
|
|
|
17
|
-
gem.add_runtime_dependency '
|
|
17
|
+
gem.add_runtime_dependency 'multi_json', '~> 1.5.0'
|
|
18
18
|
|
|
19
|
-
gem.add_development_dependency 'rails', '~> 3.2.
|
|
19
|
+
gem.add_development_dependency 'rails', '~> 3.2.11'
|
|
20
20
|
gem.add_development_dependency 'devise', '>= 2.1.2'
|
|
21
21
|
gem.add_development_dependency 'rspec-rails', '~> 2.12.0'
|
|
22
22
|
gem.add_development_dependency 'database_cleaner', '>= 0.9.1'
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe HomeController do
|
|
4
|
-
|
|
4
|
+
let(:logger_mock) { double("Rails.logger").as_null_object }
|
|
5
|
+
|
|
5
6
|
before(:each) do
|
|
6
7
|
reset_configuration
|
|
7
8
|
Ratchetio.configure do |config|
|
|
@@ -12,8 +13,6 @@ describe HomeController do
|
|
|
12
13
|
config.logger = logger_mock
|
|
13
14
|
end
|
|
14
15
|
end
|
|
15
|
-
|
|
16
|
-
let(:logger_mock) { double("Rails.logger").as_null_object }
|
|
17
16
|
|
|
18
17
|
context "ratchetio controller methods" do
|
|
19
18
|
# TODO run these for a a more-real request
|
|
@@ -43,14 +42,14 @@ describe HomeController do
|
|
|
43
42
|
:tempfile => "dummy"
|
|
44
43
|
}
|
|
45
44
|
file = ActionDispatch::Http::UploadedFile.new(file_hash)
|
|
46
|
-
|
|
45
|
+
|
|
47
46
|
params = {
|
|
48
47
|
:name => name,
|
|
49
48
|
:a_file => file
|
|
50
49
|
}
|
|
51
50
|
|
|
52
|
-
filtered = controller.send(:
|
|
53
|
-
|
|
51
|
+
filtered = controller.send(:ratchetio_filtered_params, Ratchetio.configuration.scrub_fields, params)
|
|
52
|
+
|
|
54
53
|
filtered[:name].should == name
|
|
55
54
|
filtered[:a_file].should be_a_kind_of(Hash)
|
|
56
55
|
filtered[:a_file][:content_type].should == file_hash[:type]
|
|
@@ -67,7 +66,7 @@ describe HomeController do
|
|
|
67
66
|
:tempfile => "dummy"
|
|
68
67
|
}
|
|
69
68
|
file = ActionDispatch::Http::UploadedFile.new(file_hash)
|
|
70
|
-
|
|
69
|
+
|
|
71
70
|
params = {
|
|
72
71
|
:name => name,
|
|
73
72
|
:wrapper => {
|
|
@@ -78,18 +77,18 @@ describe HomeController do
|
|
|
78
77
|
}
|
|
79
78
|
}
|
|
80
79
|
|
|
81
|
-
filtered = controller.send(:
|
|
82
|
-
|
|
80
|
+
filtered = controller.send(:ratchetio_filtered_params, Ratchetio.configuration.scrub_fields, params)
|
|
81
|
+
|
|
83
82
|
filtered[:name].should == name
|
|
84
83
|
filtered[:wrapper][:wrapper2][:foo].should == "bar"
|
|
85
|
-
|
|
84
|
+
|
|
86
85
|
filtered_file = filtered[:wrapper][:wrapper2][:a_file]
|
|
87
86
|
filtered_file.should be_a_kind_of(Hash)
|
|
88
87
|
filtered_file[:content_type].should == file_hash[:type]
|
|
89
88
|
filtered_file[:original_filename].should == file_hash[:filename]
|
|
90
89
|
filtered_file[:size].should == file_hash[:tempfile].size
|
|
91
90
|
end
|
|
92
|
-
|
|
91
|
+
|
|
93
92
|
it "should scrub the default scrub_fields" do
|
|
94
93
|
params = {
|
|
95
94
|
:passwd => "hidden",
|
|
@@ -97,29 +96,29 @@ describe HomeController do
|
|
|
97
96
|
:secret => "hidden",
|
|
98
97
|
:notpass => "visible"
|
|
99
98
|
}
|
|
100
|
-
|
|
101
|
-
filtered = controller.send(:
|
|
102
|
-
|
|
99
|
+
|
|
100
|
+
filtered = controller.send(:ratchetio_filtered_params, Ratchetio.configuration.scrub_fields, params)
|
|
101
|
+
|
|
103
102
|
filtered[:passwd].should == "******"
|
|
104
103
|
filtered[:password].should == "******"
|
|
105
104
|
filtered[:secret].should == "******"
|
|
106
105
|
filtered[:notpass].should == "visible"
|
|
107
106
|
end
|
|
108
|
-
|
|
107
|
+
|
|
109
108
|
it "should scrub custom scrub_fields" do
|
|
110
109
|
Ratchetio.configure do |config|
|
|
111
110
|
config.scrub_fields = [:notpass, :secret]
|
|
112
111
|
end
|
|
113
|
-
|
|
112
|
+
|
|
114
113
|
params = {
|
|
115
114
|
:passwd => "visible",
|
|
116
115
|
:password => "visible",
|
|
117
116
|
:secret => "hidden",
|
|
118
117
|
:notpass => "hidden"
|
|
119
118
|
}
|
|
120
|
-
|
|
121
|
-
filtered = controller.send(:
|
|
122
|
-
|
|
119
|
+
|
|
120
|
+
filtered = controller.send(:ratchetio_filtered_params, Ratchetio.configuration.scrub_fields, params)
|
|
121
|
+
|
|
123
122
|
filtered[:passwd].should == "visible"
|
|
124
123
|
filtered[:password].should == "visible"
|
|
125
124
|
filtered[:secret].should == "******"
|
|
@@ -132,7 +131,7 @@ describe HomeController do
|
|
|
132
131
|
req = controller.request
|
|
133
132
|
req.host = 'ratchet.io'
|
|
134
133
|
|
|
135
|
-
controller.send(:
|
|
134
|
+
controller.send(:ratchetio_request_data)[:url].should == 'http://ratchet.io'
|
|
136
135
|
end
|
|
137
136
|
end
|
|
138
137
|
|
|
@@ -140,16 +139,16 @@ describe HomeController do
|
|
|
140
139
|
it "should use X-Real-Ip when set" do
|
|
141
140
|
controller.request.env["HTTP_X_REAL_IP"] = '1.1.1.1'
|
|
142
141
|
controller.request.env["HTTP_X_FORWARDED_FOR"] = '1.2.3.4'
|
|
143
|
-
controller.send(:
|
|
142
|
+
controller.send(:ratchetio_request_data)[:user_ip].should == '1.1.1.1'
|
|
144
143
|
end
|
|
145
144
|
|
|
146
145
|
it "should use X-Forwarded-For when set" do
|
|
147
146
|
controller.request.env["HTTP_X_FORWARDED_FOR"] = '1.2.3.4'
|
|
148
|
-
controller.send(:
|
|
147
|
+
controller.send(:ratchetio_request_data)[:user_ip].should == '1.2.3.4'
|
|
149
148
|
end
|
|
150
149
|
|
|
151
150
|
it "should use the remote_addr when neither is set" do
|
|
152
|
-
controller.send(:
|
|
151
|
+
controller.send(:ratchetio_request_data)[:user_ip].should == '0.0.0.0'
|
|
153
152
|
end
|
|
154
153
|
end
|
|
155
154
|
|
|
@@ -172,15 +171,10 @@ describe HomeController do
|
|
|
172
171
|
end
|
|
173
172
|
end
|
|
174
173
|
|
|
175
|
-
# TODO need to figure out how to make a test request that uses enough of the middleware
|
|
176
|
-
# that it invokes the ratchetio exception catcher. just plain "get 'some_url'" doesn't
|
|
177
|
-
# seem to work.
|
|
178
|
-
it "should report uncaught exceptions"
|
|
179
|
-
|
|
180
174
|
after(:each) do
|
|
181
175
|
Ratchetio.configure do |config|
|
|
182
176
|
config.logger = ::Rails.logger
|
|
183
177
|
end
|
|
184
178
|
end
|
|
185
|
-
|
|
179
|
+
|
|
186
180
|
end
|
|
@@ -4,8 +4,11 @@ Dummy::Application.routes.draw do
|
|
|
4
4
|
end
|
|
5
5
|
root :to => "home#index"
|
|
6
6
|
devise_for :users
|
|
7
|
-
resources :users
|
|
7
|
+
resources :users do
|
|
8
|
+
member { post :start_session }
|
|
9
|
+
end
|
|
8
10
|
|
|
9
11
|
match "/cause_exception" => "home#cause_exception"
|
|
10
12
|
match "/report_exception" => "home#report_exception"
|
|
13
|
+
match "/current_user" => "home#current_user"
|
|
11
14
|
end
|
data/spec/ratchetio_spec.rb
CHANGED
|
@@ -93,7 +93,7 @@ describe Ratchetio do
|
|
|
93
93
|
it 'should report exception objects with no backtrace' do
|
|
94
94
|
payload = nil
|
|
95
95
|
Ratchetio.stub(:schedule_payload) do |*args|
|
|
96
|
-
payload =
|
|
96
|
+
payload = MultiJson.load(args[0])
|
|
97
97
|
end
|
|
98
98
|
Ratchetio.report_exception(StandardError.new("oops"))
|
|
99
99
|
payload["data"]["body"]["trace"]["frames"].should == []
|
|
@@ -227,6 +227,8 @@ describe Ratchetio do
|
|
|
227
227
|
let(:logger_mock) { double("Rails.logger").as_null_object }
|
|
228
228
|
|
|
229
229
|
it 'should send the payload using the default asynchronous handler girl_friday' do
|
|
230
|
+
logger_mock.should_receive(:info).with('[Ratchet.io] Scheduling payload')
|
|
231
|
+
logger_mock.should_receive(:info).with('[Ratchet.io] Sending payload')
|
|
230
232
|
logger_mock.should_receive(:info).with('[Ratchet.io] Success')
|
|
231
233
|
|
|
232
234
|
Ratchetio.configure do |config|
|
|
@@ -361,15 +363,13 @@ describe Ratchetio do
|
|
|
361
363
|
context 'build_payload' do
|
|
362
364
|
it 'should build valid json' do
|
|
363
365
|
json = Ratchetio.send(:build_payload, {:foo => {:bar => "baz"}})
|
|
364
|
-
hash =
|
|
366
|
+
hash = MultiJson.load(json)
|
|
365
367
|
hash["data"]["foo"]["bar"].should == "baz"
|
|
366
368
|
end
|
|
367
369
|
end
|
|
368
370
|
|
|
369
371
|
context 'base_data' do
|
|
370
|
-
before(:each)
|
|
371
|
-
configure
|
|
372
|
-
end
|
|
372
|
+
before(:each) { configure }
|
|
373
373
|
|
|
374
374
|
it 'should have the correct notifier name' do
|
|
375
375
|
Ratchetio.send(:base_data)[:notifier][:name].should == 'ratchetio-gem'
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe HomeController do
|
|
4
|
+
let(:logger_mock) { double("Rails.logger").as_null_object }
|
|
5
|
+
|
|
6
|
+
before(:each) do
|
|
7
|
+
reset_configuration
|
|
8
|
+
Ratchetio.configure do |config|
|
|
9
|
+
config.access_token = 'aaaabbbbccccddddeeeeffff00001111'
|
|
10
|
+
config.environment = ::Rails.env
|
|
11
|
+
config.root = ::Rails.root
|
|
12
|
+
config.framework = "Rails: #{::Rails::VERSION::STRING}"
|
|
13
|
+
config.logger = logger_mock
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
context "with broken request" do
|
|
18
|
+
it "should report uncaught exceptions" do
|
|
19
|
+
expect{ get 'current_user', nil, :cookie => '8%B' }.to raise_exception
|
|
20
|
+
|
|
21
|
+
exception_info = Ratchetio.last_report[:body][:trace][:exception]
|
|
22
|
+
exception_info[:class].should == 'ArgumentError'
|
|
23
|
+
exception_info[:message].should == 'invalid %-encoding (8%B)'
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context "with error hiding deep inside" do
|
|
28
|
+
let!(:cookie_method_name){ :[] }
|
|
29
|
+
let!(:original_cookie_method){ ActionDispatch::Cookies::CookieJar.instance_method(cookie_method_name) }
|
|
30
|
+
let!(:broken_cookie_method){ Proc.new{ |name| "1" - 1 } }
|
|
31
|
+
|
|
32
|
+
before(:each) do
|
|
33
|
+
ActionDispatch::Cookies::CookieJar.send(:define_method, cookie_method_name, broken_cookie_method)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
after(:each) do
|
|
37
|
+
ActionDispatch::Cookies::CookieJar.send(:define_method, cookie_method_name, original_cookie_method)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "should report uncaught exceptions" do
|
|
41
|
+
expect{ get 'current_user' }.to raise_exception
|
|
42
|
+
|
|
43
|
+
exception_info = Ratchetio.last_report[:body][:trace][:exception]
|
|
44
|
+
exception_info[:class].should == 'NoMethodError'
|
|
45
|
+
# exception_info[:message].should == 'undefined method `-\' for "1":String'
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require 'rubygems'
|
|
2
2
|
|
|
3
|
-
ENV['RAILS_ENV']
|
|
3
|
+
ENV['RAILS_ENV'] = 'test'
|
|
4
4
|
require File.expand_path('../dummyapp/config/environment', __FILE__)
|
|
5
5
|
require 'rspec/rails'
|
|
6
6
|
require 'database_cleaner'
|
|
@@ -26,10 +26,10 @@ RSpec.configure do |config|
|
|
|
26
26
|
config.after(:each) do
|
|
27
27
|
DatabaseCleaner.clean
|
|
28
28
|
end
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def reset_configuration
|
|
33
33
|
Ratchetio.reconfigure do |config|
|
|
34
34
|
end
|
|
35
|
-
end
|
|
35
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ratchetio
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,16 +9,16 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2013-01-
|
|
12
|
+
date: 2013-01-29 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
|
-
name:
|
|
15
|
+
name: multi_json
|
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
|
17
17
|
none: false
|
|
18
18
|
requirements:
|
|
19
19
|
- - ~>
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
|
-
version:
|
|
21
|
+
version: 1.5.0
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -26,7 +26,7 @@ dependencies:
|
|
|
26
26
|
requirements:
|
|
27
27
|
- - ~>
|
|
28
28
|
- !ruby/object:Gem::Version
|
|
29
|
-
version:
|
|
29
|
+
version: 1.5.0
|
|
30
30
|
- !ruby/object:Gem::Dependency
|
|
31
31
|
name: rails
|
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -34,7 +34,7 @@ dependencies:
|
|
|
34
34
|
requirements:
|
|
35
35
|
- - ~>
|
|
36
36
|
- !ruby/object:Gem::Version
|
|
37
|
-
version: 3.2.
|
|
37
|
+
version: 3.2.11
|
|
38
38
|
type: :development
|
|
39
39
|
prerelease: false
|
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -42,7 +42,7 @@ dependencies:
|
|
|
42
42
|
requirements:
|
|
43
43
|
- - ~>
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
|
-
version: 3.2.
|
|
45
|
+
version: 3.2.11
|
|
46
46
|
- !ruby/object:Gem::Dependency
|
|
47
47
|
name: devise
|
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -127,12 +127,18 @@ files:
|
|
|
127
127
|
- lib/ratchetio.rb
|
|
128
128
|
- lib/ratchetio/configuration.rb
|
|
129
129
|
- lib/ratchetio/delayed_job.rb
|
|
130
|
+
- lib/ratchetio/exception_reporter.rb
|
|
130
131
|
- lib/ratchetio/goalie.rb
|
|
132
|
+
- lib/ratchetio/middleware/rack/builder.rb
|
|
133
|
+
- lib/ratchetio/middleware/rack/test_session.rb
|
|
134
|
+
- lib/ratchetio/middleware/rails/show_exceptions.rb
|
|
135
|
+
- lib/ratchetio/rack.rb
|
|
131
136
|
- lib/ratchetio/rails.rb
|
|
132
137
|
- lib/ratchetio/rails/controller_methods.rb
|
|
133
|
-
- lib/ratchetio/rails/middleware/exception_catcher.rb
|
|
134
138
|
- lib/ratchetio/railtie.rb
|
|
135
139
|
- lib/ratchetio/rake_tasks.rb
|
|
140
|
+
- lib/ratchetio/request_data_extractor.rb
|
|
141
|
+
- lib/ratchetio/sidekiq.rb
|
|
136
142
|
- lib/ratchetio/version.rb
|
|
137
143
|
- ratchetio.gemspec
|
|
138
144
|
- spec/controllers/home_controller_spec.rb
|
|
@@ -188,6 +194,7 @@ files:
|
|
|
188
194
|
- spec/dummyapp/public/favicon.ico
|
|
189
195
|
- spec/dummyapp/script/rails
|
|
190
196
|
- spec/ratchetio_spec.rb
|
|
197
|
+
- spec/requests/home_spec.rb
|
|
191
198
|
- spec/spec_helper.rb
|
|
192
199
|
- spec/support/devise.rb
|
|
193
200
|
homepage: https://github.com/ratchetio/ratchetio-gem
|
|
@@ -268,6 +275,7 @@ test_files:
|
|
|
268
275
|
- spec/dummyapp/public/favicon.ico
|
|
269
276
|
- spec/dummyapp/script/rails
|
|
270
277
|
- spec/ratchetio_spec.rb
|
|
278
|
+
- spec/requests/home_spec.rb
|
|
271
279
|
- spec/spec_helper.rb
|
|
272
280
|
- spec/support/devise.rb
|
|
273
281
|
has_rdoc:
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
module Ratchetio
|
|
2
|
-
module Rails
|
|
3
|
-
module Middleware
|
|
4
|
-
module ExceptionCatcher
|
|
5
|
-
def self.included(base)
|
|
6
|
-
base.send(:alias_method_chain, :render_exception, :ratchetio)
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def render_exception_with_ratchetio(env, exception)
|
|
10
|
-
exception_data = nil
|
|
11
|
-
begin
|
|
12
|
-
controller = env['action_controller.instance']
|
|
13
|
-
request_data = controller.try(:ratchetio_request_data)
|
|
14
|
-
person_data = controller.try(:ratchetio_person_data)
|
|
15
|
-
exception_data = Ratchetio.report_exception(exception, request_data, person_data)
|
|
16
|
-
rescue => e
|
|
17
|
-
# TODO use logger here?
|
|
18
|
-
puts "[Ratchet.io] Exception while reporting exception to Ratchet.io: #{e}"
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# if an exception was reported, save uuid in the env
|
|
22
|
-
# so it can be displayed to the user on the error page
|
|
23
|
-
if exception_data
|
|
24
|
-
begin
|
|
25
|
-
env['ratchetio.exception_uuid'] = exception_data[:uuid]
|
|
26
|
-
rescue => e
|
|
27
|
-
puts "[Ratchet.io] Exception saving uuid in env: #{e}"
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# now continue as normal
|
|
32
|
-
render_exception_without_ratchetio(env, exception)
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|