airbrake-ruby 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: feeb44b47c5b58cdf1f478065f84640af9102ab9
4
- data.tar.gz: 2319d39125a79fb075a3a629fa3dc8c927e95dc2
3
+ metadata.gz: 28a4924a3ba67bad999146c40bb8b4a18d42c07e
4
+ data.tar.gz: 08b97f8c2ff47d1e78665df246f71b65fbc401fe
5
5
  SHA512:
6
- metadata.gz: 78028e33ffe3b0929fc6f5e26b18933d39df58edade9cbf45bd1ca9025d2121201cbf8fa114f9c9ff5614336d2521012d44d9b05dfadb0af08d9710ee40b3bcd
7
- data.tar.gz: 14e8924670cac95429572ca814d61421459797059a357a39cf23b18241bf8cb9ff084bbb5b6357441e4f85ae5e11cbff2aa18e24668ec03bc493cb45131dbe8a
6
+ metadata.gz: ac71aa148439f4cd54a6b6b6c499d0a5e832f58b0e95897dd837a65219cea8a244f700c38f59340f74f1db8ee82cc4b4fead1dfde267d0adb1207c6819c80d29
7
+ data.tar.gz: 8c6a568e091568b2b3cbb56f1897753bbaa30b5343b9b14055ff815e0fd7244c34036ff6a32f0395d9473573dc2336db414f6703fc49be5bffdbe6f54cade94c
@@ -3,7 +3,6 @@ require 'logger'
3
3
  require 'json'
4
4
  require 'thread'
5
5
  require 'set'
6
- require 'English'
7
6
  require 'socket'
8
7
 
9
8
  require 'airbrake-ruby/version'
@@ -196,6 +195,7 @@ module Airbrake
196
195
  # @return [void]
197
196
  # @since v5.0.0
198
197
  # @see .blacklist_keys
198
+ # @deprecated Please use {Airbrake::Config#whitelist_keys} instead
199
199
  def whitelist_keys(keys, notifier = :default)
200
200
  call_notifier(notifier, __method__, keys)
201
201
  end
@@ -213,6 +213,7 @@ module Airbrake
213
213
  # @return [void]
214
214
  # @since v5.0.0
215
215
  # @see .whitelist_keys
216
+ # @deprecated Please use {Airbrake::Config#blacklist_keys} instead
216
217
  def blacklist_keys(keys, notifier = :default)
217
218
  call_notifier(notifier, __method__, keys)
218
219
  end
@@ -291,8 +292,3 @@ module Airbrake
291
292
  end
292
293
  end
293
294
  end
294
-
295
- # Notify of unhandled exceptions, if there were any, but ignore SystemExit.
296
- at_exit do
297
- Airbrake.notify_sync($ERROR_INFO) if $ERROR_INFO
298
- end
@@ -14,6 +14,7 @@ module Airbrake
14
14
  @sender = SyncSender.new(config)
15
15
  @closed = false
16
16
  @workers = ThreadGroup.new
17
+ @mutex = Mutex.new
17
18
  @pid = nil
18
19
  end
19
20
 
@@ -37,19 +38,22 @@ module Airbrake
37
38
  # @return [void]
38
39
  # @raise [Airbrake::Error] when invoked more than one time
39
40
  def close
40
- if closed?
41
- raise Airbrake::Error, 'attempted to close already closed sender'
42
- end
41
+ threads = @mutex.synchronize do
42
+ if closed?
43
+ raise Airbrake::Error, 'attempted to close already closed sender'
44
+ end
43
45
 
44
- unless @unsent.empty?
45
- msg = "#{LOG_LABEL} waiting to send #{@unsent.size} unsent notice(s)..."
46
- @config.logger.debug(msg + ' (Ctrl-C to abort)')
47
- end
46
+ unless @unsent.empty?
47
+ msg = "#{LOG_LABEL} waiting to send #{@unsent.size} unsent notice(s)..."
48
+ @config.logger.debug(msg + ' (Ctrl-C to abort)')
49
+ end
48
50
 
