honeybadger 2.0.12 → 2.1.0.beta.1

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: ca4e68b01d27f3bd64f695192d1d21c99fda28f8
4
- data.tar.gz: b807a14363195319e0a9048b38b939b930a2b8b8
3
+ metadata.gz: c15d3c01298686c17a3a9d7e3bff2ea5e8f9c405
4
+ data.tar.gz: 1adcb986179de4841b049ca5e4ad32991abf6d20
5
5
  SHA512:
6
- metadata.gz: 36b00b23c94852b8cd62d78a99f7e8aa14d9658c719cbdc4d0bd809069f63f22eeb29a760d9e6b68a92a69c23f328818f0870f44ab8b0321442f467972f96e53
7
- data.tar.gz: 697be46d36628a635d0cd0adffe1cdc378a076d236dbdc6d2456afe72c4facabf75b8c0e347d289d4f144fceb50fc182bdc67b2ad18955a7e8de1c57c65f6199
6
+ metadata.gz: 3703b084a51b6b101c203ab1eb3a858a0fad93af5f65bf4fe4b2a8a661b9211feb0515f8c18dbc50663090870701b6494f17f220e76e4df78bd2389ec2f9c716
7
+ data.tar.gz: 59f5fe48854c4ee55b9ac424ffa86a2f99141aeaed3dfd66efaa873ea64b429612d06faa15ea9dbacdc73c829155f9c839bac835f8ad66090b66d3d58ae20a4f
data/CHANGELOG.md CHANGED
@@ -1,7 +1,80 @@
1
+ * Trim all sanitized strings to 2k.
2
+
3
+ *Joshua Wood*
4
+
5
+ * Add events to instrumented traces (i.e. in background jobs).
6
+
7
+ *Joshua Wood*
8
+
9
+ * Restart workers once per hour when shutdown due to 40x.
10
+
11
+ *Joshua Wood*
12
+
13
+ * Fix a tracing issue with net/http requests.
14
+
15
+ *Joshua Wood*
16
+
17
+ * Send request data with traces.
18
+
19
+ *Joshua Wood*
20
+
21
+ * Ditch per-controller metrics and add render_partial event to traces.
22
+
23
+ *Benjamin Curtis*
24
+
25
+ * Disable local variables when BetterErrors is detected.
26
+
27
+ *Joshua Wood*
28
+
29
+ * Exit with 1 from deploy command when request fails but ignore the failures in
30
+ the capistrano task.
31
+
32
+ *Joshua Wood*
33
+
34
+ * Support Resque natively.
35
+
36
+ *Joshua Wood*
37
+
38
+ * Configure sidekiq.attempt_threshold to suppress notifications until retry
39
+ threshold is reached.
40
+
41
+ *Joshua Wood*
42
+
43
+ * Prevent Sinatra from using the same middleware more than once and add
44
+ sinatra.enabled setting (default true) to disable auto-initialization
45
+ of Sinatra.
46
+
47
+ *Joshua Wood*
48
+
49
+ * Update default ignored exceptions to include the latest Rails rescue
50
+ responses. (see issue #107)
51
+
52
+ *Joshua Wood*
53
+
1
54
  * Fix bug when processing source extract for action_view templates.
2
55
 
3
56
  *Joshua Wood*
4
57
 
58
+ * Exceptions with the same type but caused within different delayed jobs are not grouped together. They have their component and action set so that the application class name and excecuted action is displayed in the UI.
59
+
60
+ *Panos Korros*
61
+
62
+ * All events logged within a delayed_job even those logged by Honeybadger.notify inherit the context of the delayed job and include the job_id, attempts, last_error and queue
63
+
64
+ *Panos Korros*
65
+
66
+ * Catch Errno::ENFILE when reading system stats.
67
+
68
+ *Dmitry Polushkin*
69
+
70
+ * Use explicit types for config options when casting from ENV.
71
+
72
+ *Joshua Wood*
73
+
74
+ * Add exceptions.unwrap to config.
75
+
76
+ *Joshua Wood*
77
+
5
78
  * Don't access secrets before Rails initialization.
6
79
 
7
80
  *Joshua Wood*
@@ -23,14 +23,12 @@ module Honeybadger
23
23
  # in the array. If only a single value is provided in the array, that is
24
24
  # returned
25
25
  def percentile(threshold)
26
- if (count > 1)
27
- self.sort!
28
- # strip off the top 100-threshold
29
- threshold_index = (((100 - threshold).to_f / 100) * count).round
30
- self[0..-threshold_index].last
31
- else
32
- self.first
33
- end
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
34
32
  end
35
33
 
36
34
  # Calculates the mean squared error of values in the array
@@ -40,12 +40,12 @@ module Honeybadger
40
40
  @marker = ConditionVariable.new
41
41
  @queue = Queue.new(1000)
42
42
  @shutdown = false
43
+ @start_at = nil
43
44
  end
44
45
 
45
46
  def push(obj)
46
- if start
47
- queue.push(obj)
48
- end
47
+ return false unless start
48
+ queue.push(obj)
49
49
  end
50
50
 
51
51
  # Internal: Shutdown the worker after sending remaining data.
@@ -76,6 +76,7 @@ module Honeybadger
76
76
  mutex.synchronize do
77
77
  @shutdown = true
78
78
  @pid = nil
79
+ queue.clear
79
80
  end
80
81
 
81
82
  d { sprintf('killing worker thread feature=%s', feature) }
@@ -101,8 +102,12 @@ module Honeybadger
101
102
  end
102
103
 
103
104
  def start
105
+ return false unless can_start?
106
+
104
107
  mutex.synchronize do
105
- return false if @shutdown
108
+ @shutdown = false
109
+ @start_at = nil
110
+
106
111
  return true if thread && thread.alive?
107
112
 
108
113
  @pid = Process.pid
@@ -117,6 +122,21 @@ module Honeybadger
117
122
  attr_reader :config, :backend, :feature, :queue, :pid, :mutex, :marker,
118
123
  :thread, :throttles
119
124
 
125
+ def can_start?
126
+ mutex.synchronize do
127
+ return true unless @shutdown
128
+ return false unless @start_at
129
+ Time.now.to_i >= @start_at
130
+ end
131
+ end
132
+
133
+ def suspend(interval)
134
+ mutex.synchronize { @start_at = Time.now.to_i + interval }
135
+
136
+ # Must be performed last since this may kill the current thread.
137
+ shutdown!
138
+ end
139
+
120
140
  def run
121
141
  begin
122
142
  d { sprintf('worker started feature=%s', feature) }
@@ -182,11 +202,11 @@ module Honeybadger
182
202
  add_throttle(1.25)
183
203
  debug { sprintf('worker applying throttle=1.25 interval=%s feature=%s code=%s', throttle_interval, feature, response.code) }
184
204
  when 402
185
- warn { sprintf('worker shutting down (payment required) feature=%s code=%s', feature, response.code) }
186
- shutdown!
205
+ warn { sprintf('data will not be reported until next restart (payment required) feature=%s code=%s', feature, response.code) }
206
+ suspend(3600)
187
207
  when 403
188
- warn { sprintf('worker shutting down (unauthorized) feature=%s code=%s', feature, response.code) }
189
- shutdown!
208
+ warn { sprintf('data will not be reported until next restart (unauthorized) feature=%s code=%s', feature, response.code) }
209
+ suspend(3600)
190
210
  when 201
191
211
  if throttle = del_throttle
192
212
  debug { sprintf('worker removing throttle=%s interval=%s feature=%s code=%s', throttle, throttle_interval, feature, response.code) }
@@ -8,6 +8,7 @@ module Honeybadger
8
8
  class Debug < Null
9
9
  def notify(feature, payload)
10
10
  logger.unknown("notifying debug backend of feature=#{feature}\n\t#{payload.to_json}")
11
+ return Response.new(ENV['DEBUG_BACKEND_STATUS'].to_i, nil) if ENV['DEBUG_BACKEND_STATUS']
11
12
  super
12
13
  end
13
14
  end
@@ -47,6 +47,7 @@ module Honeybadger
47
47
  say("Deploy notification for #{payload[:environment]} complete.", :green)
48
48
  else
49
49
  say("Deploy notification failed: #{response.code}", :red)
50
+ exit(1)
50
51
  end
51
52
  rescue => e
52
53
  say("An error occurred during deploy notification: #{e}\n\t#{e.backtrace.join("\n\t")}", :red)
@@ -179,6 +180,7 @@ module Honeybadger
179
180
  say(tab_indent(hierarchy.size) << "#{key}:")
180
181
  indent = tab_indent(hierarchy.size+1)
181
182
  say(indent + "Description: #{Config::OPTIONS[dotted_key][:description]}")
183
+ say(indent + "Type: #{Config::OPTIONS[dotted_key].fetch(:type, String).name.split('::').last}")
182
184
  say(indent + "Default: #{Config::OPTIONS[dotted_key][:default].inspect}")
183
185
  say(indent + "Current: #{value.inspect}")
184
186
  end
@@ -10,6 +10,7 @@ require 'honeybadger/backend'
10
10
  require 'honeybadger/config/defaults'
11
11
  require 'honeybadger/util/http'
12
12
  require 'honeybadger/logging'
13
+ require 'honeybadger/rack/request_hash'
13
14
 
14
15
  module Honeybadger
15
16
  class Config
@@ -25,8 +26,6 @@ module Honeybadger
25
26
 
26
27
  KEY_REPLACEMENT = Regexp.new('[^a-z\d_]', Regexp::IGNORECASE).freeze
27
28
 
28
- DISALLOWED_KEYS = [:'config.path'].freeze
29
-
30
29
  DOTTED_KEY = Regexp.new('\A([^\.]+)\.(.+)\z').freeze
31
30
 
32
31
  NOT_BLANK = Regexp.new('\S').freeze
@@ -39,6 +38,8 @@ module Honeybadger
39
38
  :'exceptions.ignore' => :'exceptions.ignore_only'
40
39
  }.freeze
