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,210 +1,210 @@
1
- require 'forwardable'
2
-
3
- module Honeybadger
4
- # +Honeybadger::Plugin+ defines the API for registering plugins with
5
- # Honeybadger. Each plugin has requirements which must be satisfied before
6
- # executing the plugin's execution block(s). This allows us to detect
7
- # optional dependencies and load the plugin for each dependency only if it's
8
- # present in the application.
9
- #
10
- # See the plugins/ directory for examples of official plugins. If you're
11
- # interested in developing a plugin for Honeybadger, see the Integration
12
- # Guide: https://docs.honeybadger.io/ruby/gem-reference/integration.html
13
- #
14
- # @example
15
- #
16
- # require 'honeybadger/plugin'
17
- # require 'honeybadger/ruby'
18
- #
19
- # module Honeybadger
20
- # module Plugins
21
- # # Register your plugin with an optional name. If the name (such as
22
- # # "my_framework") is not provided, Honeybadger will try to infer the name
23
- # # from the current file.
24
- # Plugin.register 'my_framework' do
25
- # requirement do
26
- # # Check to see if the thing you're integrating with is loaded. Return true
27
- # # if it is, or false if it isn't. An exception in this block is equivalent
28
- # # to returning false. Multiple requirement blocks are supported.
29
- # defined?(MyFramework)
30
- # end
31
- #
32
- # execution do
33
- # # Write your integration. This code will be executed only if all requirement
34
- # # blocks return true. An exception in this block will disable the plugin.
35
- # # Multiple execution blocks are supported.
36
- # MyFramework.on_exception do |exception|
37
- # Honeybadger.notify(exception)
38
- # end
39
- # end
40
- # end
41
- # end
42
- # end
43
- class Plugin
44
- # @api private
45
- CALLER_FILE = Regexp.new('\A(?:\w:)?([^:]+)(?=(:\d+))').freeze
46
-
47
- class << self
48
- # @api private
49
- @@instances = {}
50
-
51
- # @api private
52
- def instances
53
- @@instances
54
- end
55
-
56
- # Register a new plugin with Honeybadger. See {#requirement} and {#execution}.
57
- #
58
- # @example
59
- #
60
- # Honeybadger::Plugin.register 'my_framework' do
61
- # requirement { }
62
- # execution { }
63
- # end
64
- #
65
- # @param [String, Symbol] name The optional name of the plugin. Should use
66
- # +snake_case+. The name is inferred from the current file name if omitted.
67
- #
68
- # @return nil
69
- def register(name = nil, &block)
70
- name ||= name_from_caller(caller) or
71
- raise(ArgumentError, 'Plugin name is required, but was nil.')
72
- instances[key = name.to_sym] and fail("Already registered: #{name}")
73
- instances[key] = new(name).tap { |d| d.instance_eval(&block) }
74
- end
75
-
76
- # @api private
77
- def load!(config)
78
- instances.each_pair do |name, plugin|
79
- if config.load_plugin?(name)
80
- plugin.load!(config)
81
- else
82
- config.logger.debug(sprintf('skip plugin name=%s reason=disabled', name))
83
- end
84
- end
85
- end
86
-
87
- # @api private
88
- def name_from_caller(caller)
89
- caller && caller[0].match(CALLER_FILE) or
90
- fail("Unable to determine name from caller: #{caller.inspect}")
91
- File.basename($1)[/[^\.]+/]
92
- end
93
- end
94
-
95
- # @api private
96
- class Execution
97
- extend Forwardable
98
-
99
- def initialize(config, &block)
100
- @config = config
101
- @block = block
102
- end
103
-
104
- def call
105
- instance_eval(&block)
106
- end
107
-
108
- private
109
-
110
- attr_reader :config, :block
111
- def_delegator :@config, :logger
112
- end
113
-
114
- # @api private
115
- def initialize(name)
116
- @name = name
117
- @loaded = false
118
- @requirements = []
119
- @executions = []
120
- end
121
-
122
- # Define a requirement. All requirement blocks must return +true+ for the
123
- # plugin to be executed.
124
- #
125
- # @example
126
- #
127
- # Honeybadger::Plugin.register 'my_framework' do
128
- # requirement { defined?(MyFramework) }
129
- #
130
- # # Honeybadger's configuration object is available inside
131
- # # requirement blocks. It should generally not be used outside of
132
- # # internal plugins. See +Config+.
133
- # requirement { config[:'my_framework.enabled'] }
134
- #
135
- # execution { }
136
- # end
137
- #
138
- # @return nil
139
- def requirement(&block)
140
- @requirements << block
141
- end
142
-
143
- # Define an execution block. Execution blocks will be executed if all
144
- # requirement blocks return +true+.
145
- #
146
- # @example
147
- #
148
- # Honeybadger::Plugin.register 'my_framework' do
149
- # requirement { defined?(MyFramework) }
150
- #
151
- # execution do
152
- # MyFramework.on_exception {|err| Honeybadger.notify(err) }
153
- # end
154
- #
155
- # execution do
156
- # # Honeybadger's configuration object is available inside
157
- # # execution blocks. It should generally not be used outside of
158
- # # internal plugins. See +Config+.
159
- # MyFramework.use_middleware(MyMiddleware) if config[:'my_framework.use_middleware']
160
- # end
161
- # end
162
- #
163
- # @return nil
164
- def execution(&block)
165
- @executions << block
166
- end
167
-
168
- # @api private
169
- def ok?(config)
170
- @requirements.all? {|r| Execution.new(config, &r).call }
171
- rescue => e
172
- config.logger.error(sprintf("plugin error name=%s class=%s message=%s\n\t%s", name, e.class, e.message.dump, Array(e.backtrace).join("\n\t")))
173
- false
174
- end
175
-
176
- # @api private
177
- def load!(config)
178
- if @loaded
179
- config.logger.debug(sprintf('skip plugin name=%s reason=loaded', name))
180
- return false
181
- elsif ok?(config)
182
- config.logger.debug(sprintf('load plugin name=%s', name))
183
- @executions.each {|e| Execution.new(config, &e).call }
184
- @loaded = true
185
- else
186
- config.logger.debug(sprintf('skip plugin name=%s reason=requirement', name))
187
- end
188
-
189
- @loaded
190
- rescue => e
191
- config.logger.error(sprintf("plugin error name=%s class=%s message=%s\n\t%s", name, e.class, e.message.dump, Array(e.backtrace).join("\n\t")))
192
- @loaded = true
193
- false
194
- end
195
-
196
- # @api private
197
- def loaded?
198
- @loaded
199
- end
200
-
201
- # @private
202
- # Used for testing only; don't normally call this. :)
203
- def reset!
204
- @loaded = false
205
- end
206
-
207
- # @api private
208
- attr_reader :name, :requirements, :executions
209
- end
210
- end
1
+ require 'forwardable'
2
+
3
+ module Honeybadger
4
+ # +Honeybadger::Plugin+ defines the API for registering plugins with
5
+ # Honeybadger. Each plugin has requirements which must be satisfied before
6
+ # executing the plugin's execution block(s). This allows us to detect
7
+ # optional dependencies and load the plugin for each dependency only if it's
8
+ # present in the application.
9
+ #
10
+ # See the plugins/ directory for examples of official plugins. If you're
11
+ # interested in developing a plugin for Honeybadger, see the Integration
12
+ # Guide: https://docs.honeybadger.io/ruby/gem-reference/integration.html
13
+ #
14
+ # @example
15
+ #
16
+ # require 'honeybadger/plugin'
17
+ # require 'honeybadger/ruby'
18
+ #
19
+ # module Honeybadger
20
+ # module Plugins
21
+ # # Register your plugin with an optional name. If the name (such as
22
+ # # "my_framework") is not provided, Honeybadger will try to infer the name
23
+ # # from the current file.
24
+ # Plugin.register 'my_framework' do
25
+ # requirement do
26
+ # # Check to see if the thing you're integrating with is loaded. Return true
27
+ # # if it is, or false if it isn't. An exception in this block is equivalent
28
+ # # to returning false. Multiple requirement blocks are supported.
29
+ # defined?(MyFramework)
30
+ # end
31
+ #
32
+ # execution do
33
+ # # Write your integration. This code will be executed only if all requirement
34
+ # # blocks return true. An exception in this block will disable the plugin.
35
+ # # Multiple execution blocks are supported.
36
+ # MyFramework.on_exception do |exception|
37
+ # Honeybadger.notify(exception)
38
+ # end
39
+ # end
40
+ # end
41
+ # end
42
+ # end
43
+ class Plugin
44
+ # @api private
45
+ CALLER_FILE = Regexp.new('\A(?:\w:)?([^:]+)(?=(:\d+))').freeze
46
+
47
+ class << self
48
+ # @api private
49
+ @@instances = {}
50
+
51
+ # @api private
52
+ def instances
53
+ @@instances
54
+ end
55
+
56
+ # Register a new plugin with Honeybadger. See {#requirement} and {#execution}.
57
+ #
58
+ # @example
59
+ #
60
+ # Honeybadger::Plugin.register 'my_framework' do
61
+ # requirement { }
62
+ # execution { }
63
+ # end
64
+ #
65
+ # @param [String, Symbol] name The optional name of the plugin. Should use
66
+ # +snake_case+. The name is inferred from the current file name if omitted.
67
+ #
68
+ # @return nil
69
+ def register(name = nil, &block)
70
+ name ||= name_from_caller(caller) or
71
+ raise(ArgumentError, 'Plugin name is required, but was nil.')
72
+ instances[key = name.to_sym] and fail("Already registered: #{name}")
73
+ instances[key] = new(name).tap { |d| d.instance_eval(&block) }
74
+ end
75
+
76
+ # @api private
77
+ def load!(config)
78
+ instances.each_pair do |name, plugin|
79
+ if config.load_plugin?(name)
80
+ plugin.load!(config)
81
+ else
82
+ config.logger.debug(sprintf('skip plugin name=%s reason=disabled', name))
83
+ end
84
+ end
85
+ end
86
+
87
+ # @api private
88
+ def name_from_caller(caller)
89
+ caller && caller[0].match(CALLER_FILE) or
90
+ fail("Unable to determine name from caller: #{caller.inspect}")
91
+ File.basename($1)[/[^\.]+/]
92
+ end
93
+ end
94
+
95
+ # @api private
96
+ class Execution
97
+ extend Forwardable
98
+
99
+ def initialize(config, &block)
100
+ @config = config
101
+ @block = block
102
+ end
103
+
104
+ def call
105
+ instance_eval(&block)
106
+ end
107
+
108
+ private
109
+
110
+ attr_reader :config, :block
111
+ def_delegator :@config, :logger
112
+ end
113
+
114
+ # @api private
115
+ def initialize(name)
116
+ @name = name
117
+ @loaded = false
118
+ @requirements = []
119
+ @executions = []
120
+ end
121
+
122
+ # Define a requirement. All requirement blocks must return +true+ for the
123
+ # plugin to be executed.
124
+ #
125
+ # @example
126
+ #
127
+ # Honeybadger::Plugin.register 'my_framework' do
128
+ # requirement { defined?(MyFramework) }
129
+ #
130
+ # # Honeybadger's configuration object is available inside
131
+ # # requirement blocks. It should generally not be used outside of
132
+ # # internal plugins. See +Config+.
133
+ # requirement { config[:'my_framework.enabled'] }
134
+ #
135
+ # execution { }
136
+ # end
137
+ #
138
+ # @return nil
139
+ def requirement(&block)
140
+ @requirements << block
141
+ end
142
+
143
+ # Define an execution block. Execution blocks will be executed if all
144
+ # requirement blocks return +true+.
145
+ #
146
+ # @example
147
+ #
148
+ # Honeybadger::Plugin.register 'my_framework' do
149
+ # requirement { defined?(MyFramework) }
150
+ #
151
+ # execution do
152
+ # MyFramework.on_exception {|err| Honeybadger.notify(err) }
153
+ # end
154
+ #
155
+ # execution do
156
+ # # Honeybadger's configuration object is available inside
157
+ # # execution blocks. It should generally not be used outside of
158
+ # # internal plugins. See +Config+.
159
+ # MyFramework.use_middleware(MyMiddleware) if config[:'my_framework.use_middleware']
160
+ # end
161
+ # end
162
+ #
163
+ # @return nil
164
+ def execution(&block)
165
+ @executions << block
166
+ end
167
+
168
+ # @api private
169
+ def ok?(config)
170
+ @requirements.all? {|r| Execution.new(config, &r).call }
171
+ rescue => e
172
+ config.logger.error(sprintf("plugin error name=%s class=%s message=%s\n\t%s", name, e.class, e.message.dump, Array(e.backtrace).join("\n\t")))
173
+ false
174
+ end
175
+
176
+ # @api private
177
+ def load!(config)
178
+ if @loaded
179
+ config.logger.debug(sprintf('skip plugin name=%s reason=loaded', name))
180
+ return false
181
+ elsif ok?(config)
182
+ config.logger.debug(sprintf('load plugin name=%s', name))
183
+ @executions.each {|e| Execution.new(config, &e).call }
184
+ @loaded = true
185
+ else
186
+ config.logger.debug(sprintf('skip plugin name=%s reason=requirement', name))
187
+ end
188
+
189
+ @loaded
190
+ rescue => e
191
+ config.logger.error(sprintf("plugin error name=%s class=%s message=%s\n\t%s", name, e.class, e.message.dump, Array(e.backtrace).join("\n\t")))
192
+ @loaded = true
193
+ false
194
+ end
195
+
196
+ # @api private
197
+ def loaded?
198
+ @loaded
199
+ end
200
+
201
+ # @private
202
+ # Used for testing only; don't normally call this. :)
203
+ def reset!
204
+ @loaded = false
205
+ end
206
+
207
+ # @api private
208
+ attr_reader :name, :requirements, :executions
209
+ end
210
+ end
@@ -1,111 +1,111 @@
1
- require 'honeybadger/plugin'
2
- require 'honeybadger/breadcrumbs/logging'
3
-
4
- module Honeybadger
5
- module Plugins
6
- # @api private
7
- #
8
- # This plugin pounces on the dynamic nature of Ruby / Rails to inject into
9
- # the runtime and provide automatic breadcrumb events.
10
- #
11
- # === Log events
12
- #
13
- # All log messages within the execution path will automatically be appened
14
- # to the breadcrumb trace. You can disable all log events in the
15
- # Honeybadger config:
16
- #
17
- # @example
18
- #
19
- # Honeybadger.configure do |config|
20
- # config.breadcrumbs.logging.enabled = false
21
- # end
22
- #
23
- # === ActiveSupport Breadcrumbs
24
- #
25
- # We hook into Rails's ActiveSupport Instrumentation system to provide
26
- # automatic breadcrumb event generation. You can customize these events by
27
- # passing a Hash into the honeybadger configuration. The simplest method is
28
- # to alter the current defaults:
29
- #
30
- # @example
31
- # notifications = Honeybadger::Breadcrumbs::ActiveSupport.default_notifications
32
- # notifications.delete("sql.active_record")
33
- # notifications["enqueue.active_job"][:exclude_when] = lambda do |data|
34
- # data[:job].topic == "salmon_activity"
35
- # end
36
- #
37
- # Honeybadger.configure do |config|
38
- # config.breadcrumbs.active_support_notifications = notifications
39
- # end
40
- #
41
- # See RailsBreadcrumbs.send_breadcrumb_notification for specifics on the
42
- # options for customization
43
- Plugin.register :breadcrumbs do
44
- requirement { config[:'breadcrumbs.enabled'] }
45
-
46
- execution do
47
- # Rails specific breadcrumb events
48
- #
49
- if defined?(::Rails.application) && ::Rails.application
50
- config[:'breadcrumbs.active_support_notifications'].each do |name, config|
51
- RailsBreadcrumbs.subscribe_to_notification(name, config)
52
- end
53
- ActiveSupport::LogSubscriber.prepend(Honeybadger::Breadcrumbs::LogSubscriberInjector) if config[:'breadcrumbs.logging.enabled']
54
- end
55
-
56
- ::Logger.prepend(Honeybadger::Breadcrumbs::LogWrapper) if config[:'breadcrumbs.logging.enabled']
57
- end
58
- end
59
-
60
- class RailsBreadcrumbs
61
- # @api private
62
- # Used internally for sending out Rails Instrumentation breadcrumbs.
63
- #
64
- # @param [String] name The ActiveSupport instrumentation key
65
- # @param [Number] duration The time spent in the instrumentation event
66
- # @param [Hash] notification_config The instrumentation event configuration
67
- # @param [Hash] data Custom metadata from the instrumentation event
68
- #
69
- # @option notification_config [String | Proc] :message A message that describes the event. You can dynamically build the message by passing a proc that accepts the event metadata.
70
- # @option notification_config [Symbol] :category A key to group specific types of events
71
- # @option notification_config [Array] :select_keys A set of keys that filters what data we select from the instrumentation data (optional)
72
- # @option notification_config [Proc] :exclude_when A proc that accepts the data payload. A truthy return value will exclude this event from the payload (optional)
73
- # @option notification_config [Proc] :transform A proc that accepts the data payload. The return value will replace the current data hash (optional)
74
- #
75
- def self.send_breadcrumb_notification(name, duration, notification_config, data = {})
76
- return if notification_config[:exclude_when] && notification_config[:exclude_when].call(data)
77
-
78
- message =
79
- case (m = notification_config[:message])
80
- when Proc
81
- m.call(data)
82
- when String
83
- m
84
- else
85
- name
86
- end
87
-
88
- data = data.slice(*notification_config[:select_keys]) if notification_config[:select_keys]
89
- data = notification_config[:transform].call(data) if notification_config[:transform]
90
- data = data.is_a?(Hash) ? data : {}
91
-
92
- data[:duration] = duration if duration
93
-
94
- Honeybadger.add_breadcrumb(
95
- message,
96
- category: notification_config[:category] || :custom,
97
- metadata: data
98
- )
99
- end
100
-
101
- # @api private
102
- def self.subscribe_to_notification(name, notification_config)
103
- ActiveSupport::Notifications.subscribe(name) do |_, started, finished, _, data|
104
- duration = finished - started if finished && started
105
-
106
- send_breadcrumb_notification(name, duration, notification_config, data)
107
- end
108
- end
109
- end
110
- end
111
- end
1
+ require 'honeybadger/plugin'
2
+ require 'honeybadger/breadcrumbs/logging'
3
+
4
+ module Honeybadger
5
+ module Plugins
6
+ # @api private
7
+ #
8
+ # This plugin pounces on the dynamic nature of Ruby / Rails to inject into
9
+ # the runtime and provide automatic breadcrumb events.
10
+ #
11
+ # === Log events
12
+ #
13
+ # All log messages within the execution path will automatically be appened
14
+ # to the breadcrumb trace. You can disable all log events in the
15
+ # Honeybadger config:
16
+ #
17
+ # @example
18
+ #
19
+ # Honeybadger.configure do |config|
20
+ # config.breadcrumbs.logging.enabled = false
21
+ # end
22
+ #
23
+ # === ActiveSupport Breadcrumbs
24
+ #
25
+ # We hook into Rails's ActiveSupport Instrumentation system to provide
26
+ # automatic breadcrumb event generation. You can customize these events by
27
+ # passing a Hash into the honeybadger configuration. The simplest method is
28
+ # to alter the current defaults:
29
+ #
30
+ # @example
31
+ # notifications = Honeybadger::Breadcrumbs::ActiveSupport.default_notifications
32
+ # notifications.delete("sql.active_record")
33
+ # notifications["enqueue.active_job"][:exclude_when] = lambda do |data|
34
+ # data[:job].topic == "salmon_activity"
35
+ # end
36
+ #
37
+ # Honeybadger.configure do |config|
38
+ # config.breadcrumbs.active_support_notifications = notifications
39
+ # end
40
+ #
41
+ # See RailsBreadcrumbs.send_breadcrumb_notification for specifics on the
42
+ # options for customization
43
+ Plugin.register :breadcrumbs do
44
+ requirement { config[:'breadcrumbs.enabled'] }
45
+
46
+ execution do
47
+ # Rails specific breadcrumb events
48
+ #
49
+ if defined?(::Rails.application) && ::Rails.application
50
+ config[:'breadcrumbs.active_support_notifications'].each do |name, config|
51
+ RailsBreadcrumbs.subscribe_to_notification(name, config)
52
+ end
53
+ ActiveSupport::LogSubscriber.prepend(Honeybadger::Breadcrumbs::LogSubscriberInjector) if config[:'breadcrumbs.logging.enabled']
54
+ end
55
+
56
+ ::Logger.prepend(Honeybadger::Breadcrumbs::LogWrapper) if config[:'breadcrumbs.logging.enabled']
57
+ end
58
+ end
59
+
60
+ class RailsBreadcrumbs
61
+ # @api private
62
+ # Used internally for sending out Rails Instrumentation breadcrumbs.
63
+ #
64
+ # @param [String] name The ActiveSupport instrumentation key
65
+ # @param [Number] duration The time spent in the instrumentation event
66
+ # @param [Hash] notification_config The instrumentation event configuration
67
+ # @param [Hash] data Custom metadata from the instrumentation event
68
+ #
69
+ # @option notification_config [String | Proc] :message A message that describes the event. You can dynamically build the message by passing a proc that accepts the event metadata.
70
+ # @option notification_config [Symbol] :category A key to group specific types of events
71
+ # @option notification_config [Array] :select_keys A set of keys that filters what data we select from the instrumentation data (optional)
72
+ # @option notification_config [Proc] :exclude_when A proc that accepts the data payload. A truthy return value will exclude this event from the payload (optional)
73
+ # @option notification_config [Proc] :transform A proc that accepts the data payload. The return value will replace the current data hash (optional)
74
+ #
75
+ def self.send_breadcrumb_notification(name, duration, notification_config, data = {})
76
+ return if notification_config[:exclude_when] && notification_config[:exclude_when].call(data)
77
+
78
+ message =
79
+ case (m = notification_config[:message])
80
+ when Proc
81
+ m.call(data)
82
+ when String
83
+ m
84
+ else
85
+ name
86
+ end
87
+
88
+ data = data.slice(*notification_config[:select_keys]) if notification_config[:select_keys]
89
+ data = notification_config[:transform].call(data) if notification_config[:transform]
90
+ data = data.is_a?(Hash) ? data : {}
91
+
92
+ data[:duration] = duration if duration
93
+
94
+ Honeybadger.add_breadcrumb(
95
+ message,
96
+ category: notification_config[:category] || :custom,
97
+ metadata: data
98
+ )
99
+ end
100
+
101
+ # @api private
102
+ def self.subscribe_to_notification(name, notification_config)
103
+ ActiveSupport::Notifications.subscribe(name) do |_, started, finished, _, data|
104
+ duration = finished - started if finished && started
105
+
106
+ send_breadcrumb_notification(name, duration, notification_config, data)
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end