49
- @config.workers.times { @unsent << :stop }
50
- @workers.list.each(&:join)
51
- @closed = true
51
+ @config.workers.times { @unsent << :stop }
52
+ @closed = true
53
+ @workers.list.dup
54
+ end
52
55
 
56
+ threads.each(&:join)
53
57
  @config.logger.debug("#{LOG_LABEL} closed")
54
58
  end
55
59
 
@@ -57,6 +57,18 @@ module Airbrake
57
57
  # @return [Integer] The HTTP timeout in seconds.
58
58
  attr_accessor :timeout
59
59
 
60
+ ##
61
+ # @return [Array<String, Symbol, Regexp>] the keys, which should be
62
+ # filtered
63
+ # @since 1.2.0
64
+ attr_accessor :blacklist_keys
65
+
66
+ ##
67
+ # @return [Array<String, Symbol, Regexp>] the keys, which shouldn't be
68
+ # filtered
69
+ # @since 1.2.0
70
+ attr_accessor :whitelist_keys
71
+
60
72
  ##
61
73
  # @param [Hash{Symbol=>Object}] user_config the hash to be used to build the
62
74
  # config
@@ -76,6 +88,9 @@ module Airbrake
76
88
 
77
89
  self.timeout = user_config[:timeout]
78
90
 
91
+ self.blacklist_keys = []
92
+ self.whitelist_keys = []
93
+
79
94
  merge(user_config)
80
95
  end
81
96
 
@@ -30,7 +30,13 @@ module Airbrake
30
30
  # @return [Boolean] true if the key matches at least one pattern, false
31
31
  # otherwise
32
32
  def should_filter?(key)
33
- @patterns.any? { |pattern| key.to_s.match(pattern) }
33
+ @patterns.any? do |pattern|
34
+ if pattern.is_a?(Regexp)
35
+ key.match(pattern)
36
+ else
37
+ key.to_s == pattern.to_s
38
+ end
39
+ end
34
40
  end
35
41
  end
36
42
  end
@@ -9,13 +9,17 @@ module Airbrake
9
9
  # @see KeysWhitelist
10
10
  # @see KeysBlacklist
11
11
  module KeysFilter
12
+ ##
13
+ # @return [String] The label to replace real values of filtered payload
14
+ FILTERED = '[Filtered]'.freeze
15
+
12
16
  ##
13
17
  # Creates a new KeysBlacklist or KeysWhitelist filter that uses the given
14
18
  # +patterns+ for filtering a notice's payload.
15
19
  #
16
20
  # @param [Array<String,Regexp,Symbol>] patterns
17
21
  def initialize(*patterns)
18
- @patterns = patterns.map(&:to_s)
22
+ @patterns = patterns
19
23
  end
20
24
 
21
25
  ##
@@ -28,6 +32,10 @@ module Airbrake
28
32
  def call(notice)
29
33
  FILTERABLE_KEYS.each { |key| filter_hash(notice[key]) }
30
34
 
35
+ if notice[:context][:user] && should_filter?(:user)
36
+ notice[:context][:user] = FILTERED
37
+ end
38
+
31
39
  return unless notice[:context][:url]
32
40
  url = URI(notice[:context][:url])
33
41
  return if url.nil? || url.query.nil?
@@ -46,7 +54,7 @@ module Airbrake
46
54
  def filter_hash(hash)
47
55
  hash.each_key do |key|
48
56
  if should_filter?(key)
49
- hash[key] = '[Filtered]'.freeze
57
+ hash[key] = FILTERED
50
58
  elsif hash[key].is_a?(Hash)
51
59
  filter_hash(hash[key])
52
60
  end
@@ -30,7 +30,13 @@ module Airbrake
30
30
  # @return [Boolean] true if the key doesn't match any pattern, false
31
31
  # otherwise.
32
32
  def should_filter?(key)
33
- @patterns.none? { |pattern| key.to_s.match(pattern) }
33
+ @patterns.none? do |pattern|
34
+ if pattern.is_a?(Regexp)
35
+ key.match(pattern)
36
+ else
37
+ key.to_s == pattern.to_s
38
+ end
39
+ end
34
40
  end
35
41
  end
36
42
  end
@@ -7,6 +7,10 @@ module Airbrake
7
7
  # @api private