41
40
 
41
+ DEFAULT_REQUEST_HASH = {}.freeze
42
+
42
43
  def initialize(opts = {})
43
44
  l = opts.delete(:logger)
44
45
 
@@ -136,16 +137,16 @@ module Honeybadger
136
137
  end
137
138
  end
138
139
 
139
- # Internal: Path to honeybadger.yml configuration file; this should be the root
140
- # directory if no path was specified.
140
+ # Internal: Path to honeybadger.yml configuration file; this should be the
141
+ # root directory if no path was specified.
141
142
  #
142
143
  # Returns the Pathname configuration path.
143
144
  def config_path
144
- locate_absolute_path(Array(self[:'config.path']).first, self[:root])
145
+ config_paths.first
145
146
  end
146
147
 
147
148
  def config_paths
148
- Array(self[:'config.path']).map do |c|
149
+ Array(ENV['HONEYBADGER_CONFIG_PATH'] || get(:'config.path')).map do |c|
149
150
  locate_absolute_path(c, self[:root])
150
151
  end
151
152
  end
@@ -191,6 +192,11 @@ module Honeybadger
191
192
  Thread.current[:__honeybadger_request] = nil
192
193
  end
193
194
 
195
+ def request_hash
196
+ return DEFAULT_REQUEST_HASH unless request
197
+ Rack::RequestHash.new(request)
198
+ end
199
+
194
200
  def params_filters
