honeybadger 5.0.2 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +713 -701
  3. data/LICENSE +19 -19
  4. data/README.md +57 -57
  5. data/TROUBLESHOOTING.md +3 -3
  6. data/bin/honeybadger +5 -5
  7. data/lib/honeybadger/agent.rb +488 -488
  8. data/lib/honeybadger/backend/base.rb +116 -116
  9. data/lib/honeybadger/backend/debug.rb +22 -22
  10. data/lib/honeybadger/backend/null.rb +29 -29
  11. data/lib/honeybadger/backend/server.rb +62 -62
  12. data/lib/honeybadger/backend/test.rb +46 -46
  13. data/lib/honeybadger/backend.rb +27 -27
  14. data/lib/honeybadger/backtrace.rb +181 -181
  15. data/lib/honeybadger/breadcrumbs/active_support.rb +119 -119
  16. data/lib/honeybadger/breadcrumbs/breadcrumb.rb +53 -53
  17. data/lib/honeybadger/breadcrumbs/collector.rb +82 -82
  18. data/lib/honeybadger/breadcrumbs/logging.rb +51 -51
  19. data/lib/honeybadger/breadcrumbs/ring_buffer.rb +44 -44
  20. data/lib/honeybadger/breadcrumbs.rb +8 -8
  21. data/lib/honeybadger/cli/deploy.rb +43 -43
  22. data/lib/honeybadger/cli/exec.rb +143 -143
  23. data/lib/honeybadger/cli/helpers.rb +28 -28
  24. data/lib/honeybadger/cli/heroku.rb +129 -129
  25. data/lib/honeybadger/cli/install.rb +101 -101
  26. data/lib/honeybadger/cli/main.rb +237 -237
  27. data/lib/honeybadger/cli/notify.rb +67 -67
  28. data/lib/honeybadger/cli/test.rb +267 -267
  29. data/lib/honeybadger/cli.rb +14 -14
  30. data/lib/honeybadger/config/defaults.rb +336 -333
  31. data/lib/honeybadger/config/env.rb +42 -42
  32. data/lib/honeybadger/config/ruby.rb +146 -146
  33. data/lib/honeybadger/config/yaml.rb +76 -76
  34. data/lib/honeybadger/config.rb +413 -413
  35. data/lib/honeybadger/const.rb +20 -20
  36. data/lib/honeybadger/context_manager.rb +55 -55
  37. data/lib/honeybadger/conversions.rb +16 -16
  38. data/lib/honeybadger/init/rails.rb +38 -38
  39. data/lib/honeybadger/init/rake.rb +66 -66
  40. data/lib/honeybadger/init/ruby.rb +11 -11
  41. data/lib/honeybadger/init/sinatra.rb +51 -51
  42. data/lib/honeybadger/logging.rb +177 -177
  43. data/lib/honeybadger/notice.rb +579 -568
  44. data/lib/honeybadger/plugin.rb +210 -210
  45. data/lib/honeybadger/plugins/breadcrumbs.rb +111 -111
  46. data/lib/honeybadger/plugins/delayed_job/plugin.rb +56 -56
  47. data/lib/honeybadger/plugins/delayed_job.rb +22 -22
  48. data/lib/honeybadger/plugins/faktory.rb +52 -52
  49. data/lib/honeybadger/plugins/lambda.rb +71 -71
  50. data/lib/honeybadger/plugins/local_variables.rb +44 -44
  51. data/lib/honeybadger/plugins/passenger.rb +23 -23
  52. data/lib/honeybadger/plugins/rails.rb +72 -63
  53. data/lib/honeybadger/plugins/resque.rb +72 -72
  54. data/lib/honeybadger/plugins/shoryuken.rb +52 -52
  55. data/lib/honeybadger/plugins/sidekiq.rb +71 -62
  56. data/lib/honeybadger/plugins/sucker_punch.rb +18 -18
  57. data/lib/honeybadger/plugins/thor.rb +32 -32
  58. data/lib/honeybadger/plugins/warden.rb +19 -19
  59. data/lib/honeybadger/rack/error_notifier.rb +92 -92
  60. data/lib/honeybadger/rack/user_feedback.rb +88 -88
  61. data/lib/honeybadger/rack/user_informer.rb +45 -45
  62. data/lib/honeybadger/ruby.rb +2 -2
  63. data/lib/honeybadger/singleton.rb +103 -103
  64. data/lib/honeybadger/tasks.rb +22 -22
  65. data/lib/honeybadger/templates/feedback_form.erb +84 -84
  66. data/lib/honeybadger/util/http.rb +92 -92
  67. data/lib/honeybadger/util/lambda.rb +32 -32
  68. data/lib/honeybadger/util/request_hash.rb +73 -73
  69. data/lib/honeybadger/util/request_payload.rb +41 -41
  70. data/lib/honeybadger/util/revision.rb +39 -39
  71. data/lib/honeybadger/util/sanitizer.rb +214 -214
  72. data/lib/honeybadger/util/sql.rb +34 -34
  73. data/lib/honeybadger/util/stats.rb +50 -50
  74. data/lib/honeybadger/version.rb +4 -4
  75. data/lib/honeybadger/worker.rb +253 -253
  76. data/lib/honeybadger.rb +11 -11
  77. data/resources/ca-bundle.crt +3376 -3376
  78. data/vendor/capistrano-honeybadger/lib/capistrano/honeybadger.rb +5 -5
  79. data/vendor/capistrano-honeybadger/lib/capistrano/tasks/deploy.cap +89 -89
  80. data/vendor/capistrano-honeybadger/lib/honeybadger/capistrano/legacy.rb +47 -47
  81. data/vendor/capistrano-honeybadger/lib/honeybadger/capistrano.rb +2 -2
  82. data/vendor/cli/inifile.rb +628 -628
  83. data/vendor/cli/thor/actions/create_file.rb +103 -103
  84. data/vendor/cli/thor/actions/create_link.rb +59 -59
  85. data/vendor/cli/thor/actions/directory.rb +118 -118
  86. data/vendor/cli/thor/actions/empty_directory.rb +135 -135
  87. data/vendor/cli/thor/actions/file_manipulation.rb +316 -316
  88. data/vendor/cli/thor/actions/inject_into_file.rb +107 -107
  89. data/vendor/cli/thor/actions.rb +319 -319
  90. data/vendor/cli/thor/base.rb +656 -656
  91. data/vendor/cli/thor/command.rb +133 -133
  92. data/vendor/cli/thor/core_ext/hash_with_indifferent_access.rb +77 -77
  93. data/vendor/cli/thor/core_ext/io_binary_read.rb +10 -10
  94. data/vendor/cli/thor/core_ext/ordered_hash.rb +98 -98
  95. data/vendor/cli/thor/error.rb +32 -32
  96. data/vendor/cli/thor/group.rb +281 -281
  97. data/vendor/cli/thor/invocation.rb +178 -178
  98. data/vendor/cli/thor/line_editor/basic.rb +35 -35
  99. data/vendor/cli/thor/line_editor/readline.rb +88 -88
  100. data/vendor/cli/thor/line_editor.rb +17 -17
  101. data/vendor/cli/thor/parser/argument.rb +73 -73
  102. data/vendor/cli/thor/parser/arguments.rb +175 -175
  103. data/vendor/cli/thor/parser/option.rb +125 -125
  104. data/vendor/cli/thor/parser/options.rb +218 -218
  105. data/vendor/cli/thor/parser.rb +4 -4
  106. data/vendor/cli/thor/rake_compat.rb +71 -71
  107. data/vendor/cli/thor/runner.rb +322 -322
  108. data/vendor/cli/thor/shell/basic.rb +421 -421
  109. data/vendor/cli/thor/shell/color.rb +149 -149
  110. data/vendor/cli/thor/shell/html.rb +126 -126
  111. data/vendor/cli/thor/shell.rb +81 -81
  112. data/vendor/cli/thor/util.rb +267 -267
  113. data/vendor/cli/thor/version.rb +3 -3
  114. data/vendor/cli/thor.rb +484 -484
  115. metadata +10 -5