8
8
  # @since v5.0.0
9
9
  class Notifier
10
+ ##
11
+ # @return [String] the label to be prepended to the log output
12
+ LOG_LABEL = '**Airbrake:'.freeze
13
+
10
14
  ##
11
15
  # Creates a new Airbrake notifier with the given config options.
12
16
  #
@@ -31,6 +35,15 @@ module Airbrake
31
35
  end
32
36
 
33
37
  @filter_chain = FilterChain.new(@config)
38
+
39
+ if @config.blacklist_keys.any?
40
+ add_filter(Filters::KeysBlacklist.new(*@config.blacklist_keys))
41
+ end
42
+
43
+ if @config.whitelist_keys.any?
44
+ add_filter(Filters::KeysWhitelist.new(*@config.whitelist_keys))
45
+ end
46
+
34
47
  @async_sender = AsyncSender.new(@config)
35
48
  @sync_sender = SyncSender.new(@config)
36
49
  end
@@ -60,13 +73,23 @@ module Airbrake
60
73
 
61
74
  ##
62
75
  # @macro see_public_api_method
76
+ # @deprecated Please use {Airbrake::Config#whitelist_keys} instead
63
77
  def whitelist_keys(keys)
78
+ @config.logger.warn(
79
+ "#{LOG_LABEL} Airbrake.whitelist_keys is deprecated. Please use the " \
80
+ "whitelist_keys option instead (https://goo.gl/sQwpYN)"
81
+ )
64
82
  add_filter(Filters::KeysWhitelist.new(*keys))
65
83
  end
66
84
 
67
85
  ##
68
86
  # @macro see_public_api_method
87
+ # @deprecated Please use {Airbrake::Config#blacklist_keys} instead
69
88
  def blacklist_keys(keys)
89
+ @config.logger.warn(
90
+ "#{LOG_LABEL} Airbrake.blacklist_keys is deprecated. Please use the " \
91
+ "blacklist_keys option instead (https://goo.gl/jucrFt)"
92
+ )
70
93
  add_filter(Filters::KeysBlacklist.new(*keys))
71
94
  end
72
95
 
@@ -8,18 +8,6 @@ module Airbrake
8
8
  # @return [String] body for HTTP requests
9
9
  CONTENT_TYPE = 'application/json'.freeze
10
10
 
11
- ##
12
- # @return [Array] the errors to be rescued and logged during an HTTP request
13
- HTTP_ERRORS = [
14
- Timeout::Error,
15
- Net::HTTPBadResponse,
16
- Net::HTTPHeaderSyntaxError,
17
- Errno::ECONNRESET,
18
- Errno::ECONNREFUSED,
19
- EOFError,
20
- OpenSSL::SSL::SSLError
21
- ].freeze
22
-
23
11
  ##
24
12
  # @param [Airbrake::Config] config
25
13
  def initialize(config)
@@ -39,7 +27,7 @@ module Airbrake
39
27
 
40
28
  begin
41
29
  response = https.request(req)
42
- rescue *HTTP_ERRORS => ex
30
+ rescue => ex
43
31
  @config.logger.error("#{LOG_LABEL} HTTP error: #{ex}")
44
32
  return
45
33
  end
@@ -3,5 +3,5 @@
3
3
  module Airbrake
4
4
  ##
5
5
  # @return [String] the library version
6
- AIRBRAKE_RUBY_VERSION = '1.1.0'.freeze
6
+ AIRBRAKE_RUBY_VERSION = '1.2.0'.freeze
7
7
  end
@@ -66,6 +66,14 @@ RSpec.describe Airbrake::Config do
66
66
  it "doesn't set default timeout" do
67
67
  expect(config.timeout).to be_nil
68
68
  end
69
+
70
+ it "doesn't set default blacklist" do
71
+ expect(config.blacklist_keys).to be_empty
72
+ end
73
+
74
+ it "doesn't set default whitelist" do
75
+ expect(config.whitelist_keys).to be_empty
76
+ end
69
77
  end
70
78
  end
71
79
  end
@@ -495,11 +495,11 @@ RSpec.describe Airbrake::Notifier do
495
495
  it "accepts strings" do
