bugsnag 4.2.1 → 6.27.1

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.
Files changed (106) hide show
  1. checksums.yaml +5 -5
  2. data/.yardopts +12 -0
  3. data/CHANGELOG.md +814 -0
  4. data/README.md +21 -25
  5. data/VERSION +1 -1
  6. data/bugsnag.gemspec +19 -8
  7. data/lib/bugsnag/breadcrumb_type.rb +14 -0
  8. data/lib/bugsnag/breadcrumbs/breadcrumb.rb +109 -0
  9. data/lib/bugsnag/breadcrumbs/breadcrumbs.rb +13 -0
  10. data/lib/bugsnag/breadcrumbs/on_breadcrumb_callback_list.rb +48 -0
  11. data/lib/bugsnag/breadcrumbs/validator.rb +29 -0
  12. data/lib/bugsnag/cleaner.rb +170 -59
  13. data/lib/bugsnag/code_extractor.rb +137 -0
  14. data/lib/bugsnag/configuration.rb +670 -45
  15. data/lib/bugsnag/delivery/synchronous.rb +31 -14
  16. data/lib/bugsnag/delivery/thread_queue.rb +23 -6
  17. data/lib/bugsnag/delivery.rb +13 -0
  18. data/lib/bugsnag/endpoint_configuration.rb +11 -0
  19. data/lib/bugsnag/endpoint_validator.rb +80 -0
  20. data/lib/bugsnag/error.rb +25 -0
  21. data/lib/bugsnag/event.rb +5 -0
  22. data/lib/bugsnag/feature_flag.rb +74 -0
  23. data/lib/bugsnag/helpers.rb +121 -25
  24. data/lib/bugsnag/integrations/delayed_job.rb +51 -0
  25. data/lib/bugsnag/integrations/mailman.rb +43 -0
  26. data/lib/bugsnag/integrations/mongo.rb +133 -0
  27. data/lib/bugsnag/integrations/que.rb +53 -0
  28. data/lib/bugsnag/integrations/rack.rb +83 -0
  29. data/lib/bugsnag/integrations/rails/active_job.rb +100 -0
  30. data/lib/bugsnag/{rails → integrations/rails}/active_record_rescue.rb +10 -1
  31. data/lib/bugsnag/{rails → integrations/rails}/controller_methods.rb +1 -9
  32. data/lib/bugsnag/integrations/rails/rails_breadcrumbs.rb +115 -0
  33. data/lib/bugsnag/integrations/railtie.rb +153 -0
  34. data/lib/bugsnag/integrations/rake.rb +74 -0
  35. data/lib/bugsnag/integrations/resque.rb +94 -0
  36. data/lib/bugsnag/integrations/shoryuken.rb +50 -0
  37. data/lib/bugsnag/integrations/sidekiq.rb +68 -0
  38. data/lib/bugsnag/meta_data.rb +1 -0
  39. data/lib/bugsnag/middleware/active_job.rb +18 -0
  40. data/lib/bugsnag/middleware/breadcrumbs.rb +21 -0
  41. data/lib/bugsnag/middleware/callbacks.rb +6 -8
  42. data/lib/bugsnag/middleware/classify_error.rb +50 -0
  43. data/lib/bugsnag/middleware/clearance_user.rb +33 -0
  44. data/lib/bugsnag/middleware/delayed_job.rb +93 -0
  45. data/lib/bugsnag/middleware/discard_error_class.rb +30 -0
  46. data/lib/bugsnag/middleware/exception_meta_data.rb +42 -0
  47. data/lib/bugsnag/middleware/ignore_error_class.rb +26 -0
  48. data/lib/bugsnag/middleware/mailman.rb +6 -4
  49. data/lib/bugsnag/middleware/rack_request.rb +126 -30
  50. data/lib/bugsnag/middleware/rails3_request.rb +15 -17
  51. data/lib/bugsnag/middleware/rake.rb +7 -5
  52. data/lib/bugsnag/middleware/session_data.rb +25 -0
  53. data/lib/bugsnag/middleware/sidekiq.rb +9 -4
  54. data/lib/bugsnag/middleware/suggestion_data.rb +34 -0
  55. data/lib/bugsnag/middleware/warden_user.rb +11 -6
  56. data/lib/bugsnag/middleware_stack.rb +62 -9
  57. data/lib/bugsnag/on_error_callbacks.rb +33 -0
  58. data/lib/bugsnag/report.rb +516 -0
  59. data/lib/bugsnag/session_tracker.rb +182 -0
  60. data/lib/bugsnag/stacktrace.rb +82 -0
  61. data/lib/bugsnag/tasks/bugsnag.rake +2 -70
  62. data/lib/bugsnag/utility/circular_buffer.rb +62 -0
  63. data/lib/bugsnag/utility/duplicator.rb +124 -0
  64. data/lib/bugsnag/utility/feature_data_store.rb +41 -0
  65. data/lib/bugsnag/utility/feature_flag_delegate.rb +89 -0
  66. data/lib/bugsnag/utility/metadata_delegate.rb +102 -0
  67. data/lib/bugsnag.rb +528 -80
  68. metadata +61 -123
  69. data/.document +0 -5
  70. data/.gitignore +0 -52
  71. data/.rspec +0 -3
  72. data/.travis.yml +0 -14
  73. data/CONTRIBUTING.md +0 -47
  74. data/Gemfile +0 -2
  75. data/Rakefile +0 -29
  76. data/lib/bugsnag/capistrano.rb +0 -7
  77. data/lib/bugsnag/capistrano2.rb +0 -32
  78. data/lib/bugsnag/delay/resque.rb +0 -21
  79. data/lib/bugsnag/delayed_job.rb +0 -57
  80. data/lib/bugsnag/deploy.rb +0 -34
  81. data/lib/bugsnag/mailman.rb +0 -28
  82. data/lib/bugsnag/middleware/rails2_request.rb +0 -52
  83. data/lib/bugsnag/notification.rb +0 -459
  84. data/lib/bugsnag/rack.rb +0 -53
  85. data/lib/bugsnag/rails/action_controller_rescue.rb +0 -62
  86. data/lib/bugsnag/rails.rb +0 -66
  87. data/lib/bugsnag/railtie.rb +0 -80
  88. data/lib/bugsnag/rake.rb +0 -25
  89. data/lib/bugsnag/resque.rb +0 -40
  90. data/lib/bugsnag/sidekiq.rb +0 -42
  91. data/lib/bugsnag/tasks/bugsnag.cap +0 -48
  92. data/rails/init.rb +0 -7
  93. data/spec/cleaner_spec.rb +0 -138
  94. data/spec/code_spec.rb +0 -86
  95. data/spec/fixtures/crashes/end_of_file.rb +0 -9
  96. data/spec/fixtures/crashes/short_file.rb +0 -1
  97. data/spec/fixtures/crashes/start_of_file.rb +0 -9
  98. data/spec/fixtures/middleware/internal_info_setter.rb +0 -11
  99. data/spec/fixtures/middleware/public_info_setter.rb +0 -11
  100. data/spec/fixtures/tasks/Rakefile +0 -15
  101. data/spec/helper_spec.rb +0 -163
  102. data/spec/integration_spec.rb +0 -132
  103. data/spec/middleware_spec.rb +0 -181
  104. data/spec/notification_spec.rb +0 -877
  105. data/spec/rack_spec.rb +0 -56
  106. data/spec/spec_helper.rb +0 -53
