honeybadger 2.0.0.beta.6 → 2.0.0.beta.7
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 +4 -4
- data/lib/honeybadger/{worker → agent}/batch.rb +1 -1
- data/lib/honeybadger/{worker → agent}/metered_queue.rb +1 -1
- data/lib/honeybadger/{worker → agent}/metrics_collection.rb +1 -1
- data/lib/honeybadger/{worker → agent}/metrics_collector.rb +25 -15
- data/lib/honeybadger/agent/null_worker.rb +26 -0
- data/lib/honeybadger/agent/worker.rb +217 -0
- data/lib/honeybadger/agent.rb +183 -11
- data/lib/honeybadger/backtrace.rb +1 -0
- data/lib/honeybadger/config/defaults.rb +4 -0
- data/lib/honeybadger/config.rb +11 -2
- data/lib/honeybadger/const.rb +0 -1
- data/lib/honeybadger/logging.rb +2 -1
- data/lib/honeybadger/version.rb +1 -1
- data/lib/honeybadger.rb +34 -0
- metadata +8 -7
- data/lib/honeybadger/worker.rb +0 -237
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea4729431078cbb1e542a22bbb1ac422a038be32
|
4
|
+
data.tar.gz: 7da4b010726bd765840424ad160a8235e04ad5b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 685b29b9328d62dda5c9b5bfda43968601d9f6f7861060c2ad8531c9afcb650e386e1bcf82785ff37a630957b21eeebe8d07ac8b80d406d9d391e52c9b672d3b
|
7
|
+
data.tar.gz: 6c503a35df7698efa690e1c8552c8f2c6b829ccdeeca7263ae20ed3b0f4eb3afd20f8fbc4375907fb49587f51c75905c3517b2b8b3b437c8d2acfa20c658dfe9
|
@@ -2,8 +2,8 @@ require 'securerandom'
|
|
2
2
|
require 'forwardable'
|
3
3
|
|
4
4
|
module Honeybadger
|
5
|
-
class
|
6
|
-
autoload :MetricsCollection, 'honeybadger/
|
5
|
+
class Agent
|
6
|
+
autoload :MetricsCollection, 'honeybadger/agent/metrics_collection'
|
7
7
|
|
8
8
|
class MetricsCollector
|
9
9
|
extend Forwardable
|
@@ -46,21 +46,31 @@ module Honeybadger
|
|
46
46
|
now >= future
|
47
47
|
end
|
48
48
|
|
49
|
-
def
|
50
|
-
|
51
|
-
metrics
|
52
|
-
|
49
|
+
def size
|
50
|
+
mutex.synchronize do
|
51
|
+
metrics.reduce(0) do |count, hash|
|
52
|
+
count + hash[1].size
|
53
53
|
end
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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!
|
62
73
|
end
|
63
|
-
m.compact!
|
64
74
|
end
|
65
75
|
end
|
66
76
|
|
@@ -0,0 +1,26 @@
|
|
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(timeout = nil)
|
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
|
@@ -0,0 +1,217 @@
|
|
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: See Agent::Thread
|
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(1000)
|
42
|
+
@shutdown = false
|
43
|
+
end
|
44
|
+
|
45
|
+
def push(obj)
|
46
|
+
if start
|
47
|
+
queue.push(obj)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Internal: Shutdown the worker.
|
52
|
+
#
|
53
|
+
# timeout - The Integer timeout to wait before killing thread.
|
54
|
+
#
|
55
|
+
# Returns false if timeout reached, otherwise true.
|
56
|
+
def shutdown(timeout = 3)
|
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(timeout)
|
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
|
+
end
|
82
|
+
|
83
|
+
d { sprintf('killing worker thread feature=%s', feature) }
|
84
|
+
|
85
|
+
if thread
|
86
|
+
Thread.kill(thread)
|
87
|
+
thread.join # Allow ensure blocks to execute.
|
88
|
+
end
|
89
|
+
|
90
|
+
true
|
91
|
+
end
|
92
|
+
|
93
|
+
# Internal: Blocks until queue is processed up to this point in time.
|
94
|
+
#
|
95
|
+
# Returns nothing.
|
96
|
+
def flush
|
97
|
+
mutex.synchronize do
|
98
|
+
if thread && thread.alive?
|
99
|
+
queue.push(marker)
|
100
|
+
marker.wait(mutex)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def start
|
106
|
+
mutex.synchronize do
|
107
|
+
return false if @shutdown
|
108
|
+
return true if thread && thread.alive?
|
109
|
+
|
110
|
+
@pid = Process.pid
|
111
|
+
@thread = Thread.new { run }
|
112
|
+
end
|
113
|
+
|
114
|
+
true
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
attr_reader :config, :backend, :feature, :queue, :pid, :mutex, :marker,
|
120
|
+
:thread, :throttles
|
121
|
+
|
122
|
+
def run
|
123
|
+
begin
|
124
|
+
d { sprintf('worker started feature=%s', feature) }
|
125
|
+
loop do
|
126
|
+
case msg = queue.pop
|
127
|
+
when SHUTDOWN then break
|
128
|
+
when ConditionVariable then signal_marker(msg)
|
129
|
+
else process(msg)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
ensure
|
133
|
+
d { sprintf('stopping worker feature=%s', feature) }
|
134
|
+
end
|
135
|
+
rescue Exception => e
|
136
|
+
error(sprintf('error in worker thread (shutting down) feature=%s class=%s message=%s at=%s', feature, e.class, e.message.dump, e.backtrace.first.dump))
|
137
|
+
ensure
|
138
|
+
release_marker
|
139
|
+
end
|
140
|
+
|
141
|
+
def process(msg)
|
142
|
+
handle_response(notify_backend(msg))
|
143
|
+
sleep(throttle_interval)
|
144
|
+
rescue StandardError => e
|
145
|
+
error(sprintf('error in worker thread feature=%s class=%s message=%s at=%s', feature, e.class, e.message.dump, e.backtrace.first.dump))
|
146
|
+
sleep(1)
|
147
|
+
end
|
148
|
+
|
149
|
+
def throttle_interval
|
150
|
+
return 0 unless throttles[0]
|
151
|
+
mutex.synchronize do
|
152
|
+
throttles.reduce(1) {|a,e| a*e }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def notify_backend(payload)
|
157
|
+
debug { sprintf('worker notifying backend feature=%s id=%s', feature, payload.id) }
|
158
|
+
backend.notify(feature, payload)
|
159
|
+
end
|
160
|
+
|
161
|
+
def add_throttle(t)
|
162
|
+
mutex.synchronize do
|
163
|
+
throttles.push(t)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def del_throttle
|
168
|
+
mutex.synchronize do
|
169
|
+
throttles.shift
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def handle_response(response)
|
174
|
+
debug { sprintf('worker response feature=%s code=%s message=%s', feature, response.code, response.message.to_s.dump) }
|
175
|
+
|
176
|
+
case response.code
|
177
|
+
when 429, 503
|
178
|
+
add_throttle(1.25)
|
179
|
+
debug { sprintf('worker applying throttle=1.25 interval=%s feature=%s code=%s', throttle_interval, feature, response.code) }
|
180
|
+
when 402
|
181
|
+
warn { sprintf('worker shutting down (payment required) feature=%s code=%s', feature, response.code) }
|
182
|
+
shutdown!
|
183
|
+
when 403
|
184
|
+
warn { sprintf('worker shutting down (unauthorized) feature=%s code=%s', feature, response.code) }
|
185
|
+
shutdown!
|
186
|
+
when 201
|
187
|
+
if throttle = del_throttle
|
188
|
+
debug { sprintf('worker removing throttle=%s interval=%s feature=%s code=%s', throttle, throttle_interval, feature, response.code) }
|
189
|
+
end
|
190
|
+
when :error
|
191
|
+
# Error logged by backend.
|
192
|
+
else
|
193
|
+
warn { sprintf('worker unknown response feature=%s code=%s', feature, response.code) }
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# Internal: Release the marker. Important to perform during cleanup when
|
198
|
+
# shutting down, otherwise it could end up waiting indefinitely.
|
199
|
+
#
|
200
|
+
# Returns nothing.
|
201
|
+
def release_marker
|
202
|
+
signal_marker(marker)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Internal: Signal a marker.
|
206
|
+
#
|
207
|
+
# marker - The ConditionVariable marker to signal.
|
208
|
+
#
|
209
|
+
# Returns nothing.
|
210
|
+
def signal_marker(marker)
|
211
|
+
mutex.synchronize do
|
212
|
+
marker.signal
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
data/lib/honeybadger/agent.rb
CHANGED
@@ -2,18 +2,25 @@ require 'forwardable'
|
|
2
2
|
|
3
3
|
require 'honeybadger/version'
|
4
4
|
require 'honeybadger/config'
|
5
|
-
require 'honeybadger/worker'
|
6
5
|
require 'honeybadger/notice'
|
7
6
|
require 'honeybadger/plugin'
|
8
7
|
require 'honeybadger/logging'
|
9
8
|
|
10
9
|
module Honeybadger
|
11
|
-
# Internal: A broker for the configuration and the
|
10
|
+
# Internal: A broker for the configuration and the workers.
|
12
11
|
class Agent
|
13
12
|
extend Forwardable
|
14
13
|
|
15
14
|
include Logging::Helper
|
16
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
|
+
autoload :Worker, 'honeybadger/agent/worker'
|
20
|
+
autoload :NullWorker, 'honeybadger/agent/worker'
|
21
|
+
autoload :Batch, 'honeybadger/agent/batch'
|
22
|
+
autoload :MetricsCollector, 'honeybadger/agent/metrics_collector'
|
23
|
+
|
17
24
|
class << self
|
18
25
|
extend Forwardable
|
19
26
|
|
@@ -64,7 +71,8 @@ module Honeybadger
|
|
64
71
|
config.logger.info("Starting Honeybadger version #{VERSION}")
|
65
72
|
load_plugins(config)
|
66
73
|
@instance = new(config)
|
67
|
-
|
74
|
+
|
75
|
+
true
|
68
76
|
end
|
69
77
|
|
70
78
|
def self.stop(*args)
|
@@ -88,6 +96,16 @@ module Honeybadger
|
|
88
96
|
self.instance ? self.instance.increment(*args) : false
|
89
97
|
end
|
90
98
|
|
99
|
+
def self.flush(&block)
|
100
|
+
if self.instance
|
101
|
+
self.instance.flush(&block)
|
102
|
+
elsif !block_given?
|
103
|
+
false
|
104
|
+
else
|
105
|
+
yield
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
91
109
|
# Internal: Callback to perform after agent has been stopped at_exit.
|
92
110
|
#
|
93
111
|
# block - An optional block to execute.
|
@@ -112,9 +130,21 @@ module Honeybadger
|
|
112
130
|
end
|
113
131
|
end
|
114
132
|
|
133
|
+
attr_reader :delay, :workers, :pid, :thread, :traces, :metrics
|
134
|
+
|
115
135
|
def initialize(config)
|
116
136
|
@config = config
|
117
|
-
@
|
137
|
+
@delay = config.debug? ? 10 : 60
|
138
|
+
@mutex = Mutex.new
|
139
|
+
@pid = Process.pid
|
140
|
+
|
141
|
+
unless config.backend.kind_of?(Backend::Server)
|
142
|
+
warn('Initializing development backend: data will not be reported.')
|
143
|
+
end
|
144
|
+
|
145
|
+
init_workers
|
146
|
+
init_traces
|
147
|
+
init_metrics
|
118
148
|
|
119
149
|
at_exit do
|
120
150
|
stop
|
@@ -122,14 +152,47 @@ module Honeybadger
|
|
122
152
|
end
|
123
153
|
end
|
124
154
|
|
125
|
-
|
126
|
-
|
155
|
+
# Internal: Spawn the agent thread. This method is idempotent.
|
156
|
+
#
|
157
|
+
# Returns false if the Agent is stopped, otherwise true.
|
127
158
|
def start
|
128
|
-
|
129
|
-
|
159
|
+
mutex.synchronize do
|
160
|
+
return false unless pid
|
161
|
+
return true if thread && thread.alive?
|
162
|
+
|
163
|
+
debug { 'starting agent' }
|
164
|
+
|
165
|
+
@pid = Process.pid
|
166
|
+
@thread = Thread.new { run }
|
130
167
|
end
|
131
168
|
|
132
|
-
|
169
|
+
true
|
170
|
+
end
|
171
|
+
|
172
|
+
def stop(force = false)
|
173
|
+
debug { 'stopping agent' }
|
174
|
+
|
175
|
+
mutex.synchronize do
|
176
|
+
@pid = nil
|
177
|
+
end
|
178
|
+
|
179
|
+
# Kill the collector
|
180
|
+
Thread.kill(thread) if thread
|
181
|
+
|
182
|
+
unless force
|
183
|
+
flush_traces
|
184
|
+
flush_metrics
|
185
|
+
end
|
186
|
+
|
187
|
+
workers.each_pair do |key, worker|
|
188
|
+
worker.send(force ? :shutdown! : :shutdown)
|
189
|
+
end
|
190
|
+
|
191
|
+
true
|
192
|
+
end
|
193
|
+
|
194
|
+
def fork
|
195
|
+
# noop
|
133
196
|
end
|
134
197
|
|
135
198
|
def notice(opts)
|
@@ -141,13 +204,122 @@ module Honeybadger
|
|
141
204
|
false
|
142
205
|
else
|
143
206
|
debug { sprintf('notice feature=notices id=%s', notice.id) }
|
144
|
-
|
207
|
+
workers[:notices].push(notice)
|
145
208
|
notice.id
|
146
209
|
end
|
147
210
|
end
|
148
211
|
|
212
|
+
def trace(trace)
|
213
|
+
start
|
214
|
+
|
215
|
+
if trace.duration > config[:'traces.threshold']
|
216
|
+
debug { sprintf('agent adding trace duration=%s feature=traces id=%s', trace.duration.round(2), trace.id) }
|
217
|
+
mutex.synchronize { traces.push(trace) }
|
218
|
+
flush_traces if traces.flush?
|
219
|
+
true
|
220
|
+
else
|
221
|
+
debug { sprintf('agent discarding trace duration=%s feature=traces id=%s', trace.duration.round(2), trace.id) }
|
222
|
+
false
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def timing(*args, &block)
|
227
|
+
start
|
228
|
+
|
229
|
+
mutex.synchronize { metrics.timing(*args, &block) }
|
230
|
+
flush_metrics if metrics.flush?
|
231
|
+
|
232
|
+
true
|
233
|
+
end
|
234
|
+
|
235
|
+
def increment(*args, &block)
|
236
|
+
start
|
237
|
+
|
238
|
+
mutex.synchronize { metrics.increment(*args, &block) }
|
239
|
+
flush_metrics if metrics.flush?
|
240
|
+
|
241
|
+
true
|
242
|
+
end
|
243
|
+
|
244
|
+
# Internal: Flush the workers. See Honeybadger#flush.
|
245
|
+
#
|
246
|
+
# block - an option block which is executed before flushing data.
|
247
|
+
#
|
248
|
+
# Returns value from block if block is given, otherwise true.
|
249
|
+
def flush
|
250
|
+
return true unless block_given?
|
251
|
+
yield
|
252
|
+
ensure
|
253
|
+
flush_metrics
|
254
|
+
flush_traces
|
255
|
+
workers.values.each(&:flush)
|
256
|
+
end
|
257
|
+
|
149
258
|
private
|
150
259
|
|
151
|
-
attr_reader :
|
260
|
+
attr_reader :config, :mutex
|
261
|
+
|
262
|
+
def push(feature, object)
|
263
|
+
unless config.features[feature]
|
264
|
+
debug { sprintf('agent dropping feature=%s reason=ping', feature) }
|
265
|
+
return false
|
266
|
+
end
|
267
|
+
|
268
|
+
workers[feature].push(object)
|
269
|
+
|
270
|
+
true
|
271
|
+
end
|
272
|
+
|
273
|
+
def run
|
274
|
+
loop { work }
|
275
|
+
rescue Exception => e
|
276
|
+
error(sprintf('error in agent thread (shutting down) class=%s message=%s at=%s', e.class, e.message.dump, e.backtrace.first.dump))
|
277
|
+
ensure
|
278
|
+
d { sprintf('stopping agent') }
|
279
|
+
end
|
280
|
+
|
281
|
+
def work
|
282
|
+
flush_metrics if metrics.flush?
|
283
|
+
flush_traces if traces.flush?
|
284
|
+
rescue StandardError => e
|
285
|
+
error(sprintf('error in agent thread class=%s message=%s at=%s', e.class, e.message.dump, e.backtrace.first.dump))
|
286
|
+
ensure
|
287
|
+
sleep(delay)
|
288
|
+
end
|
289
|
+
|
290
|
+
def init_workers
|
291
|
+
@workers = Hash.new(NullWorker.new)
|
292
|
+
workers[:notices] = Worker.new(config, :notices)
|
293
|
+
workers[:traces] = Worker.new(config, :traces)
|
294
|
+
workers[:metrics] = Worker.new(config, :metrics)
|
295
|
+
end
|
296
|
+
|
297
|
+
def init_traces
|
298
|
+
@traces = Batch.new(config, :traces, 20, config.debug? ? 10 : 60)
|
299
|
+
end
|
300
|
+
|
301
|
+
def init_metrics
|
302
|
+
@metrics = MetricsCollector.new(config, config.debug? ? 10 : 60)
|
303
|
+
end
|
304
|
+
|
305
|
+
def flush_metrics
|
306
|
+
mutex.synchronize do
|
307
|
+
if (count = metrics.size) > 0
|
308
|
+
debug { sprintf('agent flushing metrics feature=metrics count=%d', count) }
|
309
|
+
end
|
310
|
+
metrics.chunk(100, &method(:push).to_proc.curry[:metrics])
|
311
|
+
init_metrics
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def flush_traces
|
316
|
+
mutex.synchronize do
|
317
|
+
if (count = traces.size) > 0
|
318
|
+
debug { sprintf('agent flushing traces feature=traces count=%d', count) }
|
319
|
+
end
|
320
|
+
push(:traces, traces) unless traces.empty?
|
321
|
+
init_traces
|
322
|
+
end
|
323
|
+
end
|
152
324
|
end
|
153
325
|
end
|
@@ -69,6 +69,10 @@ module Honeybadger
|
|
69
69
|
description: 'The log level.',
|
70
70
|
default: 'INFO'
|
71
71
|
},
|
72
|
+
:'logging.debug' => {
|
73
|
+
description: 'Override debug logging.',
|
74
|
+
default: nil
|
75
|
+
},
|
72
76
|
:'logging.tty_level' => {
|
73
77
|
description: 'Level to log when attached to a terminal (anything < logging.level will always be ignored).',
|
74
78
|
default: 'DEBUG'
|
data/lib/honeybadger/config.rb
CHANGED
@@ -79,10 +79,14 @@ module Honeybadger
|
|
79
79
|
Backend.for((self[:backend] || default_backend).to_sym).new(self)
|
80
80
|
end
|
81
81
|
|
82
|
+
def dev?
|
83
|
+
self[:env] && Array(self[:development_environments]).include?(self[:env])
|
84
|
+
end
|
85
|
+
|
82
86
|
def public?
|
83
87
|
return true if self[:report_data]
|
84
88
|
return false if self[:report_data] == false
|
85
|
-
!self[:env] || !
|
89
|
+
!self[:env] || !dev?
|
86
90
|
end
|
87
91
|
|
88
92
|
def default_backend
|
@@ -98,7 +102,12 @@ module Honeybadger
|
|
98
102
|
end
|
99
103
|
|
100
104
|
def debug?
|
101
|
-
self[:debug]
|
105
|
+
!!self[:debug]
|
106
|
+
end
|
107
|
+
|
108
|
+
def log_debug?
|
109
|
+
return debug? if self[:'logging.debug'].nil?
|
110
|
+
!!self[:'logging.debug']
|
102
111
|
end
|
103
112
|
|
104
113
|
# Internal: Optional path to honeybadger.log log file. If nil, STDOUT will be used
|
data/lib/honeybadger/const.rb
CHANGED
@@ -7,7 +7,6 @@ module Honeybadger
|
|
7
7
|
autoload :Config, 'honeybadger/config'
|
8
8
|
autoload :Logging, 'honeybadger/logging'
|
9
9
|
autoload :Notice, 'honeybadger/notice'
|
10
|
-
autoload :Worker, 'honeybadger/worker'
|
11
10
|
autoload :Trace, 'honeybadger/trace'
|
12
11
|
autoload :Plugin, 'honeybadger/plugin'
|
13
12
|
|
data/lib/honeybadger/logging.rb
CHANGED
@@ -11,6 +11,7 @@ module Honeybadger
|
|
11
11
|
# method is defined/block captured in this module rather than delegating to
|
12
12
|
# the logger directly to avoid extra object allocation.
|
13
13
|
module Helper
|
14
|
+
private
|
14
15
|
def debug(msg = nil)
|
15
16
|
return true unless logger.debug?
|
16
17
|
msg = yield if block_given?
|
@@ -134,7 +135,7 @@ module Honeybadger
|
|
134
135
|
end
|
135
136
|
|
136
137
|
def debug?
|
137
|
-
@config.
|
138
|
+
@config.log_debug?
|
138
139
|
end
|
139
140
|
|
140
141
|
private
|
data/lib/honeybadger/version.rb
CHANGED
data/lib/honeybadger.rb
CHANGED
@@ -171,6 +171,40 @@ module Honeybadger
|
|
171
171
|
def clear!
|
172
172
|
Thread.current[:__honeybadger_context] = nil
|
173
173
|
end
|
174
|
+
|
175
|
+
# Public: Flushes all data from workers before returning. This is most useful
|
176
|
+
# in tests when using the test backend, where normally the asynchronous
|
177
|
+
# nature of this library could create race conditions.
|
178
|
+
#
|
179
|
+
# block - The optional block to execute (exceptions will propagate after data
|
180
|
+
# is flushed).
|
181
|
+
#
|
182
|
+
# Examples:
|
183
|
+
#
|
184
|
+
# # Without a block:
|
185
|
+
# it "sends a notification to Honeybadger" do
|
186
|
+
# expect {
|
187
|
+
# Honeybadger.notify(StandardError.new('test backend'))
|
188
|
+
# Honeybadger.flush
|
189
|
+
# }.to change(Honeybadger::Backend::Test.notifications[:notices], :size).by(1)
|
190
|
+
# end
|
191
|
+
#
|
192
|
+
# # With a block:
|
193
|
+
# it "sends a notification to Honeybadger" do
|
194
|
+
# expect {
|
195
|
+
# Honeybadger.flush do
|
196
|
+
# 50.times do
|
197
|
+
# Honeybadger.notify(StandardError.new('test backend'))
|
198
|
+
# end
|
199
|
+
# end
|
200
|
+
# }.to change(Honeybadger::Backend::Test.notifications[:notices], :size).by(50)
|
201
|
+
# end
|
202
|
+
#
|
203
|
+
# Returns value of block if block is given, otherwise true on success or
|
204
|
+
# false if Honeybadger isn't running.
|
205
|
+
def flush(&block)
|
206
|
+
Agent.flush(&block)
|
207
|
+
end
|
174
208
|
end
|
175
209
|
|
176
210
|
if defined?(::Rails::Railtie)
|
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.0.0.beta.
|
4
|
+
version: 2.0.0.beta.7
|
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: 2014-
|
11
|
+
date: 2014-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Make managing application errors a more pleasant experience.
|
14
14
|
email:
|
@@ -23,6 +23,12 @@ files:
|
|
23
23
|
- bin/honeybadger
|
24
24
|
- lib/honeybadger.rb
|
25
25
|
- lib/honeybadger/agent.rb
|
26
|
+
- lib/honeybadger/agent/batch.rb
|
27
|
+
- lib/honeybadger/agent/metered_queue.rb
|
28
|
+
- lib/honeybadger/agent/metrics_collection.rb
|
29
|
+
- lib/honeybadger/agent/metrics_collector.rb
|
30
|
+
- lib/honeybadger/agent/null_worker.rb
|
31
|
+
- lib/honeybadger/agent/worker.rb
|
26
32
|
- lib/honeybadger/backend.rb
|
27
33
|
- lib/honeybadger/backend/base.rb
|
28
34
|
- lib/honeybadger/backend/debug.rb
|
@@ -64,11 +70,6 @@ files:
|
|
64
70
|
- lib/honeybadger/util/sanitizer.rb
|
65
71
|
- lib/honeybadger/util/stats.rb
|
66
72
|
- lib/honeybadger/version.rb
|
67
|
-
- lib/honeybadger/worker.rb
|
68
|
-
- lib/honeybadger/worker/batch.rb
|
69
|
-
- lib/honeybadger/worker/metered_queue.rb
|
70
|
-
- lib/honeybadger/worker/metrics_collection.rb
|
71
|
-
- lib/honeybadger/worker/metrics_collector.rb
|
72
73
|
- resources/ca-bundle.crt
|
73
74
|
- vendor/capistrano-honeybadger/lib/capistrano/honeybadger.rb
|
74
75
|
- vendor/capistrano-honeybadger/lib/capistrano/tasks/deploy.cap
|
data/lib/honeybadger/worker.rb
DELETED
@@ -1,237 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
require 'net/http'
|
3
|
-
|
4
|
-
require 'honeybadger/logging'
|
5
|
-
|
6
|
-
module Honeybadger
|
7
|
-
class Worker
|
8
|
-
extend Forwardable
|
9
|
-
|
10
|
-
include Honeybadger::Logging::Helper
|
11
|
-
|
12
|
-
autoload :Batch, 'honeybadger/worker/batch'
|
13
|
-
autoload :MetricsCollector, 'honeybadger/worker/metrics_collector'
|
14
|
-
autoload :MeteredQueue, 'honeybadger/worker/metered_queue'
|
15
|
-
|
16
|
-
# Sub-class thread so we have a named thread (useful for debugging in Thread.list).
|
17
|
-
class Thread < ::Thread; end
|
18
|
-
|
19
|
-
attr_reader :backend, :queue, :features, :metrics, :traces, :pid, :mutex, :thread
|
20
|
-
|
21
|
-
def initialize(config)
|
22
|
-
@backend = config.backend
|
23
|
-
@config = config
|
24
|
-
@mutex = Mutex.new
|
25
|
-
prepare
|
26
|
-
end
|
27
|
-
|
28
|
-
def start
|
29
|
-
debug { 'starting worker' }
|
30
|
-
|
31
|
-
@pid = Process.pid
|
32
|
-
@thread = Thread.new { run }
|
33
|
-
|
34
|
-
true
|
35
|
-
end
|
36
|
-
|
37
|
-
def stop(force = false)
|
38
|
-
debug { 'stopping worker' }
|
39
|
-
if thread
|
40
|
-
if force
|
41
|
-
debug { 'killing worker' }
|
42
|
-
Thread.kill(thread)
|
43
|
-
else
|
44
|
-
thread[:should_exit] = true
|
45
|
-
unless thread.eql?(Thread.current)
|
46
|
-
mutex.unlock if mutex.locked?
|
47
|
-
thread.join
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
@thread = nil
|
52
|
-
@pid = nil
|
53
|
-
end
|
54
|
-
|
55
|
-
def fork
|
56
|
-
debug { 'forking worker' }
|
57
|
-
|
58
|
-
stop
|
59
|
-
|
60
|
-
mutex.synchronize { prepare }
|
61
|
-
|
62
|
-
start
|
63
|
-
end
|
64
|
-
|
65
|
-
def notice(notice)
|
66
|
-
debug { sprintf('worker adding notice feature=notices id=%s', notice.id) }
|
67
|
-
push(:notices, notice)
|
68
|
-
end
|
69
|
-
|
70
|
-
def trace(trace)
|
71
|
-
if trace.duration > config[:'traces.threshold']
|
72
|
-
debug { sprintf('worker adding trace duration=%s feature=traces id=%s', trace.duration.round(2), trace.id) }
|
73
|
-
traces.push(trace)
|
74
|
-
flush_traces if traces.flush?
|
75
|
-
true
|
76
|
-
else
|
77
|
-
debug { sprintf('worker discarding trace duration=%s feature=traces id=%s', trace.duration.round(2), trace.id) }
|
78
|
-
false
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def timing(*args, &block)
|
83
|
-
metrics.timing(*args, &block)
|
84
|
-
flush_metrics if metrics.flush?
|
85
|
-
true
|
86
|
-
end
|
87
|
-
|
88
|
-
def increment(*args, &block)
|
89
|
-
metrics.increment(*args, &block)
|
90
|
-
flush_metrics if metrics.flush?
|
91
|
-
true
|
92
|
-
end
|
93
|
-
|
94
|
-
private
|
95
|
-
|
96
|
-
attr_reader :config
|
97
|
-
|
98
|
-
def init_queue
|
99
|
-
@queue = {
|
100
|
-
notices: MeteredQueue.new,
|
101
|
-
metrics: MeteredQueue.new,
|
102
|
-
traces: MeteredQueue.new
|
103
|
-
}.freeze
|
104
|
-
|
105
|
-
@features = {
|
106
|
-
notices: true,
|
107
|
-
metrics: true,
|
108
|
-
traces: true
|
109
|
-
}.freeze
|
110
|
-
end
|
111
|
-
|
112
|
-
def init_traces
|
113
|
-
@traces = Batch.new(config, :traces, 20, config[:debug] ? 10 : 60)
|
114
|
-
end
|
115
|
-
|
116
|
-
def init_metrics
|
117
|
-
@metrics = MetricsCollector.new(config, config[:debug] ? 10 : 60)
|
118
|
-
end
|
119
|
-
|
120
|
-
def flush_metrics
|
121
|
-
debug { 'worker flushing metrics feature=metrics' } # TODO: Include count.
|
122
|
-
mutex.synchronize do
|
123
|
-
metrics.chunk(100, &method(:push).to_proc.curry[:metrics])
|
124
|
-
init_metrics
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def flush_traces
|
129
|
-
debug { sprintf('worker flushing traces feature=traces count=%d', traces.size) }
|
130
|
-
mutex.synchronize do
|
131
|
-
push(:traces, traces) unless traces.empty?
|
132
|
-
init_traces
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def flush_queue
|
137
|
-
mutex.synchronize do
|
138
|
-
queue.each_pair do |feature, queue|
|
139
|
-
while payload = queue.pop!
|
140
|
-
handle_response(feature, notify_backend(feature, payload))
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
def prepare
|
147
|
-
init_queue
|
148
|
-
init_metrics
|
149
|
-
init_traces
|
150
|
-
end
|
151
|
-
|
152
|
-
def push(feature, object)
|
153
|
-
unless features[feature]
|
154
|
-
debug { sprintf('worker dropping feature=%s reason=collector', feature) }
|
155
|
-
return false
|
156
|
-
end
|
157
|
-
|
158
|
-
unless config.features[feature]
|
159
|
-
debug { sprintf('worker dropping feature=%s reason=ping', feature) }
|
160
|
-
return false
|
161
|
-
end
|
162
|
-
|
163
|
-
queue[feature].push(object)
|
164
|
-
|
165
|
-
true
|
166
|
-
end
|
167
|
-
|
168
|
-
def run
|
169
|
-
begin
|
170
|
-
debug { 'worker started' }
|
171
|
-
work until finish
|
172
|
-
rescue Exception => e
|
173
|
-
error(sprintf('error in worker thread (shutting down) class=%s message=%s at=%s', e.class, e.message.dump, e.backtrace.first.dump))
|
174
|
-
raise e
|
175
|
-
ensure
|
176
|
-
debug { 'stopping worker' }
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
def work
|
181
|
-
flush_metrics if metrics.flush?
|
182
|
-
flush_traces if traces.flush?
|
183
|
-
|
184
|
-
queue.each_pair do |feature, queue|
|
185
|
-
if payload = queue.pop
|
186
|
-
handle_response(feature, notify_backend(feature, payload))
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
sleep(0.1)
|
191
|
-
rescue StandardError => e
|
192
|
-
error(sprintf('error in worker thread class=%s message=%s at=%s', e.class, e.message.dump, e.backtrace.first.dump))
|
193
|
-
sleep(1)
|
194
|
-
end
|
195
|
-
|
196
|
-
def finish
|
197
|
-
if Thread.current[:should_exit]
|
198
|
-
debug { 'flushing worker data' }
|
199
|
-
|
200
|
-
flush_metrics
|
201
|
-
flush_traces
|
202
|
-
flush_queue
|
203
|
-
|
204
|
-
true
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
def notify_backend(feature, payload)
|
209
|
-
debug { sprintf('worker notifying backend feature=%s id=%s', feature, payload.id) }
|
210
|
-
backend.notify(feature, payload)
|
211
|
-
end
|
212
|
-
|
213
|
-
def handle_response(feature, response)
|
214
|
-
debug { sprintf('worker response feature=%s code=%s message=%s', feature, response.code, response.message.to_s.dump) }
|
215
|
-
|
216
|
-
case response.code
|
217
|
-
when 429, 503
|
218
|
-
debug { sprintf('worker applying throttle=1.25 feature=%s code=%s', feature, response.code) }
|
219
|
-
queue[feature].throttle(1.25)
|
220
|
-
when 402
|
221
|
-
warn { sprintf('worker disabling feature=%s code=%s', feature, response.code) }
|
222
|
-
mutex.synchronize { features[feature] = false }
|
223
|
-
when 403
|
224
|
-
error { sprintf('worker shutting down (unauthorized) feature=%s code=%s', feature, response.code) }
|
225
|
-
Honeybadger::Agent.stop(true)
|
226
|
-
when 201
|
227
|
-
if throttle = queue[feature].unthrottle
|
228
|
-
debug { sprintf('worker removing throttle=%s feature=%s code=%s', throttle, feature, response.code) }
|
229
|
-
end
|
230
|
-
when :error
|
231
|
-
# Error logged by backend.
|
232
|
-
else
|
233
|
-
warn { sprintf('worker unknown response feature=%s code=%s', feature, response.code) }
|
234
|
-
end
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|