496
496
  @airbrake.blacklist_keys('bingo')
497
497
 
498
- @airbrake.notify_sync(ex, bingo: 'bango')
498
+ @airbrake.notify_sync(ex, bingo: 'bango', bbingoo: 'bbangoo')
499
499
 
500
500
  expect(
501
501
  a_request(:post, endpoint).
502
- with(body: /"params":{"bingo":"\[Filtered\]"}/)
502
+ with(body: /"params":{"bingo":"\[Filtered\]","bbingoo":"bbangoo"}/)
503
503
  ).to have_been_made.once
504
504
  end
505
505
  end
@@ -553,6 +553,17 @@ RSpec.describe Airbrake::Notifier do
553
553
  with(body: expected_body)
554
554
  ).to have_been_made.once
555
555
  end
556
+
557
+ it "filters out user" do
558
+ @airbrake.blacklist_keys('user')
559
+
560
+ notice = @airbrake.build_notice(ex)
561
+ notice[:context][:user] = { id: 1337, name: 'Bingo Bango' }
562
+
563
+ @airbrake.notify_sync(notice)
564
+
565
+ expect_a_request_with_body(/"user":"\[Filtered\]"/)
566
+ end
556
567
  end
557
568
 
558
569
  describe "#whitelist_keys" do
@@ -586,13 +597,20 @@ RSpec.describe Airbrake::Notifier do
586
597
  it "accepts strings" do
587
598
  @airbrake.whitelist_keys('bash')
588
599
 
589
- @airbrake.notify_sync(ex, bingo: 'bango', bongo: 'bish', bash: 'bosh')
590
-
591
- body = /"params":{"bingo":"\[Filtered\]","bongo":"\[Filtered\]","bash":"bosh"}/
600
+ @airbrake.notify_sync(
601
+ ex,
602
+ bingo: 'bango',
603
+ bongo: 'bish',
604
+ bash: 'bosh',
605
+ bbashh: 'bboshh'
606
+ )
592
607
 
593
608
  expect(
594
609
  a_request(:post, endpoint).
595
- with(body: body)
610
+ with(
611
+ body: /"params":{"bingo":"\[Filtered\]","bongo":"\[Filtered\]",
612
+ "bash":"bosh","bbashh":"\[Filtered\]"}/x
613
+ )
596
614
  ).to have_been_made.once
597
615
  end
598
616
  end
@@ -1,6 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe Airbrake::Notifier do
4
+ def expect_a_request_with_body(body)
5
+ expect(a_request(:post, endpoint).with(body: body)).to have_been_made.once
6
+ end
7
+
4
8
  let(:project_id) { 105138 }
5
9
  let(:project_key) { 'fd04e13d806a90f96614ad8e529b2822' }
6
10
  let(:localhost) { 'http://localhost:8080' }
@@ -213,5 +217,200 @@ RSpec.describe Airbrake::Notifier do
213
217
  include_examples 'sent notice', environment: :development
214
218
  end
215
219
  end
