airbrake-ruby 2.9.0 → 2.10.0
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/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
|