@@ -1,132 +1,757 @@
1
- require "set"
2
- require "socket"
3
- require "logger"
1
+ require "bugsnag/breadcrumbs/on_breadcrumb_callback_list"
2
+
3
+ require "bugsnag/endpoint_configuration"
4
+ require "bugsnag/endpoint_validator"
5
+
6
+ require "bugsnag/middleware/breadcrumbs"
7
+ require "bugsnag/middleware/callbacks"
8
+ require "bugsnag/middleware/classify_error"
9
+ require "bugsnag/middleware/clearance_user"
10
+ require "bugsnag/middleware/delayed_job"
11
+ require "bugsnag/middleware/discard_error_class"
12
+ require "bugsnag/middleware/exception_meta_data"
13
+ require "bugsnag/middleware/ignore_error_class"
14
+ require "bugsnag/middleware/mailman"
15
+ require "bugsnag/middleware/rack_request"
16
+ require "bugsnag/middleware/rails3_request"
17
+ require "bugsnag/middleware/rake"
18
+ require "bugsnag/middleware/session_data"
19
+ require "bugsnag/middleware/sidekiq"
20
+ require "bugsnag/middleware/suggestion_data"
21
+ require "bugsnag/middleware/warden_user"
22
+
4
23
  require "bugsnag/middleware_stack"
5
24
 
25
+ require "bugsnag/utility/circular_buffer"
26
+
6
27
  module Bugsnag
7
28
  class Configuration
29
+ # Your Integration API Key
30
+ # @return [String, nil]
8
31
  attr_accessor :api_key
32
+
33
+ # The current stage of the release process, e.g. 'development', production'
34
+ # @return [String, nil]
9
35
  attr_accessor :release_stage
36
+
37
+ # A list of which release stages should cause notifications to be sent
38
+ # @deprecated Use {#enabled_release_stages} instead
39
+ # @return [Array<String>, nil]
10
40
  attr_accessor :notify_release_stages
41
+
42
+ # Whether notifications should automatically be sent
43
+ # @return [Boolean]
11
44
  attr_accessor :auto_notify
12
- attr_accessor :use_ssl
45
+
46
+ # @return [String, nil]
13
47
  attr_accessor :ca_file
48
+
49
+ # Whether to automatically attach the Rack environment to notifications
50
+ # @return [Boolean]
14
51
  attr_accessor :send_environment
