rollbar 2.10.0 → 2.11.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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +7 -2
  3. data/CHANGELOG.md +20 -0
  4. data/README.md +73 -16
  5. data/docs/configuration.md +10 -0
  6. data/gemfiles/rails30.gemfile +2 -0
  7. data/gemfiles/rails31.gemfile +2 -0
  8. data/gemfiles/rails32.gemfile +2 -0
  9. data/gemfiles/rails40.gemfile +2 -0
  10. data/gemfiles/rails41.gemfile +2 -0
  11. data/gemfiles/rails42.gemfile +2 -0
  12. data/gemfiles/rails50.gemfile +2 -0
  13. data/gemfiles/ruby_1_8_and_1_9_2.gemfile +43 -0
  14. data/lib/rollbar.rb +139 -353
  15. data/lib/rollbar/configuration.rb +4 -0
  16. data/lib/rollbar/item.rb +225 -0
  17. data/lib/rollbar/item/backtrace.rb +97 -0
  18. data/lib/rollbar/js.rb +0 -28
  19. data/lib/rollbar/language_support.rb +10 -0
  20. data/lib/rollbar/{js/middleware.rb → middleware/js.rb} +3 -4
  21. data/lib/rollbar/plugin.rb +63 -0
  22. data/lib/rollbar/plugins.rb +41 -0
  23. data/lib/rollbar/{active_job.rb → plugins/active_job.rb} +0 -0
  24. data/lib/rollbar/plugins/basic_socket.rb +16 -0
  25. data/lib/rollbar/plugins/delayed_job.rb +12 -0
  26. data/lib/rollbar/plugins/delayed_job/job_data.rb +16 -0
  27. data/lib/rollbar/{delayed_job.rb → plugins/delayed_job/plugin.rb} +1 -17
  28. data/lib/rollbar/plugins/goalie.rb +46 -0
  29. data/lib/rollbar/plugins/rack.rb +16 -0
  30. data/lib/rollbar/plugins/rails.rb +77 -0
  31. data/lib/rollbar/{rails → plugins/rails}/controller_methods.rb +0 -0
  32. data/lib/rollbar/plugins/rails/railtie30.rb +17 -0
  33. data/lib/rollbar/plugins/rails/railtie32.rb +18 -0
  34. data/lib/rollbar/plugins/rails/railtie_mixin.rb +33 -0
  35. data/lib/rollbar/plugins/rake.rb +45 -0
  36. data/lib/rollbar/plugins/sidekiq.rb +35 -0
  37. data/lib/rollbar/{sidekiq.rb → plugins/sidekiq/plugin.rb} +0 -18
  38. data/lib/rollbar/plugins/thread.rb +13 -0
  39. data/lib/rollbar/plugins/validations.rb +33 -0
  40. data/lib/rollbar/request_data_extractor.rb +30 -18
  41. data/lib/rollbar/scrubbers/params.rb +4 -2
  42. data/lib/rollbar/scrubbers/url.rb +30 -28
  43. data/lib/rollbar/util.rb +10 -0
  44. data/lib/rollbar/version.rb +1 -1
  45. data/spec/controllers/home_controller_spec.rb +4 -3
  46. data/spec/dummyapp/app/models/post.rb +9 -0
  47. data/spec/dummyapp/app/models/user.rb +2 -0
  48. data/spec/dummyapp/config/initializers/rollbar.rb +1 -0
  49. data/spec/fixtures/plugins/dummy1.rb +5 -0
  50. data/spec/fixtures/plugins/dummy2.rb +5 -0
  51. data/spec/rollbar/item_spec.rb +635 -0
  52. data/spec/rollbar/logger_proxy_spec.rb +4 -0
  53. data/spec/rollbar/{js/middleware_spec.rb → middleware/js_spec.rb} +32 -3
  54. data/spec/rollbar/plugin_spec.rb +147 -0
  55. data/spec/rollbar/{active_job_spec.rb → plugins/active_job_spec.rb} +0 -1
  56. data/spec/rollbar/{delayed_job → plugins/delayed_job}/job_data.rb +0 -0
  57. data/spec/rollbar/{delayed_job_spec.rb → plugins/delayed_job_spec.rb} +3 -6
  58. data/spec/rollbar/{middleware/rack/builder_spec.rb → plugins/rack_spec.rb} +2 -1
  59. data/spec/rollbar/{js/frameworks/rails_spec.rb → plugins/rails_js_spec.rb} +1 -1
  60. data/spec/rollbar/{rake_spec.rb → plugins/rake_spec.rb} +2 -1
  61. data/spec/rollbar/{sidekiq_spec.rb → plugins/sidekiq_spec.rb} +2 -1
  62. data/spec/rollbar/plugins/validations_spec.rb +43 -0
  63. data/spec/rollbar/plugins_spec.rb +68 -0
  64. data/spec/rollbar/request_data_extractor_spec.rb +56 -10
  65. data/spec/rollbar/scrubbers/params_spec.rb +13 -10
  66. data/spec/rollbar/scrubbers/url_spec.rb +17 -12
  67. data/spec/rollbar/sidekig/clear_scope_spec.rb +2 -1
  68. data/spec/rollbar/util_spec.rb +61 -0
  69. data/spec/rollbar_bc_spec.rb +10 -10
  70. data/spec/rollbar_spec.rb +57 -706
  71. data/spec/spec_helper.rb +8 -0
  72. data/spec/support/notifier_helpers.rb +1 -0
  73. data/spec/support/rollbar_api.rb +57 -0
  74. metadata +57 -33
  75. data/lib/rollbar/active_record_extension.rb +0 -14
  76. data/lib/rollbar/core_ext/basic_socket.rb +0 -7
  77. data/lib/rollbar/core_ext/thread.rb +0 -9
  78. data/lib/rollbar/goalie.rb +0 -33
  79. data/lib/rollbar/js/frameworks.rb +0 -6
  80. data/lib/rollbar/js/frameworks/rails.rb +0 -49
  81. data/lib/rollbar/js/version.rb +0 -5
  82. data/lib/rollbar/rack.rb +0 -9
  83. data/lib/rollbar/railtie.rb +0 -46
  84. data/lib/rollbar/rake.rb +0 -40
