rollbar 2.7.1 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.gitmodules +3 -0
- data/.rubocop.yml +21 -0
- data/.travis.yml +1 -4
- data/CHANGELOG.md +25 -0
- data/README.md +146 -4
- data/Rakefile +5 -3
- data/data/rollbar.snippet.js +1 -0
- data/docs/configuration_options.md +281 -0
- data/lib/rollbar.rb +122 -32
- data/lib/rollbar/configuration.rb +18 -0
- data/lib/rollbar/exceptions.rb +3 -0
- data/lib/rollbar/js.rb +32 -0
- data/lib/rollbar/js/frameworks.rb +6 -0
- data/lib/rollbar/js/frameworks/rails.rb +29 -0
- data/lib/rollbar/js/middleware.rb +129 -0
- data/lib/rollbar/js/version.rb +5 -0
- data/lib/rollbar/lazy_store.rb +83 -0
- data/lib/rollbar/tasks/rollbar.cap +1 -1
- data/lib/rollbar/version.rb +1 -1
- data/lib/tasks/tasks.rake +13 -0
- data/rollbar.gemspec +1 -1
- data/spec/dummyapp/app/controllers/home_controller.rb +4 -0
- data/spec/dummyapp/app/views/js/test.html.erb +1 -0
- data/spec/dummyapp/app/views/layouts/simple.html.erb +18 -0
- data/spec/dummyapp/config/initializers/rollbar.rb +5 -2
- data/spec/dummyapp/config/routes.rb +1 -0
- data/spec/rollbar/js/frameworks/rails_spec.rb +19 -0
- data/spec/rollbar/js/middleware_spec.rb +154 -0
- data/spec/rollbar/lazy_store_spec.rb +99 -0
- data/spec/rollbar_spec.rb +272 -11
- data/spec/spec_helper.rb +2 -8
- metadata +26 -4
data/lib/rollbar.rb
CHANGED
@@ -11,6 +11,7 @@ end
|
|
11
11
|
|
12
12
|
require 'rollbar/version'
|
13
13
|
require 'rollbar/json'
|
14
|
+
require 'rollbar/js'
|
14
15
|
require 'rollbar/configuration'
|
15
16
|
require 'rollbar/encoding'
|
16
17
|
require 'rollbar/logger_proxy'
|
@@ -20,6 +21,8 @@ require 'rollbar/railtie' if defined?(Rails::VERSION)
|
|
20
21
|
require 'rollbar/delay/girl_friday' if defined?(GirlFriday)
|
21
22
|
require 'rollbar/delay/thread'
|
22
23
|
require 'rollbar/truncation'
|
24
|
+
require 'rollbar/exceptions'
|
25
|
+
require 'rollbar/lazy_store'
|
23
26
|
|
24
27
|
module Rollbar
|
25
28
|
ATTACHMENT_CLASSES = %w[
|
@@ -33,18 +36,20 @@ module Rollbar
|
|
33
36
|
class Notifier
|
34
37
|
attr_accessor :configuration
|
35
38
|
attr_accessor :last_report
|
39
|
+
attr_reader :scope_object
|
36
40
|
|
37
41
|
@file_semaphore = Mutex.new
|
38
42
|
|
39
|
-
def initialize(parent_notifier = nil, payload_options = nil)
|
43
|
+
def initialize(parent_notifier = nil, payload_options = nil, scope = nil)
|
40
44
|
if parent_notifier
|
41
45
|
@configuration = parent_notifier.configuration.clone
|
46
|
+
@scope_object = parent_notifier.scope_object.clone
|
42
47
|
|
43
|
-
if payload_options
|
44
|
-
|
45
|
-
end
|
48
|
+
Rollbar::Util.deep_merge(@configuration.payload_options, payload_options) if payload_options
|
49
|
+
Rollbar::Util.deep_merge(@scope_object, scope) if scope
|
46
50
|
else
|
47
51
|
@configuration = ::Rollbar::Configuration.new
|
52
|
+
@scope_object = ::Rollbar::LazyStore.new(scope)
|
48
53
|
end
|
49
54
|
end
|
50
55
|
|
@@ -62,11 +67,12 @@ module Rollbar
|
|
62
67
|
end
|
63
68
|
|
64
69
|
def scope(options = {})
|
65
|
-
self.class.new(self, options)
|
70
|
+
self.class.new(self, nil, options)
|
66
71
|
end
|
67
72
|
|
68
73
|
def scope!(options = {})
|
69
|
-
Rollbar::Util.deep_merge(
|
74
|
+
Rollbar::Util.deep_merge(scope_object, options)
|
75
|
+
|
70
76
|
self
|
71
77
|
end
|
72
78
|
|
@@ -117,33 +123,28 @@ module Rollbar
|
|
117
123
|
def log(level, *args)
|
118
124
|
return 'disabled' unless configuration.enabled
|
119
125
|
|
120
|
-
message =
|
121
|
-
exception = nil
|
122
|
-
extra = nil
|
123
|
-
|
124
|
-
args.each do |arg|
|
125
|
-
if arg.is_a?(String)
|
126
|
-
message = arg
|
127
|
-
elsif arg.is_a?(Exception)
|
128
|
-
exception = arg
|
129
|
-
elsif arg.is_a?(Hash)
|
130
|
-
extra = arg
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
126
|
+
message, exception, extra = extract_arguments(args)
|
134
127
|
use_exception_level_filters = extra && extra.delete(:use_exception_level_filters) == true
|
135
128
|
|
136
129
|
return 'ignored' if ignored?(exception, use_exception_level_filters)
|
137
130
|
|
138
|
-
|
139
|
-
|
140
|
-
|
131
|
+
begin
|
132
|
+
call_before_process(:level => level,
|
133
|
+
:exception => exception,
|
134
|
+
:message => message,
|
135
|
+
:extra => extra)
|
136
|
+
rescue Rollbar::Ignore
|
137
|
+
return 'ignored'
|
141
138
|
end
|
142
139
|
|
140
|
+
level = lookup_exception_level(level, exception,
|
141
|
+
use_exception_level_filters)
|
142
|
+
|
143
143
|
begin
|
144
144
|
report(level, message, exception, extra)
|
145
145
|
rescue Exception => e
|
146
146
|
report_internal_error(e)
|
147
|
+
|
147
148
|
'error'
|
148
149
|
end
|
149
150
|
end
|
@@ -241,6 +242,56 @@ module Rollbar
|
|
241
242
|
|
242
243
|
private
|
243
244
|
|
245
|
+
def call_before_process(options)
|
246
|
+
options = {
|
247
|
+
:level => options[:level],
|
248
|
+
:scope => scope_object,
|
249
|
+
:exception => options[:exception],
|
250
|
+
:message => options[:message],
|
251
|
+
:extra => options[:extra]
|
252
|
+
}
|
253
|
+
handlers = configuration.before_process
|
254
|
+
|
255
|
+
handlers.each do |handler|
|
256
|
+
begin
|
257
|
+
handler.call(options)
|
258
|
+
rescue Rollbar::Ignore
|
259
|
+
raise
|
260
|
+
rescue => e
|
261
|
+
log_error("[Rollbar] Error calling the `before_process` hook: #{e}")
|
262
|
+
|
263
|
+
break
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def extract_arguments(args)
|
269
|
+
message = nil
|
270
|
+
exception = nil
|
271
|
+
extra = nil
|
272
|
+
|
273
|
+
args.each do |arg|
|
274
|
+
if arg.is_a?(String)
|
275
|
+
message = arg
|
276
|
+
elsif arg.is_a?(Exception)
|
277
|
+
exception = arg
|
278
|
+
elsif arg.is_a?(Hash)
|
279
|
+
extra = arg
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
[message, exception, extra]
|
284
|
+
end
|
285
|
+
|
286
|
+
def lookup_exception_level(orig_level, exception, use_exception_level_filters)
|
287
|
+
return orig_level unless use_exception_level_filters
|
288
|
+
|
289
|
+
exception_level = filtered_level(exception)
|
290
|
+
return exception_level if exception_level
|
291
|
+
|
292
|
+
orig_level
|
293
|
+
end
|
294
|
+
|
244
295
|
def ignored?(exception, use_exception_level_filters = false)
|
245
296
|
return false unless exception
|
246
297
|
return true if use_exception_level_filters && filtered_level(exception) == 'ignore'
|
@@ -336,10 +387,7 @@ module Rollbar
|
|
336
387
|
data[:uuid] = SecureRandom.uuid if defined?(SecureRandom) && SecureRandom.respond_to?(:uuid)
|
337
388
|
|
338
389
|
Rollbar::Util.deep_merge(data, configuration.payload_options)
|
339
|
-
|
340
|
-
data[:person] = data[:person].call if data[:person].respond_to?(:call)
|
341
|
-
data[:request] = data[:request].call if data[:request].respond_to?(:call)
|
342
|
-
data[:context] = data[:context].call if data[:context].respond_to?(:call)
|
390
|
+
Rollbar::Util.deep_merge(data, scope_object)
|
343
391
|
|
344
392
|
# Our API doesn't allow null context values, so just delete
|
345
393
|
# the key if value is nil.
|
@@ -352,9 +400,37 @@ module Rollbar
|
|
352
400
|
|
353
401
|
enforce_valid_utf8(payload)
|
354
402
|
|
403
|
+
call_transform(:level => level,
|
404
|
+
:exception => exception,
|
405
|
+
:message => message,
|
406
|
+
:extra => extra,
|
407
|
+
:payload => payload)
|
408
|
+
|
355
409
|
payload
|
356
410
|
end
|
357
411
|
|
412
|
+
def call_transform(options)
|
413
|
+
options = {
|
414
|
+
:level => options[:level],
|
415
|
+
:scope => scope_object,
|
416
|
+
:exception => options[:exception],
|
417
|
+
:message => options[:message],
|
418
|
+
:extra => options[:extra],
|
419
|
+
:payload => options[:payload]
|
420
|
+
}
|
421
|
+
handlers = configuration.transform
|
422
|
+
|
423
|
+
handlers.each do |handler|
|
424
|
+
begin
|
425
|
+
handler.call(options)
|
426
|
+
rescue => e
|
427
|
+
log_error("[Rollbar] Error calling the `transform` hook: #{e}")
|
428
|
+
|
429
|
+
break
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
358
434
|
def build_payload_body(message, exception, extra)
|
359
435
|
extra = Rollbar::Util.deep_merge(custom_data, extra || {}) if custom_data_method?
|
360
436
|
|
@@ -372,7 +448,7 @@ module Rollbar
|
|
372
448
|
def report_custom_data_error(e)
|
373
449
|
data = safely.error(e)
|
374
450
|
|
375
|
-
return {} unless data[:uuid]
|
451
|
+
return {} unless data.is_a?(Hash) && data[:uuid]
|
376
452
|
|
377
453
|
uuid_url = uuid_rollbar_url(data)
|
378
454
|
|
@@ -744,6 +820,8 @@ module Rollbar
|
|
744
820
|
# to configure it without initializing any of the third party hooks
|
745
821
|
def preconfigure
|
746
822
|
yield(configuration)
|
823
|
+
|
824
|
+
reset_notifier!
|
747
825
|
end
|
748
826
|
|
749
827
|
def configure
|
@@ -752,9 +830,7 @@ module Rollbar
|
|
752
830
|
|
753
831
|
yield(configuration)
|
754
832
|
|
755
|
-
|
756
|
-
require_core_extensions
|
757
|
-
|
833
|
+
prepare
|
758
834
|
reset_notifier!
|
759
835
|
end
|
760
836
|
|
@@ -774,10 +850,24 @@ module Rollbar
|
|
774
850
|
@configuration ||= Configuration.new
|
775
851
|
end
|
776
852
|
|
853
|
+
def scope_object
|
854
|
+
@scope_obejct ||= ::Rollbar::LazyStore.new({})
|
855
|
+
end
|
856
|
+
|
777
857
|
def safely?
|
778
858
|
configuration.safely?
|
779
859
|
end
|
780
860
|
|
861
|
+
def prepare
|
862
|
+
prepare_js
|
863
|
+
require_hooks
|
864
|
+
require_core_extensions
|
865
|
+
end
|
866
|
+
|
867
|
+
def prepare_js
|
868
|
+
::Rollbar::Js.prepare if configuration.js_enabled
|
869
|
+
end
|
870
|
+
|
781
871
|
def require_hooks
|
782
872
|
return if configuration.disable_monkey_patch
|
783
873
|
wrap_delayed_worker
|
@@ -786,7 +876,7 @@ module Rollbar
|
|
786
876
|
require 'rollbar/sidekiq' if defined?(Sidekiq)
|
787
877
|
require 'rollbar/active_job' if defined?(ActiveJob)
|
788
878
|
require 'rollbar/goalie' if defined?(Goalie)
|
789
|
-
require 'rollbar/rack' if defined?(Rack)
|
879
|
+
require 'rollbar/rack' if defined?(Rack) unless configuration.disable_rack_monkey_patch
|
790
880
|
require 'rollbar/rake' if defined?(Rake)
|
791
881
|
end
|
792
882
|
|
@@ -6,11 +6,13 @@ module Rollbar
|
|
6
6
|
attr_accessor :access_token
|
7
7
|
attr_accessor :async_handler
|
8
8
|
attr_accessor :branch
|
9
|
+
attr_accessor :before_process
|
9
10
|
attr_accessor :code_version
|
10
11
|
attr_accessor :custom_data_method
|
11
12
|
attr_accessor :delayed_job_enabled
|
12
13
|
attr_accessor :default_logger
|
13
14
|
attr_accessor :disable_monkey_patch
|
15
|
+
attr_accessor :disable_rack_monkey_patch
|
14
16
|
attr_accessor :disable_core_monkey_patch
|
15
17
|
attr_accessor :dj_threshold
|
16
18
|
attr_accessor :enabled
|
@@ -31,6 +33,8 @@ module Rollbar
|
|
31
33
|
attr_accessor :report_dj_data
|
32
34
|
attr_accessor :request_timeout
|
33
35
|
attr_accessor :root
|
36
|
+
attr_accessor :js_options
|
37
|
+
attr_accessor :js_enabled
|
34
38
|
attr_accessor :safely
|
35
39
|
attr_accessor :scrub_fields
|
36
40
|
attr_accessor :scrub_user
|
@@ -40,6 +44,7 @@ module Rollbar
|
|
40
44
|
attr_accessor :uncaught_exception_level
|
41
45
|
attr_accessor :scrub_headers
|
42
46
|
attr_accessor :sidekiq_threshold
|
47
|
+
attr_accessor :transform
|
43
48
|
attr_accessor :verify_ssl_peer
|
44
49
|
attr_accessor :use_async
|
45
50
|
attr_accessor :use_eventmachine
|
@@ -55,12 +60,14 @@ module Rollbar
|
|
55
60
|
|
56
61
|
def initialize
|
57
62
|
@async_handler = nil
|
63
|
+
@before_process = []
|
58
64
|
@code_version = nil
|
59
65
|
@custom_data_method = nil
|
60
66
|
@default_logger = lambda { Logger.new(STDERR) }
|
61
67
|
@delayed_job_enabled = true
|
62
68
|
@disable_monkey_patch = false
|
63
69
|
@disable_core_monkey_patch = false
|
70
|
+
@disable_rack_monkey_patch = false
|
64
71
|
@dj_threshold = 0
|
65
72
|
@enabled = nil # set to true when configure is called
|
66
73
|
@endpoint = DEFAULT_ENDPOINT
|
@@ -82,6 +89,8 @@ module Rollbar
|
|
82
89
|
@populate_empty_backtraces = false
|
83
90
|
@report_dj_data = true
|
84
91
|
@request_timeout = 3
|
92
|
+
@js_enabled = false
|
93
|
+
@js_options = {}
|
85
94
|
@scrub_fields = [:passwd, :password, :password_confirmation, :secret,
|
86
95
|
:confirm_password, :password_confirmation, :secret_token,
|
87
96
|
:api_key, :access_token ]
|
@@ -92,6 +101,7 @@ module Rollbar
|
|
92
101
|
@scrub_headers = ['Authorization']
|
93
102
|
@sidekiq_threshold = 0
|
94
103
|
@safely = false
|
104
|
+
@transform = []
|
95
105
|
@use_async = false
|
96
106
|
@use_eventmachine = false
|
97
107
|
@verify_ssl_peer = true
|
@@ -164,6 +174,14 @@ module Rollbar
|
|
164
174
|
end.flatten.uniq.map(&:gem_dir)
|
165
175
|
end
|
166
176
|
|
177
|
+
def before_process=(*handler)
|
178
|
+
@before_process = Array(handler)
|
179
|
+
end
|
180
|
+
|
181
|
+
def transform=(*handler)
|
182
|
+
@transform = Array(handler)
|
183
|
+
end
|
184
|
+
|
167
185
|
# allow params to be read like a hash
|
168
186
|
def [](option)
|
169
187
|
send(option)
|
data/lib/rollbar/js.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require "rollbar/js/version"
|
2
|
+
|
3
|
+
module Rollbar
|
4
|
+
module Js
|
5
|
+
extend self
|
6
|
+
|
7
|
+
attr_reader :framework
|
8
|
+
attr_reader :framework_loader
|
9
|
+
|
10
|
+
def prepare
|
11
|
+
@framework ||= detect_framework
|
12
|
+
@framework_loader ||= load_framework_class.new
|
13
|
+
|
14
|
+
@framework_loader.prepare
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def detect_framework
|
20
|
+
case
|
21
|
+
when defined?(::Rails::VERSION)
|
22
|
+
:rails
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def load_framework_class
|
27
|
+
require "rollbar/js/frameworks/#{framework}"
|
28
|
+
|
29
|
+
Rollbar::Js::Frameworks.const_get(framework.to_s.capitalize)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Rollbar
|
2
|
+
module Js
|
3
|
+
module Frameworks
|
4
|
+
class Rails
|
5
|
+
attr_accessor :prepared
|
6
|
+
|
7
|
+
alias prepared? prepared
|
8
|
+
|
9
|
+
def prepare
|
10
|
+
return if prepared?
|
11
|
+
|
12
|
+
require 'rollbar/js/middleware'
|
13
|
+
|
14
|
+
config = {
|
15
|
+
:options => Rollbar.configuration.js_options,
|
16
|
+
:enabled => Rollbar.configuration.js_enabled
|
17
|
+
}
|
18
|
+
rails_config.middleware.use(::Rollbar::Js::Middleware, config)
|
19
|
+
|
20
|
+
self.prepared = true
|
21
|
+
end
|
22
|
+
|
23
|
+
def rails_config
|
24
|
+
::Rails.configuration
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'rack/response'
|
3
|
+
|
4
|
+
|
5
|
+
module Rollbar
|
6
|
+
module Js
|
7
|
+
class Middleware
|
8
|
+
attr_reader :app
|
9
|
+
attr_reader :config
|
10
|
+
|
11
|
+
JS_IS_INJECTED_KEY = 'rollbar.js_is_injected'
|
12
|
+
SNIPPET = File.read(File.expand_path('../../../../data/rollbar.snippet.js', __FILE__))
|
13
|
+
|
14
|
+
def initialize(app, config)
|
15
|
+
@app = app
|
16
|
+
@config = config
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
result = app.call(env)
|
21
|
+
|
22
|
+
_call(env, result)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def _call(env, result)
|
28
|
+
return result unless should_add_js?(env, result[0], result[1])
|
29
|
+
|
30
|
+
if response_string = add_js(result[2])
|
31
|
+
env[JS_IS_INJECTED_KEY] = true
|
32
|
+
response = ::Rack::Response.new(response_string, result[0], result[1])
|
33
|
+
|
34
|
+
response.finish
|
35
|
+
else
|
36
|
+
result
|
37
|
+
end
|
38
|
+
rescue => e
|
39
|
+
Rollbar.log_error("[Rollbar] Rollbar.js could not be added because #{e} exception")
|
40
|
+
result
|
41
|
+
end
|
42
|
+
|
43
|
+
def enabled?
|
44
|
+
!!config[:enabled]
|
45
|
+
end
|
46
|
+
|
47
|
+
def should_add_js?(env, status, headers)
|
48
|
+
enabled? &&
|
49
|
+
status == 200 &&
|
50
|
+
!env[JS_IS_INJECTED_KEY] &&
|
51
|
+
html?(headers) &&
|
52
|
+
!attachment?(headers) &&
|
53
|
+
!streaming?(env)
|
54
|
+
end
|
55
|
+
|
56
|
+
def html?(headers)
|
57
|
+
headers['Content-Type'] && headers['Content-Type'].include?('text/html')
|
58
|
+
end
|
59
|
+
|
60
|
+
def attachment?(headers)
|
61
|
+
headers['Content-Disposition'].to_s.include?('attachment')
|
62
|
+
end
|
63
|
+
|
64
|
+
def streaming?(env)
|
65
|
+
return false unless defined?(ActionController::Live)
|
66
|
+
|
67
|
+
env['action_controller.instance'].class.included_modules.include?(ActionController::Live)
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_js(response)
|
71
|
+
body = join_body(response)
|
72
|
+
close_old_response(response)
|
73
|
+
|
74
|
+
return nil unless body
|
75
|
+
|
76
|
+
head_open_end = find_end_of_head_open(body)
|
77
|
+
return nil unless head_open_end
|
78
|
+
|
79
|
+
if head_open_end
|
80
|
+
body = body[0..head_open_end] <<
|
81
|
+
config_js_tag <<
|
82
|
+
snippet_js_tag <<
|
83
|
+
body[head_open_end..-1]
|
84
|
+
end
|
85
|
+
|
86
|
+
body
|
87
|
+
rescue => e
|
88
|
+
Rollbar.log_error("[Rollbar] Rollbar.js could not be added because #{e} exception")
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
|
92
|
+
def find_end_of_head_open(body)
|
93
|
+
head_open = body.index('<head')
|
94
|
+
body.index('>', head_open) + 1 if head_open
|
95
|
+
end
|
96
|
+
|
97
|
+
def join_body(response)
|
98
|
+
source = nil
|
99
|
+
response.each { |fragment| source ? (source << fragment.to_s) : (source = fragment.to_s)}
|
100
|
+
source
|
101
|
+
end
|
102
|
+
|
103
|
+
def close_old_response(response)
|
104
|
+
response.close if response.respond_to?(:close)
|
105
|
+
end
|
106
|
+
|
107
|
+
def config_js_tag
|
108
|
+
script_tag("var _rollbarConfig = #{config[:options].to_json};")
|
109
|
+
end
|
110
|
+
|
111
|
+
def snippet_js_tag
|
112
|
+
script_tag(js_snippet)
|
113
|
+
end
|
114
|
+
|
115
|
+
def js_snippet
|
116
|
+
SNIPPET
|
117
|
+
end
|
118
|
+
|
119
|
+
def script_tag(content)
|
120
|
+
html_safe_if_needed("\n<script type=\"text/javascript\">#{content}</script>")
|
121
|
+
end
|
122
|
+
|
123
|
+
def html_safe_if_needed(string)
|
124
|
+
string = string.html_safe if string.respond_to?(:html_safe)
|
125
|
+
string
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|