honeybadger 2.6.1 → 2.7.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.
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