rollbar 2.10.0 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.
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