195
201
  self[:'request.filter_keys'] + rails_params_filters
196
202
  end
@@ -2,206 +2,279 @@ require 'socket'
2
2
 
3
3
  module Honeybadger
4
4
  class Config
5
- IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound',
6
- 'ActionController::RoutingError',
5
+ class Boolean; end
6
+
7
+ IGNORE_DEFAULT = ['ActionController::RoutingError',
8
+ 'AbstractController::ActionNotFound',
9
+ 'ActionController::MethodNotAllowed',
10
+ 'ActionController::UnknownHttpMethod',
11
+ 'ActionController::NotImplemented',
12
+ 'ActionController::UnknownFormat',
7
13
  'ActionController::InvalidAuthenticityToken',
14
+ 'ActionController::InvalidCrossOriginRequest',
15
+ 'ActionDispatch::ParamsParser::ParseError',
16
+ 'ActionController::BadRequest',
17
+ 'ActionController::ParameterMissing',
18
+ 'ActiveRecord::RecordNotFound',
8
19
  'ActionController::UnknownAction',
9
- 'ActionController::UnknownFormat',
10
- 'AbstractController::ActionNotFound',
11
20
  'CGI::Session::CookieStore::TamperedWithCookie',
12
21
  'Mongoid::Errors::DocumentNotFound',
13
22
  'Sinatra::NotFound'].map(&:freeze).freeze
