honeybadger 2.7.2 → 3.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -3
  3. data/README.md +8 -15
  4. data/lib/honeybadger.rb +9 -232
  5. data/lib/honeybadger/agent.rb +292 -134
  6. data/lib/honeybadger/backend.rb +6 -6
  7. data/lib/honeybadger/backend/base.rb +11 -0
  8. data/lib/honeybadger/backend/server.rb +2 -14
  9. data/lib/honeybadger/cli.rb +0 -2
  10. data/lib/honeybadger/cli/deploy.rb +42 -0
  11. data/lib/honeybadger/cli/exec.rb +138 -0
  12. data/lib/honeybadger/cli/heroku.rb +1 -22
  13. data/lib/honeybadger/cli/install.rb +74 -0
  14. data/lib/honeybadger/cli/main.rb +138 -153
  15. data/lib/honeybadger/cli/notify.rb +66 -0
  16. data/lib/honeybadger/cli/test.rb +266 -0
  17. data/lib/honeybadger/config.rb +178 -162
  18. data/lib/honeybadger/config/defaults.rb +5 -5
  19. data/lib/honeybadger/config/env.rb +8 -6
  20. data/lib/honeybadger/config/ruby.rb +100 -0
  21. data/lib/honeybadger/config/yaml.rb +18 -19
  22. data/lib/honeybadger/const.rb +3 -16
  23. data/lib/honeybadger/context_manager.rb +50 -0
  24. data/lib/honeybadger/init/rails.rb +9 -21
  25. data/lib/honeybadger/init/rake.rb +2 -0
  26. data/lib/honeybadger/init/ruby.rb +9 -0
  27. data/lib/honeybadger/init/sinatra.rb +13 -6
  28. data/lib/honeybadger/notice.rb +29 -14
  29. data/lib/honeybadger/plugins/delayed_job/plugin.rb +4 -5
  30. data/lib/honeybadger/plugins/passenger.rb +1 -2
  31. data/lib/honeybadger/plugins/rails.rb +0 -28
  32. data/lib/honeybadger/plugins/resque.rb +2 -5
  33. data/lib/honeybadger/plugins/shoryuken.rb +2 -2
  34. data/lib/honeybadger/plugins/sidekiq.rb +2 -2
  35. data/lib/honeybadger/plugins/sucker_punch.rb +1 -0
  36. data/lib/honeybadger/plugins/thor.rb +2 -2
  37. data/lib/honeybadger/plugins/warden.rb +1 -0
  38. data/lib/honeybadger/rack/error_notifier.rb +11 -9
  39. data/lib/honeybadger/rack/user_feedback.rb +6 -4
  40. data/lib/honeybadger/rack/user_informer.rb +6 -4
  41. data/lib/honeybadger/ruby.rb +2 -0
  42. data/lib/honeybadger/singleton.rb +26 -0
  43. data/lib/honeybadger/util/http.rb +12 -0
  44. data/lib/honeybadger/util/request_hash.rb +71 -0
  45. data/lib/honeybadger/util/sanitizer.rb +101 -64
  46. data/lib/honeybadger/version.rb +1 -1
  47. data/lib/honeybadger/worker.rb +246 -0
  48. metadata +17 -13
  49. data/lib/honeybadger/agent/batch.rb +0 -50
  50. data/lib/honeybadger/agent/null_worker.rb +0 -26
  51. data/lib/honeybadger/agent/worker.rb +0 -243
  52. data/lib/honeybadger/cli/helpers.rb +0 -160
  53. data/lib/honeybadger/config/callbacks.rb +0 -70
  54. data/lib/honeybadger/plugins/unicorn.rb +0 -27
  55. data/lib/honeybadger/rack/metrics_reporter.rb +0 -16
  56. data/lib/honeybadger/rack/request_hash.rb +0 -55
@@ -1,4 +1,4 @@
1
1
  module Honeybadger
2
2
  # Public: The current String Honeybadger version.
3
- VERSION = '2.7.2'.freeze
3
+ VERSION = '3.0.0.beta1'.freeze
4
4
  end
