airbrake-ruby 2.9.0 → 2.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/airbrake-ruby.rb +40 -18
- data/lib/airbrake-ruby/async_sender.rb +0 -6
- data/lib/airbrake-ruby/backtrace.rb +0 -10
- data/lib/airbrake-ruby/code_hunk.rb +0 -4
- data/lib/airbrake-ruby/config.rb +23 -22
- data/lib/airbrake-ruby/config/validator.rb +0 -10
- data/lib/airbrake-ruby/file_cache.rb +0 -6
- data/lib/airbrake-ruby/filter_chain.rb +0 -5
- data/lib/airbrake-ruby/filters/context_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/dependency_filter.rb +31 -0
- data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +45 -0
- data/lib/airbrake-ruby/filters/gem_root_filter.rb +2 -3
- data/lib/airbrake-ruby/filters/keys_blacklist.rb +1 -2
- data/lib/airbrake-ruby/filters/keys_filter.rb +9 -14
- data/lib/airbrake-ruby/filters/keys_whitelist.rb +0 -2
- data/lib/airbrake-ruby/filters/root_directory_filter.rb +2 -3
- data/lib/airbrake-ruby/filters/system_exit_filter.rb +2 -3
- data/lib/airbrake-ruby/filters/thread_filter.rb +2 -4
- data/lib/airbrake-ruby/nested_exception.rb +0 -2
- data/lib/airbrake-ruby/notice.rb +6 -44
- data/lib/airbrake-ruby/notifier.rb +4 -40
- data/lib/airbrake-ruby/promise.rb +0 -6
- data/lib/airbrake-ruby/response.rb +0 -4
- data/lib/airbrake-ruby/sync_sender.rb +0 -4
- data/lib/airbrake-ruby/version.rb +1 -3
- data/spec/airbrake_spec.rb +71 -140
- data/spec/async_sender_spec.rb +9 -0
- data/spec/config_spec.rb +4 -0
- data/spec/filters/dependency_filter_spec.rb +16 -0
- data/spec/filters/exception_attributes_filter_spec.rb +65 -0
- data/spec/filters/keys_whitelist_spec.rb +17 -23
- data/spec/notice_spec.rb +111 -69
- data/spec/notifier_spec.rb +304 -495
- data/spec/response_spec.rb +82 -0
- data/spec/sync_sender_spec.rb +31 -14
- metadata +10 -2
@@ -1,16 +1,14 @@
|
|
1
1
|
module Airbrake
|
2
|
-
##
|
3
2
|
# This class is reponsible for sending notices to Airbrake. It supports
|
4
3
|
# synchronous and asynchronous delivery.
|
5
4
|
#
|
6
5
|
# @see Airbrake::Config The list of options
|
7
6
|
# @since v1.0.0
|
7
|
+
# @api private
|
8
8
|
class Notifier
|
9
|
-
##
|
10
9
|
# @return [String] the label to be prepended to the log output
|
11
10
|
LOG_LABEL = '**Airbrake:'.freeze
|
12
11
|
|
13
|
-
##
|
14
12
|
# Creates a new Airbrake notifier with the given config options.
|
15
13
|
#
|
16
14
|
# @example Configuring with a Hash
|
@@ -40,29 +38,21 @@ module Airbrake
|
|
40
38
|
@sync_sender = SyncSender.new(@config)
|
41
39
|
end
|
42
40
|
|
43
|
-
##
|
44
|
-
# @!macro see_public_api_method
|
45
|
-
# @see Airbrake.$0
|
46
|
-
|
47
|
-
##
|
48
41
|
# @macro see_public_api_method
|
49
42
|
def notify(exception, params = {}, &block)
|
50
43
|
send_notice(exception, params, default_sender, &block)
|
51
44
|
end
|
52
45
|
|
53
|
-
##
|
54
46
|
# @macro see_public_api_method
|
55
47
|
def notify_sync(exception, params = {}, &block)
|
56
48
|
send_notice(exception, params, @sync_sender, &block).value
|
57
49
|
end
|
58
50
|
|
59
|
-
##
|
60
51
|
# @macro see_public_api_method
|
61
52
|
def add_filter(filter = nil, &block)
|
62
53
|
@filter_chain.add_filter(block_given? ? block : filter)
|
63
54
|
end
|
64
55
|
|
65
|
-
##
|
66
56
|
# @macro see_public_api_method
|
67
57
|
def build_notice(exception, params = {})
|
68
58
|
if @async_sender.closed?
|
@@ -78,13 +68,11 @@ module Airbrake
|
|
78
68
|
end
|
79
69
|
end
|
80
70
|
|
81
|
-
##
|
82
71
|
# @macro see_public_api_method
|
83
72
|
def close
|
84
73
|
@async_sender.close
|
85
74
|
end
|
86
75
|
|
87
|
-
##
|
88
76
|
# @macro see_public_api_method
|
89
77
|
def create_deploy(deploy_params)
|
90
78
|
deploy_params[:environment] ||= @config.environment
|
@@ -94,13 +82,11 @@ module Airbrake
|
|
94
82
|
promise
|
95
83
|
end
|
96
84
|
|
97
|
-
##
|
98
85
|
# @macro see_public_api_method
|
99
86
|
def configured?
|
100
87
|
@config.valid?
|
101
88
|
end
|
102
89
|
|
103
|
-
##
|
104
90
|
# @macro see_public_api_method
|
105
91
|
def merge_context(context)
|
106
92
|
@context.merge!(context)
|
@@ -171,6 +157,9 @@ module Airbrake
|
|
171
157
|
end
|
172
158
|
|
173
159
|
@filter_chain.add_filter(Airbrake::Filters::ContextFilter.new(@context))
|
160
|
+
@filter_chain.add_filter(
|
161
|
+
Airbrake::Filters::ExceptionAttributesFilter.new(@config.logger)
|
162
|
+
)
|
174
163
|
|
175
164
|
return unless (root_directory = @config.root_directory)
|
176
165
|
@filter_chain.add_filter(
|
@@ -178,29 +167,4 @@ module Airbrake
|
|
178
167
|
)
|
179
168
|
end
|
180
169
|
end
|
181
|
-
|
182
|
-
##
|
183
|
-
# NilNotifier is a no-op notifier, which mimics +Airbrake::Notifier+ and
|
184
|
-
# serves only for the purpose of making the library API easier to use.
|
185
|
-
#
|
186
|
-
# @since 2.1.0
|
187
|
-
class NilNotifier
|
188
|
-
def notify(_exception, _params = {}, &block); end
|
189
|
-
|
190
|
-
def notify_sync(_exception, _params = {}, &block); end
|
191
|
-
|
192
|
-
def add_filter(_filter = nil, &_block); end
|
193
|
-
|
194
|
-
def build_notice(_exception, _params = {}); end
|
195
|
-
|
196
|
-
def close; end
|
197
|
-
|
198
|
-
def create_deploy(_deploy_params); end
|
199
|
-
|
200
|
-
def configured?
|
201
|
-
false
|
202
|
-
end
|
203
|
-
|
204
|
-
def merge_context(_context); end
|
205
|
-
end
|
206
170
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
module Airbrake
|
2
|
-
##
|
3
2
|
# Represents a simplified promise object (similar to promises found in
|
4
3
|
# JavaScript), which allows chaining callbacks that are executed when the
|
5
4
|
# promise is either resolved or rejected.
|
@@ -8,7 +7,6 @@ module Airbrake
|
|
8
7
|
# @see https://github.com/ruby-concurrency/concurrent-ruby/blob/master/lib/concurrent/promise.rb
|
9
8
|
# @since v1.7.0
|
10
9
|
class Promise
|
11
|
-
##
|
12
10
|
# @api private
|
13
11
|
# @return [Hash<String,String>] either successful response containing the
|
14
12
|
# +id+ key or unsuccessful response containing the +error+ key
|
@@ -22,7 +20,6 @@ module Airbrake
|
|
22
20
|
@mutex = Mutex.new
|
23
21
|
end
|
24
22
|
|
25
|
-
##
|
26
23
|
# Attaches a callback to be executed when the promise is resolved.
|
27
24
|
#
|
28
25
|
# @example
|
@@ -46,7 +43,6 @@ module Airbrake
|
|
46
43
|
self
|
47
44
|
end
|
48
45
|
|
49
|
-
##
|
50
46
|
# Attaches a callback to be executed when the promise is rejected.
|
51
47
|
#
|
52
48
|
# @example
|
@@ -68,7 +64,6 @@ module Airbrake
|
|
68
64
|
self
|
69
65
|
end
|
70
66
|
|
71
|
-
##
|
72
67
|
# Resolves the promise.
|
73
68
|
#
|
74
69
|
# @example
|
@@ -85,7 +80,6 @@ module Airbrake
|
|
85
80
|
self
|
86
81
|
end
|
87
82
|
|
88
|
-
##
|
89
83
|
# Rejects the promise.
|
90
84
|
#
|
91
85
|
# @example
|
@@ -1,20 +1,16 @@
|
|
1
1
|
module Airbrake
|
2
|
-
##
|
3
2
|
# Parses responses coming from the Airbrake API. Handles HTTP errors by
|
4
3
|
# logging them.
|
5
4
|
#
|
6
5
|
# @api private
|
7
6
|
# @since v1.0.0
|
8
7
|
module Response
|
9
|
-
##
|
10
8
|
# @return [Integer] the limit of the response body
|
11
9
|
TRUNCATE_LIMIT = 100
|
12
10
|
|
13
|
-
##
|
14
11
|
# @return [Integer] HTTP code returned when an IP sends over 10k/min notices
|
15
12
|
TOO_MANY_REQUESTS = 429
|
16
13
|
|
17
|
-
##
|
18
14
|
# Parses HTTP responses from the Airbrake API.
|
19
15
|
#
|
20
16
|
# @param [Net::HTTPResponse] response
|
@@ -1,23 +1,19 @@
|
|
1
1
|
module Airbrake
|
2
|
-
##
|
3
2
|
# Responsible for sending notices to Airbrake synchronously. Supports proxies.
|
4
3
|
#
|
5
4
|
# @see AsyncSender
|
6
5
|
# @api private
|
7
6
|
# @since v1.0.0
|
8
7
|
class SyncSender
|
9
|
-
##
|
10
8
|
# @return [String] body for HTTP requests
|
11
9
|
CONTENT_TYPE = 'application/json'.freeze
|
12
10
|
|
13
|
-
##
|
14
11
|
# @param [Airbrake::Config] config
|
15
12
|
def initialize(config)
|
16
13
|
@config = config
|
17
14
|
@rate_limit_reset = Time.now
|
18
15
|
end
|
19
16
|
|
20
|
-
##
|
21
17
|
# Sends a POST request to the given +endpoint+ with the +notice+ payload.
|
22
18
|
#
|
23
19
|
# @param [Airbrake::Notice] notice
|
data/spec/airbrake_spec.rb
CHANGED
@@ -1,182 +1,113 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe Airbrake do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
described_class.configure do |c|
|
8
|
-
c.project_id = 113743
|
9
|
-
c.project_key = 'fd04e13d806a90f96614ad8e529b2822'
|
4
|
+
describe ".[]" do
|
5
|
+
it "returns a NilNotifier" do
|
6
|
+
expect(described_class[:test]).to be_an(Airbrake::NilNotifier)
|
10
7
|
end
|
11
8
|
end
|
12
9
|
|
13
|
-
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
after do
|
18
|
-
described_class.instance_variable_set(
|
19
|
-
:@notifiers,
|
20
|
-
Hash.new(Airbrake::NilNotifier.new)
|
21
|
-
)
|
10
|
+
let(:default_notifier) do
|
11
|
+
described_class.instance_variable_get(:@notifiers)[:default]
|
22
12
|
end
|
23
13
|
|
24
|
-
|
25
|
-
|
26
|
-
described_class.instance_variable_set(
|
27
|
-
:@notifiers,
|
28
|
-
Hash.new(Airbrake::NilNotifier.new)
|
29
|
-
)
|
30
|
-
expect(described_class.__send__(method, 'bingo')).to be_nil
|
31
|
-
end
|
32
|
-
end
|
14
|
+
describe ".configure" do
|
15
|
+
let(:config_params) { { project_id: 1, project_key: 'abc' } }
|
33
16
|
|
34
|
-
|
35
|
-
include_examples 'non-configured notifier handling', :notify
|
17
|
+
after { described_class.instance_variable_get(:@notifiers).clear }
|
36
18
|
|
37
|
-
it "
|
38
|
-
|
39
|
-
|
40
|
-
|
19
|
+
it "yields the config" do
|
20
|
+
expect do |b|
|
21
|
+
begin
|
22
|
+
described_class.configure(&b)
|
23
|
+
rescue Airbrake::Error
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end.to yield_with_args(Airbrake::Config)
|
41
27
|
end
|
42
28
|
|
43
|
-
|
44
|
-
|
45
|
-
|
29
|
+
context "when invoked with a notifier name" do
|
30
|
+
it "sets notifier name to the provided name" do
|
31
|
+
described_class.configure(:test) { |c| c.merge(config_params) }
|
32
|
+
expect(described_class[:test]).to be_an(Airbrake::Notifier)
|
46
33
|
end
|
47
|
-
|
48
|
-
sleep 1
|
49
|
-
|
50
|
-
expect(
|
51
|
-
a_request(:post, endpoint).
|
52
|
-
with(body: /params":{.*"bingo":"bango".*}/)
|
53
|
-
).to have_been_made.once
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
describe ".notify_sync" do
|
58
|
-
include_examples 'non-configured notifier handling', :notify_sync
|
59
|
-
|
60
|
-
it "sends exceptions synchronously" do
|
61
|
-
expect(described_class.notify_sync('bingo')).to be_a(Hash)
|
62
|
-
expect(a_request(:post, endpoint)).to have_been_made.once
|
63
34
|
end
|
64
35
|
|
65
|
-
|
66
|
-
|
67
|
-
|
36
|
+
context "when invoked without a notifier name" do
|
37
|
+
it "defaults to the :default notifier name" do
|
38
|
+
described_class.configure { |c| c.merge(config_params) }
|
39
|
+
expect(described_class[:default]).to be_an(Airbrake::Notifier)
|
68
40
|
end
|
69
|
-
|
70
|
-
expect(
|
71
|
-
a_request(:post, endpoint).
|
72
|
-
with(body: /params":{.*"bingo":"bango".*}/)
|
73
|
-
).to have_been_made.once
|
74
41
|
end
|
75
42
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
{"errors":\[{"type":"RuntimeError","message":"bingo","backtrace":\[
|
84
|
-
{"file":"/PROJECT_ROOT/spec/airbrake_spec.rb","line":\d+,"function":"[\w/\s\(\)<>]+","code".+},
|
85
|
-
{"file":"/GEM_ROOT/gems/rspec-core-.+/.+","line":\d+,"function":"[\w/\s\(\)<>]+".+
|
86
|
-
|x
|
87
|
-
# rubocop:enable Metrics/LineLength
|
88
|
-
|
89
|
-
expect(
|
90
|
-
a_request(:post, endpoint).
|
91
|
-
with(body: expected_body)
|
92
|
-
).to have_been_made.once
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
context "given a String" do
|
97
|
-
include_examples(
|
98
|
-
'backtrace building',
|
99
|
-
'converts it to a RuntimeException and builds a fake backtrace',
|
100
|
-
'bingo'
|
101
|
-
)
|
102
|
-
end
|
103
|
-
|
104
|
-
context "given an Exception with missing backtrace" do
|
105
|
-
include_examples(
|
106
|
-
'backtrace building',
|
107
|
-
'builds a backtrace for it and sends the notice',
|
108
|
-
RuntimeError.new('bingo')
|
43
|
+
context "when invoked twice with the same notifier name" do
|
44
|
+
it "raises Airbrake::Error" do
|
45
|
+
described_class.configure { |c| c.merge(config_params) }
|
46
|
+
expect do
|
47
|
+
described_class.configure { |c| c.merge(config_params) }
|
48
|
+
end.to raise_error(
|
49
|
+
Airbrake::Error, "the 'default' notifier was already configured"
|
109
50
|
)
|
110
51
|
end
|
111
52
|
end
|
112
53
|
end
|
113
54
|
|
114
|
-
describe ".
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
c.project_id = 123
|
119
|
-
c.project_key = '321'
|
120
|
-
end
|
121
|
-
|
122
|
-
notifiers = described_class.instance_variable_get(:@notifiers)
|
123
|
-
|
124
|
-
expect(notifiers).to be_a(Hash)
|
125
|
-
expect(notifiers.keys).to eq(%i[default bingo])
|
126
|
-
expect(notifiers.values).to all(satisfy { |v| v.is_a?(Airbrake::Notifier) })
|
127
|
-
end
|
128
|
-
|
129
|
-
it "raises error when a notifier of the given type was already configured" do
|
130
|
-
described_class.configure(:bingo) do |c|
|
131
|
-
c.project_id = 123
|
132
|
-
c.project_key = '321'
|
133
|
-
end
|
134
|
-
|
135
|
-
expect do
|
136
|
-
described_class.configure(:bingo) do |c|
|
137
|
-
c.project_id = 123
|
138
|
-
c.project_key = '321'
|
139
|
-
end
|
140
|
-
end.to raise_error(Airbrake::Error,
|
141
|
-
"the 'bingo' notifier was already configured")
|
142
|
-
end
|
55
|
+
describe ".configured?" do
|
56
|
+
it "forwards 'configured?' to the notifier" do
|
57
|
+
expect(default_notifier).to receive(:configured?)
|
58
|
+
described_class.configured?
|
143
59
|
end
|
144
60
|
end
|
145
61
|
|
146
|
-
describe ".
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
)
|
62
|
+
describe ".notify" do
|
63
|
+
it "forwards 'notify' to the notifier" do
|
64
|
+
block = proc {}
|
65
|
+
expect(default_notifier).to receive(:notify).with('ex', foo: 'bar', &block)
|
66
|
+
described_class.notify('ex', foo: 'bar', &block)
|
152
67
|
end
|
68
|
+
end
|
153
69
|
|
154
|
-
|
155
|
-
|
70
|
+
describe ".notify_sync" do
|
71
|
+
it "forwards 'notify_sync' to the notifier" do
|
72
|
+
block = proc {}
|
73
|
+
expect(default_notifier).to receive(:notify).with('ex', foo: 'bar', &block)
|
74
|
+
described_class.notify('ex', foo: 'bar', &block)
|
156
75
|
end
|
157
76
|
end
|
158
77
|
|
159
78
|
describe ".add_filter" do
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
expect(filters.size).to eq(4)
|
79
|
+
it "forwards 'add_filter' to the notifier" do
|
80
|
+
block = proc {}
|
81
|
+
expect(default_notifier).to receive(:add_filter).with(nil, &block)
|
82
|
+
described_class.add_filter(&block)
|
83
|
+
end
|
84
|
+
end
|
167
85
|
|
168
|
-
|
86
|
+
describe ".build_notice" do
|
87
|
+
it "forwards 'build_notice' to the notifier" do
|
88
|
+
expect(default_notifier).to receive(:build_notice).with('ex', foo: 'bar')
|
89
|
+
described_class.build_notice('ex', foo: 'bar')
|
90
|
+
end
|
91
|
+
end
|
169
92
|
|
170
|
-
|
171
|
-
|
93
|
+
describe ".close" do
|
94
|
+
it "forwards 'close' to the notifier" do
|
95
|
+
expect(default_notifier).to receive(:close)
|
96
|
+
described_class.close
|
172
97
|
end
|
173
98
|
end
|
174
99
|
|
175
|
-
describe ".
|
176
|
-
|
100
|
+
describe ".create_deploy" do
|
101
|
+
it "forwards 'create_deploy' to the notifier" do
|
102
|
+
expect(default_notifier).to receive(:create_deploy).with(foo: 'bar')
|
103
|
+
described_class.create_deploy(foo: 'bar')
|
104
|
+
end
|
177
105
|
end
|
178
106
|
|
179
107
|
describe ".merge_context" do
|
180
|
-
|
108
|
+
it "forwards 'merge_context' to the notifier" do
|
109
|
+
expect(default_notifier).to receive(:merge_context).with(foo: 'bar')
|
110
|
+
described_class.merge_context(foo: 'bar')
|
111
|
+
end
|
181
112
|
end
|
182
113
|
end
|