14
23
 
15
24
  DEVELOPMENT_ENVIRONMENTS = ['development', 'test', 'cucumber'].map(&:freeze).freeze
16
25
 
26
+ DEFAULT_PATHS = ['honeybadger.yml', 'config/honeybadger.yml'].map(&:freeze).freeze
27
+
17
28
  OPTIONS = {
18
29
  api_key: {
19
30
  description: 'The API key for your Honeybadger project.',
20
- default: nil
31
+ default: nil,
32
+ type: String
21
33
  },
22
34
  env: {
23
35
  description: 'The current application\'s environment name.',
24
- default: ENV['HONEYBADGER_ENV'] || ENV['RACK_ENV']
36
+ default: ENV['HONEYBADGER_ENV'] || ENV['RACK_ENV'],
37
+ type: String
25
38
  },
26
39
  report_data: {
27
40
  description: 'Enable/disable reporting of data. Defaults to true for non-development environments.',
28
- default: nil
41
+ default: nil,
42
+ type: Boolean
29
43
  },
30
44
  root: {
31
45
  description: 'The project\'s absolute root path.',
32
- default: Dir.pwd
46
+ default: Dir.pwd,
47
+ type: String
33
48
  },
34
49
  hostname: {
35
50
  description: 'The hostname of the current box.',
36
- default: Socket.gethostname
51
+ default: Socket.gethostname,
52
+ type: String
37
53
  },
38
54
  backend: {
39
55
  description: 'An alternate backend to use for reporting data.',
40
- default: nil
56
+ default: nil,
57
+ type: String
41
58
  },
42
59
  debug: {
43
60
  description: 'Forces metrics and traces to be reported every 10 seconds rather than 60.',
44
- default: false
61
+ default: false,
62
+ type: Boolean
45
63
  },
46
64
  disabled: {
47
65
  description: 'Prevents Honeybadger from starting entirely.',
48
- default: false
66
+ default: false,
67
+ type: Boolean
49
68
  },
50
69
  development_environments: {
51
70
  description: 'Environments which will not report data by default (use report_data to enable/disable explicitly).',
52
- default: DEVELOPMENT_ENVIRONMENTS
71
+ default: DEVELOPMENT_ENVIRONMENTS,
72
+ type: Array
53
73
  },
54
74
  :'send_data_at_exit' => {
55
75
  description: 'Send remaining data when Ruby exits.',
56
- default: true
76
+ default: true,
77
+ type: Boolean
57
78
  },
58
79
  plugins: {
59
80
  description: 'An optional list of plugins to load. Default is to load all plugins.',
60
- default: nil
81
+ default: nil,
82
+ type: Array
61
83
  },
62
84
  :'plugins.skip' => {
63
85
  description: 'An optional list of plugins to skip.',
64
- default: nil
86
+ default: nil,
87
+ type: Array
65
88
  },
66
89
  :'config.path' => {
67
90
  description: 'The path (absolute, or relative from config.root) to the project\'s YAML configuration file.',
68
- default: ENV['HONEYBADGER_CONFIG_PATH'] || ['honeybadger.yml', 'config/honeybadger.yml']
91
+ default: DEFAULT_PATHS,
92
+ type: String
69
93
  },
70
94
  :'logging.path' => {
71
95
  description: 'The path (absolute, or relative from config.root) to the log file.',
72
- default: nil
96
+ default: nil,
97
+ type: String
73
98
  },
74
99
  :'logging.level' => {
75
100
  description: 'The log level.',
76
- default: 'INFO'
101
+ default: 'INFO',
102
+ type: String
77
103
  },
78
104
  :'logging.debug' => {
79
105
  description: 'Override debug logging.',
80
- default: nil
106
+ default: nil,
107
+ type: Boolean
81
108
  },
82
109
  :'logging.tty_level' => {
83
110
  description: 'Level to log when attached to a terminal (anything < logging.level will always be ignored).',
84
- default: 'DEBUG'
111
+ default: 'DEBUG',
112
+ type: String
85
113
  },
86
114
  :'connection.secure' => {
87
115
  description: 'Use SSL when sending data.',
88
- default: true
116
+ default: true,
117
+ type: Boolean
89
118
  },
90
119
  :'connection.host' => {
91
120
  description: 'The host to use when sending data.',
92
- default: 'api.honeybadger.io'.freeze
121
+ default: 'api.honeybadger.io'.freeze,
122
+ type: String
93
123
  },
94
124
  :'connection.port' => {
95
125
  description: 'The port to use when sending data.',
96
- default: nil
126
+ default: nil,
127
+ type: Integer
97
128
  },
98
129
  :'connection.system_ssl_cert_chain' => {
99
130
  description: 'Use the system\'s SSL certificate chain (if available).',
100
- default: false
131
+ default: false,
132
+ type: Boolean
101
133
  },
102
134
  :'connection.http_open_timeout' => {
103
135
  description: 'The HTTP open timeout when connecting to the server.',
104
- default: 2
136
+ default: 2,
137
+ type: Integer
105
138
  },
106
139
  :'connection.http_read_timeout' => {
107
140
  description: 'The HTTP read timeout when connecting to the server.',
108
- default: 5
141
+ default: 5,
142
+ type: Integer
109
143
  },
110
144
  :'connection.proxy_host' => {
111
145
  description: 'The proxy host to use when sending data.',
112
- default: nil
146
+ default: nil,
147
+ type: String
113
148
  },
114
149
  :'connection.proxy_port' => {
115
150
  description: 'The proxy port to use when sending data.',
116
- default: nil
151
+ default: nil,
152
+ type: Integer
117
153
  },
118
154
  :'connection.proxy_user' => {
119
155
  description: 'The proxy user to use when sending data.',
120
- default: nil
156
+ default: nil,
157
+ type: String
121
158
  },
122
159
  :'connection.proxy_pass' => {
123
160
  description: 'The proxy password to use when sending data.',
124
- default: nil
161
+ default: nil,
162
+ type: String
125
163
  },
126
164
  :'request.filter_keys' => {
127
165
  description: 'A list of keys to filter when sending request data.',
128
- default: ['password'.freeze, 'password_confirmation'.freeze].freeze
166
+ default: ['password'.freeze, 'password_confirmation'.freeze].freeze,
167
+ type: Array
129
168
  },
130
169
  :'request.disable_session' => {
131
170
  description: 'Prevent session from being sent with request data.',
132
- default: false
171
+ default: false,
172
+ type: Boolean
133
173
  },
134
174
  :'request.disable_params' => {
135
175
  description: 'Prevent params from being sent with request data.',
136
- default: false
176
+ default: false,
177
+ type: Boolean
137
178
  },
138
179
  :'request.disable_environment' => {
139
180
  description: 'Prevent Rack environment from being sent with request data.',
140
- default: false
181
+ default: false,
182
+ type: Boolean
141
183
  },
142
184
  :'request.disable_url' => {
143
185
  description: 'Prevent url from being sent with request data (Rack environment may still contain it in some cases).',
144
- default: false
186
+ default: false,
187
+ type: Boolean
145
188
  },
146
189
  :'user_informer.enabled' => {
147
190
  description: 'Enable the UserInformer middleware.',
148
- default: true
191
+ default: true,
192
+ type: Boolean
149
193
  },
150
194
  :'user_informer.info' => {
151
195
  description: 'Replacement string for HTML comment in templates.',
152
- default: 'Honeybadger Error {{error_id}}'.freeze
196
+ default: 'Honeybadger Error {{error_id}}'.freeze,
197
+ type: String
153
198
  },
154
199
  :'feedback.enabled' => {
155
200
  description: 'Enable the UserFeedback middleware.',
156
- default: true
201
+ default: true,
202
+ type: Boolean
157
203
  },
158
204
  :'exceptions.enabled' => {
159
205
  description: 'Enable automatic reporting of exceptions.',
160
- default: true
206
+ default: true,
207
+ type: Boolean
161
208
  },
162
209
  :'exceptions.ignore' => {
163
210
  description: 'A list of additional exceptions to ignore (includes default ignored exceptions).',
164
- default: IGNORE_DEFAULT
211
+ default: IGNORE_DEFAULT,
212
+ type: Array
165
213
  },
166
214
  :'exceptions.ignore_only' => {
167
215
  description: 'A list of exceptions to ignore (overrides the default ignored exceptions).',
168
- default: [].freeze
216
+ default: [].freeze,
217
+ type: Array
169
218
  },
170
219
  :'exceptions.ignored_user_agents' => {
171
220
  description: 'A list of user agents to ignore.',
172
- default: [].freeze
221
+ default: [].freeze,
222
+ type: Array
173
223
  },
174
224
  :'exceptions.rescue_rake' => {
175
225
  description: 'Enable rescuing exceptions in rake tasks.',
176
- default: true
226
+ default: true,
227
+ type: Boolean
177
228
  },
178
229
  :'exceptions.source_radius' => {
179
230
  description: 'The number of lines before and after the source when reporting snippets.',
180
- default: 2
231
+ default: 2,
232
+ type: Integer
181
233
  },
182
234
  :'exceptions.local_variables' => {
183
235
  description: 'Enable sending local variables. Requires binding_of_caller to be loaded.',
184
- default: false
236
+ default: false,
237
+ type: Boolean
238
+ },
239
+ :'exceptions.unwrap' => {
240
+ description: 'Reports #original_exception or #cause one level up from rescued exception when available.',
241
+ default: false,
242
+ type: Boolean
185
243
  },
186
244
  :'metrics.enabled' => {
187
245
  description: 'Enable sending metrics.',
188
- default: true
246
+ default: true,
247
+ type: Boolean
189
248
  },
190
249
  :'metrics.gc_profiler' => {
191
250
  description: 'Enable sending GC metrics (GC::Profiler must be enabled)',
192
- default: false
251
+ default: false,
252
+ type: Boolean
193
253
  },
194
254
  :'traces.enabled' => {
195
255
  description: 'Enable sending traces.',
196
- default: true
256
+ default: true,
257
+ type: Boolean
197
258
  },
198
259
  :'traces.threshold' => {
199
260
  description: 'The threshold in seconds to send traces.',
200
- default: 2000
261
+ default: 2000,
262
+ type: Integer
201
263
  },
202
264
  :'delayed_job.attempt_threshold' => {
203
265
  description: 'The number of attempts before notifications will be sent.',
204
- default: 0
266
+ default: 0,
267
+ type: Integer
268
+ },
269
+ :'sidekiq.attempt_threshold' => {
270
+ description: 'The number of attempts before notifications will be sent.',
271
+ default: 0,
272
+ type: Integer
273
+ },
274
+ :'sinatra.enabled' => {
275
+ description: 'Enable Sinatra auto-initialization.',
276
+ default: true,
277
+ type: Boolean
205
278
  }
206
279
  }.freeze
207
280