52
+
53
+ # Whether code snippets from the exception stacktrace should be sent with notifications
54
+ # @return [Boolean]
15
55
  attr_accessor :send_code
56
+
57
+ # Any stacktrace lines that match this path will be marked as 'in project'
58
+ # @return [String, nil]
16
59
  attr_accessor :project_root
17
- attr_accessor :vendor_paths
60
+
61
+ # The current version of your application
62
+ # @return [String, nil]
18
63
  attr_accessor :app_version
19
- attr_accessor :app_type
20
- attr_accessor :params_filters
21
- attr_accessor :ignore_user_agents
22
- attr_accessor :endpoint
64
+
65
+ # A list of keys that should be filtered out from the report and breadcrumb
66
+ # metadata before sending them to Bugsnag
67
+ # @deprecated Use {#redacted_keys} instead
68
+ # @return [Set<String, Regexp>]
69
+ attr_accessor :meta_data_filters
70
+
71
+ # A set of keys that should be redacted from the report and breadcrumb
72
+ # metadata before sending them to Bugsnag
73
+ #
74
+ # When adding strings, keys that are equal to the string (ignoring case)
75
+ # will be redacted. When adding regular expressions, any keys which match
76
+ # the regular expression will be redacted
77
+ #
78
+ # @return [Set<String, Regexp>]
79
+ attr_accessor :redacted_keys
80
+
81
+ # The logger to use for Bugsnag log messages
82
+ # @return [Logger]
23
83
  attr_accessor :logger
84
+
85
+ # The middleware stack that will run on every notification
86
+ # @return [MiddlewareStack]
24
87
  attr_accessor :middleware
88
+
89
+ # @api private
90
+ # @return [MiddlewareStack]
25
91
  attr_accessor :internal_middleware
26
- attr_accessor :delay_with_resque
27
- attr_accessor :debug
92
+
93
+ # The host address of the HTTP proxy that should be used when making requests
94
+ # @see parse_proxy
95
+ # @return [String, nil]
28
96
  attr_accessor :proxy_host
97
+
98
+ # The port number of the HTTP proxy that should be used when making requests
99
+ # @see parse_proxy
100
+ # @return [Integer, nil]
29
101
  attr_accessor :proxy_port
102
+
103
+ # The user that should be used when making requests via a HTTP proxy
104
+ # @see parse_proxy
105
+ # @return [String, nil]
30
106
  attr_accessor :proxy_user
107
+
108
+ # The password for the user that should be used when making requests via a HTTP proxy
109
+ # @see parse_proxy
110
+ # @return [String, nil]
31
111
  attr_accessor :proxy_password
112
+
113
+ # The HTTP request timeout, defaults to 15 seconds
114
+ # @return [Integer]
32
115
  attr_accessor :timeout
116
+
117
+ # The name or descriptor of the Ruby server host
118
+ # @return [String]
33
119
  attr_accessor :hostname
34
- attr_accessor :delivery_method
35
- attr_writer :ignore_classes
36
120
 
121
+ # @api private
122
+ # @return [Hash{String => String}]
123
+ attr_accessor :runtime_versions
124
+
125
+ # Exception classes that will be discarded and not sent to Bugsnag
126
+ # @return [Set<String, Regexp>]
127
+ attr_accessor :discard_classes
128
+
129
+ # Whether Bugsnag should automatically record sessions
130
+ # @deprecated Use {#auto_track_sessions} instead
131
+ # @return [Boolean]
132
+ attr_accessor :auto_capture_sessions
133
+
134
+ # @deprecated Use {#discard_classes} instead
135
+ # @return [Set<Class, Proc>]
136
+ attr_accessor :ignore_classes
137
+
138
+ # The URLs to send events and sessions to
139
+ # @return [EndpointConfiguration]
140
+ attr_reader :endpoints
141
+
142
+ # Whether events will be delivered
143
+ # @api private
144
+ # @return [Boolean]
145
+ attr_reader :enable_events
146
+
147
+ # Whether sessions will be delivered
148
+ # @api private
149
+ # @return [Boolean]
150
+ attr_reader :enable_sessions
151
+
152
+ # A list of strings indicating allowable automatic breadcrumb types
153
+ # @deprecated Use {#enabled_breadcrumb_types} instead
154
+ # @see Bugsnag::BreadcrumbType
155
+ # @return [Array<String>]
156
+ attr_accessor :enabled_automatic_breadcrumb_types
157
+
158
+ # Callables to be run before a breadcrumb is logged
159
+ # @return [Array<#call>]
160
+ attr_accessor :before_breadcrumb_callbacks
161
+
162
+ # The maximum allowable amount of breadcrumbs per thread
163
+ # @return [Integer]
164
+ attr_reader :max_breadcrumbs
165
+
166
+ # @deprecated Use {vendor_paths} instead
167
+ # @return [Regexp]
168
+ attr_accessor :vendor_path
169
+
170
+ # An array of paths within the {project_root} that should not be considered
171
+ # as "in project"
172
+ #
173
+ # These paths should be relative to the {project_root} and will only match
174
+ # whole directory names
175
+ #
176
+ # @return [Array<String>]
177
+ attr_accessor :vendor_paths
178
+
179
+ # The default context for all future events
180
+ # Setting this will disable automatic context setting
181
+ # @return [String, nil]
182
+ attr_accessor :context
183
+
184
+ # Global metadata added to every event
185
+ # @return [Hash]
186
+ attr_reader :metadata
187
+
188
+ # @api private
189
+ # @return [Array<String>]
190
+ attr_reader :scopes_to_filter
191
+
192
+ # Expose on_breadcrumb_callbacks internally for Bugsnag.leave_breadcrumb
193
+ # @api private
194
+ # @return [Breadcrumbs::OnBreadcrumbCallbackList]
195
+ attr_reader :on_breadcrumb_callbacks
196
+
197
+ API_KEY_REGEX = /[0-9a-f]{32}/i
37
198
  THREAD_LOCAL_NAME = "bugsnag_req_data"