@@ -0,0 +1,246 @@
1
+ require 'forwardable'
2
+ require 'net/http'
3
+
4
+ require 'honeybadger/logging'
5
+
6
+ module Honeybadger
7
+ # Internal: A concurrent queue to notify the backend.
8
+ class Worker
9
+ extend Forwardable
10
+
11
+ include Honeybadger::Logging::Helper
12
+
13
+ # Internal: Sub-class thread so we have a named thread (useful for debugging in Thread.list).
14
+ class Thread < ::Thread; end
15
+
16
+ # Internal: A queue which enforces a maximum size.
17
+ class Queue < ::Queue
18
+ attr_reader :max_size
19
+
20
+ def initialize(max_size)
21
+ @max_size = max_size
22
+ super()
23
+ end
24
+
25
+ def push(msg)
26
+ super unless size == max_size
27
+ end
28
+ end
29
+
30
+ SHUTDOWN = :__hb_worker_shutdown!
31
+
32
+ def initialize(config)
33
+ @config = config
34
+ @throttles = []
35
+ @mutex = Mutex.new
36
+ @marker = ConditionVariable.new
37
+ @queue = Queue.new(config.max_queue_size)
38
+ @shutdown = false
39
+ @start_at = nil
40
+ end
41
+
42
+ def push(msg)
43
+ return false unless start
44
+ queue.push(msg)
45
+ end
46
+
47
+ def send_now(msg)
48
+ handle_response(msg, notify_backend(msg))
49
+ end
50
+
51
+ # Internal: Shutdown the worker after sending remaining data.
52
+ #
53
+ # Returns true.
54
+ def shutdown
55
+ d { 'shutting down worker' }
56
+
57
+ mutex.synchronize do
58
+ @shutdown = true
59
+ @pid = nil
60
+ queue.push(SHUTDOWN)
61
+ end
62
+
63
+ return true unless thread
64
+
65
+ r = true
66
+ unless Thread.current.eql?(thread)
67
+ begin
68
+ r = !!thread.join
69
+ ensure
70
+ shutdown! unless r
71
+ end
72
+ end
73
+
74
+ r
75
+ end
76
+
77
+ def shutdown!
78
+ mutex.synchronize do
79
+ @shutdown = true
80
+ @pid = nil
81
+ queue.clear
82
+ end
83
+
84
+ d { 'killing worker thread' }
85
+
86
+ if thread
87
+ Thread.kill(thread)
88
+ thread.join # Allow ensure blocks to execute.
89
+ end
90
+
91
+ true
92
+ end
93
+
94
+ # Internal: Blocks until queue is processed up to this point in time.
95
+ #
96
+ # Returns nothing.
97
+ def flush
98
+ mutex.synchronize do
99
+ if thread && thread.alive?
100
+ queue.push(marker)
101
+ marker.wait(mutex)
102
+ end
103
+ end
104
+ end
105
+
106
+ def start
107
+ return false unless can_start?
108
+
109
+ mutex.synchronize do
110
+ @shutdown = false
111
+ @start_at = nil
112
+
113
+ return true if thread && thread.alive?
114
+
115
+ @pid = Process.pid
116
+ @thread = Thread.new { run }
117
+ end
118
+
119
+ true
120
+ end
121
+
122
+ private
123
+
124
+ attr_reader :config, :queue, :pid, :mutex, :marker,
125
+ :thread, :throttles
126
+
127
+ def_delegator :config, :backend
128
+
129
+ def can_start?
130
+ mutex.synchronize do
131
+ return true unless @shutdown
132
+ return false unless @start_at
133
+ Time.now.to_i >= @start_at
134
+ end
135
+ end
136
+
137
+ def suspend(interval)
138
+ mutex.synchronize { @start_at = Time.now.to_i + interval }
139
+
140
+ # Must be performed last since this may kill the current thread.
141
+ shutdown!
142
+ end
143
+
144
+ def run
145
+ begin
146
+ d { 'worker started' }
147
+ loop do
148
+ case msg = queue.pop
149
+ when SHUTDOWN then break
150
+ when ConditionVariable then signal_marker(msg)
151
+ else work(msg)
152
+ end
153
+ end
154
+ ensure
155
+ d { 'stopping worker' }
156
+ end
157
+ rescue Exception => e
158
+ error {
159
+ msg = "error in worker thread (shutting down) class=%s message=%s\n\t%s"
160
+ sprintf(msg, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
161
+ }
162
+ ensure
163
+ release_marker
164
+ end
165
+
166
+ def work(msg)
167
+ send_now(msg)
168
+ sleep(throttle_interval)
169
+ rescue StandardError => e
170
+ error {
171
+ msg = "Error in worker thread class=%s message=%s\n\t%s"
172
+ sprintf(msg, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
173
+ }
174
+ sleep(1)
175
+ end
176
+
177
+ def throttle_interval
178
+ return 0 unless throttles[0]
179
+ mutex.synchronize do
180
+ throttles.reduce(1) {|a,e| a*e }
181
+ end
182
+ end
183
+
184
+ def notify_backend(payload)
185
+ debug { sprintf('worker notifying backend id=%s', payload.id) }
186
+ backend.notify(:notices, payload)
187
+ end
188
+
189
+ def add_throttle(t)
190
+ mutex.synchronize do
191
+ throttles.push(t)
192
+ end
193
+ end
194
+
195
+ def del_throttle
196
+ mutex.synchronize do
197
+ throttles.shift
198
+ end
199
+ end
200
+
201
+ def handle_response(msg, response)
202
+ debug { sprintf('worker response code=%s message=%s', response.code, response.message.to_s.dump) }
203
+
204
+ case response.code
205
+ when 429, 503
206
+ warn { sprintf('Error report failed: project is sending too many errors. id=%s code=%s throttle=1.25 interval=%s', msg.id, throttle_interval, response.code) }
207
+ add_throttle(1.25)
208
+ when 402
209
+ warn { sprintf('Error report failed: payment is required. id=%s code=%s', msg.id, response.code) }
210
+ suspend(3600)
211
+ when 403
212
+ warn { sprintf('Error report failed: API key is invalid. id=%s code=%s', msg.id, response.code) }
213
+ suspend(3600)
214
+ when 201
215
+ if throttle = del_throttle
216
+ info { sprintf('Success ⚡ https://app.honeybadger.io/notice/%s id=%s code=%s throttle=%s interval=%s', msg.id, msg.id, response.code, throttle_interval, response.code) }
217
+ else
218
+ info { sprintf('Success ⚡ https://app.honeybadger.io/notice/%s id=%s code=%s', msg.id, msg.id, response.code) }
219
+ end
220
+ when :error
221
+ warn { sprintf('Error report failed: an unknown error occurred. code=%s error=%s', response.code, response.message.to_s.dump) }
222
+ else
223
+ warn { sprintf('Error report failed: unknown response from server. code=%s', response.code) }
224
+ end
225
+ end
226
+
227
+ # Internal: Release the marker. Important to perform during cleanup when
228
+ # shutting down, otherwise it could end up waiting indefinitely.
229
+ #
230
+ # Returns nothing.
231
+ def release_marker
232
+ signal_marker(marker)
233
+ end
234
+
235
+ # Internal: Signal a marker.
236
+ #
237
+ # marker - The ConditionVariable marker to signal.
238
+ #
239
+ # Returns nothing.
240
+ def signal_marker(marker)
241
+ mutex.synchronize do
242
+ marker.signal
243
+ end
244
+ end
245
+ end
246
+ 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.7.2
4
+ version: 3.0.0.beta1
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-12-13 00:00:00.000000000 Z
11
+ date: 2016-12-03 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Make managing application errors a more pleasant experience.
14
14
  email:
@@ -25,9 +25,6 @@ files:
25
25
  - bin/honeybadger
26
26
  - lib/honeybadger.rb
27
27
  - lib/honeybadger/agent.rb
28
- - lib/honeybadger/agent/batch.rb
29
- - lib/honeybadger/agent/null_worker.rb
30
- - lib/honeybadger/agent/worker.rb
31
28
  - lib/honeybadger/backend.rb
32
29
  - lib/honeybadger/backend/base.rb
33
30
  - lib/honeybadger/backend/debug.rb
@@ -36,17 +33,23 @@ files:
36
33
  - lib/honeybadger/backend/test.rb
37
34
  - lib/honeybadger/backtrace.rb
38
35
  - lib/honeybadger/cli.rb
39
- - lib/honeybadger/cli/helpers.rb
36
+ - lib/honeybadger/cli/deploy.rb
37
+ - lib/honeybadger/cli/exec.rb
40
38
  - lib/honeybadger/cli/heroku.rb
39
+ - lib/honeybadger/cli/install.rb
41
40
  - lib/honeybadger/cli/main.rb
41
+ - lib/honeybadger/cli/notify.rb
42
+ - lib/honeybadger/cli/test.rb
42
43
  - lib/honeybadger/config.rb
43
- - lib/honeybadger/config/callbacks.rb
44
44
  - lib/honeybadger/config/defaults.rb
45
45
  - lib/honeybadger/config/env.rb
46
+ - lib/honeybadger/config/ruby.rb
46
47
  - lib/honeybadger/config/yaml.rb
47
48
  - lib/honeybadger/const.rb
49
+ - lib/honeybadger/context_manager.rb
48
50
  - lib/honeybadger/init/rails.rb
49
51
  - lib/honeybadger/init/rake.rb
52
+ - lib/honeybadger/init/ruby.rb
50
53
  - lib/honeybadger/init/sinatra.rb
51
54
  - lib/honeybadger/logging.rb
52
55
  - lib/honeybadger/notice.rb
@@ -61,20 +64,21 @@ files:
61
64
  - lib/honeybadger/plugins/sidekiq.rb
62
65
  - lib/honeybadger/plugins/sucker_punch.rb
63
66
  - lib/honeybadger/plugins/thor.rb
64
- - lib/honeybadger/plugins/unicorn.rb
65
67
  - lib/honeybadger/plugins/warden.rb
66
68
  - lib/honeybadger/rack/error_notifier.rb
67
- - lib/honeybadger/rack/metrics_reporter.rb
68
- - lib/honeybadger/rack/request_hash.rb
69
69
  - lib/honeybadger/rack/user_feedback.rb
70
70
  - lib/honeybadger/rack/user_informer.rb
71
+ - lib/honeybadger/ruby.rb
72
+ - lib/honeybadger/singleton.rb
71
73
  - lib/honeybadger/tasks.rb
72
74
  - lib/honeybadger/templates/feedback_form.erb
73
75
  - lib/honeybadger/util/http.rb
76
+ - lib/honeybadger/util/request_hash.rb
74
77
  - lib/honeybadger/util/request_payload.rb
75
78
  - lib/honeybadger/util/sanitizer.rb
76
79
  - lib/honeybadger/util/stats.rb
77
80
  - lib/honeybadger/version.rb
81
+ - lib/honeybadger/worker.rb
78
82
  - resources/ca-bundle.crt
79
83
  - vendor/capistrano-honeybadger/lib/capistrano/honeybadger.rb
80
84
  - vendor/capistrano-honeybadger/lib/capistrano/tasks/deploy.cap
@@ -128,12 +132,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
128
132
  requirements:
129
133
  - - ">="
130
134
  - !ruby/object:Gem::Version
131
- version: 1.9.3
135
+ version: 2.1.0
132
136
  required_rubygems_version: !ruby/object:Gem::Requirement
133
137
  requirements:
134
- - - ">="
138
+ - - ">"
135
139
  - !ruby/object:Gem::Version
136
- version: '0'
140
+ version: 1.3.1
137
141
  requirements: []
138
142
  rubyforge_project:
139
143
  rubygems_version: 2.5.1
@@ -1,50 +0,0 @@
1
- require 'securerandom'
2
-
3
- module Honeybadger
4
- class Agent
5
- class Batch
6
- def initialize(config, name, opts = {})
7
- @id = SecureRandom.uuid
8
- @config = config
9
- @name = name
10
- @max = opts.fetch(:max, 100)
11
- @interval = opts.fetch(:interval, 60)
12
- @future = opts.fetch(:now, now()) + interval
13
- @values = opts.fetch(:collection, Array.new)
14
- @mutex = Mutex.new
15
- end
16
-
17
- attr_reader :id
18
-
19
- def push(val)
20
- mutex.synchronize { values.push(val) }
21
- end
22
-
23
- def empty?
24
- mutex.synchronize { values.empty? }
25
- end
26
-
27
- def size
28
- mutex.synchronize { values.size }
29
- end
30
-
31
- def flush?
32
- size >= max || now >= future
33
- end
34
-
35
- def as_json(*args)
36
- mutex.synchronize do
37
- { name => values.map(&:to_h), :environment => config[:env], :hostname => config[:hostname] }
38
- end
39
- end
40
-
41
- private
42
-
43
- attr_reader :config, :name, :max, :interval, :values, :future, :mutex
44
-
45
- def now
46
- Time.now.to_i
47
- end
48
- end
49
- end
50
- end
@@ -1,26 +0,0 @@
1
- module Honeybadger
2
- class Agent
3
- # Internal: A default worker which does nothing.
4
- class NullWorker
5
- def push(obj)
6
- true
7
- end
8
-
9
- def shutdown
10
- true
11
- end
12
-
13
- def shutdown!
14
- true
15
- end
16
-
17
- def flush
18
- true
19
- end
20
-
21
- def start
22
- true
23
- end
24
- end
25
- end
26
- end
@@ -1,243 +0,0 @@
1
- require 'forwardable'
2
- require 'net/http'
3
-
4
- require 'honeybadger/logging'
5
- require 'honeybadger/agent/null_worker'
6
-
7
- module Honeybadger
8
- class Agent
9
- # Internal: A concurrent queue to notify the backend.
10
- class Worker
11
- extend Forwardable
12
-
13
- include Honeybadger::Logging::Helper
14
-
15
- # Internal: Sub-class thread so we have a named thread (useful for debugging in Thread.list).
16
- class Thread < ::Thread; end
17
-
18
- # Internal: A queue which enforces a maximum size.
19
- class Queue < ::Queue
20
- attr_reader :max_size
21
-
22
- def initialize(max_size)
23
- @max_size = max_size
24
- super()
25
- end
26
-
27
- def push(obj)
28
- super unless size == max_size
29
- end
30
- end
31
-
32
- SHUTDOWN = :__hb_worker_shutdown!
33
-
34
- def initialize(config, feature)
35
- @config = config
36
- @feature = feature
37
- @backend = config.backend
38
- @throttles = []
39
- @mutex = Mutex.new
40
- @marker = ConditionVariable.new
41
- @queue = Queue.new(config.max_queue_size)
42
- @shutdown = false
43
- @start_at = nil
44
- end
45
-
46
- def push(obj)
47
- return false unless start
48
- queue.push(obj)
49
- end
50
-
51
- # Internal: Shutdown the worker after sending remaining data.
52
- #
53
- # Returns true.
54
- def shutdown
55
- d { sprintf('shutting down worker feature=%s', feature) }
56
-
57
- mutex.synchronize do
58
- @shutdown = true
59
- @pid = nil
60
- queue.push(SHUTDOWN)
61
- end
62
-
63
- return true unless thread
64
-
65
- r = true
66
- unless Thread.current.eql?(thread)
67
- begin
68
- r = !!thread.join
69
- ensure
70
- shutdown! unless r
71
- end
72
- end
73
-
74
- r
75
- end
76
-
77
- def shutdown!
78
- mutex.synchronize do
79
- @shutdown = true
80
- @pid = nil
81
- queue.clear
82
- end
83
-
84
- d { sprintf('killing worker thread feature=%s', feature) }
85
-
86
- if thread
87
- Thread.kill(thread)
88
- thread.join # Allow ensure blocks to execute.
89
- end
90
-
91
- true
92
- end
93
-
94
- # Internal: Blocks until queue is processed up to this point in time.
95
- #
96
- # Returns nothing.
97
- def flush
98
- mutex.synchronize do
99
- if thread && thread.alive?
100
- queue.push(marker)
101
- marker.wait(mutex)
102
- end
103
- end
104
- end
105
-
106
- def start
107
- return false unless can_start?
108
-
109
- mutex.synchronize do
110
- @shutdown = false
111
- @start_at = nil
112
-
113
- return true if thread && thread.alive?
114
-
115
- @pid = Process.pid
116
- @thread = Thread.new { run }
117
- end
118
-
119
- true
120
- end
121
-
122
- private
123
-
124
- attr_reader :config, :backend, :feature, :queue, :pid, :mutex, :marker,
125
- :thread, :throttles
126
-
127
- def can_start?
128
- mutex.synchronize do
129
- return true unless @shutdown
130
- return false unless @start_at
131
- Time.now.to_i >= @start_at
132
- end
133
- end
134
-
135
- def suspend(interval)
136
- mutex.synchronize { @start_at = Time.now.to_i + interval }
137
-
138
- # Must be performed last since this may kill the current thread.
139
- shutdown!
140
- end
141
-
142
- def run
143
- begin
144
- d { sprintf('worker started feature=%s', feature) }
145
- loop do
146
- case msg = queue.pop
147
- when SHUTDOWN then break
148
- when ConditionVariable then signal_marker(msg)
149
- else process(msg)
150
- end
151
- end
152
- ensure
153
- d { sprintf('stopping worker feature=%s', feature) }
154
- end
155
- rescue Exception => e
156
- error {
157
- msg = "error in worker thread (shutting down) feature=%s class=%s message=%s\n\t%s"
158
- sprintf(msg, feature, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
159
- }
160
- ensure
161
- release_marker
162
- end
163
-
164
- def process(msg)
165
- handle_response(notify_backend(msg))
166
- sleep(throttle_interval)
167
- rescue StandardError => e
168
- error {
169
- msg = "error in worker thread feature=%s class=%s message=%s\n\t%s"
170
- sprintf(msg, feature, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
171
- }
172
- sleep(1)
173
- end
174
-
175
- def throttle_interval
176
- return 0 unless throttles[0]
177
- mutex.synchronize do
178
- throttles.reduce(1) {|a,e| a*e }
179
- end
180
- end
181
-
182
- def notify_backend(payload)
183
- debug { sprintf('worker notifying backend feature=%s id=%s', feature, payload.id) }
184
- backend.notify(feature, payload)
185
- end
186
-
187
- def add_throttle(t)
188
- mutex.synchronize do
189
- throttles.push(t)
190
- end
191
- end
192
-
193
- def del_throttle
194
- mutex.synchronize do
195
- throttles.shift
196
- end
197
- end
198
-
199
- def handle_response(response)
200
- debug { sprintf('worker response feature=%s code=%s message=%s', feature, response.code, response.message.to_s.dump) }
201
-
202
- case response.code
203
- when 429, 503
204
- add_throttle(1.25)
205
- debug { sprintf('worker applying throttle=1.25 interval=%s feature=%s code=%s', throttle_interval, feature, response.code) }
206
- when 402
207
- warn { sprintf('data will not be reported (payment required) feature=%s code=%s', feature, response.code) }
208
- suspend(3600)
209
- when 403
210
- warn { sprintf('data will not be reported feature=%s code=%s error=%s', feature, response.code, response.error.to_s.dump) }
211
- suspend(3600)
212
- when 201
213
- if throttle = del_throttle
214
- debug { sprintf('worker removing throttle=%s interval=%s feature=%s code=%s', throttle, throttle_interval, feature, response.code) }
215
- end
216
- when :error
217
- # Error logged by backend.
218
- else
219
- warn { sprintf('worker unknown response feature=%s code=%s', feature, response.code) }
220
- end
221
- end
222
-
223
- # Internal: Release the marker. Important to perform during cleanup when
224
- # shutting down, otherwise it could end up waiting indefinitely.
225
- #
226
- # Returns nothing.
227
- def release_marker
228
- signal_marker(marker)
229
- end
230
-
231
- # Internal: Signal a marker.
232
- #
233
- # marker - The ConditionVariable marker to signal.
234
- #
235
- # Returns nothing.
236
- def signal_marker(marker)
237
- mutex.synchronize do
238
- marker.signal
239
- end
240
- end
241
- end
242
- end
243
- end