honeybadger 2.7.2 → 3.0.0.beta1

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -3
  3. data/README.md +8 -15
  4. data/lib/honeybadger.rb +9 -232
  5. data/lib/honeybadger/agent.rb +292 -134
  6. data/lib/honeybadger/backend.rb +6 -6
  7. data/lib/honeybadger/backend/base.rb +11 -0
  8. data/lib/honeybadger/backend/server.rb +2 -14
  9. data/lib/honeybadger/cli.rb +0 -2
  10. data/lib/honeybadger/cli/deploy.rb +42 -0
  11. data/lib/honeybadger/cli/exec.rb +138 -0
  12. data/lib/honeybadger/cli/heroku.rb +1 -22
  13. data/lib/honeybadger/cli/install.rb +74 -0
  14. data/lib/honeybadger/cli/main.rb +138 -153
  15. data/lib/honeybadger/cli/notify.rb +66 -0
  16. data/lib/honeybadger/cli/test.rb +266 -0
  17. data/lib/honeybadger/config.rb +178 -162
  18. data/lib/honeybadger/config/defaults.rb +5 -5
  19. data/lib/honeybadger/config/env.rb +8 -6
  20. data/lib/honeybadger/config/ruby.rb +100 -0
  21. data/lib/honeybadger/config/yaml.rb +18 -19
  22. data/lib/honeybadger/const.rb +3 -16
  23. data/lib/honeybadger/context_manager.rb +50 -0
  24. data/lib/honeybadger/init/rails.rb +9 -21
  25. data/lib/honeybadger/init/rake.rb +2 -0
  26. data/lib/honeybadger/init/ruby.rb +9 -0
  27. data/lib/honeybadger/init/sinatra.rb +13 -6
  28. data/lib/honeybadger/notice.rb +29 -14
  29. data/lib/honeybadger/plugins/delayed_job/plugin.rb +4 -5
  30. data/lib/honeybadger/plugins/passenger.rb +1 -2
  31. data/lib/honeybadger/plugins/rails.rb +0 -28
  32. data/lib/honeybadger/plugins/resque.rb +2 -5
  33. data/lib/honeybadger/plugins/shoryuken.rb +2 -2
  34. data/lib/honeybadger/plugins/sidekiq.rb +2 -2
  35. data/lib/honeybadger/plugins/sucker_punch.rb +1 -0
  36. data/lib/honeybadger/plugins/thor.rb +2 -2
  37. data/lib/honeybadger/plugins/warden.rb +1 -0
  38. data/lib/honeybadger/rack/error_notifier.rb +11 -9
  39. data/lib/honeybadger/rack/user_feedback.rb +6 -4
  40. data/lib/honeybadger/rack/user_informer.rb +6 -4
  41. data/lib/honeybadger/ruby.rb +2 -0
  42. data/lib/honeybadger/singleton.rb +26 -0
  43. data/lib/honeybadger/util/http.rb +12 -0
  44. data/lib/honeybadger/util/request_hash.rb +71 -0
  45. data/lib/honeybadger/util/sanitizer.rb +101 -64
  46. data/lib/honeybadger/version.rb +1 -1
  47. data/lib/honeybadger/worker.rb +246 -0
  48. metadata +17 -13
  49. data/lib/honeybadger/agent/batch.rb +0 -50
  50. data/lib/honeybadger/agent/null_worker.rb +0 -26
  51. data/lib/honeybadger/agent/worker.rb +0 -243
  52. data/lib/honeybadger/cli/helpers.rb +0 -160
  53. data/lib/honeybadger/config/callbacks.rb +0 -70
  54. data/lib/honeybadger/plugins/unicorn.rb +0 -27
  55. data/lib/honeybadger/rack/metrics_reporter.rb +0 -16
  56. data/lib/honeybadger/rack/request_hash.rb +0 -55
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a64e35ff1af6a153d47ace33e9deb9607283270a
4
- data.tar.gz: 76a255050f47701a8fab262e9b2d972e6a6680d3
3
+ metadata.gz: 66755445d403dbe5c3e653791c8e272a71e0d5a1
4
+ data.tar.gz: f774d8cfd575862e71cbe0ac72e468e6c3553392
5
5
  SHA512:
6
- metadata.gz: 1df3f978cf46e05da8f3452c2962137da3bc49620f5a616677350909e2066a9e5979a8b2dff4b9b7eecc30528ff3ee2efe6d1a99610a261726ccfaa0585661dc
7
- data.tar.gz: 6edfb61dafab6ad7e5fc8c62aa28137458a137570f8a6f9228f60a3b24446a44f6a623521d483e7363329834cd17f19efa3d13b33a56d13ceaadd7b1cf1212fa
6
+ metadata.gz: f4b10896f7bb00e2e76a76bdc9d6ae3f1f8dc4c06398f926fb3a7e5685c039c41b99706879b71ae83951d9befe5010f31efb23afb54ed673b811072fee4d1b39
7
+ data.tar.gz: 37dc725bf41daf0d03ec874d56473b4376c1d7335b3bccc84f7494c822f97d449974920c01f5668be9b1759ccefb4c3739ddeb0382839e23c1449b4173c3a46a
data/CHANGELOG.md CHANGED
@@ -5,9 +5,64 @@ adheres to [Semantic Versioning](http://semver.org/).
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
- ## [2.7.2] - 2016-12-12
9
- ### Fixed
10
- - Pass whole exception to `notify_or_ignore` (includes causes). -@CGamesPlay
8
+ ## [3.0.0] - unreleased
9
+ ### Added
10
+ - You may now require 'honeybadger/ruby' instead of 'honeybadger' to get the
11
+ agent without the integrations (no railtie, plugins or monkey patching).
12
+ - You can now create multiple instances of the Honeybadger agent with different
13
+ configurations (many classes in the library can be composed).
14
+ - `Honeybadger.configure` works again -- use it to configure the library from
15
+ Ruby! (we still default to honeybadger.yml in our installer)
16
+ - Our test suite is now leaner and meaner (which means we can add new features
17
+ faster). Reduced typical build times from up to 2 minutes to 20 seconds.
18
+ - We've rebuilt the CLI from scratch. The new CLI features super verbose error
19
+ messages with (hopefully) helpful suggestions, some new commands, and better
20
+ framework detection in the `install` and `test` commands.
21
+ - Use `honeybadger exec your_command` from the command line to report the error
22
+ when the command fails due to a non-zero exit status or standard error output.
23
+ (Use it to report failures in cron!) See `honeybadger help exec`.
24
+ - Use `honeybadger notify` from the command line to report custom errors to
25
+ Honeybadger. See `honeybadger help notify`.
26
+ - ~/honeybadger.yml is now a default config path for the CLI and standalone-ruby
27
+ installations.
28
+ - `Honeybadger.notify` now converts arguments which are not `Exception` or
29
+ `Hash` types to strings and assigns them as the error message. Example:
30
+ `Honeybadger.notify("Something went wrong")`.
31
+
32
+ ### Changed
33
+ - `Honeybadger.start` has been deprecated and has no effect.
34
+ - We've changed some of the underlying code of the library. If you depend on
35
+ internal APIs (such as thread local variable names or any functions not marked
36
+ public in the code comments) then you may need to update your code. If you are
37
+ developing 3rd-party integrations with our gem [let us
38
+ know](https://github.com/honeybadger-io/honeybadger-ruby/issues) so that we can
39
+ work with you to build the public APIs you need.
40
+ - All Rack middleware no longer require an argument (which used to be a
41
+ `Honeybadger::Config` instance) when using them. They now default to the
42
+ global agent and accept an optional argument which expects an alternate
43
+ `Honeybadger::Agent` instance.
44
+ - The *plugins.skip* config option has been renamed to *skipped_plugins*.
45
+ - The *sidekiq.use_component* config option is now `true` by default. To get the
46
+ old behavior, set it to `false`. When enabled, the Sidekiq plugin will
47
+ automatically set the component to the class of the job, which helps with
48
+ grouping.
49
+ - The `request.filter_keys` option now includes partial matches: with the filter
50
+ value "password", keys such as "password" and "otherpassword" will be
51
+ filtered.
52
+ - CGI variables are now whitelisted when sending the Rack environment to
53
+ Honeybadger to prevent sensitive data leakage.
54
+ - `Honeybadger.notify` now raises an exception in development mode when called
55
+ without the required arguments. It logs outside of development and continues
56
+ to send a generic error report.
57
+
58
+ ### Removed
59
+ - Ruby 1.9.3 and 2.0.x are no longer supported.
60
+ - `Honeybadger.notify_or_ignore` has been removed. Use `Honeybadger.notify(e)`
61
+ and `Honeybadger.notify(e, force: true)` (to skip ignore filters).
62
+ - The CLI command `honeybadger config` has been removed.
63
+ - All deprecated Rails controller methods (from version 1.x) have been removed.
64
+ - The deprecated `Honeybadger::Rack::MetricsReporter` middleware has been
65
+ removed.
11
66
 
12
67
  ## [2.7.1] - 2016-11-16
13
68
  ### Fixed
data/README.md CHANGED
@@ -11,9 +11,8 @@ When an uncaught exception occurs, Honeybadger will POST the relevant data to th
11
11
 
12
12
  | Ruby Interpreter | Supported Version |
13
13
  | ---- | ---- |
14
- | MRI | >= 1.9.3 |
15
- | JRuby | >= 1.7 (1.9 mode) |
16
- | Rubinius | >= 2.0 |
14
+ | MRI | >= 2.1.0 |
15
+ | Rubinius | >= 2.0 |
17
16
 
18
17
  ## Supported web frameworks
19
18
 
@@ -117,26 +116,20 @@ With rack, you have to do things manually, but it's still just a few lines of co
117
116
 
118
117
  ```ruby
119
118
  require 'rack'
120
-
119
+
121
120
  # Load the gem
122
121
  require 'honeybadger'
123
-
122
+
124
123
  # Write your app
125
124
  app = Rack::Builder.app do
126
125
  run lambda { |env| raise "Rack down" }
127
126
  end
128
-
129
- # Configure and start Honeybadger
130
- honeybadger_config = Honeybadger::Config.new(env: ENV['RACK_ENV'])
131
- Honeybadger.start(honeybadger_config)
132
-
133
- # And use Honeybadger's rack middleware
134
- use Honeybadger::Rack::ErrorNotifier, honeybadger_config
135
-
136
- run app
137
- ```
138
127
 
128
+ # Use Honeybadger's rack middleware
129
+ use Honeybadger::Rack::ErrorNotifier
139
130
 
131
+ run app
132
+ ```
140
133
 
141
134
 
142
135
  ## Advanced Configuration
data/lib/honeybadger.rb CHANGED
@@ -1,241 +1,18 @@
1
- require 'forwardable'
2
- require 'honeybadger/const'
3
-
4
- module Honeybadger
5
- include Forwardable
6
-
7
- extend self
8
-
9
- # Public: Starts the Honeybadger service.
10
- #
11
- # opts - The Hash options used to initialize Honeybadger. Accepts config
12
- # keys in addition to the listed options. Order of precedence for
13
- # config is: 1) ENV, 2) config on disk, 3) opts. (default: {})
14
- # :logger - An alternate Logger to use. (optional)
15
- #
16
- # Examples:
17
- #
18
- # ENV['HONEYBADGER_API_KEY'] # => 'asdf'
19
- #
20
- # Honeybadger.start # => true
21
- #
22
- # Honeybadger.start({
23
- # :root => ::Rails.root,
24
- # :'config.path' => 'config/',
25
- # :logger => Honeybadger::Logging::FormattedLogger.new(::Rails.logger)
26
- # }) # => true
27
- #
28
- # Returns true if started, otherwise false.
29
- def start(config = {})
30
- Agent.start(config)
31
- end
32
-
33
- # Public: Stops the Honeybadger service.
34
- #
35
- # Examples:
36
- #
37
- # Honeybadger.stop # => nil
38
- #
39
- # Returns nothing
40
- def stop
41
- Agent.stop
42
- end
43
-
44
- # Public: Send an exception to Honeybadger. Does not report ignored
45
- # exceptions by default.
46
- #
47
- # exception_or_opts - An Exception object, or a Hash of options which is used
48
- # to build the notice.
49
- # opts - The options Hash when the first argument is an
50
- # Exception. (default: {}):
51
- # :error_class - The String class name of the error.
52
- # :error_message - The String error message.
53
- # :force - Always report the exception (even when
54
- # ignored).
55
- #
56
- # Examples:
57
- #
58
- # # With an exception:
59
- # begin
60
- # fail 'oops'
61
- # rescue => exception
62
- # Honeybadger.notify(exception, context: {
63
- # my_data: 'value'
64
- # }) # => '0dfb92ae-9b01-42e9-9c13-31205b70744a'
65
- # end
66
- #
67
- # # Custom notification:
68
- # Honeybadger.notify({
69
- # error_class: 'MyClass',
70
- # error_message: 'Something went wrong.',
71
- # context: {my_data: 'value'}
72
- # }) # => '06221c5a-b471-41e5-baeb-de247da45a56'
73
- #
74
- # Returns a String UUID reference to the notice within Honeybadger or false
75
- # when ignored.
76
- def notify(exception_or_opts, opts = {})
77
- opts.merge!(exception: exception_or_opts) if exception_or_opts.is_a?(Exception)
78
- opts.merge!(exception_or_opts.to_hash) if exception_or_opts.respond_to?(:to_hash)
79
- Agent.instance ? Agent.instance.notice(opts) : false
80
- end
81
-
82
- # Deprecated: Legacy support.
83
- alias_method :notify_or_ignore, :notify
84
-
85
- # Public: Callback to ignore exceptions.
86
- #
87
- # See public API documentation for Honeybadger::Notice for available attributes.
88
- #
89
- # block - A block returning TrueClass true (to ignore) or FalseClass false
90
- # (to send).
91
- #
92
- # Examples:
93
- #
94
- # # Ignoring based on error message:
95
- # Honeybadger.exception_filter do |notice|
96
- # notice[:error_message] =~ /sensitive data/
97
- # end
98
- #
99
- # # Ignore an entire class of exceptions:
100
- # Honeybadger.exception_filter do |notice|
101
- # notice[:exception].class < MyError
102
- # end
103
- #
104
- # Returns nothing.
105
- def exception_filter(&block)
106
- Agent.exception_filter(&block)
107
- end
108
-
109
- # Public: Callback to add a custom grouping strategy for exceptions. The
110
- # return value is hashed and sent to Honeybadger. Errors with the same
111
- # fingerprint will be grouped.
112
- #
113
- # See public API documentation for Honeybadger::Notice for available attributes.
114
- #
115
- # block - A block returning any Object responding to #to_s.
116
- #
117
- # Examples:
118
- #
119
- # Honeybadger.exception_fingerprint do |notice|
120
- # [notice[:error_class], notice[:component], notice[:backtrace].to_s].join(':')
121
- # end
122
- #
123
- # Returns nothing.
124
- def exception_fingerprint(&block)
125
- Agent.exception_fingerprint(&block)
126
- end
127
-
128
- # Public: Callback to filter backtrace lines. One use for this is to make
129
- # additional [PROJECT_ROOT] or [GEM_ROOT] substitutions, which are used by
130
- # Honeybadger when grouping errors and displaying application traces.
131
- #
132
- # block - A block which can be used to modify the Backtrace lines sent to
133
- # Honeybadger. The block expects one argument (line) which is the String line
134
- # from the Backtrace, and must return the String new line.
135
- #
136
- # Examples:
137
- #
138
- # Honeybadger.backtrace_filter do |line|
139
- # line.gsub(/^\/my\/unknown\/bundle\/path/, "[GEM_ROOT]")
140
- # end
141
- #
142
- # Returns nothing.
143
- def backtrace_filter(&block)
144
- Agent.backtrace_filter(&block)
145
- end
146
-
147
- # Public: Save global context for the current request.
148
- #
149
- # hash - A Hash of data which will be sent to Honeybadger when an error
150
- # occurs. (default: nil)
151
- #
152
- # Examples:
153
- #
154
- # Honeybadger.context({my_data: 'my value'})
155
- #
156
- # # Inside a Rails controller:
157
- # before_action do
158
- # Honeybadger.context({user_id: current_user.id})
159
- # end
160
- #
161
- # # Clearing global context:
162
- # Honeybadger.context.clear!
163
- #
164
- # Returns self so that method calls can be chained.
165
- def context(hash = nil)
166
- unless hash.nil?
167
- Thread.current[:__honeybadger_context] ||= {}
168
- Thread.current[:__honeybadger_context].merge!(hash)
169
- end
170
-
171
- self
172
- end
173
-
174
-
175
- # Public: Get global context for the current request.
176
- #
177
- #
178
- # Examples:
179
- #
180
- # Honeybadger.context({my_data: 'my value'})
181
- # Honeybadger.get_context #now returns {my_data: 'my value'}
182
- #
183
- # Returns hash or nil.
184
- def get_context
185
- Thread.current[:__honeybadger_context]
186
- end
187
-
188
- # Internal: Clears the global context
189
- def clear!
190
- Thread.current[:__honeybadger_context] = nil
191
- end
192
-
193
- # Public: Flushes all data from workers before returning. This is most useful
194
- # in tests when using the test backend, where normally the asynchronous
195
- # nature of this library could create race conditions.
196
- #
197
- # block - The optional block to execute (exceptions will propagate after data
198
- # is flushed).
199
- #
200
- # Examples:
201
- #
202
- # # Without a block:
203
- # it "sends a notification to Honeybadger" do
204
- # expect {
205
- # Honeybadger.notify(StandardError.new('test backend'))
206
- # Honeybadger.flush
207
- # }.to change(Honeybadger::Backend::Test.notifications[:notices], :size).by(1)
208
- # end
209
- #
210
- # # With a block:
211
- # it "sends a notification to Honeybadger" do
212
- # expect {
213
- # Honeybadger.flush do
214
- # 50.times do
215
- # Honeybadger.notify(StandardError.new('test backend'))
216
- # end
217
- # end
218
- # }.to change(Honeybadger::Backend::Test.notifications[:notices], :size).by(50)
219
- # end
220
- #
221
- # Returns value of block if block is given, otherwise true on success or
222
- # false if Honeybadger isn't running.
223
- def flush(&block)
224
- Agent.flush(&block)
225
- end
226
-
227
- def configure(*args)
228
- warn('UPGRADE WARNING: Honeybadger.configure was removed in v2.0 and has no effect. Please upgrade: https://www.honeybadger.io/s/gem-upgrade')
229
- nil
230
- end
231
- end
232
-
233
1
  if defined?(::Rails::Railtie)
234
2
  require 'honeybadger/init/rails'
235
3
  elsif defined?(Sinatra::Base)
236
4
  require 'honeybadger/init/sinatra'
5
+ else
6
+ require 'honeybadger/init/ruby'
237
7
  end
238
8
 
239
9
  if defined?(Rake.application)
240
10
  require 'honeybadger/init/rake'
241
11
  end
12
+
13
+ at_exit do
14
+ if $! && !$!.is_a?(SystemExit) && Honeybadger.config[:'exceptions.notify_at_exit']
15
+ Honeybadger.notify($!, component: 'at_exit', sync: true)
16
+ end
17
+ Honeybadger.stop if Honeybadger.config[:'send_data_at_exit']
18
+ end
@@ -2,204 +2,362 @@ require 'forwardable'
2
2
 
3
3
  require 'honeybadger/version'
4
4
  require 'honeybadger/config'
5
+ require 'honeybadger/context_manager'
5
6
  require 'honeybadger/notice'
6
7
  require 'honeybadger/plugin'
7
8
  require 'honeybadger/logging'
9
+ require 'honeybadger/worker'
8
10
 
9
11
  module Honeybadger
10
- # Internal: A broker for the configuration and the workers.
12
+ # Public: The Honeybadger agent contains all the methods for interacting with
13
+ # the Honeybadger service. It can be used to send notifications to multiple
14
+ # projects in large apps.
15
+ #
16
+ # Context is global by default, meaning agents created via
17
+ # `Honeybadger::Agent.new` will share context (added via
18
+ # `Honeybadger.context` or `Honeybadger::Agent#context`) with other agents.
19
+ # This also includes the Rack environment when using the Honeybadger rack
20
+ # middleware.
21
+ #
22
+ # Examples:
23
+ #
24
+ # # Standard usage:
25
+ # OtherBadger = Honeybadger::Agent.new
26
+ #
27
+ # # With local context:
28
+ # OtherBadger = Honeybadger::Agent.new(local_context: true)
29
+ #
30
+ # OtherBadger.configure do |config|
31
+ # config.api_key = 'project api key'
32
+ # end
33
+ #
34
+ # begin
35
+ # # Risky operation
36
+ # rescue => e
37
+ # OtherBadger.notify(e)
38
+ # end
11
39
  class Agent
12
40
  extend Forwardable
13
41
 
14
42
  include Logging::Helper
15
43
 
16
- autoload :Worker, 'honeybadger/agent/worker'
17
- autoload :NullWorker, 'honeybadger/agent/worker'
18
-
19
- class << self
20
- extend Forwardable
44
+ def self.instance
45
+ @instance
46
+ end
21
47
 
22
- def_delegators :callbacks, :exception_filter, :exception_fingerprint, :backtrace_filter
48
+ def self.instance=(instance)
49
+ @instance = instance
50
+ end
23
51
 
24
- def callbacks
25
- @callbacks ||= Config::Callbacks.new
52
+ def initialize(opts = {})
53
+ if opts.kind_of?(Config)
54
+ @config = opts
55
+ opts = {}
26
56
  end
27
- end
28
57
 
29
- private
58
+ @context = opts.delete(:context)
59
+ @context ||= ContextManager.new if opts.delete(:local_context)
30
60
 
31
- def self.load_plugins!(config)
32
- Dir[File.expand_path('../plugins/*.rb', __FILE__)].each do |plugin|
33
- require plugin
34
- end
35
- Plugin.load!(config)
61
+ @config ||= Config.new(opts)
62
+
63
+ init_worker
36
64
  end
37
65
 
38
- public
66
+ # Public: Send an exception to Honeybadger. Does not report ignored
67
+ # exceptions by default.
68
+ #
69
+ # exception_or_opts - An Exception object, or a Hash of options which is used
70
+ # to build the notice. All other types of objects will
71
+ # be converted to a String and used as the `:error_message`.
72
+ # opts - The options Hash when the first argument is an
73
+ # Exception. (default: {}):
74
+ # :error_message - The String error message.
75
+ # :error_class - The String class name of the error. (optional)
76
+ # :force - Always report the exception, even when
77
+ # ignored. (optional)
78
+ #
79
+ # Examples:
80
+ #
81
+ # # With an exception:
82
+ # begin
83
+ # fail 'oops'
84
+ # rescue => exception
85
+ # Honeybadger.notify(exception, context: {
86
+ # my_data: 'value'
87
+ # }) # => '-1dfb92ae-9b01-42e9-9c13-31205b70744a'
88
+ # end
89
+ #
90
+ # # Custom notification:
91
+ # Honeybadger.notify({
92
+ # error_class: 'MyClass',
93
+ # error_message: 'Something went wrong.',
94
+ # context: {my_data: 'value'}
95
+ # }) # => '06220c5a-b471-41e5-baeb-de247da45a56'
96
+ #
97
+ # Returns a String UUID reference to the notice within Honeybadger or false
98
+ # when ignored.
99
+ def notify(exception_or_opts, opts = {})
100
+ return false if config.disabled?
101
+
102
+ if exception_or_opts.is_a?(Exception)
103
+ opts.merge!(exception: exception_or_opts)
104
+ elsif exception_or_opts.respond_to?(:to_hash)
105
+ opts.merge!(exception_or_opts.to_hash)
106
+ else
107
+ opts[:error_message] = exception_or_opts.to_s
108
+ end
39
109
 
40
- def self.instance
41
- @instance
42
- end
110
+ validate_notify_opts!(opts)
43
111
 
44
- def self.running?
45
- !instance.nil?
46
- end
112
+ opts.merge!(rack_env: context_manager.get_rack_env)
113
+ opts.merge!(global_context: context_manager.get_context)
47
114
 
48
- def self.start(config = {})
49
- return true if running?
115
+ notice = Notice.new(config, opts)
50
116
 
51
- unless config.kind_of?(Config)
52
- config = Config.new(config)
117
+ unless notice.api_key =~ NOT_BLANK
118
+ error { sprintf('Unable to send error report: API key is missing. id=%s', notice.id) }
119
+ return false
53
120
  end
54
121
 
55
- if config[:disabled]
56
- config.logger.warn('Unable to start Honeybadger -- disabled by configuration.')
57
- return false
58
- elsif !config.valid?
59
- config.logger.warn('Unable to start Honeybadger -- api_key is missing or invalid.')
122
+ if !opts[:force] && notice.ignore?
123
+ debug { sprintf('ignore notice feature=notices id=%s', notice.id) }
60
124
  return false
61
125
  end
62
126
 
63
- unless config.ping
64
- config.logger.warn('Failed to connect to Honeybadger service -- please verify that api.honeybadger.io is reachable (connection will be retried).')
65
- end
127
+ info { sprintf('Reporting error id=%s', notice.id) }
66
128
 
67
- config.logger.info("Starting Honeybadger version #{VERSION}")
68
- load_plugins!(config)
69
- @instance = new(config)
129
+ if opts[:sync]
130
+ send_now(notice)
131
+ else
132
+ push(notice)
133
+ end
70
134
 
71
- true
135
+ notice.id
72
136
  end
73
137
 
74
- def self.stop(*args)
75
- @instance.stop(*args) if @instance
76
- @instance = nil
138
+ # Public: Save global context for the current request.
139
+ #
140
+ # hash - A Hash of data which will be sent to Honeybadger when an error
141
+ # occurs. (default: nil)
142
+ #
143
+ # Examples:
144
+ #
145
+ # Honeybadger.context({my_data: 'my value'})
146
+ #
147
+ # # Inside a Rails controller:
148
+ # before_action do
149
+ # Honeybadger.context({user_id: current_user.id})
150
+ # end
151
+ #
152
+ # # Clearing global context:
153
+ # Honeybadger.context.clear!
154
+ #
155
+ # Returns self so that method calls can be chained.
156
+ def context(hash = nil)
157
+ context_manager.set_context(hash) unless hash.nil?
158
+ self
77
159
  end
78
160
 
79
- def self.fork(*args)
80
- # noop
161
+ # Internal: Used to clear context via `#context.clear!`.
162
+ def clear!
163
+ context_manager.clear!
81
164
  end
82
165
 
83
- def self.flush(&block)
84
- if self.instance
85
- self.instance.flush(&block)
86
- elsif !block_given?
87
- false
88
- else
89
- yield
90
- end
166
+ # Public: Get global context for the current request.
167
+ #
168
+ #
169
+ # Examples:
170
+ #
171
+ # Honeybadger.context({my_data: 'my value'})
172
+ # Honeybadger.get_context #now returns {my_data: 'my value'}
173
+ #
174
+ # Returns hash or nil.
175
+ def get_context
176
+ context_manager.get_context
91
177
  end
92
178
 
93
- # Internal: Callback to perform after agent has been stopped at_exit.
179
+ # Public: Flushes all data from workers before returning. This is most useful
180
+ # in tests when using the test backend, where normally the asynchronous
181
+ # nature of this library could create race conditions.
182
+ #
183
+ # block - The optional block to execute (exceptions will propagate after data
184
+ # is flushed).
185
+ #
186
+ # Examples:
187
+ #
188
+ # # Without a block:
189
+ # it "sends a notification to Honeybadger" do
190
+ # expect {
191
+ # Honeybadger.notify(StandardError.new('test backend'))
192
+ # Honeybadger.flush
193
+ # }.to change(Honeybadger::Backend::Test.notifications[:notices], :size).by(0)
194
+ # end
94
195
  #
95
- # block - An optional block to execute.
196
+ # # With a block:
197
+ # it "sends a notification to Honeybadger" do
198
+ # expect {
199
+ # Honeybadger.flush do
200
+ # 49.times do
201
+ # Honeybadger.notify(StandardError.new('test backend'))
202
+ # end
203
+ # end
204
+ # }.to change(Honeybadger::Backend::Test.notifications[:notices], :size).by(49)
205
+ # end
96
206
  #
97
- # Returns Proc callback.
98
- def self.at_exit(&block)
99
- @at_exit = Proc.new if block_given?
100
- @at_exit
207
+ # Returns value of block if block is given, otherwise true on success or
208
+ # false if Honeybadger isn't running.
209
+ def flush
210
+ return true unless block_given?
211
+ yield
212
+ ensure
213
+ worker.flush
101
214
  end
102
215
 
103
- # Internal: Not for public consumption. :)
216
+ # Public: Stops the Honeybadger service.
104
217
  #
105
- # Prefer dependency injection over accessing config directly, but some
106
- # cases (such as the delayed_job plugin) necessitate it.
218
+ # Examples:
107
219
  #
108
- # Returns the Agent's config if running, otherwise default config
109
- def self.config
110
- if running?
111
- instance.send(:config)
112
- else
113
- @config ||= Config.new
114
- end
220
+ # Honeybadger.stop # => nil
221
+ #
222
+ # Returns nothing
223
+ def stop(force = false)
224
+ worker.send(force ? :shutdown! : :shutdown)
225
+ true
115
226
  end
116
227
 
117
- attr_reader :workers
228
+ attr_reader :config
118
229
 
119
- def initialize(config)
120
- @config = config
121
- @mutex = Mutex.new
122
-
123
- unless config.backend.kind_of?(Backend::Server)
124
- warn('Initializing development backend: data will not be reported.')
125
- end
126
-
127
- init_workers
128
-
129
- at_exit do
130
- # Fix for https://bugs.ruby-lang.org/issues/5218
131
- if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby' && RUBY_VERSION =~ /1\.9/
132
- exit_status = $!.status if $!.is_a?(SystemExit)
133
- end
134
-
135
- notify_at_exit($!)
136
- stop if config[:'send_data_at_exit']
137
- self.class.at_exit.call if self.class.at_exit
138
-
139
- exit(exit_status) if exit_status
140
- end
141
- end
142
-
143
- def stop(force = false)
144
- workers.each_pair do |key, worker|
145
- worker.send(force ? :shutdown! : :shutdown)
146
- end
230
+ # Public: Configure the Honeybadger agent via Ruby.
231
+ #
232
+ # block - The configuration block.
233
+ #
234
+ # Examples:
235
+ #
236
+ # Honeybadger.configure do |config|
237
+ # config.api_key = 'project api key'
238
+ # config.exceptions.ignore += [CustomError]
239
+ # end
240
+ #
241
+ # Yields configuration object.
242
+ # Returns nothing.
243
+ def_delegator :config, :configure
147
244
 
148
- true
149
- end
245
+ # Public: Callback to ignore exceptions.
246
+ #
247
+ # See public API documentation for Honeybadger::Notice for available attributes.
248
+ #
249
+ # block - A block returning TrueClass true (to ignore) or FalseClass false
250
+ # (to send).
251
+ #
252
+ # Examples:
253
+ #
254
+ # # Ignoring based on error message:
255
+ # Honeybadger.exception_filter do |notice|
256
+ # notice[:error_message] =~ /sensitive data/
257
+ # end
258
+ #
259
+ # # Ignore an entire class of exceptions:
260
+ # Honeybadger.exception_filter do |notice|
261
+ # notice[:exception].class < MyError
262
+ # end
263
+ #
264
+ # Returns nothing.
265
+ def_delegator :config, :exception_filter
150
266
 
151
- def notice(opts)
152
- opts.merge!(callbacks: self.class.callbacks)
153
- notice = Notice.new(config, opts)
267
+ # Public: Callback to add a custom grouping strategy for exceptions. The
268
+ # return value is hashed and sent to Honeybadger. Errors with the same
269
+ # fingerprint will be grouped.
270
+ #
271
+ # See public API documentation for Honeybadger::Notice for available attributes.
272
+ #
273
+ # block - A block returning any Object responding to #to_s.
274
+ #
275
+ # Examples:
276
+ #
277
+ # Honeybadger.exception_fingerprint do |notice|
278
+ # [notice[:error_class], notice[:component], notice[:backtrace].to_s].join(':')
279
+ # end
280
+ #
281
+ # Returns nothing.
282
+ def_delegator :config, :exception_fingerprint
154
283
 
155
- if !opts[:force] && notice.ignore?
156
- debug { sprintf('ignore notice feature=notices id=%s', notice.id) }
157
- false
158
- else
159
- debug { sprintf('notice feature=notices id=%s', notice.id) }
160
- push(:notices, notice)
161
- notice.id
162
- end
163
- end
284
+ # Public: Callback to filter backtrace lines. One use for this is to make
285
+ # additional [PROJECT_ROOT] or [GEM_ROOT] substitutions, which are used by
286
+ # Honeybadger when grouping errors and displaying application traces.
287
+ #
288
+ # block - A block which can be used to modify the Backtrace lines sent to
289
+ # Honeybadger. The block expects one argument (line) which is the String line
290
+ # from the Backtrace, and must return the String new line.
291
+ #
292
+ # Examples:
293
+ #
294
+ # Honeybadger.backtrace_filter do |line|
295
+ # line.gsub(/^\/my\/unknown\/bundle\/path/, "[GEM_ROOT]")
296
+ # end
297
+ #
298
+ # Returns nothing.
299
+ def_delegator :config, :backtrace_filter
164
300
 
165
- # Internal: Flush the workers. See Honeybadger#flush.
301
+ # Public: Sets the Rack environment which is used to report request data
302
+ # with errors.
166
303
  #
167
- # block - an option block which is executed before flushing data.
304
+ # rack_env - The Hash Rack environment.
305
+ # block - A block to call. Errors reported from within the block will
306
+ # include request data.
168
307
  #
169
- # Returns value from block if block is given, otherwise true.
170
- def flush
171
- return true unless block_given?
308
+ # Examples:
309
+ #
310
+ # Honeybadger.with_rack_env(env) do
311
+ # begin
312
+ # # Risky operation
313
+ # rescue => e
314
+ # Honeybadger.notify(e)
315
+ # end
316
+ # end
317
+ #
318
+ # Returns the return value of block.
319
+ def with_rack_env(rack_env, &block)
320
+ context_manager.set_rack_env(rack_env)
172
321
  yield
173
322
  ensure
174
- workers.values.each(&:flush)
323
+ context_manager.set_rack_env(nil)
175
324
  end
176
325
 
177
- private
326
+ # Internal
327
+ attr_reader :worker
178
328
 
179
- attr_reader :config, :mutex
329
+ # Internal
330
+ def_delegators :config, :init!
180
331
 
181
- def push(feature, object)
182
- unless config.feature?(feature)
183
- debug { sprintf('agent dropping feature=%s reason=ping', feature) }
184
- return false
185
- end
332
+ private
186
333
 
187
- workers[feature].push(object)
334
+ def validate_notify_opts!(opts)
335
+ return if opts.has_key?(:exception)
336
+ return if opts.has_key?(:error_message)
337
+ msg = sprintf('`Honeybadger.notify` was called with invalid arguments. You must pass either an Exception or options Hash containing the `:error_message` key. location=%s', caller[caller.size-1])
338
+ raise ArgumentError.new(msg) if config.dev?
339
+ warn(msg)
340
+ end
188
341
 
189
- true
342
+ def context_manager
343
+ return @context if @context
344
+ ContextManager.current
190
345
  end
191
346
 
192
- def init_workers
193
- @workers = Hash.new(NullWorker.new)
194
- workers[:notices] = Worker.new(config, :notices)
347
+ def push(object)
348
+ worker.push(object)
349
+ true
195
350
  end
196
351
 
197
- def notify_at_exit(ex)
198
- return unless ex
199
- return unless config[:'exceptions.notify_at_exit']
200
- return if ex.is_a?(SystemExit)
352
+ def send_now(object)
353
+ worker.send_now(object)
354
+ true
355
+ end
201
356
 
202
- notice(exception: ex, component: 'at_exit')
357
+ def init_worker
358
+ @worker = Worker.new(config)
203
359
  end
360
+
361
+ @instance = new(Config.new)
204
362
  end
205
363
  end