honeybadger 2.6.1 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d602a9205496510494a04ef272fd244394280071
4
- data.tar.gz: a2a9ef904df4c7aae69a2a5ecd7aedcf520ebd81
3
+ metadata.gz: bfe22be20c43fd975d126fc91fb7cb898c670757
4
+ data.tar.gz: 23b9996827b81249114e8c87b016d00f01f08762
5
5
  SHA512:
6
- metadata.gz: 4f437187a727ce3cc61542e0df3a64dbd7e0af43fcee10e8346a9476f0a78f1a50225a0a3e3861090f5de16e33362c2f7bcc4e3c5ee13da0bab780abab5cffb9
7
- data.tar.gz: 84a063b994896e85b8a10024b60cb79674e6d904ffcdb3412445afdf4d549d85a6885ad294f6cc3c8f36f93f6ba927b6ab9e3ff76222d422dfe9c0c62d067771
6
+ metadata.gz: 6d5319144574a8f37e9256aa3786162a185da7abdaf2805844e9b100a4952659a53edce559b3e7c03b51a211e16cc2817338a512f35f159c19ec0a875192a3da
7
+ data.tar.gz: b15017e7866fb9c3acc9d09543644d088b5914a1f671fa85bdbfaf7665b20a2755f1fee272614370536efe5e2bb98d6911584d934347a054a11f3120964fba27
data/CHANGELOG.md CHANGED
@@ -5,6 +5,15 @@ adheres to [Semantic Versioning](http://semver.org/).
5
5
 
6
6
  ## [Unreleased][unreleased]
7
7
 
8
+ ## [2.7.0] - 2016-10-20
9
+ ### Added
10
+ - Support Sinatra 2.0.
11
+
12
+ ### Changed
13
+ - Sunset performance metrics. See
14
+ http://blog.honeybadger.io/sunsetting-performance-metrics/
15
+ - Backtraces are now limited to a maximum of 1000 lines.
16
+
8
17
  ## [2.6.1] - 2016-08-24
9
18
  ### Added
10
19
  - shoryuken plugin. -@ivanvc
data/README.md CHANGED
@@ -132,7 +132,6 @@ Honeybadger.start(honeybadger_config)
132
132
 
133
133
  # And use Honeybadger's rack middleware
134
134
  use Honeybadger::Rack::ErrorNotifier, honeybadger_config
135
- use Honeybadger::Rack::MetricsReporter, honeybadger_config
136
135
 
137
136
  run app
138
137
  ```
@@ -222,8 +221,8 @@ You can use any of the options below in your config file, or in the environment.
222
221
  |`root` | String | The project's absolute root path.<br/>_Default: `Dir.pwd`_|
223
222
  |`hostname` | String | The hostname of the current box.<br/>_Default: `Socket.gethostname`_|
224
223
  |`backend` | String | An alternate backend to use for reporting data.<br/>_Default: `nil`_|
225
- |`debug` | Boolean | Forces metrics and traces to be reported every 10 seconds rather than 60, and enables verbose debug logging.<br/>_Default: `false`_|
226
- |`send_data_at_exit` | Boolean | Finish sending enqueued exceptions and metrics data before allowing program to exit.<br/>_Default: `true`_|
224
+ |`debug` | Boolean | Enables verbose debug logging.<br/>_Default: `false`_|
225
+ |`send_data_at_exit` | Boolean | Finish sending enqueued exceptions before allowing program to exit.<br/>_Default: `true`_|
227
226
  |`disabled` | Boolean | Prevents Honeybadger from starting entirely.<br/>_Default: `false`_|
228
227
  | `config_path` | String | The path of the honeybadger config file. Can only be set via the `$HONEYBADGER_CONFIG_PATH` environment variable |
229
228
  |`development_environments` | Array | Environments which will not report data by default (use report_data to enable/disable explicitly).<br/>_Default: `["development", "test", "cucumber"]`_|
@@ -269,13 +268,6 @@ You can use any of the options below in your config file, or in the environment.
269
268
  |`exceptions.local_variables` | Boolean | Enable sending local variables. Requires the [binding_of_caller gem](https://rubygems.org/gems/binding_of_caller).<br/>_Default: `false`_|
270
269
  |`exceptions.unwrap` | Boolean | Reports #original_exception or #cause one level up from rescued exception when available.<br/>_Default: `false`_|
271
270
  |&nbsp; | ||
272
- |__METRIC REPORTING__ | ||
273
- |`metrics.enabled` | Boolean | Enable sending metrics, such as requests per minute.<br/>_Default: `true`_|
274
- |`metrics.gc_profiler` | Boolean | Enable sending GC metrics (GC::Profiler must be enabled)<br/>_Default: `false`_|
275
- |&nbsp; | ||
276
- |__TRACE REPORTING__ | ||
277
- |`traces.enabled` | Boolean | Enable sending performance traces for slow actions.<br/>_Default: `true`_|
278
- |`traces.threshold` | Integer | The threshold in seconds to send traces.<br/>_Default: `2000`_|
279
271
  |__SIDEKIQ__ | ||
280
272
  |`sidekiq.attempt_threshold` | Integer | The number of attempts before notifications will be sent.<br/>_Default: `0`_|
281
273
  |`sidekiq.use_component` | Boolean | Automatically set the component to the class of the job. Helps with grouping.<br/>_Default: `false`_|
@@ -12,7 +12,7 @@ module Honeybadger
12
12
 
13
13
  include Honeybadger::Logging::Helper
14
14
 
15
- # Internal: See Agent::Thread
15
+ # Internal: Sub-class thread so we have a named thread (useful for debugging in Thread.list).
16
16
  class Thread < ::Thread; end
17
17
 
18
18
  # Internal: A queue which enforces a maximum size.
@@ -52,6 +52,8 @@ module Honeybadger
52
52
  #
53
53
  # Returns true.
54
54
  def shutdown
55
+ d { sprintf('shutting down worker feature=%s', feature) }
56
+
55
57
  mutex.synchronize do
56
58
  @shutdown = true
57
59
  @pid = nil
@@ -13,14 +13,8 @@ module Honeybadger
13
13
 
14
14
  include Logging::Helper
15
15
 
16
- # Internal: Sub-class thread so we have a named thread (useful for debugging in Thread.list).
17
- class Thread < ::Thread; end
18
-
19
16
  autoload :Worker, 'honeybadger/agent/worker'
20
17
  autoload :NullWorker, 'honeybadger/agent/worker'
21
- autoload :Batch, 'honeybadger/agent/batch'
22
- autoload :MetricsCollector, 'honeybadger/agent/metrics_collector'
23
- autoload :TraceCollection, 'honeybadger/agent/trace_collection'
24
18
 
25
19
  class << self
26
20
  extend Forwardable
@@ -83,19 +77,7 @@ module Honeybadger
83
77
  end
84
78
 
85
79
  def self.fork(*args)
86
- self.instance ? self.instance.fork(*args) : false
87
- end
88
-
89
- def self.trace(*args)
90
- self.instance ? self.instance.trace(*args) : false
91
- end
92
-
93
- def self.timing(*args)
94
- self.instance ? self.instance.timing(*args) : false
95
- end
96
-
97
- def self.increment(*args)
98
- self.instance ? self.instance.increment(*args) : false
80
+ # noop
99
81
  end
100
82
 
101
83
  def self.flush(&block)
@@ -132,21 +114,17 @@ module Honeybadger
132
114
  end
133
115
  end
134
116
 
135
- attr_reader :delay, :workers, :pid, :thread, :traces, :metrics
117
+ attr_reader :workers
136
118
 
137
119
  def initialize(config)
138
120
  @config = config
139
- @delay = config.debug? ? 10 : 60
140
121
  @mutex = Mutex.new
141
- @pid = Process.pid
142
122
 
143
123
  unless config.backend.kind_of?(Backend::Server)
144
124
  warn('Initializing development backend: data will not be reported.')
145
125
  end
146
126
 
147
127
  init_workers
148
- init_traces
149
- init_metrics
150
128
 
151
129
  at_exit do
152
130
  # Fix for https://bugs.ruby-lang.org/issues/5218
@@ -162,38 +140,7 @@ module Honeybadger
162
140
  end
163
141
  end
164
142
 
165
- # Internal: Spawn the agent thread. This method is idempotent.
166
- #
167
- # Returns false if the Agent is stopped, otherwise true.
168
- def start
169
- mutex.synchronize do
170
- return false unless pid
171
- return true if thread && thread.alive?
172
-
173
- debug { 'starting agent' }
174
-
175
- @pid = Process.pid
176
- @thread = Thread.new { run }
177
- end
178
-
179
- true
180
- end
181
-
182
143
  def stop(force = false)
183
- debug { 'stopping agent' }
184
-
185
- mutex.synchronize do
186
- @pid = nil
187
- end
188
-
189
- # Kill the collector
190
- Thread.kill(thread) if thread
191
-
192
- unless force
193
- flush_traces
194
- flush_metrics
195
- end
196
-
197
144
  workers.each_pair do |key, worker|
198
145
  worker.send(force ? :shutdown! : :shutdown)
199
146
  end
@@ -201,10 +148,6 @@ module Honeybadger
201
148
  true
202
149
  end
203
150
 
204
- def fork
205
- # noop
206
- end
207
-
208
151
  def notice(opts)
209
152
  opts.merge!(callbacks: self.class.callbacks)
210
153
  notice = Notice.new(config, opts)
@@ -219,44 +162,6 @@ module Honeybadger
219
162
  end
220
163
  end
221
164
 
222
- def trace(trace)
223
- return false unless config.traces?
224
-
225
- start
226
-
227
- if trace.duration > config[:'traces.threshold']
228
- debug { sprintf('agent adding trace duration=%s feature=traces id=%s', trace.duration.round(2), trace.id) }
229
- mutex.synchronize { traces.push(trace) }
230
- flush_traces if traces.flush?
231
- true
232
- else
233
- debug { sprintf('agent discarding trace duration=%s feature=traces id=%s', trace.duration.round(2), trace.id) }
234
- false
235
- end
236
- end
237
-
238
- def timing(*args, &block)
239
- return false unless config.metrics?
240
-
241
- start
242
-
243
- mutex.synchronize { metrics.timing(*args, &block) }
244
- flush_metrics if metrics.flush?
245
-
246
- true
247
- end
248
-
249
- def increment(*args, &block)
250
- return false unless config.metrics?
251
-
252
- start
253
-
254
- mutex.synchronize { metrics.increment(*args, &block) }
255
- flush_metrics if metrics.flush?
256
-
257
- true
258
- end
259
-
260
165
  # Internal: Flush the workers. See Honeybadger#flush.
261
166
  #
262
167
  # block - an option block which is executed before flushing data.
@@ -266,8 +171,6 @@ module Honeybadger
266
171
  return true unless block_given?
267
172
  yield
268
173
  ensure
269
- flush_metrics
270
- flush_traces
271
174
  workers.values.each(&:flush)
272
175
  end
273
176
 
@@ -286,62 +189,9 @@ module Honeybadger
286
189
  true
287
190
  end
288
191
 
289
- def run
290
- loop { work }
291
- rescue Exception => e
292
- error {
293
- msg = "error in agent thread (shutting down) class=%s message=%s\n\t%s"
294
- sprintf(msg, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
295
- }
296
- ensure
297
- d { sprintf('stopping agent') }
298
- end
299
-
300
- def work
301
- flush_metrics if metrics.flush?
302
- flush_traces if traces.flush?
303
- sleep(delay)
304
- rescue StandardError => e
305
- error {
306
- msg = "error in agent thread class=%s message=%s\n\t%s"
307
- sprintf(msg, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
308
- }
309
- sleep(delay)
310
- end
311
-
312
192
  def init_workers
313
193
  @workers = Hash.new(NullWorker.new)
314
194
  workers[:notices] = Worker.new(config, :notices)
315
- workers[:traces] = Worker.new(config, :traces)
316
- workers[:metrics] = Worker.new(config, :metrics)
317
- end
318
-
319
- def init_traces
320
- @traces = Batch.new(config, :traces, max: 20, interval: config.debug? ? 10 : 60, collection: TraceCollection.new)
321
- end
322
-
323
- def init_metrics
324
- @metrics = MetricsCollector.new(config, config.debug? ? 10 : 60)
325
- end
326
-
327
- def flush_metrics
328
- mutex.synchronize do
329
- if (count = metrics.size) > 0
330
- debug { sprintf('agent flushing metrics feature=metrics count=%d', count) }
331
- end
332
- metrics.chunk(100, &method(:push).to_proc.curry[:metrics])
333
- init_metrics
334
- end
335
- end
336
-
337
- def flush_traces
338
- mutex.synchronize do
339
- if (count = traces.size) > 0
340
- debug { sprintf('agent flushing traces feature=traces count=%d', count) }
341
- end
342
- push(:traces, traces) unless traces.empty?
343
- init_traces
344
- end
345
195
  end
346
196
 
347
197
  def notify_at_exit(ex)
@@ -60,7 +60,7 @@ module Honeybadger
60
60
  # Internal: Process payload for feature.
61
61
  #
62
62
  # feature - A Symbol feature name (corresponds to HTTP endpoint). Current
63
- # options are: :notices, :metrics, :traces.
63
+ # options are: :notices, :deploys, :ping
64
64
  # payload - Any Object responding to #to_json.
65
65
  #
66
66
  # Examples:
@@ -12,8 +12,6 @@ module Honeybadger
12
12
  ENDPOINTS = {
13
13
  ping: '/v1/ping'.freeze,
14
14
  notices: '/v1/notices'.freeze,
15
- metrics: '/v1/metrics'.freeze,
16
- traces: '/v1/traces'.freeze,
17
15
  deploys: '/v1/deploys'.freeze
18
16
  }.freeze
19
17
 
@@ -39,20 +39,22 @@ module Honeybadger
39
39
  if filtered_line
40
40
  _, file, number, method = unparsed_line.match(INPUT_FORMAT).to_a
41
41
  _, *filtered_args = filtered_line.match(INPUT_FORMAT).to_a
42
- new(file, number, method, *filtered_args)
42
+ new(file, number, method, *filtered_args, opts.fetch(:source_radius, 2))
43
43
  else
44
44
  nil
45
45
  end
46
46
  end
47
47
 
48
48
  def initialize(file, number, method, filtered_file = file,
49
- filtered_number = number, filtered_method = method)
49
+ filtered_number = number, filtered_method = method,
50
+ source_radius = 2)
50
51
  self.filtered_file = filtered_file
51
52
  self.filtered_number = filtered_number
52
53
  self.filtered_method = filtered_method
53
54
  self.file = file
54
55
  self.number = number
55
56
  self.method = method
57
+ self.source_radius = source_radius
56
58
  end
57
59
 
58
60
  # Reconstructs the line in a readable fashion.
@@ -73,16 +75,16 @@ module Honeybadger
73
75
  (filtered_file =~ /^\[PROJECT_ROOT\]/i) && !(filtered_file =~ /^\[PROJECT_ROOT\]\/vendor/i)
74
76
  end
75
77
 
76
- # An excerpt from the source file, lazily loaded to preserve
77
- # performance.
78
- def source(radius = 2)
79
- @source ||= get_source(file, number, radius)
78
+ def source
79
+ @source ||= get_source(file, number, source_radius)
80
80
  end
81
81
 
82
82
  private
83
83
 
84
84
  attr_writer :file, :number, :method, :filtered_file, :filtered_number, :filtered_method
85
85
 
86
+ attr_accessor :source_radius
87
+
86
88
  # Open source file and read line(s).
87
89
  #
88
90
  # Returns an array of line(s) from source file.
@@ -126,7 +128,7 @@ module Honeybadger
126
128
  #
127
129
  # Returns array containing backtrace lines.
128
130
  def to_ary
129
- lines.map { |l| { :number => l.filtered_number, :file => l.filtered_file, :method => l.filtered_method } }
131
+ lines.take(1000).map { |l| { :number => l.filtered_number, :file => l.filtered_file, :method => l.filtered_method, :source => l.source } }
130
132
  end
131
133
  alias :to_a :to_ary
132
134
 
@@ -57,7 +57,7 @@ module Honeybadger
57
57
  type: String
58
58
  },
59
59
  debug: {
60
- description: 'Forces metrics and traces to be reported every 10 seconds rather than 60.',
60
+ description: 'Enables debug logging.',
61
61
  default: false,
62
62
  type: Boolean
63
63
  },
@@ -256,26 +256,6 @@ module Honeybadger
256
256
  default: false,
257
257
  type: Boolean
258
258
  },
259
- :'metrics.enabled' => {
260
- description: 'Enable sending metrics.',
261
- default: true,
262
- type: Boolean
263
- },
264
- :'metrics.gc_profiler' => {
265
- description: 'Enable sending GC metrics (GC::Profiler must be enabled)',
266
- default: false,
267
- type: Boolean
268
- },
269
- :'traces.enabled' => {
270
- description: 'Enable sending traces.',
271
- default: true,
272
- type: Boolean
273
- },
274
- :'traces.threshold' => {
275
- description: 'The threshold in seconds to send traces.',
276
- default: 2000,
277
- type: Integer
278
- },
279
259
  :'delayed_job.attempt_threshold' => {
280
260
  description: 'The number of attempts before notifications will be sent.',
281
261
  default: 0,
@@ -30,7 +30,7 @@ module Honeybadger
30
30
 
31
31
  NOT_BLANK = Regexp.new('\S').freeze
32
32
 
33
- FEATURES = [:notices, :local_variables, :metrics, :traces].freeze
33
+ FEATURES = [:notices, :local_variables].freeze
34
34
 
35
35
  MERGE_DEFAULT = [:'exceptions.ignore'].freeze
36
36
 
@@ -88,14 +88,6 @@ module Honeybadger
88
88
  !!features[feature.to_sym]
89
89
  end
90
90
 
91
- def traces?
92
- feature?(:traces) && !!self[:'traces.enabled']
93
- end
94
-
95
- def metrics?
96
- feature?(:metrics) && !!self[:'metrics.enabled']
97
- end
98
-
99
91
  def logger
100
92
  @logger || Logging::BootLogger.instance
101
93
  end
@@ -7,12 +7,10 @@ module Honeybadger
7
7
  autoload :Config, 'honeybadger/config'
8
8
  autoload :Logging, 'honeybadger/logging'
9
9
  autoload :Notice, 'honeybadger/notice'
10
- autoload :Trace, 'honeybadger/trace'
11
10
  autoload :Plugin, 'honeybadger/plugin'
12
11
 
13
12
  module Rack
14
13
  autoload :ErrorNotifier, 'honeybadger/rack/error_notifier'
15
- autoload :MetricsReporter, 'honeybadger/rack/metrics_reporter'
16
14
  autoload :UserFeedback, 'honeybadger/rack/user_feedback'
17
15
  autoload :UserInformer, 'honeybadger/rack/user_informer'
18
16
  autoload :RequestHash, 'honeybadger/rack/request_hash'
@@ -25,45 +25,6 @@ module Honeybadger
25
25
  middleware.insert_before(Honeybadger::Rack::ErrorNotifier, Honeybadger::Rack::UserFeedback, config) if config[:'feedback.enabled']
26
26
  end
27
27
  end
28
-
29
- if config.traces?
30
- ActiveSupport::Notifications.subscribe('start_processing.action_controller') do |name, started, finished, id, data|
31
- Trace.create(id)
32
- end
33
-
34
- ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
35
- event = ActiveSupport::Notifications::Event.new(*args)
36
- Trace.current.add_query(event) if Trace.current and event.name != 'SCHEMA'
37
- end
38
-
39
- ActiveSupport::Notifications.subscribe(/^render_(template|partial|action|collection)\.action_view/) do |*args|
40
- event = ActiveSupport::Notifications::Event.new(*args)
41
- Trace.current.add(event) if Trace.current
42
- end
43
-
44
- ActiveSupport::Notifications.subscribe('net_http.request') do |*args|
45
- event = ActiveSupport::Notifications::Event.new(*args)
46
- Trace.current.add(event) if Trace.current
47
- end
48
-
49
- ActiveSupport::Notifications.subscribe('process_action.action_controller') do |*args|
50
- event = ActiveSupport::Notifications::Event.new(*args)
51
- if Trace.current && event.payload[:controller] && event.payload[:action]
52
- payload = { source: 'web' }
53
- payload[:path] = Util::Sanitizer.new(filters: config.params_filters).filter_url(event.payload[:path]) if event.payload[:path]
54
- payload[:request] = request_data(event, config)
55
- Trace.current.complete(event, payload)
56
- end
57
- end
58
- end
59
-
60
- if config.metrics?
61
- ActiveSupport::Notifications.subscribe('process_action.action_controller') do |*args|
62
- event = ActiveSupport::Notifications::Event.new(*args)
63
- status = event.payload[:exception] ? 500 : event.payload[:status]
64
- Agent.timing("app.request.#{status}", event.duration)
65
- end
66
- end
67
28
  end
68
29
  end
69
30
 
@@ -78,27 +39,6 @@ module Honeybadger
78
39
  :framework => :rails
79
40
  }
80
41
  end
81
-
82
- def request_data(event, config)
83
- h = {
84
- url: event.payload[:path],
85
- component: event.payload[:controller],
86
- action: event.payload[:action],
87
- params: event.payload[:params]
88
- }
89
- h.merge!(config.request_hash)
90
- h.delete_if {|k,v| config.excluded_request_keys.include?(k) }
91
- h[:sanitizer] = Util::Sanitizer.new(filters: config.params_filters)
92
- Util::RequestPayload.build(h).update({
93
- context: context_data
94
- })
95
- end
96
-
97
- def context_data
98
- if Thread.current[:__honeybadger_context]
99
- Util::Sanitizer.new.sanitize(Thread.current[:__honeybadger_context])
100
- end
101
- end
102
42
  end
103
43
  end
104
44
  end
@@ -12,7 +12,8 @@ module Honeybadger
12
12
 
13
13
  def honeybadger_config(app)
14
14
  {
15
- api_key: defined?(honeybadger_api_key) ? honeybadger_api_key : nil
15
+ api_key: defined?(honeybadger_api_key) ? honeybadger_api_key : nil,
16
+ env: ENV['APP_ENV']
16
17
  }
17
18
  end
18
19
 
@@ -23,7 +24,6 @@ module Honeybadger
23
24
  return unless Honeybadger.start(config)
24
25
 
25
26
  install_honeybadger_middleware(Honeybadger::Rack::ErrorNotifier, config) if config.feature?(:notices) && config[:'exceptions.enabled']
26
- install_honeybadger_middleware(Honeybadger::Rack::MetricsReporter, config) if config.feature?(:metrics) && config[:'metrics.enabled']
27
27
  end
28
28
 
29
29
  def install_honeybadger_middleware(klass, config)
@@ -64,7 +64,7 @@ module Honeybadger
64
64
  # Public: The message from the exception, or a general description of the error.
65
65
  attr_reader :error_message
66
66
 
67
- # Public: Excerpt from source file.
67
+ # Deprecated: Excerpt from source file.
68
68
  attr_reader :source
69
69
 
70
70
  # Public: CGI variables such as HTTP_METHOD.
@@ -139,7 +139,6 @@ module Honeybadger
139
139
  "#{exception.class.name}: #{exception.message}"
140
140
  end
141
141
  @backtrace = parse_backtrace(exception_attribute(:backtrace, caller))
142
- @source = extract_source_from_backtrace(@backtrace, config, opts)
143
142
  @fingerprint = construct_fingerprint(opts)
144
143
 
145
144
  @request = construct_request_hash(config, opts)
@@ -175,7 +174,6 @@ module Honeybadger
175
174
  class: s(error_class),
176
175
  message: s(error_message),
177
176
  backtrace: s(backtrace.to_a),
178
- source: s(source),
179
177
  fingerprint: s(fingerprint),
180
178
  tags: s(tags),
181
179
  causes: s(causes)
@@ -316,16 +314,6 @@ module Honeybadger
316
314
  context.empty? ? nil : context
317
315
  end
318
316
 
319
- def extract_source_from_backtrace(backtrace, config, opts)
320
- return nil if backtrace.lines.empty?
321
-
322
- if backtrace.application_lines.any?
323
- backtrace.application_lines.first.source(config[:'exceptions.source_radius'])
324
- else
325
- backtrace.lines.first.source(config[:'exceptions.source_radius'])
326
- end
327
- end
328
-
329
317
  def fingerprint_from_opts(opts)
330
318
  callback = opts[:fingerprint]
331
319
  callback ||= opts[:callbacks] && opts[:callbacks].exception_fingerprint
@@ -398,7 +386,8 @@ module Honeybadger
398
386
  Backtrace.parse(
399
387
  backtrace,
400
388
  filters: construct_backtrace_filters(opts),
401
- config: config
389
+ config: config,
390
+ source_radius: config[:'exceptions.source_radius']
402
391
  )
403
392
  end
404
393
 
@@ -34,9 +34,7 @@ module Honeybadger
34
34
  :queue => job.queue
35
35
  )
36
36
 
37
- ::Honeybadger::Trace.instrument("#{job.payload_object.class}#perform", {source: 'delayed_job', jid: job.id, class: job.payload_object.class.name}) do
38
- block.call(job)
39
- end
37
+ block.call(job)
40
38
  rescue Exception => error
41
39
  ::Honeybadger.notify_or_ignore(
42
40
  :component => component,
@@ -8,9 +8,7 @@ module Honeybadger
8
8
  def around_perform_with_honeybadger(*args)
9
9
  Honeybadger.flush do
10
10
  begin
11
- Honeybadger::Trace.instrument("#{self.name}#perform", { source: 'resque', class: self.name }) do
12
- yield
13
- end
11
+ yield
14
12
  rescue Exception => e
15
13
  Honeybadger.notify(e, parameters: { job_arguments: args }) if send_exception?(e, args)
16
14
  raise e
@@ -11,18 +11,15 @@ module Honeybadger
11
11
  return
12
12
  end
13
13
 
14
- klass = worker.class.name
15
14
  Honeybadger.flush do
16
- Honeybadger::Trace.instrument("#{klass}#perform", { :source => 'shoryuken'.freeze, :queue => sqs_msg.queue_name.freeze, :message_id => sqs_msg.data.message_id.freeze, :class => klass }) do
17
- begin
18
- yield
19
- rescue => e
20
- receive_count = sqs_msg.attributes['ApproximateReceiveCount'.freeze]
21
- if receive_count && ::Honeybadger::Agent.config[:'shoryuken.attempt_threshold'].to_i <= receive_count.to_i
22
- Honeybadger.notify(e, parameters: body)
23
- end
24
- raise e
15
+ begin
16
+ yield
17
+ rescue => e
18
+ receive_count = sqs_msg.attributes['ApproximateReceiveCount'.freeze]
19
+ if receive_count && ::Honeybadger::Agent.config[:'shoryuken.attempt_threshold'].to_i <= receive_count.to_i
20
+ Honeybadger.notify(e, parameters: body)
25
21
  end
22
+ raise e
26
23
  end
27
24
  end
28
25
  ensure
@@ -7,10 +7,7 @@ module Honeybadger
7
7
  class Middleware
8
8
  def call(worker, msg, queue)
9
9
  Honeybadger.context.clear!
10
- klass = msg['wrapped'.freeze] || msg['class'.freeze]
11
- Honeybadger::Trace.instrument("#{klass}#perform", { :source => 'sidekiq'.freeze, :jid => msg['jid'.freeze], :class => klass }) do
12
- yield
13
- end
10
+ yield
14
11
  end
15
12
  end
16
13
 
@@ -3,38 +3,13 @@ require 'honeybadger'
3
3
  module Honeybadger
4
4
  module Rack
5
5
  class MetricsReporter
6
- GC_TIME_METRIC = 'app.gc.time'.freeze
7
- GC_COLLECTIONS_METRIC = 'app.gc.collections'.freeze
8
-
9
6
  def initialize(app, config)
10
7
  @app = app
11
- @config = config
8
+ config.logger.warn('DEPRECATION WARNING: `Honeybadger::Rack::MetricsReporter` no longer has any effect and will be removed.')
12
9
  end
13
10
 
14
11
  def call(env)
15
- start = Time.now
16
- track_gc? and GC::Profiler.clear
17
- status, headers, body = app.call(env)
18
- duration = (Time.now - start) * 1000
19
- report_metrics(status, duration)
20
- [status, headers, body]
21
- end
22
-
23
- private
24
-
25
- attr_reader :app, :config
26
-
27
- def track_gc?
28
- config[:'metrics.gc_profiler']
29
- end
30
-
31
- def report_metrics(status, duration)
32
- Agent.timing("app.request.#{status}", duration)
33
-
34
- if track_gc? && GC::Profiler.total_time > 0
35
- Agent.timing(GC_TIME_METRIC, GC::Profiler.total_time * 1000)
36
- Agent.increment(GC_COLLECTIONS_METRIC, GC::Profiler.result[/\d+/].to_i)
37
- end
12
+ @app.call(env)
38
13
  end
39
14
  end
40
15
  end
@@ -4,7 +4,7 @@ module Honeybadger
4
4
  # /v1/notices API specification.
5
5
  class RequestHash < ::Hash
6
6
  # Internal
7
- CGI_BLACKLIST = ['QUERY_STRING', 'RAW_POST_DATA'].freeze
7
+ CGI_BLACKLIST = ['QUERY_STRING', 'RAW_POST_DATA', 'ORIGINAL_FULLPATH', 'REQUEST_URI'].freeze
8
8
  CGI_KEY_REGEXP = /\A[A-Z_]+\Z/
9
9
 
10
10
  def initialize(request)
@@ -2,7 +2,7 @@ require 'honeybadger/util/sanitizer'
2
2
 
3
3
  module Honeybadger
4
4
  module Util
5
- # Internal: Constructs/sanitizes request data for notices and traces.
5
+ # Internal: Constructs/sanitizes request data for notices
6
6
  module RequestPayload
7
7
  # Internal: Default values to use for request data.
8
8
  DEFAULTS = {
@@ -1,4 +1,4 @@
1
1
  module Honeybadger
2
2
  # Public: The current String Honeybadger version.
3
- VERSION = '2.6.1'.freeze
3
+ VERSION = '2.7.0'.freeze
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: honeybadger
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.1
4
+ version: 2.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Honeybadger Industries LLC
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-24 00:00:00.000000000 Z
11
+ date: 2016-10-20 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Make managing application errors a more pleasant experience.
14
14
  email:
@@ -26,11 +26,7 @@ files:
26
26
  - lib/honeybadger.rb
27
27
  - lib/honeybadger/agent.rb
28
28
  - lib/honeybadger/agent/batch.rb
29
- - lib/honeybadger/agent/metered_queue.rb
30
- - lib/honeybadger/agent/metrics_collection.rb
31
- - lib/honeybadger/agent/metrics_collector.rb
32
29
  - lib/honeybadger/agent/null_worker.rb
33
- - lib/honeybadger/agent/trace_collection.rb
34
30
  - lib/honeybadger/agent/worker.rb
35
31
  - lib/honeybadger/backend.rb
36
32
  - lib/honeybadger/backend/base.rb
@@ -58,7 +54,6 @@ files:
58
54
  - lib/honeybadger/plugins/delayed_job.rb
59
55
  - lib/honeybadger/plugins/delayed_job/plugin.rb
60
56
  - lib/honeybadger/plugins/local_variables.rb
61
- - lib/honeybadger/plugins/net_http.rb
62
57
  - lib/honeybadger/plugins/passenger.rb
63
58
  - lib/honeybadger/plugins/rails.rb
64
59
  - lib/honeybadger/plugins/resque.rb
@@ -75,7 +70,6 @@ files:
75
70
  - lib/honeybadger/rack/user_informer.rb
76
71
  - lib/honeybadger/tasks.rb
77
72
  - lib/honeybadger/templates/feedback_form.erb
78
- - lib/honeybadger/trace.rb
79
73
  - lib/honeybadger/util/http.rb
80
74
  - lib/honeybadger/util/request_payload.rb
81
75
  - lib/honeybadger/util/sanitizer.rb
@@ -1,80 +0,0 @@
1
- module Honeybadger
2
- class Agent
3
- # Internal: A thread-safe first-in-first-out queue. Values are pushed onto
4
- # the queue and released at a defined interval.
5
- class MeteredQueue
6
- def initialize(interval = 1, max = 1000, now = now())
7
- @interval = interval
8
- @max = max
9
- @values = Array.new
10
- @throttles = Array.new
11
- @future = calculate_future(now, interval)
12
- @mutex = Mutex.new
13
- end
14
-
15
- def size
16
- mutex.synchronize { values.size }
17
- end
18
-
19
- def push(value)
20
- unless values.size == max
21
- mutex.synchronize { values.push(value) }
22
- end
23
- end
24
-
25
- def pop
26
- if now.to_i >= future
27
- mutex.synchronize do
28
- @future = calculate_future
29
- values.shift
30
- end
31
- end
32
- end
33
-
34
- def pop!
35
- mutex.synchronize { values.shift }
36
- end
37
-
38
- # Applies a new throttle to this queue and adjusts the future.
39
- #
40
- # Returns nothing
41
- def throttle(throttle)
42
- mutex.synchronize do
43
- old_interval = throttled_interval
44
- throttles << throttle
45
- @future += (throttled_interval - old_interval)
46
- end
47
- end
48
-
49
- # Removes the last throttle from this queue and adjusts the future.
50
- #
51
- # Returns Float throttle
52
- def unthrottle
53
- mutex.synchronize do
54
- old_interval = throttled_interval
55
- throttles.pop.tap do |throttle|
56
- if throttle
57
- @future -= (old_interval - throttled_interval)
58
- end
59
- end
60
- end
61
- end
62
-
63
- private
64
-
65
- attr_reader :interval, :max, :values, :throttles, :future, :mutex
66
-
67
- def now
68
- Time.now
69
- end
70
-
71
- def calculate_future(now = now(), interval = interval())
72
- now.to_i + throttled_interval(interval)
73
- end
74
-
75
- def throttled_interval(interval = interval())
76
- throttles.reduce(interval) {|a,e| a * e }
77
- end
78
- end
79
- end
80
- end
@@ -1,59 +0,0 @@
1
- module Honeybadger
2
- class Agent
3
- # This code comes from batsd
4
- class MetricsCollection < Array
5
- PERCENTILE_METHOD_SIGNATURE = /\Apercentile_(.+)\z/.freeze
6
-
7
- # Calculates the sum of values in the array
8
- def sum
9
- inject( nil ) { |sum,x| sum ? sum+x : x };
10
- end
11
-
12
- # Calculates the arithmetic mean of values in the array
13
- def mean
14
- self.sum.to_f / self.length
15
- end
16
-
17
- # Calculates the median of values in the array
18
- def median
19
- self.sort[self.length/2]
20
- end
21
-
22
- # Calculates the value of the upper percentile of values
23
- # in the array. If only a single value is provided in the array, that is
24
- # returned
25
- def percentile(threshold)
26
- return self.first unless count > 1
27
-
28
- self.sort!
29
- # strip off the top 100-threshold
30
- threshold_index = (((100 - threshold).to_f / 100) * count).round
31
- self[0..-threshold_index].last
32
- end
33
-
34
- # Calculates the mean squared error of values in the array
35
- def mean_squared
36
- m = mean
37
- self.class.new(map{|v| (v-m)**2}).sum
38
- end
39
-
40
- # Calculates the standard deviatiation of values in the array
41
- def standard_dev
42
- (mean_squared/(count-1))**0.5
43
- end
44
-
45
- # Allow [1,2,3].percentile_90, [1,2,3].percentile(75), etc.
46
- def method_missing(method, *args, &block)
47
- if method.to_s =~ PERCENTILE_METHOD_SIGNATURE
48
- percentile($1.to_i)
49
- else
50
- super
51
- end
52
- end
53
-
54
- def respond_to_missing?(method, include_private = false)
55
- method.to_s =~ PERCENTILE_METHOD_SIGNATURE or super
56
- end
57
- end
58
- end
59
- end
@@ -1,106 +0,0 @@
1
- require 'securerandom'
2
- require 'forwardable'
3
-
4
- module Honeybadger
5
- class Agent
6
- autoload :MetricsCollection, 'honeybadger/agent/metrics_collection'
7
-
8
- class MetricsCollector
9
- extend Forwardable
10
-
11
- class Chunk
12
- extend Forwardable
13
-
14
- attr_reader :id
15
-
16
- def initialize(id, metrics)
17
- @id = SecureRandom.uuid
18
- @metrics = metrics
19
- end
20
-
21
- def_delegators :@metrics, :to_json, :size
22
- end
23
-
24
- def initialize(config, interval = 60, now = now())
25
- @id = SecureRandom.uuid
26
- @config = config
27
- @interval = interval
28
- @future = now + interval
29
- @mutex = Mutex.new
30
- @metrics = { :timing => {}, :counter => {} }
31
- end
32
-
33
- attr_reader :id
34
-
35
- def_delegators :@metrics, :[]
36
-
37
- def timing(name, value)
38
- add_metric(name, value, :timing)
39
- end
40
-
41
- def increment(name, value)
42
- add_metric(name, value, :counter)
43
- end
44
-
45
- def flush?
46
- now >= future
47
- end
48
-
49
- def size
50
- mutex.synchronize do
51
- metrics.reduce(0) do |count, hash|
52
- count + hash[1].size
53
- end
54
- end
55
- end
56
-
57
- def to_a
58
- mutex.synchronize do
59
- [].tap do |m|
60
- metrics[:counter].each do |metric, values|
61
- m << "#{metric} #{values.sum}"
62
- end
63
- metrics[:timing].each do |metric, values|
64
- m << "#{metric}:mean #{values.mean}"
65
- m << "#{metric}:median #{values.median}"
66
- m << "#{metric}:percentile_90 #{values.percentile(90)}"
67
- m << "#{metric}:min #{values.min}"
68
- m << "#{metric}:max #{values.max}"
69
- m << "#{metric}:stddev #{values.standard_dev}" if values.count > 1
70
- m << "#{metric} #{values.count}"
71
- end
72
- m.compact!
73
- end
74
- end
75
- end
76
-
77
- def chunk(size)
78
- to_a.each_slice(size) do |metrics|
79
- yield(Chunk.new(id, as_json(metrics)))
80
- end
81
- end
82
-
83
- def as_json(metrics = to_a)
84
- {metrics: metrics, :environment => config[:env], :hostname => config[:hostname]}
85
- end
86
-
87
- def to_json(*args)
88
- as_json.to_json(*args)
89
- end
90
-
91
- private
92
-
93
- attr_reader :config, :future, :metrics, :mutex
94
-
95
- def now
96
- Time.now.to_i
97
- end
98
-
99
- def add_metric(name, value, kind)
100
- mutex.synchronize do
101
- (metrics[kind][name] ||= MetricsCollection.new) << value
102
- end
103
- end
104
- end
105
- end
106
- end
@@ -1,32 +0,0 @@
1
- require 'forwardable'
2
-
3
- # Internal: A collection for de-duping traces. Not currently thread-safe (so
4
- # make sure access is synchronized.)
5
- module Honeybadger
6
- class Agent
7
- class TraceCollection
8
- extend Forwardable
9
- include Enumerable
10
-
11
- def initialize
12
- @traces = {}
13
- end
14
-
15
- def_delegators :to_a, :each, :empty?, :size
16
-
17
- def push(trace)
18
- if !traces.key?(trace.key) || traces[trace.key].duration < trace.duration
19
- traces[trace.key] = trace
20
- end
21
- end
22
-
23
- def to_a
24
- traces.values
25
- end
26
-
27
- private
28
-
29
- attr_reader :traces
30
- end
31
- end
32
- end
@@ -1,38 +0,0 @@
1
- require 'honeybadger/plugin'
2
- require 'honeybadger/trace'
3
-
4
- module Honeybadger
5
- module Plugins
6
- module NetHttp
7
- module Instrumentation
8
- def self.included(base)
9
- base.send(:alias_method, :request_without_honeybadger, :request)
10
- base.send(:alias_method, :request, :request_with_honeybadger)
11
- end
12
-
13
- def request_with_honeybadger(*args, &block)
14
- request = args[0]
15
- uri = request.path.to_s.match(%r{https?://}) ? URI(request.path) : URI("http#{use_ssl? ? 's' : ''}://#{address}:#{port}#{request.path}")
16
-
17
- if uri.host.to_s.match("honeybadger.io")
18
- return request_without_honeybadger(*args, &block)
19
- end
20
-
21
- ActiveSupport::Notifications.instrument("net_http.request", { :uri => uri, :method => request.method }) do
22
- # Disable tracing during #request so that additional calls (i.e.
23
- # when connection wasn't started) don't result in double counting.
24
- Trace.ignore_events { request_without_honeybadger(*args, &block) }
25
- end
26
- end
27
- end
28
-
29
- Plugin.register do
30
- requirement { defined?(::ActiveSupport::Notifications) }
31
- requirement { defined?(::Net::HTTP) }
32
- requirement { config[:'traces.enabled'] }
33
-
34
- execution { ::Net::HTTP.send(:include, Instrumentation) }
35
- end
36
- end
37
- end
38
- end
@@ -1,229 +0,0 @@
1
- require 'securerandom'
2
-
3
- require 'honeybadger/agent'
4
- require 'honeybadger/util/sanitizer'
5
-
6
- module Honeybadger
7
- class Trace
8
- attr_reader :id, :duration, :key
9
-
10
- def self.current
11
- Thread.current[:__hb_trace]
12
- end
13
-
14
- def self.create(id)
15
- Thread.current[:__hb_trace] = new(id)
16
- end
17
-
18
- def self.instrument(key, payload = {}, &block)
19
- current = self.current
20
- self.create(SecureRandom.uuid).instrument(key, payload, &block)
21
- ensure
22
- Thread.current[:__hb_trace] = current
23
- end
24
-
25
- # Internal: Disables event tracing for executed code block.
26
- #
27
- # block - The code which should not be traced.
28
- #
29
- # Returns the return value from the block.
30
- def self.ignore_events
31
- return yield if ignoring_events?
32
-
33
- begin
34
- Thread.current[:__hb_ignore_trace_events] = true
35
- yield
36
- ensure
37
- Thread.current[:__hb_ignore_trace_events] = false
38
- end
39
- end
40
-
41
- # Internal: Is event tracing currently disabled?
42
- def self.ignoring_events?
43
- !!Thread.current[:__hb_ignore_trace_events]
44
- end
45
-
46
- def initialize(id)
47
- @id = id
48
- @events = []
49
- @meta = {}
50
- @fast_queries = {}
51
- @duration = 0
52
- end
53
-
54
- def add(event)
55
- return if ignoring_events?
56
- ce = clean_event(event)
57
- @events << ce.to_a if ce.render?
58
- end
59
-
60
- def add_query(event)
61
- return if ignoring_events?
62
- return add(event) unless event.duration < 6
63
-
64
- ce = clean_event(event)
65
- return unless ce.render?
66
- query = ce.to_s
67
- if @fast_queries[query]
68
- @fast_queries[query][:duration] += ce.event.duration
69
- @fast_queries[query][:count] += 1
70
- else
71
- @fast_queries[query] = { :duration => ce.event.duration, :count => 1 }
72
- end
73
- end
74
-
75
- def complete(event, payload = {})
76
- @meta = clean_event(event).to_h.merge(payload)
77
- @duration = event.duration
78
- @key = "#{event.payload[:controller]}##{event.payload[:action]}"
79
- Thread.current[:__hb_trace] = nil
80
- Agent.trace(self)
81
- end
82
-
83
- def instrument(key, payload)
84
- @key = key
85
- @meta = payload
86
- started = Time.now
87
- yield
88
- rescue Exception => e
89
- @meta[:exception] = [e.class.name, e.message]
90
- raise e
91
- ensure
92
- @meta.merge!(:duration => @duration = 1000.0 * (Time.now - started))
93
- Agent.trace(self)
94
- end
95
-
96
- def to_h
97
- @meta.merge({ :events => @events, :key => @key, :fast_queries => @fast_queries.map {|k,v| [ k, v[:duration], v[:count] ] } })
98
- end
99
-
100
- # Private helpers: use at your own risk.
101
-
102
- attr_reader :meta
103
-
104
- protected
105
-
106
- def ignoring_events?
107
- self.class.ignoring_events?
108
- end
109
-
110
- def clean_event(event)
111
- TraceCleaner.create(event)
112
- end
113
-
114
- end
115
-
116
- module TraceCleaner
117
-
118
- def self.create(event)
119
- Classes[event.name].new(event)
120
- end
121
-
122
- class Base
123
- attr_reader :event
124
-
125
- def initialize(event)
126
- @event = event
127
- end
128
-
129
- def render?
130
- true
131
- end
132
-
133
- def payload
134
- event.payload
135
- end
136
-
137
- def to_s
138
- payload[:path] || payload[:key] || payload.inspect
139
- end
140
-
141
- def to_h
142
- { :name => event.name, :desc => to_s, :duration => event.duration }
143
- end
144
-
145
- def to_a
146
- [ event.name, event.duration, to_s ]
147
- end
148
-
149
- end
150
-
151
- class NetHttpRequest < Base
152
- Replacement = "..."
153
- def to_s
154
- uri = payload[:uri]
155
- uri.user = Replacement if uri.user
156
- uri.password = Replacement if uri.password
157
- uri.query = Replacement if uri.query
158
- "#{payload[:method]} #{uri}"
159
- end
160
- end
161
-
162
- class ActiveRecord < Base
163
- Schema = "SCHEMA".freeze
164
- SchemaMigrations = /schema_migrations/.freeze
165
- EscapedQuotes = /(\\"|\\')/.freeze
166
- SQuotedData = /'(?:[^']|'')*'/.freeze
167
- DQuotedData = /"(?:[^"]|"")*"/.freeze
168
- NumericData = /\b\d+\b/.freeze
169
- Newline = /\n/.freeze
170
- Replacement = "?".freeze
171
- EmptyReplacement = "".freeze
172
- DoubleQuoters = /(postgres|sqlite|postgis)/.freeze
173
-
174
- def initialize(event)
175
- super
176
- @sql = Util::Sanitizer.sanitize_string(event.payload[:sql])
177
- end
178
-
179
- def render?
180
- event.payload[:name] != Schema && !sql.match(SchemaMigrations)
181
- end
182
-
183
- def to_s
184
- s = sql.dup
185
- s.gsub!(EscapedQuotes, EmptyReplacement)
186
- s.gsub!(SQuotedData, Replacement)
187
- s.gsub!(DQuotedData, Replacement) unless ::ActiveRecord::Base.connection_pool.spec.config[:adapter] =~ DoubleQuoters
188
- s.gsub!(NumericData, Replacement)
189
- s.gsub!(Newline, EmptyReplacement)
190
- s.squeeze!(' ')
191
- s
192
- end
193
-
194
- private
195
- attr_reader :sql
196
- end
197
-
198
- class ActionView < Base
199
- EmptyReplacement = "".freeze
200
-
201
- def to_s
202
- event.payload[:identifier].to_s.gsub(::Rails.root.to_s + '/', EmptyReplacement)
203
- end
204
- end
205
-
206
- class ActionController < Base
207
- def payload
208
- event.payload.reject {|k, v| k == :params || k == :headers }
209
- end
210
-
211
- def to_s
212
- payload.inspect
213
- end
214
-
215
- def to_h
216
- payload.merge({ :duration => event.duration })
217
- end
218
- end
219
-
220
- Classes = Hash.new(Base).merge({
221
- 'sql.active_record' => ActiveRecord,
222
- 'render_template.action_view' => ActionView,
223
- 'render_partial.action_view' => ActionView,
224
- 'render_collection.action_view' => ActionView,
225
- 'process_action.action_controller' => ActionController,
226
- 'net_http.request' => NetHttpRequest
227
- })
228
- end
229
- end