220
+
221
+ describe ":blacklist_keys" do
222
+ describe "the list of values" do
223
+ it "accepts regexps" do
224
+ params = { blacklist_keys: [/\Abin/] }
225
+ airbrake = described_class.new(airbrake_params.merge(params))
226
+
227
+ airbrake.notify_sync(ex, bingo: 'bango')
228
+
229
+ expect_a_request_with_body(/"params":{"bingo":"\[Filtered\]"}/)
230
+ end
231
+
232
+ it "accepts symbols" do
233
+ params = { blacklist_keys: [:bingo] }
234
+ airbrake = described_class.new(airbrake_params.merge(params))
235
+
236
+ airbrake.notify_sync(ex, bingo: 'bango')
237
+
238
+ expect_a_request_with_body(/"params":{"bingo":"\[Filtered\]"}/)
239
+ end
240
+
241
+ it "accepts strings" do
242
+ params = { blacklist_keys: ['bingo'] }
243
+ airbrake = described_class.new(airbrake_params.merge(params))
244
+
245
+ airbrake.notify_sync(ex, bingo: 'bango')
246
+
247
+ expect_a_request_with_body(/"params":{"bingo":"\[Filtered\]"}/)
248
+ end
249
+ end
250
+
251
+ describe "hash values" do
252
+ context "non-recursive" do
253
+ it "filters nested hashes" do
254
+ params = { blacklist_keys: ['bish'] }
255
+ airbrake = described_class.new(airbrake_params.merge(params))
256
+
257
+ airbrake.notify_sync(ex, bongo: { bish: 'bash' })
258
+
259
+ expect_a_request_with_body(/"params":{"bongo":{"bish":"\[Filtered\]"}}/)
260
+ end
261
+ end
262
+
263
+ context "recursive" do
264
+ it "filters recursive hashes" do
265
+ params = { blacklist_keys: ['bango'] }
266
+ airbrake = described_class.new(airbrake_params.merge(params))
267
+
268
+ bongo = { bingo: {} }
269
+ bongo[:bingo][:bango] = bongo
270
+
271
+ airbrake.notify_sync(ex, bongo)
272
+
273
+ expect_a_request_with_body(/"params":{"bingo":{"bango":"\[Filtered\]"}}/)
274
+ end
275
+ end
276
+ end
277
+
278
+ it "filters query parameters correctly" do
279
+ params = { blacklist_keys: ['bish'] }
280
+ airbrake = described_class.new(airbrake_params.merge(params))
281
+
282
+ notice = airbrake.build_notice(ex)
283
+ notice[:context][:url] = 'http://localhost:3000/crash?foo=bar&baz=bongo&bish=bash&color=%23FFAAFF'
284
+
285
+ airbrake.notify_sync(notice)
286
+
287
+ # rubocop:disable Metrics/LineLength
288
+ expected_body =
289
+ %r("context":{.*"url":"http://localhost:3000/crash\?foo=bar&baz=bongo&bish=\[Filtered\]&color=%23FFAAFF".*})
290
+ # rubocop:enable Metrics/LineLength
291
+
292
+ expect_a_request_with_body(expected_body)
293
+ end
294
+
295
+ it "filters out user" do
296
+ params = { blacklist_keys: ['user'] }
297
+ airbrake = described_class.new(airbrake_params.merge(params))
298
+
299
+ notice = airbrake.build_notice(ex)
300
+ notice[:context][:user] = { id: 1337, name: 'Bingo Bango' }
301
+
302
+ airbrake.notify_sync(notice)
303
+
304
+ expect_a_request_with_body(/"user":"\[Filtered\]"/)
305
+ end
306
+ end
307
+
308
+ describe ":whitelist_keys" do
309
+ describe "the list of values" do
310
+ it "accepts regexes" do
311
+ params = { whitelist_keys: [/\Abin/] }
312
+ airbrake = described_class.new(airbrake_params.merge(params))
313
+
314
+ airbrake.notify_sync(ex, bingo: 'bango', bongo: 'bish', bash: 'bosh')
315
+
316
+ expect_a_request_with_body(
317
+ /"params":{"bingo":"bango","bongo":"\[Filtered\]","bash":"\[Filtered\]"}/
318
+ )
319
+ end
320
+
321
+ it "accepts symbols" do
322
+ params = { whitelist_keys: [:bongo] }
323
+ airbrake = described_class.new(airbrake_params.merge(params))
324
+
325
+ airbrake.notify_sync(ex, bingo: 'bango', bongo: 'bish', bash: 'bosh')
326
+
327
+ expect_a_request_with_body(
328
+ /"params":{"bingo":"\[Filtered\]","bongo":"bish","bash":"\[Filtered\]"}/
329
+ )
330
+ end
331
+
332
+ it "accepts strings" do
333
+ params = { whitelist_keys: ['bash'] }
334
+ airbrake = described_class.new(airbrake_params.merge(params))
335
+
336
+ airbrake.notify_sync(
337
+ ex,
338
+ bingo: 'bango',
339
+ bongo: 'bish',
340
+ bash: 'bosh',
341
+ bbashh: 'bboshh'
342
+ )
343
+
344
+ expect_a_request_with_body(
345
+ /"params":{"bingo":"\[Filtered\]","bongo":"\[Filtered\]",
346
+ "bash":"bosh","bbashh":"\[Filtered\]"}/x
347
+ )
348
+ end
349
+ end
350
+
351
+ describe "hash values" do
352
+ context "non-recursive" do
353
+ it "filters out everything but the provided keys" do
354
+ params = { whitelist_keys: %w(bongo bish) }
355
+ airbrake = described_class.new(airbrake_params.merge(params))
356
+
357
+ airbrake.notify_sync(ex, bingo: 'bango', bongo: { bish: 'bash' })
358
+
359
+ expect_a_request_with_body(
360
+ /"params":{"bingo":"\[Filtered\]","bongo":{"bish":"bash"}}/
361
+ )
362
+ end
363
+ end
364
+
365
+ context "recursive" do
366
+ it "errors when nested hashes are not filtered" do
367
+ params = { whitelist_keys: %w(bingo bango) }
368
+ airbrake = described_class.new(airbrake_params.merge(params))
369
+
370
+ bongo = { bingo: {} }
371
+ bongo[:bingo][:bango] = bongo
372
+
373
+ if RUBY_ENGINE == 'jruby'
374
+ # JRuby might raise two different exceptions, which represent the
375
+ # same thing. One is a Java exception, the other is a Ruby
376
+ # exception. It's probably a JRuby bug:
377
+ # https://github.com/jruby/jruby/issues/1903
378
+ begin
379
+ expect do
380
+ airbrake.notify_sync(ex, bongo)
381
+ end.to raise_error(SystemStackError)
382
+ rescue RSpec::Expectations::ExpectationNotMetError
383
+ expect do
384
+ airbrake.notify_sync(ex, bongo)
385
+ end.to raise_error(java.lang.StackOverflowError)
386
+ end
387
+ else
388
+ expect do
389
+ airbrake.notify_sync(ex, bongo)
390
+ end.to raise_error(SystemStackError)
391
+ end
392
+ end
393
+ end
394
+ end
395
+
396
+ describe "context/url" do
397
+ it "filters query parameters correctly" do
398
+ params = { whitelist_keys: %w(bish) }
399
+ airbrake = described_class.new(airbrake_params.merge(params))
400
+
401
+ notice = airbrake.build_notice(ex)
402
+ notice[:context][:url] = 'http://localhost:3000/crash?foo=bar&baz=bongo&bish=bash'
403
+
404
+ airbrake.notify_sync(notice)
405
+
406
+ # rubocop:disable Metrics/LineLength
407
+ expected_body =
408
+ %r("context":{.*"url":"http://localhost:3000/crash\?foo=\[Filtered\]&baz=\[Filtered\]&bish=bash".*})
409
+ # rubocop:enable Metrics/LineLength
410
+
411
+ expect_a_request_with_body(expected_body)
412
+ end
413
+ end
414
+ end
216
415
  end
217
416
  end
@@ -10,4 +10,18 @@ RSpec.describe Airbrake::SyncSender do
10
10
  expect(https.read_timeout).to eq(10)
11
11
  end
12
12
  end
13
+
14
+ describe "#send" do
15
+ it "catches exceptions raised when sending" do
16
+ stdout = StringIO.new
17
+ config = Airbrake::Config.new(logger: Logger.new(stdout))
18
+ sender = described_class.new config
19
+ notice = Airbrake::Notice.new(config, AirbrakeTestError.new)
20
+ https = double("foo")
21
+ allow(sender).to receive(:build_https).and_return(https)
22
+ allow(https).to receive(:request).and_raise(StandardError.new('foo'))
23
+ expect(sender.send(notice)).to be_nil
24
+ expect(stdout.string).to match(/ERROR -- : .+ HTTP error: foo/)
25
+ end
26
+ end
13
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: airbrake-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Airbrake Technologies, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-26 00:00:00.000000000 Z
11
+ date: 2016-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -160,3 +160,4 @@ test_files:
160
160
  - spec/payload_truncator_spec.rb
161
161
  - spec/spec_helper.rb
162
162
  - spec/sync_sender_spec.rb
163
+ has_rdoc: