-
Notifications
You must be signed in to change notification settings - Fork 67
/
airbrake-ruby.rb
294 lines (276 loc) · 9.49 KB
/
airbrake-ruby.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
require 'net/https'
require 'logger'
require 'json'
require 'thread'
require 'set'
require 'English'
require 'airbrake-ruby/version'
require 'airbrake-ruby/config'
require 'airbrake-ruby/sync_sender'
require 'airbrake-ruby/async_sender'
require 'airbrake-ruby/response'
require 'airbrake-ruby/notice'
require 'airbrake-ruby/backtrace'
require 'airbrake-ruby/filter_chain'
require 'airbrake-ruby/payload_truncator'
require 'airbrake-ruby/filters'
require 'airbrake-ruby/filters/keys_filter'
require 'airbrake-ruby/filters/keys_whitelist'
require 'airbrake-ruby/filters/keys_blacklist'
require 'airbrake-ruby/notifier'
##
# This module defines the Airbrake API. The user is meant to interact with
# Airbrake via its public class methods. Before using the library, you must to
# {configure} the default notifier.
#
# The module supports multiple notifiers, each of which can be configured
# differently. By default, every method is invoked in context of the default
# notifier. To use a different notifier, you need to {configure} it first and
# pass the notifier's name as the last argument of the method you're calling.
#
# You can have as many notifiers as you want, but they must have unique names.
#
# @example Configuring multiple notifiers and using them
# # Configure the default notifier.
# Airbrake.configure do |c|
# c.project_id = 113743
# c.project_key = 'fd04e13d806a90f96614ad8e529b2822'
# end
#
# # Configure a named notifier.
# Airbrake.configure(:my_other_project) do |c|
# c.project_id = 224854
# c.project_key = '91ac5e4a37496026c6837f63276ed2b6'
# end
#
# # Send an exception via the default notifier.
# Airbrake.notify('Oops!')
#
# # Send an exception via other configured notifier.
# params = {}
# Airbrake.notify('Oops', params, :my_other_project)
#
# @see Airbrake::Notifier
module Airbrake
##
# The general error that this library uses when it wants to raise.
Error = Class.new(StandardError)
##
# @return [String] the label to be prepended to the log output
LOG_LABEL = '**Airbrake:'.freeze
##
# A Hash that holds all notifiers. The keys of the Hash are notifier
# names, the values are Airbrake::Notifier instances.
@notifiers = {}
class << self
##
# Configures a new +notifier+ with the given name. If the name is not given,
# configures the default notifier.
#
# @example Configuring the default notifier
# Airbrake.configure do |c|
# c.project_id = 113743
# c.project_key = 'fd04e13d806a90f96614ad8e529b2822'
# end
#
# @example Configuring a named notifier
# # Configure a new Airbrake instance and
# # assign +:my_other_project+ as its name.
# Airbrake.configure(:my_other_project) do |c|
# c.project_id = 224854
# c.project_key = '91ac5e4a37496026c6837f63276ed2b6'
# end
#
# @param [Symbol] notifier the name to be associated with the notifier
# @yield [config] The configuration object
# @yieldparam config [Airbrake::Config]
# @return [void]
# @raise [Airbrake::Error] when trying to reconfigure already
# existing notifier
# @note There's no way to reconfigure a notifier
# @note There's no way to read config values outside of this library
def configure(notifier = :default)
yield config = Airbrake::Config.new
if @notifiers.key?(notifier)
raise Airbrake::Error,
"the '#{notifier}' notifier was already configured"
else
@notifiers[notifier] = Notifier.new(config)
end
end
# @!macro proxy_method
# @param [Symbol] notifier The name of the notifier
# @raise [Airbrake::Error] if +notifier+ doesn't exist
# @see Airbrake::Notifier#$0
##
# Sends an exception to Airbrake asynchronously.
#
# @macro proxy_method
# @example Sending an exception
# Airbrake.notify(RuntimeError.new('Oops!'))
# @example Sending a string
# # Converted to RuntimeError.new('Oops!') internally
# Airbrake.notify('Oops!')
# @example Sending a Notice
# notice = airbrake.build_notice(RuntimeError.new('Oops!'))
# airbrake.notify(notice)
#
# @param [Exception, String, Airbrake::Notice] exception The exception to be
# sent to Airbrake
# @param [Hash] params The additional payload to be sent to Airbrake. Can
# contain any values. The provided values will be displayed in the Params
# tab in your project's dashboard
# @return [nil]
# @see .notify_sync
def notify(exception, params = {}, notifier = :default)
call_notifier(notifier, __method__, exception, params)
end
##
# Sends an exception to Airbrake synchronously.
#
# @macro proxy_method
# @example
# Airbrake.notify_sync('App crashed!')
# #=> {"id"=>"123", "url"=>"https://airbrake.io/locate/321"}
#
# @return [Hash{String=>String}] the reponse from the server
# @see .notify
# @since v5.0.0
def notify_sync(exception, params = {}, notifier = :default)
call_notifier(notifier, __method__, exception, params)
end
##
# Runs a callback before {.notify} or {.notify_sync} kicks in. This is
# useful if you want to ignore specific notices or filter the data the
# notice contains.
#
# @macro proxy_method
# @example Ignore all notices
# Airbrake.add_filter(&:ignore!)
# @example Ignore based on some condition
# Airbrake.add_filter do |notice|
# notice.ignore! if notice[:error_class] == 'StandardError'
# end
# @example Ignore with help of a class
# class MyFilter
# def call(notice)
# # ...
# end
# end
#
# Airbrake.add_filter(MyFilter.new)
#
# @param [#call] filter The filter object
# @yield [notice] The notice to filter
# @yieldparam [Airbrake::Notice]
# @yieldreturn [void]
# @return [void]
# @since v5.0.0
# @note Once a filter was added, there's no way to delete it
def add_filter(filter = nil, notifier = :default, &block)
call_notifier(notifier, __method__, filter, &block)
end
##
# Specifies which keys should *not* be filtered. All other keys will be
# substituted with the +[Filtered]+ label.
#
# @macro proxy_method
# @example
# Airbrake.whitelist([:email, /user/i, 'account_id'])
#
# @param [Array<String, Symbol, Regexp>] keys The keys, which shouldn't be
# filtered
# @return [void]
# @since v5.0.0
# @see .blacklist_keys
def whitelist_keys(keys, notifier = :default)
call_notifier(notifier, __method__, keys)
end
##
# Specifies which keys *should* be filtered. Such keys will be replaced with
# the +[Filtered]+ label.
#
# @macro proxy_method
# @example
# Airbrake.blacklist_keys([:email, /credit/i, 'password'])
#
# @param [Array<String, Symbol, Regexp>] keys The keys, which should be
# filtered
# @return [void]
# @since v5.0.0
# @see .whitelist_keys
def blacklist_keys(keys, notifier = :default)
call_notifier(notifier, __method__, keys)
end
##
# Builds an Airbrake notice. This is useful, if you want to add or modify a
# value only for a specific notice. When you're done modifying the notice,
# send it with {.notify} or {.notify_sync}.
#
# @macro proxy_method
# @example
# notice = airbrake.build_notice('App crashed!')
# notice[:params][:username] = user.name
# airbrake.notify_sync(notice)
#
# @param [Exception] exception The exception on top of which the notice
# should be built
# @param [Hash] params The additional params attached to the notice
# @return [Airbrake::Notice] the notice built with help of the given
# arguments
# @since v5.0.0
def build_notice(exception, params = {}, notifier = :default)
call_notifier(notifier, __method__, exception, params)
end
##
# Makes the notifier a no-op, which means you cannot use the {.notify} and
# {.notify_sync} methods anymore. It also stops the notifier's worker
# threads.
#
# @macro proxy_method
# @example
# Airbrake.close
# Airbrake.notify('App crashed!') #=> raises Airbrake::Error
#
# @return [void]
# @since v5.0.0
def close(notifier = :default)
call_notifier(notifier, __method__)
end
##
# Pings the Airbrake Deploy API endpoint about the occurred deploy. This
# method is used by the airbrake gem for various integrations.
#
# @macro proxy_method
# @param [Hash{Symbol=>String}] deploy_params The params for the API
# @option deploy_params [Symbol] :environment
# @option deploy_params [Symbol] :username
# @option deploy_params [Symbol] :repository
# @option deploy_params [Symbol] :revision
# @option deploy_params [Symbol] :version
# @since v5.0.0
# @api private
def create_deploy(deploy_params, notifier = :default)
call_notifier(notifier, __method__, deploy_params)
end
private
##
# Calls +method+ on +notifier+ with provided +args+.
#
# @raise [Airbrake::Error] if none of the notifiers exist
def call_notifier(notifier, method, *args, &block)
if @notifiers.key?(notifier)
@notifiers[notifier].__send__(method, *args, &block)
else
raise Airbrake::Error,
"the '#{notifier}' notifier isn't configured"
end
end
end
end
# Notify of unhandled exceptions, if there were any, but ignore SystemExit.
at_exit do
if $ERROR_INFO && $ERROR_INFO.class != SystemExit
Airbrake.notify_sync($ERROR_INFO)
end
end