38
199
 
39
- DEFAULT_ENDPOINT = "notify.bugsnag.com"
200
+ DEFAULT_NOTIFY_ENDPOINT = "https://notify.bugsnag.com"
201
+ DEFAULT_SESSION_ENDPOINT = "https://sessions.bugsnag.com"
202
+ DEFAULT_ENDPOINT = DEFAULT_NOTIFY_ENDPOINT
40
203
 
41
- DEFAULT_PARAMS_FILTERS = [
204
+ DEFAULT_META_DATA_FILTERS = [
42
205
  /authorization/i,
43
206
  /cookie/i,
44
207
  /password/i,
45
208
  /secret/i,
209
+ /warden\.user\.([^.]+)\.key/,
46
210
  "rack.request.form_vars"
47
211
  ].freeze
48
212
 
49
- DEFAULT_IGNORE_CLASSES = [
50
- "AbstractController::ActionNotFound",
51
- "ActionController::InvalidAuthenticityToken",
52
- "ActionController::ParameterMissing",
53
- "ActionController::RoutingError",
54
- "ActionController::UnknownAction",
55
- "ActionController::UnknownFormat",
56
- "ActionController::UnknownHttpMethod",
57
- "ActiveRecord::RecordNotFound",
58
- "CGI::Session::CookieStore::TamperedWithCookie",
59
- "Mongoid::Errors::DocumentNotFound",
60
- "SignalException",
61
- "SystemExit",
62
- ].freeze
213
+ DEFAULT_MAX_BREADCRUMBS = 25
63
214
 
64
- DEFAULT_IGNORE_USER_AGENTS = [].freeze
215
+ # Path to vendored code. Used to mark file paths as out of project.
216
+ DEFAULT_VENDOR_PATH = %r{^(vendor/|\.bundle/)}
65
217
 
66
- DEFAULT_DELIVERY_METHOD = :thread_queue
218
+ # @api private
219
+ DEFAULT_SCOPES_TO_FILTER = ['events.metaData', 'events.breadcrumbs.metaData'].freeze
220
+
221
+ alias :track_sessions :auto_capture_sessions
222
+ alias :track_sessions= :auto_capture_sessions=
67
223
 
68
224
  def initialize
69
225
  @mutex = Mutex.new
70
226
 
71
227
  # Set up the defaults
72
228
  self.auto_notify = true
73
- self.use_ssl = true
74
229
  self.send_environment = false
75
230
  self.send_code = true
76
- self.params_filters = Set.new(DEFAULT_PARAMS_FILTERS)
77
- self.ignore_classes = Set.new(DEFAULT_IGNORE_CLASSES)
78
- self.ignore_user_agents = Set.new(DEFAULT_IGNORE_USER_AGENTS)
79
- self.endpoint = DEFAULT_ENDPOINT
231
+ self.meta_data_filters = Set.new(DEFAULT_META_DATA_FILTERS)
232
+ @redacted_keys = Set.new
233
+ self.scopes_to_filter = DEFAULT_SCOPES_TO_FILTER
80
234
  self.hostname = default_hostname
81
- self.delivery_method = DEFAULT_DELIVERY_METHOD
235
+ self.runtime_versions = {}
236
+ self.runtime_versions["ruby"] = RUBY_VERSION
237
+ self.runtime_versions["jruby"] = JRUBY_VERSION if defined?(JRUBY_VERSION)
82
238
  self.timeout = 15
83
- self.vendor_paths = [%r{vendor/}]
239
+ self.release_stage = ENV['BUGSNAG_RELEASE_STAGE']
84
240
  self.notify_release_stages = nil
241
+ self.auto_capture_sessions = true
242
+
243
+ # All valid breadcrumb types should be allowable initially
244
+ self.enabled_automatic_breadcrumb_types = Bugsnag::Breadcrumbs::VALID_BREADCRUMB_TYPES.dup
245
+ self.before_breadcrumb_callbacks = []
246
+ @on_breadcrumb_callbacks = Breadcrumbs::OnBreadcrumbCallbackList.new(self)
247
+
248
+ # Store max_breadcrumbs here instead of outputting breadcrumbs.max_items
249
+ # to avoid infinite recursion when creating breadcrumb buffer
250
+ @max_breadcrumbs = DEFAULT_MAX_BREADCRUMBS
251
+
252
+ @endpoints = EndpointConfiguration.new(DEFAULT_NOTIFY_ENDPOINT, DEFAULT_SESSION_ENDPOINT)
253
+
254
+ @enable_events = true
255
+ @enable_sessions = true
256
+
257
+ @metadata = {}
258
+ @metadata_delegate = Utility::MetadataDelegate.new
259
+
260
+ # SystemExit and SignalException are common Exception types seen with
261
+ # successful exits and are not automatically reported to Bugsnag
262
+ # TODO move these defaults into `discard_classes` when `ignore_classes`
263
+ # is removed
264
+ self.ignore_classes = Set.new([SystemExit, SignalException])
265
+ self.discard_classes = Set.new([])
85
266
 
86
267
  # Read the API key from the environment
87
268
  self.api_key = ENV["BUGSNAG_API_KEY"]
88
269
 
270
+ # Read NET::HTTP proxy environment variables
271
+ if (proxy_uri = ENV["https_proxy"] || ENV['http_proxy'])
272
+ parse_proxy(proxy_uri)
273
+ end
274
+
275
+ # Set up vendor_path regex to mark stacktrace file paths as out of project.
276
+ # Stacktrace lines that matches regex will be marked as "out of project"
277
+ # will only appear in the full trace.
278
+ self.vendor_path = DEFAULT_VENDOR_PATH
279
+ @vendor_paths = []
280
+
89
281
  # Set up logging
90
282
  self.logger = Logger.new(STDOUT)
91
- self.logger.level = Logger::WARN
283
+ self.logger.level = Logger::INFO
284
+ self.logger.formatter = proc do |severity, datetime, progname, msg|
285
+ "** #{progname} #{datetime}: #{msg}\n"
286
+ end
92
287
 
93
288
  # Configure the bugsnag middleware stack
94
289
  self.internal_middleware = Bugsnag::MiddlewareStack.new
290
+ self.internal_middleware.use Bugsnag::Middleware::ExceptionMetaData
291
+ self.internal_middleware.use Bugsnag::Middleware::DiscardErrorClass
292
+ self.internal_middleware.use Bugsnag::Middleware::IgnoreErrorClass
293
+ self.internal_middleware.use Bugsnag::Middleware::SuggestionData
294
+ self.internal_middleware.use Bugsnag::Middleware::ClassifyError
295
+ self.internal_middleware.use Bugsnag::Middleware::SessionData
296
+ self.internal_middleware.use Bugsnag::Middleware::Breadcrumbs
95
297
 
96
298
  self.middleware = Bugsnag::MiddlewareStack.new
97
299
  self.middleware.use Bugsnag::Middleware::Callbacks
98
300
  end
99
301
 
100
- # Accept both String and Class instances as an ignored class
101
- def ignore_classes
102
- @mutex.synchronize { @ignore_classes.map! { |klass| klass.is_a?(Class) ? klass.name : klass } }
302
+ ##
303
+ # Gets the delivery_method that Bugsnag will use to communicate with the
304
+ # notification endpoint.
305
+ #
306
+ # @return [Symbol]
307
+ def delivery_method
308
+ @delivery_method || @default_delivery_method || :thread_queue
309
+ end
310
+
311
+ ##
312
+ # Sets the delivery_method that Bugsnag will use to communicate with the
313
+ # notification endpoint.
314
+ #
315
+ # The default delivery methods are ':thread_queue' and ':synchronous'.
316
+ #
317
+ # @param delivery_method [Symbol]
318
+ # @return [void]
319
+ def delivery_method=(delivery_method)
320
+ @delivery_method = delivery_method
321
+ end
322
+
323
+ ##
324
+ # Used to set a new default delivery method that will be used if one is not
325
+ # set with #delivery_method.
326
+ #
327
+ # @api private
328
+ #
329
+ # @param delivery_method [Symbol]
330
+ # @return [void]
331
+ def default_delivery_method=(delivery_method)
332
+ @default_delivery_method = delivery_method
333
+ end
334
+
335
+ ##
336
+ # Get the type of application executing the current code
337
+ #
338
+ # This is usually used to represent if you are running in a Rails server,
339
+ # Sidekiq job, Rake task etc... Bugsnag will automatically detect most
340
+ # application types for you
341
+ #
342
+ # @return [String, nil]
343
+ def app_type
344
+ @app_type || @detected_app_type
345
+ end
346
+
347
+ ##
348
+ # Set the type of application executing the current code
349
+ #
350
+ # If an app_type is set, this will be used instead of the automatically
351
+ # detected app_type that Bugsnag would otherwise use
352
+ #
353
+ # @param app_type [String]
354
+ # @return [void]
355
+ def app_type=(app_type)
356
+ @app_type = app_type
357
+ end
358
+
359
+ ##
360
+ # Get the detected app_type, which is used when one isn't set explicitly
361
+ #
362
+ # @api private
363
+ #
364
+ # @return [String, nil]
365
+ def detected_app_type
366
+ @detected_app_type
367
+ end
368
+
369
+ ##
370
+ # Set the detected app_type, which is used when one isn't set explicitly
371
+ #
372
+ # This allows Bugsnag's integrations to say 'this is a Rails app' while
373
+ # allowing the user to overwrite this if they wish
374
+ #
375
+ # @api private
376
+ #
377
+ # @param app_type [String]
378
+ # @return [void]
379
+ def detected_app_type=(app_type)
380
+ @detected_app_type = app_type
103
381
  end
104
382
 
105
- def should_notify?
383
+ ##
384
+ # Indicates whether the notifier should send a notification based on the
385
+ # configured release stage.
386
+ #
387
+ # @return [Boolean]
388
+ def should_notify_release_stage?
106
389
  @release_stage.nil? || @notify_release_stages.nil? || @notify_release_stages.include?(@release_stage)
107
390
  end
108
391
 
392
+ ##
393
+ # Tests whether the configured API key is valid.
394
+ #
395
+ # @return [Boolean]
396
+ def valid_api_key?
397
+ !api_key.nil? && api_key =~ API_KEY_REGEX
398
+ end
399
+
400
+ ##
401
+ # Returns the array of data that will be automatically attached to every
402
+ # error notification.
403
+ #
404
+ # @return [Hash]
109
405
  def request_data
110
406
  Thread.current[THREAD_LOCAL_NAME] ||= {}
111
407
  end
112
408
 
409
+ ##
410
+ # Sets an entry in the array of data attached to every error notification.
411
+ #
412
+ # @param key [String, #to_s]
413
+ # @param value [Object]
414
+ # @return [void]
113
415
  def set_request_data(key, value)
114
416
  self.request_data[key] = value
115
417
  end
116
418
 
419
+ ##
420
+ # Unsets an entry in the array of data attached to every error notification.
421
+ #
422
+ # @param (see set_request_data)
423
+ # @return [void]
117
424
  def unset_request_data(key, value)
118
425
  self.request_data.delete(key)
119
426
  end
120
427
 
428
+ ##
429
+ # Clears the array of data attached to every error notification.
430
+ #
431
+ # @return [void]
121
432
  def clear_request_data
122
433
  Thread.current[THREAD_LOCAL_NAME] = nil
123
434
  end
124
435
 
436
+ ##
437
+ # Logs an info level message
438
+ #
439
+ # @param message [String, #to_s] The message to log
440
+ def info(message)
441
+ logger.info(PROG_NAME) { message }
442
+ end
443
+
444
+ ##
445
+ # Logs a warning level message
446
+ #
447
+ # @param message [String, #to_s] The message to log
448
+ def warn(message)
449
+ logger.warn(PROG_NAME) { message }
450
+ end
451
+
452
+ ##
453
+ # Logs an error level message
454
+ #
455
+ # @param message [String, #to_s] The message to log
456
+ def error(message)
457
+ logger.error(PROG_NAME) { message }
458
+ end
459
+
460
+ ##
461
+ # Logs a debug level message
462
+ #
463
+ # @param message [String, #to_s] The message to log
464
+ def debug(message)
465
+ logger.debug(PROG_NAME) { message }
466
+ end
467
+
468
+ ##
469
+ # Parses and sets proxy from a uri
470
+ #
471
+ # @param uri [String, #to_s] The URI to parse and extract proxy details from
472
+ # @return [void]
473
+ def parse_proxy(uri)
474
+ proxy = URI.parse(uri)
475
+ self.proxy_host = proxy.host
476
+ self.proxy_port = proxy.port
477
+ self.proxy_user = proxy.user
478
+ self.proxy_password = proxy.password
479
+ end
480
+
481
+ ##
482
+ # Sets the maximum allowable amount of breadcrumbs
483
+ #
484
+ # @param new_max_breadcrumbs [Integer] the new maximum breadcrumb limit
485
+ # @return [void]
486
+ def max_breadcrumbs=(new_max_breadcrumbs)
487
+ @max_breadcrumbs = new_max_breadcrumbs
488
+ breadcrumbs.max_items = new_max_breadcrumbs
489
+ end
490
+
491
+ ##
492
+ # Returns the current list of breadcrumbs
493
+ #
494
+ # This is a per-thread circular buffer, containing at most 'max_breadcrumbs'
495
+ # breadcrumbs
496
+ #
497
+ # @return [Bugsnag::Utility::CircularBuffer]
498
+ def breadcrumbs
499
+ request_data[:breadcrumbs] ||= Bugsnag::Utility::CircularBuffer.new(@max_breadcrumbs)
500
+ end
501
+
502
+ # The URL error notifications will be delivered to
503
+ # @!attribute notify_endpoint
504
+ # @return [String]
505
+ # @deprecated Use {#endpoints} instead
506
+ def notify_endpoint
507
+ @endpoints.notify
508
+ end
509
+
510
+ alias :endpoint :notify_endpoint
511
+
512
+ # Sets the notification endpoint
513
+ #
514
+ # @deprecated Use {#endpoints} instead
515
+ #
516
+ # @param new_notify_endpoint [String] The URL to deliver error notifications to
517
+ # @return [void]
518
+ def endpoint=(new_notify_endpoint)
519
+ warn("The 'endpoint' configuration option is deprecated. Set both endpoints with the 'endpoints=' method instead")
520
+ set_endpoints(new_notify_endpoint, session_endpoint) # Pass the existing session_endpoint through so it doesn't get overwritten
521
+ end
522
+
523
+ # The URL session notifications will be delivered to
524
+ # @!attribute session_endpoint
525
+ # @return [String]
526
+ # @deprecated Use {#endpoints} instead
527
+ def session_endpoint
528
+ @endpoints.sessions
529
+ end
530
+
531
+ ##
532
+ # Sets the sessions endpoint
533
+ #
534
+ # @deprecated Use {#endpoints} instead
535
+ #
536
+ # @param new_session_endpoint [String] The URL to deliver session notifications to
537
+ # @return [void]
538
+ def session_endpoint=(new_session_endpoint)
539
+ warn("The 'session_endpoint' configuration option is deprecated. Set both endpoints with the 'endpoints=' method instead")
540
+ set_endpoints(notify_endpoint, new_session_endpoint) # Pass the existing notify_endpoint through so it doesn't get overwritten
541
+ end
542
+
543
+ ##
544
+ # Sets the notification and session endpoints
545
+ #
546
+ # @param new_notify_endpoint [String] The URL to deliver error notifications to
547
+ # @param new_session_endpoint [String] The URL to deliver session notifications to
548
+ # @return [void]
549
+ # @deprecated Use {#endpoints} instead
550
+ def set_endpoints(new_notify_endpoint, new_session_endpoint)
551
+ self.endpoints = EndpointConfiguration.new(new_notify_endpoint, new_session_endpoint)
552
+ end
553
+
554
+ def endpoints=(endpoint_configuration)
555
+ result = EndpointValidator.validate(endpoint_configuration)
556
+
557
+ if result.valid?
558
+ @enable_events = true
559
+ @enable_sessions = true
560
+ else
561
+ warn(result.reason)
562
+
563
+ @enable_events = result.keep_events_enabled_for_backwards_compatibility?
564
+ @enable_sessions = false
565
+ end
566
+
567
+ # use the given endpoints even if they are invalid
568
+ @endpoints = endpoint_configuration
569
+ end
570
+
571
+ ##
572
+ # Disables session tracking and delivery. Cannot be undone
573
+ #
574
+ # @return [void]
575
+ def disable_sessions
576
+ self.auto_capture_sessions = false
577
+ @enable_sessions = false
578
+ end
579
+
580
+ ##
581
+ # Add the given block to the list of on_error callbacks
582
+ #
583
+ # The on_error callbacks will be called when an error is captured or reported
584
+ # and are passed a {Bugsnag::Report} object
585
+ #
586
+ # Returning false from an on_error callback will cause the error to be ignored
587
+ # and will prevent any remaining callbacks from being called
588
+ #
589
+ # @return [void]
590
+ def on_error(&block)
591
+ middleware.use(block)
592
+ end
593
+
594
+ ##
595
+ # Add the given callback to the list of on_error callbacks
596
+ #
597
+ # The on_error callbacks will be called when an error is captured or reported
598
+ # and are passed a {Bugsnag::Report} object
599
+ #
600
+ # Returning false from an on_error callback will cause the error to be ignored
601
+ # and will prevent any remaining callbacks from being called
602
+ #
603
+ # @param callback [Proc, Method, #call]
604
+ # @return [void]
605
+ def add_on_error(callback)
606
+ middleware.use(callback)
607
+ end
608
+
609
+ ##
610
+ # Remove the given callback from the list of on_error callbacks
611
+ #
612
+ # Note that this must be the same instance that was passed to
613
+ # {#add_on_error}, otherwise it will not be removed
614
+ #
615
+ # @param callback [Proc, Method, #call]
616
+ # @return [void]
617
+ def remove_on_error(callback)
618
+ middleware.remove(callback)
619
+ end
620
+
621
+ ##
622
+ # Add the given callback to the list of on_breadcrumb callbacks
623
+ #
624
+ # The on_breadcrumb callbacks will be called when a breadcrumb is left and
625
+ # are passed the {Breadcrumbs::Breadcrumb Breadcrumb} object
626
+ #
627
+ # Returning false from an on_breadcrumb callback will cause the breadcrumb
628
+ # to be ignored and will prevent any remaining callbacks from being called
629
+ #
630
+ # @param callback [Proc, Method, #call]
631
+ # @return [void]
632
+ def add_on_breadcrumb(callback)
633
+ @on_breadcrumb_callbacks.add(callback)
634
+ end
635
+
636
+ ##
637
+ # Remove the given callback from the list of on_breadcrumb callbacks
638
+ #
639
+ # Note that this must be the same instance that was passed to
640
+ # {add_on_breadcrumb}, otherwise it will not be removed
641
+ #
642
+ # @param callback [Proc, Method, #call]
643
+ # @return [void]
644
+ def remove_on_breadcrumb(callback)
645
+ @on_breadcrumb_callbacks.remove(callback)
646
+ end
647
+
648
+ ##
649
+ # Add values to metadata
650
+ #
651
+ # @overload add_metadata(section, data)
652
+ # Merges data into the given section of metadata
653
+ # @param section [String, Symbol]
654
+ # @param data [Hash]
655
+ #
656
+ # @overload add_metadata(section, key, value)
657
+ # Sets key to value in the given section of metadata. If the value is nil
658
+ # the key will be deleted
659
+ # @param section [String, Symbol]
660
+ # @param key [String, Symbol]
661
+ # @param value
662
+ #
663
+ # @return [void]
664
+ def add_metadata(section, key_or_data, *args)
665
+ @mutex.synchronize do
666
+ @metadata_delegate.add_metadata(@metadata, section, key_or_data, *args)
667
+ end
668
+ end
669
+
670
+ ##
671
+ # Clear values from metadata
672
+ #
673
+ # @overload clear_metadata(section)
674
+ # Clears the given section of metadata
675
+ # @param section [String, Symbol]
676
+ #
677
+ # @overload clear_metadata(section, key)
678
+ # Clears the key in the given section of metadata
679
+ # @param section [String, Symbol]
680
+ # @param key [String, Symbol]
681
+ #
682
+ # @return [void]
683
+ def clear_metadata(section, *args)
684
+ @mutex.synchronize do
685
+ @metadata_delegate.clear_metadata(@metadata, section, *args)
686
+ end
687
+ end
688
+
689
+ ##
690
+ # Has the context been explicitly set?
691
+ #
692
+ # This is necessary to differentiate between the context not being set and
693
+ # the context being set to 'nil' explicitly
694
+ #
695
+ # @api private
696
+ # @return [Boolean]
697
+ def context_set?
698
+ defined?(@context) != nil
699
+ end
700
+
701
+ # TODO: These methods can be a simple attr_accessor when they replace the
702
+ # methods they are aliasing
703
+ # NOTE: they are not aliases as YARD doesn't allow documenting the non-alias
704
+ # as deprecated without also marking the alias as deprecated
705
+
706
+ # A list of which release stages should cause notifications to be sent
707
+ # @!attribute enabled_release_stages
708
+ # @return [Array<String>, nil]
709
+ def enabled_release_stages
710
+ @notify_release_stages
711
+ end
712
+
713
+ # @param release_stages [Array<String>, nil]
714
+ # @return [void]
715
+ def enabled_release_stages=(release_stages)
716
+ @notify_release_stages = release_stages
717
+ end
718
+
719
+ # A list of breadcrumb types that Bugsnag will collect automatically
720
+ # @!attribute enabled_breadcrumb_types
721
+ # @see Bugsnag::BreadcrumbType
722
+ # @return [Array<String>]
723
+ def enabled_breadcrumb_types
724
+ @enabled_automatic_breadcrumb_types
725
+ end
726
+
727
+ # @param breadcrumb_types [Array<String>]
728
+ # @return [void]
729
+ def enabled_breadcrumb_types=(breadcrumb_types)
730
+ @enabled_automatic_breadcrumb_types = breadcrumb_types
731
+ end
732
+
733
+ # Whether sessions should be tracked automatically
734
+ # @!attribute auto_track_sessions
735
+ # @return [Boolean]
736
+ def auto_track_sessions
737
+ @auto_capture_sessions
738
+ end
739
+
740
+ # @param track_sessions [Boolean]
741
+ # @return [void]
742
+ def auto_track_sessions=(track_sessions)
743
+ @auto_capture_sessions = track_sessions
744
+ end
745
+
125
746
  private
126
747
 
748
+ attr_writer :scopes_to_filter
749
+
750
+ PROG_NAME = "[Bugsnag]"
751
+
127
752
  def default_hostname
128
- # Don't send the hostname on Heroku
129
- Socket.gethostname unless ENV["DYNO"]
753
+ # Send the heroku dyno name instead of hostname if available
754
+ ENV["DYNO"] || Socket.gethostname;
130
755
  end
131
756
  end
132
757
  end