@@ -1,413 +1,413 @@
1
- require 'pathname'
2
- require 'delegate'
3
- require 'logger'
4
- require 'fileutils'
5
- require 'openssl'
6
-
7
- require 'honeybadger/version'
8
- require 'honeybadger/logging'
9
- require 'honeybadger/backend'
10
- require 'honeybadger/config/defaults'
11
- require 'honeybadger/util/http'
12
- require 'honeybadger/util/revision'
13
- require 'honeybadger/logging'
14
-
15
- module Honeybadger
16
- # @api private
17
- # The Config class is used to manage Honeybadger's initialization and
18
- # configuration.
19
- class Config
20
- extend Forwardable
21
-
22
- include Logging::Helper
23
-
24
- class ConfigError < StandardError; end
25
-
26
- # Config subclasses have circular dependencies, so they must be loaded
27
- # after constants are defined.
28
- autoload :Env, 'honeybadger/config/env'
29
- autoload :Yaml, 'honeybadger/config/yaml'
30
- autoload :Ruby, 'honeybadger/config/ruby'
31
-
32
- KEY_REPLACEMENT = Regexp.new('[^a-z\d_]', Regexp::IGNORECASE).freeze
33
-
34
- DOTTED_KEY = Regexp.new('\A([^\.]+)\.(.+)\z').freeze
35
-
36
- NOT_BLANK = Regexp.new('\S').freeze
37
-
38
- IVARS = [:@ruby, :@env, :@yaml, :@framework].freeze
39
-
40
- def initialize(opts = {})
41
- @ruby = opts.freeze
42
- @env = {}.freeze
43
- @yaml = {}.freeze
44
- @framework = {}.freeze
45
- end
46
-
47
- attr_accessor :ruby, :env, :yaml, :framework
48
-
49
- # Called by framework (see lib/honeybadger/init/) at the point of
50
- # initialization. This is not required for the notifier to work (i.e. with
51
- # `require 'honeybadger/ruby'`).
52
- def init!(opts = {}, env = ENV)
53
- load!(framework: opts, env: env)
54
-
55
- init_logging!
56
- init_backend!
57
-
58
- logger.info(sprintf('Initializing Honeybadger Error Tracker for Ruby. Ship it! version=%s framework=%s', Honeybadger::VERSION, detected_framework))
59
- logger.warn('Development mode is enabled. Data will not be reported until you deploy your app.') if warn_development?
60
-
61
- self
62
- end
63
-
64
- def load!(framework: {}, env: ENV)
65
- return self if @loaded
66
- self.framework = framework.freeze
67
- self.env = Env.new(env).freeze
68
- load_config_from_disk {|yaml| self.yaml = yaml.freeze }
69
- detect_revision!
70
- @loaded = true
71
- self
72
- end
73
-
74
- def configure
75
- new_ruby = Ruby.new(self)
76
- yield(new_ruby)
77
- self.ruby = ruby.merge(new_ruby).freeze
78
- @logger = @backend = nil
79
- self
80
- end
81
-
82
- def backtrace_filter(&block)
83
- if block_given?
84
- warn('DEPRECATED: backtrace_filter is deprecated. Please use before_notify instead. See https://docs.honeybadger.io/ruby/support/v4-upgrade#backtrace_filter')
85
- self[:backtrace_filter] = block
86
- end
87
-
88
- self[:backtrace_filter]
89
- end
90
-
91
- def before_notify_hooks
92
- (ruby[:before_notify] || []).clone
93
- end
94
-
95
- def exception_filter(&block)
96
- if block_given?
97
- warn('DEPRECATED: exception_filter is deprecated. Please use before_notify instead. See https://docs.honeybadger.io/ruby/support/v4-upgrade#exception_filter')
98
- self[:exception_filter] = block
99
- end
100
-
101
- self[:exception_filter]
102
- end
103
-
104
- def exception_fingerprint(&block)
105
- if block_given?
106
- warn('DEPRECATED: exception_fingerprint is deprecated. Please use before_notify instead. See https://docs.honeybadger.io/ruby/support/v4-upgrade#exception_fingerprint')
107
- self[:exception_fingerprint] = block
108
- end
109
-
110
- self[:exception_fingerprint]
111
- end
112
-
113
- def get(key)
114
- IVARS.each do |var|
115
- source = instance_variable_get(var)
116
- if source.has_key?(key)
117
- return source[key]
118
- end
119
- end
120
-
121
- DEFAULTS[key]
122
- end
123
- alias [] :get
124
-
125
- def set(key, value)
126
- self.ruby = ruby.merge(key => value).freeze
127
- @logger = @backend = nil
128
- end
129
- alias []= :set
130
-
131
- def to_hash(defaults = false)
132
- hash = [:@ruby, :@env, :@yaml, :@framework].reverse.reduce({}) do |a,e|
133
- a.merge!(instance_variable_get(e))
134
- end
135
-
136
- hash = DEFAULTS.merge(hash) if defaults
137
-
138
- undotify_keys(hash.select {|k,v| DEFAULTS.has_key?(k) })
139
- end
140
- alias to_h to_hash
141
-
142
- # Internal Helpers
143
-
144
-
145
- def logger
146
- init_logging! unless @logger
147
- @logger
148
- end
149
-
150
- def backend
151
- init_backend! unless @backend
152
- @backend
153
- end
154
-
155
- def backend=(backend)
156
- set(:backend, backend)
157
- @backend = nil
158
- end
159
-
160
- def dev?
161
- self[:env] && Array(self[:development_environments]).include?(self[:env])
162
- end
163
-
164
- def warn_development?
165
- dev? && backend.kind_of?(Backend::Null)
166
- end
167
-
168
- def public?
169
- return true if self[:report_data]
170
- return false if self[:report_data] == false
171
- !self[:env] || !dev?
172
- end
173
-
174
- def debug?
175
- !!self[:debug]
176
- end
177
-
178
- def log_debug?
179
- return debug? if self[:'logging.debug'].nil?
180
- !!self[:'logging.debug']
181
- end
182
-
183
- def ignored_classes
184
- ignore_only = get(:'exceptions.ignore_only')
185
- return ignore_only if ignore_only
186
- return DEFAULTS[:'exceptions.ignore'] unless ignore = get(:'exceptions.ignore')
187
-
188
- DEFAULTS[:'exceptions.ignore'] | Array(ignore)
189
- end
190
-
191
- def ca_bundle_path
192
- if self[:'connection.system_ssl_cert_chain'] && File.exist?(OpenSSL::X509::DEFAULT_CERT_FILE)
193
- OpenSSL::X509::DEFAULT_CERT_FILE
194
- elsif self[:'connection.ssl_ca_bundle_path']
195
- self[:'connection.ssl_ca_bundle_path']
196
- else
197
- local_cert_path
198
- end
199
- end
200
-
201
- def local_cert_path
202
- File.expand_path(File.join('..', '..', '..', 'resources', 'ca-bundle.crt'), __FILE__)
203
- end
204
-
205
- def connection_port
206
- if self[:'connection.port']
207
- self[:'connection.port']
208
- elsif self[:'connection.secure']
209
- 443
210
- else
211
- 80
212
- end
213
- end
214
-
215
- def connection_protocol
216
- if self[:'connection.secure']
217
- 'https'
218
- else
219
- 'http'
220
- end
221
- end
222
-
223
- def max_queue_size
224
- self[:max_queue_size]
225
- end
226
-
227
- def params_filters
228
- Array(self[:'request.filter_keys'])
229
- end
230
-
231
- def excluded_request_keys
232
- [].tap do |keys|
233
- keys << :session if self[:'request.disable_session']
234
- keys << :params if self[:'request.disable_params']
235
- keys << :cgi_data if self[:'request.disable_environment']
236
- keys << :url if self[:'request.disable_url']
237
- end
238
- end
239
-
240
- def log_level(key = :'logging.level')
241
- case self[key].to_s
242
- when /\A(0|debug)\z/i then Logger::DEBUG
243
- when /\A(1|info)\z/i then Logger::INFO
244
- when /\A(2|warn)\z/i then Logger::WARN
245
- when /\A(3|error)\z/i then Logger::ERROR
246
- else
247
- Logger::INFO
248
- end
249
- end
250
-
251
- def load_plugin?(name)
252
- return false if includes_token?(self[:'skipped_plugins'], name)
253
- return true unless self[:plugins].kind_of?(Array)
254
- includes_token?(self[:plugins], name)
255
- end
256
-
257
- def root_regexp
258
- return @root_regexp if @root_regexp
259
- return nil if @no_root
260
-
261
- root = get(:root).to_s
262
- @no_root = true and return nil unless root =~ NOT_BLANK
263
-
264
- @root_regexp = Regexp.new("^#{ Regexp.escape(root) }")
265
- end
266
-
267
- def detected_framework
268
- if self[:framework] =~ NOT_BLANK
269
- self[:framework].to_sym
270
- elsif defined?(::Rails::VERSION) && ::Rails::VERSION::STRING > '3.0'
271
- :rails
272
- elsif defined?(::Sinatra::VERSION)
273
- :sinatra
274
- elsif defined?(::Rack.release)
275
- :rack
276
- else
277
- :ruby
278
- end
279
- end
280
-
281
- def framework_name
282
- case detected_framework
283
- when :rails then "Rails #{::Rails::VERSION::STRING}"
284
- when :sinatra then "Sinatra #{::Sinatra::VERSION}"
285
- when :rack then "Rack #{::Rack.release}"
286
- else
287
- "Ruby #{RUBY_VERSION}"
288
- end
289
- end
290
-
291
- private
292
-
293
- def detect_revision!
294
- return if self[:revision]
295
- set(:revision, Util::Revision.detect(self[:root]))
296
- end
297
-
298
- def log_path
299
- return if log_stdout?
300
- return if !self[:'logging.path']
301
- locate_absolute_path(self[:'logging.path'], self[:root])
302
- end
303
-
304
- def config_path
305
- config_paths.first
306
- end
307
-
308
- def config_paths
309
- Array(ENV['HONEYBADGER_CONFIG_PATH'] || get(:'config.path')).map do |c|
310
- locate_absolute_path(c, self[:root])
311
- end
312
- end
313
-
314
- def default_backend
315
- return Backend::Server.new(self) if public?
316
- Backend::Null.new(self)
317
- end
318
-
319
- def init_backend!
320
- if self[:backend].is_a?(String) || self[:backend].is_a?(Symbol)
321
- @backend = Backend.for(self[:backend].to_sym).new(self)
322
- return
323
- end
324
-
325
- if ruby[:backend].respond_to?(:notify)
326
- @backend = ruby[:backend]
327
- return
328
- end
329
-
330
- if ruby[:backend]
331
- logger.warn(sprintf('Unknown backend: %p; default will be used. Backend must respond to #notify', self[:backend]))
332
- end
333
-
334
- @backend = default_backend
335
- end
336
-
337
- def build_stdout_logger
338
- logger = Logger.new($stdout)
339
- logger.formatter = lambda do |severity, datetime, progname, msg|
340
- "#{msg}\n"
341
- end
342
- logger.level = log_level
343
- Logging::FormattedLogger.new(logger)
344
- end
345
-
346
- def build_file_logger(path)
347
- Logger.new(path).tap do |logger|
348
- logger.level = log_level
349
- logger.formatter = Logger::Formatter.new
350
- end
351
- end
352
-
353
- def log_stdout?
354
- self[:'logging.path'] && self[:'logging.path'].to_s.downcase == 'stdout'
355
- end
356
-
357
- def build_logger
358
- return ruby[:logger] if ruby[:logger]
359
-
360
- return build_stdout_logger if log_stdout?
361
-
362
- if path = log_path
363
- FileUtils.mkdir_p(path.dirname) unless path.dirname.writable?
364
- return build_file_logger(path)
365
- end
366
-
367
- return framework[:logger] if framework[:logger]
368
-
369
- Logger.new(nil)
370
- end
371
-
372
- def init_logging!
373
- @logger = Logging::ConfigLogger.new(self, build_logger)
374
- end
375
-
376
- # Takes an Array and a value and returns true if the value exists in the
377
- # array in String or Symbol form, otherwise false.
378
- def includes_token?(obj, value)
379
- return false unless obj.kind_of?(Array)
380
- obj.map(&:to_sym).include?(value.to_sym)
381
- end
382
-
383
- def locate_absolute_path(path, root)
384
- path = Pathname.new(path.to_s)
385
- if path.absolute?
386
- path
387
- else
388
- Pathname.new(root.to_s).join(path.to_s)
389
- end
390
- end
391
-
392
- def load_config_from_disk
393
- if (path = config_paths.find(&:exist?)) && path.file?
394
- Yaml.new(path, self[:env]).tap do |yml|
395
- yield(yml) if block_given?
396
- end
397
- end
398
- end
399
-
400
- def undotify_keys(hash)
401
- {}.tap do |new_hash|
402
- hash.each_pair do |k,v|
403
- if k.to_s =~ DOTTED_KEY
404
- new_hash[$1] ||= {}
405
- new_hash[$1] = undotify_keys(new_hash[$1].merge({$2 => v}))
406
- else
407
- new_hash[k.to_s] = v
408
- end
409
- end
410
- end
411
- end
412
- end
413
- end
1
+ require 'pathname'
2
+ require 'delegate'
3
+ require 'logger'
4
+ require 'fileutils'
5
+ require 'openssl'
6
+
7
+ require 'honeybadger/version'
8
+ require 'honeybadger/logging'
9
+ require 'honeybadger/backend'
10
+ require 'honeybadger/config/defaults'
11
+ require 'honeybadger/util/http'
12
+ require 'honeybadger/util/revision'
13
+ require 'honeybadger/logging'
14
+
15
+ module Honeybadger
16
+ # @api private
17
+ # The Config class is used to manage Honeybadger's initialization and
18
+ # configuration.
19
+ class Config
20
+ extend Forwardable
21
+
22
+ include Logging::Helper
23
+
24
+ class ConfigError < StandardError; end
25
+
26
+ # Config subclasses have circular dependencies, so they must be loaded
27
+ # after constants are defined.
28
+ autoload :Env, 'honeybadger/config/env'
29
+ autoload :Yaml, 'honeybadger/config/yaml'
30
+ autoload :Ruby, 'honeybadger/config/ruby'
31
+
32
+ KEY_REPLACEMENT = Regexp.new('[^a-z\d_]', Regexp::IGNORECASE).freeze
33
+
34
+ DOTTED_KEY = Regexp.new('\A([^\.]+)\.(.+)\z').freeze
35
+
36
+ NOT_BLANK = Regexp.new('\S').freeze
37
+
38
+ IVARS = [:@ruby, :@env, :@yaml, :@framework].freeze
39
+
40
+ def initialize(opts = {})
41
+ @ruby = opts.freeze
42
+ @env = {}.freeze
43
+ @yaml = {}.freeze
44
+ @framework = {}.freeze
45
+ end
46
+
47
+ attr_accessor :ruby, :env, :yaml, :framework
48
+
49
+ # Called by framework (see lib/honeybadger/init/) at the point of
50
+ # initialization. This is not required for the notifier to work (i.e. with
51
+ # `require 'honeybadger/ruby'`).
52
+ def init!(opts = {}, env = ENV)
53
+ load!(framework: opts, env: env)
54
+
55
+ init_logging!
56
+ init_backend!
57
+
58
+ logger.info(sprintf('Initializing Honeybadger Error Tracker for Ruby. Ship it! version=%s framework=%s', Honeybadger::VERSION, detected_framework))
59
+ logger.warn('Development mode is enabled. Data will not be reported until you deploy your app.') if warn_development?
60
+
61
+ self
62
+ end
63
+
64
+ def load!(framework: {}, env: ENV)
65
+ return self if @loaded
66
+ self.framework = framework.freeze
67
+ self.env = Env.new(env).freeze
68
+ load_config_from_disk {|yaml| self.yaml = yaml.freeze }
69
+ detect_revision!
70
+ @loaded = true
71
+ self
72
+ end
73
+
74
+ def configure
75
+ new_ruby = Ruby.new(self)
76
+ yield(new_ruby)
77
+ self.ruby = ruby.merge(new_ruby).freeze
78
+ @logger = @backend = nil
79
+ self
80
+ end
81
+
82
+ def backtrace_filter(&block)
83
+ if block_given?
84
+ warn('DEPRECATED: backtrace_filter is deprecated. Please use before_notify instead. See https://docs.honeybadger.io/ruby/support/v4-upgrade#backtrace_filter')
85
+ self[:backtrace_filter] = block
86
+ end
87
+
88
+ self[:backtrace_filter]
89
+ end
90
+
91
+ def before_notify_hooks
92
+ (ruby[:before_notify] || []).clone
93
+ end
94
+
95
+ def exception_filter(&block)
96
+ if block_given?
97
+ warn('DEPRECATED: exception_filter is deprecated. Please use before_notify instead. See https://docs.honeybadger.io/ruby/support/v4-upgrade#exception_filter')
98
+ self[:exception_filter] = block
99
+ end
100
+
101
+ self[:exception_filter]
102
+ end
103
+
104
+ def exception_fingerprint(&block)
105
+ if block_given?
106
+ warn('DEPRECATED: exception_fingerprint is deprecated. Please use before_notify instead. See https://docs.honeybadger.io/ruby/support/v4-upgrade#exception_fingerprint')
107
+ self[:exception_fingerprint] = block
108
+ end
109
+
110
+ self[:exception_fingerprint]
111
+ end
112
+
113
+ def get(key)
114
+ IVARS.each do |var|
115
+ source = instance_variable_get(var)
116
+ if source.has_key?(key)
117
+ return source[key]
118
+ end
119
+ end
120
+
121
+ DEFAULTS[key]
122
+ end
123
+ alias [] :get
124
+
125
+ def set(key, value)
126
+ self.ruby = ruby.merge(key => value).freeze
127
+ @logger = @backend = nil
128
+ end
129
+ alias []= :set
130
+
131
+ def to_hash(defaults = false)
132
+ hash = [:@ruby, :@env, :@yaml, :@framework].reverse.reduce({}) do |a,e|
133
+ a.merge!(instance_variable_get(e))
134
+ end
135
+
136
+ hash = DEFAULTS.merge(hash) if defaults
137
+
138
+ undotify_keys(hash.select {|k,v| DEFAULTS.has_key?(k) })
139
+ end
140
+ alias to_h to_hash
141
+
142
+ # Internal Helpers
143
+
144
+
145
+ def logger
146
+ init_logging! unless @logger
147
+ @logger
148
+ end
149
+
150
+ def backend
151
+ init_backend! unless @backend
152
+ @backend
153
+ end
154
+
155
+ def backend=(backend)
156
+ set(:backend, backend)
157
+ @backend = nil
158
+ end
159
+
160
+ def dev?
161
+ self[:env] && Array(self[:development_environments]).include?(self[:env])
162
+ end
163
+
164
+ def warn_development?
165
+ dev? && backend.kind_of?(Backend::Null)
166
+ end
167
+
168
+ def public?
169
+ return true if self[:report_data]
170
+ return false if self[:report_data] == false
171
+ !self[:env] || !dev?
172
+ end
173
+
174
+ def debug?
175
+ !!self[:debug]
176
+ end
177
+
178
+ def log_debug?
179
+ return debug? if self[:'logging.debug'].nil?
180
+ !!self[:'logging.debug']
181
+ end
182
+
183
+ def ignored_classes
184
+ ignore_only = get(:'exceptions.ignore_only')
185
+ return ignore_only if ignore_only
186
+ return DEFAULTS[:'exceptions.ignore'] unless ignore = get(:'exceptions.ignore')
187
+
188
+ DEFAULTS[:'exceptions.ignore'] | Array(ignore)
189
+ end
190
+
191
+ def ca_bundle_path
192
+ if self[:'connection.system_ssl_cert_chain'] && File.exist?(OpenSSL::X509::DEFAULT_CERT_FILE)
193
+ OpenSSL::X509::DEFAULT_CERT_FILE
194
+ elsif self[:'connection.ssl_ca_bundle_path']
195
+ self[:'connection.ssl_ca_bundle_path']
196
+ else
197
+ local_cert_path
198
+ end
199
+ end
200
+
201
+ def local_cert_path
202
+ File.expand_path(File.join('..', '..', '..', 'resources', 'ca-bundle.crt'), __FILE__)
203
+ end
204
+
205
+ def connection_port
206
+ if self[:'connection.port']
207
+ self[:'connection.port']
208
+ elsif self[:'connection.secure']
209
+ 443
210
+ else
211
+ 80
212
+ end
213
+ end
214
+
215
+ def connection_protocol
216
+ if self[:'connection.secure']
217
+ 'https'
218
+ else
219
+ 'http'
220
+ end
221
+ end
222
+
223
+ def max_queue_size
224
+ self[:max_queue_size]
225
+ end
226
+
227
+ def params_filters
228
+ Array(self[:'request.filter_keys'])
229
+ end
230
+
231
+ def excluded_request_keys
232
+ [].tap do |keys|
233
+ keys << :session if self[:'request.disable_session']
234
+ keys << :params if self[:'request.disable_params']
235
+ keys << :cgi_data if self[:'request.disable_environment']
236
+ keys << :url if self[:'request.disable_url']
237
+ end
238
+ end
239
+
240
+ def log_level(key = :'logging.level')
241
+ case self[key].to_s
242
+ when /\A(0|debug)\z/i then Logger::DEBUG
243
+ when /\A(1|info)\z/i then Logger::INFO
244
+ when /\A(2|warn)\z/i then Logger::WARN
245
+ when /\A(3|error)\z/i then Logger::ERROR
246
+ else
247
+ Logger::INFO
248
+ end
249
+ end
250
+
251
+ def load_plugin?(name)
252
+ return false if includes_token?(self[:'skipped_plugins'], name)
253
+ return true unless self[:plugins].kind_of?(Array)
254
+ includes_token?(self[:plugins], name)
255
+ end
256
+
257
+ def root_regexp
258
+ return @root_regexp if @root_regexp
259
+ return nil if @no_root
260
+
261
+ root = get(:root).to_s
262
+ @no_root = true and return nil unless root =~ NOT_BLANK
263
+
264
+ @root_regexp = Regexp.new("^#{ Regexp.escape(root) }")
265
+ end
266
+
267
+ def detected_framework
268
+ if self[:framework] =~ NOT_BLANK
269
+ self[:framework].to_sym
270
+ elsif defined?(::Rails::VERSION) && ::Rails::VERSION::STRING > '3.0'
271
+ :rails
272
+ elsif defined?(::Sinatra::VERSION)
273
+ :sinatra
274
+ elsif defined?(::Rack.release)
275
+ :rack
276
+ else
277
+ :ruby
278
+ end
279
+ end
280
+
281
+ def framework_name
282
+ case detected_framework
283
+ when :rails then "Rails #{::Rails::VERSION::STRING}"
284
+ when :sinatra then "Sinatra #{::Sinatra::VERSION}"
285
+ when :rack then "Rack #{::Rack.release}"
286
+ else
287
+ "Ruby #{RUBY_VERSION}"
288
+ end
289
+ end
290
+
291
+ private
292
+
293
+ def detect_revision!
294
+ return if self[:revision]
295
+ set(:revision, Util::Revision.detect(self[:root]))
296
+ end
297
+
298
+ def log_path
299
+ return if log_stdout?
300
+ return if !self[:'logging.path']
301
+ locate_absolute_path(self[:'logging.path'], self[:root])
302
+ end
303
+
304
+ def config_path
305
+ config_paths.first
306
+ end
307
+
308
+ def config_paths
309
+ Array(ENV['HONEYBADGER_CONFIG_PATH'] || get(:'config.path')).map do |c|
310
+ locate_absolute_path(c, self[:root])
311
+ end
312
+ end
313
+
314
+ def default_backend
315
+ return Backend::Server.new(self) if public?
316
+ Backend::Null.new(self)
317
+ end
318
+
319
+ def init_backend!
320
+ if self[:backend].is_a?(String) || self[:backend].is_a?(Symbol)
321
+ @backend = Backend.for(self[:backend].to_sym).new(self)
322
+ return
323
+ end
324
+
325
+ if ruby[:backend].respond_to?(:notify)
326
+ @backend = ruby[:backend]
327
+ return
328
+ end
329
+
330
+ if ruby[:backend]
331
+ logger.warn(sprintf('Unknown backend: %p; default will be used. Backend must respond to #notify', self[:backend]))
332
+ end
333
+
334
+ @backend = default_backend
335
+ end
336
+
337
+ def build_stdout_logger
338
+ logger = Logger.new($stdout)
339
+ logger.formatter = lambda do |severity, datetime, progname, msg|
340
+ "#{msg}\n"
341
+ end
342
+ logger.level = log_level
343
+ Logging::FormattedLogger.new(logger)
344
+ end
345
+
346
+ def build_file_logger(path)
347
+ Logger.new(path).tap do |logger|
348
+ logger.level = log_level
349
+ logger.formatter = Logger::Formatter.new
350
+ end
351
+ end
352
+
353
+ def log_stdout?
354
+ self[:'logging.path'] && self[:'logging.path'].to_s.downcase == 'stdout'
355
+ end
356
+
357
+ def build_logger
358
+ return ruby[:logger] if ruby[:logger]
359
+
360
+ return build_stdout_logger if log_stdout?
361
+
362
+ if path = log_path
363
+ FileUtils.mkdir_p(path.dirname) unless path.dirname.writable?
364
+ return build_file_logger(path)
365
+ end
366
+
367
+ return framework[:logger] if framework[:logger]
368
+
369
+ Logger.new(nil)
370
+ end
371
+
372
+ def init_logging!
373
+ @logger = Logging::ConfigLogger.new(self, build_logger)
374
+ end
375
+
376
+ # Takes an Array and a value and returns true if the value exists in the
377
+ # array in String or Symbol form, otherwise false.
378
+ def includes_token?(obj, value)
379
+ return false unless obj.kind_of?(Array)
380
+ obj.map(&:to_sym).include?(value.to_sym)
381
+ end
382
+
383
+ def locate_absolute_path(path, root)
384
+ path = Pathname.new(path.to_s)
385
+ if path.absolute?
386
+ path
387
+ else
388
+ Pathname.new(root.to_s).join(path.to_s)
389
+ end
390
+ end
391
+
392
+ def load_config_from_disk
393
+ if (path = config_paths.find(&:exist?)) && path.file?
394
+ Yaml.new(path, self[:env]).tap do |yml|
395
+ yield(yml) if block_given?
396
+ end
397
+ end
398
+ end
399
+
400
+ def undotify_keys(hash)
401
+ {}.tap do |new_hash|
402
+ hash.each_pair do |k,v|
403
+ if k.to_s =~ DOTTED_KEY
404
+ new_hash[$1] ||= {}
405
+ new_hash[$1] = undotify_keys(new_hash[$1].merge({$2 => v}))
406
+ else
407
+ new_hash[k.to_s] = v
408
+ end
409
+ end
410
+ end
411
+ end
412
+ end
413
+ end