@@ -31,7 +31,9 @@ module Rollbar
31
31
  attr_accessor :person_email_method
32
32
  attr_accessor :populate_empty_backtraces
33
33
  attr_accessor :report_dj_data
34
+ attr_accessor :open_timeout
34
35
  attr_accessor :request_timeout
36
+ attr_accessor :net_retries
35
37
  attr_accessor :root
36
38
  attr_accessor :js_options
37
39
  attr_accessor :js_enabled
@@ -88,7 +90,9 @@ module Rollbar
88
90
  @project_gems = []
89
91
  @populate_empty_backtraces = false
90
92
  @report_dj_data = true
93
+ @open_timeout = 3
91
94
  @request_timeout = 3
95
+ @net_retries = 3
92
96
  @js_enabled = false
93
97
  @js_options = {}
94
98
  @scrub_fields = [:passwd, :password, :password_confirmation, :secret,
@@ -0,0 +1,225 @@
1
+ require 'socket'
2
+ require 'forwardable'
3
+
4
+ begin
5
+ require 'securerandom'
6
+ rescue LoadError
7
+ nil
8
+ end
9
+
10
+ require 'rollbar/item/backtrace'
11
+ require 'rollbar/util'
12
+ require 'rollbar/encoding'
13
+
14
+ module Rollbar
15
+ # This class represents the payload to be sent to the API.
16
+ # It contains the logic to build the payload, trucante it
17
+ # and dump the JSON.
18
+ class Item
19
+ extend Forwardable
20
+
21
+ attr_writer :payload
22
+
23
+ attr_reader :level
24
+ attr_reader :message
25
+ attr_reader :exception
26
+ attr_reader :extra
27
+
28
+ attr_reader :configuration
29
+ attr_reader :scope
30
+ attr_reader :logger
31
+ attr_reader :notifier
32
+
33
+ def_delegators :payload, :[]
34
+
35
+ class << self
36
+ def build_with(payload, options = {})
37
+ new(options).tap do |item|
38
+ item.payload = payload
39
+ end
40
+ end
41
+ end
42
+
43
+ def initialize(options)
44
+ @level = options[:level]
45
+ @message = options[:message]
46
+ @exception = options[:exception]
47
+ @extra = options[:extra]
48
+ @configuration = options[:configuration]
49
+ @logger = options[:logger]
50
+ @scope = options[:scope]
51
+ @payload = nil
52
+ @notifier = options[:notifier]
53
+ end
54
+
55
+ def payload
56
+ @payload ||= build
57
+ end
58
+
59
+ def build
60
+ data = build_data
61
+ self.payload = {
62
+ 'access_token' => configuration.access_token,
63
+ 'data' => data
64
+ }
65
+
66
+ enforce_valid_utf8
67
+ transform
68
+ payload
69
+ end
70
+
71
+ def build_data
72
+ data = {
73
+ :timestamp => Time.now.to_i,
74
+ :environment => build_environment,
75
+ :level => level,
76
+ :language => 'ruby',
77
+ :framework => configuration.framework,
78
+ :server => server_data,
79
+ :notifier => {
80
+ :name => 'rollbar-gem',
81
+ :version => VERSION
82
+ },
83
+ :body => build_body
84
+ }
85
+ data[:project_package_paths] = configuration.project_gem_paths if configuration.project_gem_paths
86
+ data[:code_version] = configuration.code_version if configuration.code_version
87
+ data[:uuid] = SecureRandom.uuid if defined?(SecureRandom) && SecureRandom.respond_to?(:uuid)
88
+
89
+ Util.deep_merge(data, configuration.payload_options)
90
+ Util.deep_merge(data, scope)
91
+
92
+ # Our API doesn't allow null context values, so just delete
93
+ # the key if value is nil.
94
+ data.delete(:context) unless data[:context]
95
+
96
+ data
97
+ end
98
+
99
+ def dump
100
+ # Ensure all keys are strings since we can receive the payload inline or
101
+ # from an async handler job, which can be serialized.
102
+ stringified_payload = Util::Hash.deep_stringify_keys(payload)
103
+ result = Truncation.truncate(stringified_payload)
104
+ return result unless Truncation.truncate?(result)
105
+
106
+ original_size = Rollbar::JSON.dump(payload).bytesize
107
+ final_size = result.bytesize
108
+ notifier.send_failsafe("Could not send payload due to it being too large after truncating attempts. Original size: #{original_size} Final size: #{final_size}", nil)
109
+ logger.error("[Rollbar] Payload too large to be sent: #{Rollbar::JSON.dump(payload)}")
110
+
111
+ nil
112
+ end
113
+
114
+ def ignored?
115
+ data = payload['data']
116
+
117
+ return unless data[:person]
118
+
119
+ person_id = data[:person][configuration.person_id_method.to_sym]
120
+ configuration.ignored_person_ids.include?(person_id)
121
+ end
122
+
123
+ private
124
+
125
+ def build_environment
126
+ env = configuration.environment
127
+ env = 'unspecified' if env.nil? || env.empty?
128
+
129
+ env
130
+ end
131
+
132
+ def build_body
133
+ exception ? build_backtrace_body : build_message_body
134
+ end
135
+
136
+ def build_backtrace_body
137
+ backtrace = Backtrace.new(exception,
138
+ :message => message,
139
+ :extra => build_extra,
140
+ :configuration => configuration
141
+ )
142
+
143
+ backtrace.build
144
+ end
145
+
146
+ def build_extra
147
+ if custom_data_method?
148
+ Util.deep_merge(custom_data, extra || {})
149
+ else
150
+ extra
151
+ end
152
+ end
153
+
154
+ def custom_data_method?
155
+ !!configuration.custom_data_method
156
+ end
157
+
158
+ def custom_data
159
+ data = configuration.custom_data_method.call
160
+ Rollbar::Util.deep_copy(data)
161
+ rescue => e
162
+ return {} if configuration.safely?
163
+
164
+ report_custom_data_error(e)
165
+ end
166
+
167
+ def report_custom_data_error(e)
168
+ data = notifier.safely.error(e)
169
+
170
+ return {} unless data.is_a?(Hash) && data[:uuid]
171
+
172
+ uuid_url = Util.uuid_rollbar_url(data, configuration)
173
+
174
+ { :_error_in_custom_data_method => uuid_url }
175
+ end
176
+
177
+ def build_message_body
178
+ extra = build_extra
179
+ result = { :body => message || 'Empty message' }
180
+ result[:extra] = extra if extra
181
+
182
+ { :message => result }
183
+ end
184
+
185
+ def server_data
186
+ data = {
187
+ :host => Socket.gethostname
188
+ }
189
+ data[:root] = configuration.root.to_s if configuration.root
190
+ data[:branch] = configuration.branch if configuration.branch
191
+ data[:pid] = Process.pid
192
+
193
+ data
194
+ end
195
+
196
+ def enforce_valid_utf8
197
+ Util.enforce_valid_utf8(payload)
198
+ end
199
+
200
+ def transform
201
+ handlers = configuration.transform
202
+
203
+ handlers.each do |handler|
204
+ begin
205
+ handler.call(transform_options)
206
+ rescue => e
207
+ logger.error("[Rollbar] Error calling the `transform` hook: #{e}")
208
+
209
+ break
210
+ end
211
+ end
212
+ end
213
+
214
+ def transform_options
215
+ {
216
+ :level => level,
217
+ :scope => scope,
218
+ :exception => exception,
219
+ :message => message,
220
+ :extra => extra,
221
+ :payload => payload
222
+ }
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,97 @@
1
+ module Rollbar
2
+ class Item
3
+ class Backtrace
4
+ attr_reader :exception
5
+ attr_reader :message
6
+ attr_reader :extra
7
+ attr_reader :configuration
8
+
9
+ def initialize(exception, options = {})
10
+ @exception = exception
11
+ @message = options[:message]
12
+ @extra = options[:extra]
13
+ @configuration = options[:configuration]
14
+ end
15
+
16
+ def build
17
+ traces = trace_chain
18
+
19
+ traces[0][:exception][:description] = message if message
20
+ traces[0][:extra] = extra if extra
21
+
22
+ if traces.size > 1
23
+ { :trace_chain => traces }
24
+ elsif traces.size == 1
25
+ { :trace => traces[0] }
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def trace_chain
32
+ exception
33
+ traces = [trace_data(exception)]
34
+ visited = [exception]
35
+
36
+ current_exception = exception
37
+
38
+ while current_exception.respond_to?(:cause) && (cause = current_exception.cause) && cause.is_a?(Exception) && !visited.include?(cause)
39
+ traces << trace_data(cause)
40
+ visited << cause
41
+ current_exception = cause
42
+ end
43
+
44
+ traces
45
+ end
46
+
47
+ def trace_data(current_exception)
48
+ frames = reduce_frames(current_exception)
49
+ # reverse so that the order is as rollbar expects
50
+ frames.reverse!
51
+
52
+ {
53
+ :frames => frames,
54
+ :exception => {
55
+ :class => current_exception.class.name,
56
+ :message => current_exception.message
57
+ }
58
+ }
59
+ end
60
+
61
+ def reduce_frames(current_exception)
62
+ exception_backtrace(current_exception).map do |frame|
63
+ # parse the line
64
+ match = frame.match(/(.*):(\d+)(?::in `([^']+)')?/)
65
+
66
+ if match
67
+ { :filename => match[1], :lineno => match[2].to_i, :method => match[3] }
68
+ else
69
+ { :filename => '<unknown>', :lineno => 0, :method => frame }
70
+ end
71
+ end
72
+ end
73
+
74
+ # Returns the backtrace to be sent to our API. There are 3 options:
75
+ #
76
+ # 1. The exception received has a backtrace, then that backtrace is returned.
77
+ # 2. configuration.populate_empty_backtraces is disabled, we return [] here
78
+ # 3. The user has configuration.populate_empty_backtraces is enabled, then:
79
+ #
80
+ # We want to send the caller as backtrace, but the first lines of that array
81
+ # are those from the user's Rollbar.error line until this method. We want
82
+ # to remove those lines.
83
+ def exception_backtrace(current_exception)
84
+ return current_exception.backtrace if current_exception.backtrace.respond_to?(:map)
85
+ return [] unless configuration.populate_empty_backtraces
86
+
87
+ caller_backtrace = caller
88
+ caller_backtrace.shift while caller_backtrace[0].include?(rollbar_lib_gem_dir)
89
+ caller_backtrace
90
+ end
91
+
92
+ def rollbar_lib_gem_dir
93
+ Gem::Specification.find_by_name('rollbar').gem_dir + '/lib'
94
+ end
95
+ end
96
+ end
97
+ end
data/lib/rollbar/js.rb CHANGED
@@ -1,32 +1,4 @@
1
- require "rollbar/js/version"
2
-
3
1
  module Rollbar
4
2
  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
3
  end
32
4
  end
@@ -26,10 +26,20 @@ module Rollbar
26
26
  version?('1.8')
27
27
  end
28
28
 
29
+ def ruby_19?
30
+ version?('1.9')
31
+ end
32
+
29
33
  def version?(version)
30
34
  numbers = version.split('.')
31
35
 
32
36
  numbers == ::RUBY_VERSION.split('.')[0, numbers.size]
33
37
  end
38
+
39
+ def timeout_exceptions
40
+ return [] if ruby_18? || ruby_19?
41
+
42
+ [Net::ReadTimeout, Net::OpenTimeout]
43
+ end
34
44
  end
35
45
  end
@@ -1,10 +1,9 @@
1
1
  require 'rack'
2
2
  require 'rack/response'
3
3
 
4
-
5
4
  module Rollbar
6
- module Js
7
- class Middleware
5
+ module Middleware
6
+ class Js
8
7
  attr_reader :app
9
8
  attr_reader :config
10
9
 
@@ -117,7 +116,7 @@ module Rollbar
117
116
  end
118
117
 
119
118
  def script_tag(content, env)
120
- if defined?(::SecureHeaders)
119
+ if defined?(::SecureHeaders) && ::SecureHeaders.respond_to?(:content_security_policy_script_nonce)
121
120
  nonce = ::SecureHeaders.content_security_policy_script_nonce(::Rack::Request.new(env))
122
121
  script_tag_content = "\n<script type=\"text/javascript\" nonce=\"#{nonce}\">#{content}</script>"
123
122
  else
@@ -0,0 +1,63 @@
1
+ module Rollbar
2
+ # Represents a plugin in the gem. Every plugin can have multiple dependencies
3
+ # and multiple execution blocks.
4
+ # On Rollbar initialization, all plugins will be saved in memory and those that
5
+ # satisfy the dependencies will be loaded
6
+ class Plugin
7
+ attr_reader :name
8
+ attr_reader :dependencies
9
+ attr_reader :callables
10
+ attr_accessor :loaded
11
+
12
+ private :loaded=
13
+
14
+ def initialize(name)
15
+ @name = name
16
+ @dependencies = []
17
+ @callables = []
18
+ @loaded = false
19
+ end
20
+
21
+ def configuration
22
+ Rollbar.configuration
23
+ end
24
+
25
+ def load!
26
+ return unless load?
27
+
28
+ begin
29
+ callables.each(&:call)
30
+ rescue => e
31
+ log_loading_error(e)
32
+ ensure
33
+ self.loaded = true
34
+ end
35
+ end
36
+
37
+ def execute(&block)
38
+ callables << block
39
+ end
40
+
41
+ def execute!(&block)
42
+ block.call if load?
43
+ end
44
+
45
+ private
46
+
47
+ def dependency(&block)
48
+ dependencies << block
49
+ end
50
+
51
+ def load?
52
+ !loaded && dependencies.all?(&:call)
53
+ rescue => e
54
+ log_loading_error(e)
55
+
56
+ false
57
+ end
58
+
59
+ def log_loading_error(e)
60
+ Rollbar.log_error("Error trying to load plugin '#{name}': #{e.class}, #{e.message}")
61
+ end
62
+ end
63
+ end