honeybadger 2.7.2 → 3.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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