honeybadger 1.0.0
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.
- data/Gemfile +13 -0
- data/Gemfile.lock +114 -0
- data/Guardfile +5 -0
- data/MIT-LICENSE +22 -0
- data/README.md +271 -0
- data/Rakefile +261 -0
- data/SUPPORTED_RAILS_VERSIONS +26 -0
- data/TESTING.md +33 -0
- data/features/metal.feature +18 -0
- data/features/rack.feature +56 -0
- data/features/rails.feature +211 -0
- data/features/rake.feature +27 -0
- data/features/sinatra.feature +29 -0
- data/features/step_definitions/file_steps.rb +10 -0
- data/features/step_definitions/metal_steps.rb +23 -0
- data/features/step_definitions/rack_steps.rb +23 -0
- data/features/step_definitions/rails_application_steps.rb +394 -0
- data/features/step_definitions/rake_steps.rb +17 -0
- data/features/support/env.rb +17 -0
- data/features/support/honeybadger_shim.rb.template +8 -0
- data/features/support/rails.rb +201 -0
- data/features/support/rake/Rakefile +68 -0
- data/features/support/terminal.rb +107 -0
- data/generators/honeybadger/honeybadger_generator.rb +94 -0
- data/generators/honeybadger/lib/insert_commands.rb +34 -0
- data/generators/honeybadger/lib/rake_commands.rb +24 -0
- data/generators/honeybadger/templates/capistrano_hook.rb +6 -0
- data/generators/honeybadger/templates/honeybadger_tasks.rake +25 -0
- data/generators/honeybadger/templates/initializer.rb +6 -0
- data/honeybadger.gemspec +109 -0
- data/lib/honeybadger.rb +162 -0
- data/lib/honeybadger/backtrace.rb +123 -0
- data/lib/honeybadger/capistrano.rb +43 -0
- data/lib/honeybadger/configuration.rb +273 -0
- data/lib/honeybadger/notice.rb +314 -0
- data/lib/honeybadger/rack.rb +55 -0
- data/lib/honeybadger/rails.rb +34 -0
- data/lib/honeybadger/rails/action_controller_catcher.rb +30 -0
- data/lib/honeybadger/rails/controller_methods.rb +69 -0
- data/lib/honeybadger/rails/middleware/exceptions_catcher.rb +29 -0
- data/lib/honeybadger/rails3_tasks.rb +84 -0
- data/lib/honeybadger/railtie.rb +45 -0
- data/lib/honeybadger/rake_handler.rb +65 -0
- data/lib/honeybadger/sender.rb +120 -0
- data/lib/honeybadger/shared_tasks.rb +36 -0
- data/lib/honeybadger/tasks.rb +82 -0
- data/lib/honeybadger_tasks.rb +65 -0
- data/lib/rails/generators/honeybadger/honeybadger_generator.rb +99 -0
- data/rails/init.rb +1 -0
- data/resources/README.md +34 -0
- data/resources/ca-bundle.crt +3376 -0
- data/script/integration_test.rb +38 -0
- data/test/test_helper.rb +143 -0
- data/test/unit/backtrace_test.rb +180 -0
- data/test/unit/capistrano_test.rb +34 -0
- data/test/unit/configuration_test.rb +201 -0
- data/test/unit/honeybadger_tasks_test.rb +163 -0
- data/test/unit/logger_test.rb +72 -0
- data/test/unit/notice_test.rb +406 -0
- data/test/unit/notifier_test.rb +245 -0
- data/test/unit/rack_test.rb +56 -0
- data/test/unit/rails/action_controller_catcher_test.rb +300 -0
- data/test/unit/rails_test.rb +35 -0
- data/test/unit/sender_test.rb +257 -0
- metadata +315 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
# Defines deploy:notify_honeybadger which will send information about the deploy to Honeybadger.
|
2
|
+
require 'capistrano'
|
3
|
+
|
4
|
+
module Honeybadger
|
5
|
+
module Capistrano
|
6
|
+
def self.load_into(configuration)
|
7
|
+
configuration.load do
|
8
|
+
after "deploy", "honeybadger:deploy"
|
9
|
+
after "deploy:migrations", "honeybadger:deploy"
|
10
|
+
|
11
|
+
namespace :honeybadger do
|
12
|
+
desc <<-DESC
|
13
|
+
Notify Honeybadger of the deployment by running the notification on the REMOTE machine.
|
14
|
+
- Run remotely so we use remote API keys, environment, etc.
|
15
|
+
DESC
|
16
|
+
task :deploy, :except => { :no_release => true } do
|
17
|
+
rails_env = fetch(:rails_env, "production")
|
18
|
+
honeybadger_env = fetch(:honeybadger_env, fetch(:rails_env, "production"))
|
19
|
+
local_user = ENV['USER'] || ENV['USERNAME']
|
20
|
+
executable = RUBY_PLATFORM.downcase.include?('mswin') ? fetch(:rake, 'rake.bat') : fetch(:rake, 'rake')
|
21
|
+
directory = configuration.current_release
|
22
|
+
notify_command = "cd #{directory}; #{executable} RAILS_ENV=#{rails_env} honeybadger:deploy TO=#{honeybadger_env} REVISION=#{current_revision} REPO=#{repository} USER=#{local_user}"
|
23
|
+
notify_command << " DRY_RUN=true" if dry_run
|
24
|
+
notify_command << " API_KEY=#{ENV['API_KEY']}" if ENV['API_KEY']
|
25
|
+
logger.info "Notifying Honeybadger of Deploy (#{notify_command})"
|
26
|
+
if configuration.dry_run
|
27
|
+
logger.info "DRY RUN: Notification not actually run."
|
28
|
+
else
|
29
|
+
result = ""
|
30
|
+
run(notify_command, :once => true) { |ch, stream, data| result << data }
|
31
|
+
# TODO: Check if SSL is active on account via result content.
|
32
|
+
end
|
33
|
+
logger.info "Honeybadger Notification Complete."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
if Capistrano::Configuration.instance
|
42
|
+
Honeybadger::Capistrano.load_into(Capistrano::Configuration.instance)
|
43
|
+
end
|
@@ -0,0 +1,273 @@
|
|
1
|
+
module Honeybadger
|
2
|
+
class Configuration
|
3
|
+
OPTIONS = [:api_key, :backtrace_filters, :development_environments, :environment_name,
|
4
|
+
:host, :http_open_timeout, :http_read_timeout, :ignore, :ignore_by_filters,
|
5
|
+
:ignore_user_agent, :notifier_name, :notifier_url, :notifier_version,
|
6
|
+
:params_filters, :project_root, :port, :protocol, :proxy_host, :proxy_pass,
|
7
|
+
:proxy_port, :proxy_user, :secure, :use_system_ssl_cert_chain, :framework,
|
8
|
+
:user_information, :rescue_rake_exceptions].freeze
|
9
|
+
|
10
|
+
# The API key for your project, found on the project edit form.
|
11
|
+
attr_accessor :api_key
|
12
|
+
|
13
|
+
# The host to connect to (defaults to honeybadger.io).
|
14
|
+
attr_accessor :host
|
15
|
+
|
16
|
+
# The port on which your Honeybadger server runs (defaults to 443 for secure
|
17
|
+
# connections, 80 for insecure connections).
|
18
|
+
attr_accessor :port
|
19
|
+
|
20
|
+
# +true+ for https connections, +false+ for http connections.
|
21
|
+
attr_accessor :secure
|
22
|
+
|
23
|
+
# +true+ to use whatever CAs OpenSSL has installed on your system. +false+ to use the ca-bundle.crt file included in Honeybadger itself (reccomended and default)
|
24
|
+
attr_accessor :use_system_ssl_cert_chain
|
25
|
+
|
26
|
+
# The HTTP open timeout in seconds (defaults to 2).
|
27
|
+
attr_accessor :http_open_timeout
|
28
|
+
|
29
|
+
# The HTTP read timeout in seconds (defaults to 5).
|
30
|
+
attr_accessor :http_read_timeout
|
31
|
+
|
32
|
+
# The hostname of your proxy server (if using a proxy)
|
33
|
+
attr_accessor :proxy_host
|
34
|
+
|
35
|
+
# The port of your proxy server (if using a proxy)
|
36
|
+
attr_accessor :proxy_port
|
37
|
+
|
38
|
+
# The username to use when logging into your proxy server (if using a proxy)
|
39
|
+
attr_accessor :proxy_user
|
40
|
+
|
41
|
+
# The password to use when logging into your proxy server (if using a proxy)
|
42
|
+
attr_accessor :proxy_pass
|
43
|
+
|
44
|
+
# A list of parameters that should be filtered out of what is sent to Honeybadger.
|
45
|
+
# By default, all "password" attributes will have their contents replaced.
|
46
|
+
attr_reader :params_filters
|
47
|
+
|
48
|
+
# A list of filters for cleaning and pruning the backtrace. See #filter_backtrace.
|
49
|
+
attr_reader :backtrace_filters
|
50
|
+
|
51
|
+
# A list of filters for ignoring exceptions. See #ignore_by_filter.
|
52
|
+
attr_reader :ignore_by_filters
|
53
|
+
|
54
|
+
# A list of exception classes to ignore. The array can be appended to.
|
55
|
+
attr_reader :ignore
|
56
|
+
|
57
|
+
# A list of user agents that are being ignored. The array can be appended to.
|
58
|
+
attr_reader :ignore_user_agent
|
59
|
+
|
60
|
+
# A list of environments in which notifications should not be sent.
|
61
|
+
attr_accessor :development_environments
|
62
|
+
|
63
|
+
# The name of the environment the application is running in
|
64
|
+
attr_accessor :environment_name
|
65
|
+
|
66
|
+
# The path to the project in which the error occurred, such as the Rails.root
|
67
|
+
attr_accessor :project_root
|
68
|
+
|
69
|
+
# The name of the notifier library being used to send notifications (such as "Honeybadger Notifier")
|
70
|
+
attr_accessor :notifier_name
|
71
|
+
|
72
|
+
# The version of the notifier library being used to send notifications (such as "1.0.2")
|
73
|
+
attr_accessor :notifier_version
|
74
|
+
|
75
|
+
# The url of the notifier library being used to send notifications
|
76
|
+
attr_accessor :notifier_url
|
77
|
+
|
78
|
+
# The logger used by Honeybadger
|
79
|
+
attr_accessor :logger
|
80
|
+
|
81
|
+
# The text that the placeholder is replaced with. {{error_id}} is the actual error number.
|
82
|
+
attr_accessor :user_information
|
83
|
+
|
84
|
+
# The framework Honeybadger is configured to use
|
85
|
+
attr_accessor :framework
|
86
|
+
|
87
|
+
# Should Honeybadger catch exceptions from Rake tasks?
|
88
|
+
# (boolean or nil; set to nil to catch exceptions when rake isn't running from a terminal; default is nil)
|
89
|
+
attr_accessor :rescue_rake_exceptions
|
90
|
+
|
91
|
+
DEFAULT_PARAMS_FILTERS = %w(password password_confirmation).freeze
|
92
|
+
|
93
|
+
DEFAULT_BACKTRACE_FILTERS = [
|
94
|
+
lambda { |line|
|
95
|
+
if defined?(Honeybadger.configuration.project_root) && Honeybadger.configuration.project_root.to_s != ''
|
96
|
+
line.sub(/#{Honeybadger.configuration.project_root}/, "[PROJECT_ROOT]")
|
97
|
+
else
|
98
|
+
line
|
99
|
+
end
|
100
|
+
},
|
101
|
+
lambda { |line| line.gsub(/^\.\//, "") },
|
102
|
+
lambda { |line|
|
103
|
+
if defined?(Gem)
|
104
|
+
Gem.path.inject(line) do |line, path|
|
105
|
+
line.gsub(/#{path}/, "[GEM_ROOT]")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
},
|
109
|
+
lambda { |line| line if line !~ %r{lib/honeybadger} }
|
110
|
+
].freeze
|
111
|
+
|
112
|
+
IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound',
|
113
|
+
'ActionController::RoutingError',
|
114
|
+
'ActionController::InvalidAuthenticityToken',
|
115
|
+
'CGI::Session::CookieStore::TamperedWithCookie',
|
116
|
+
'ActionController::UnknownAction',
|
117
|
+
'AbstractController::ActionNotFound',
|
118
|
+
'Mongoid::Errors::DocumentNotFound']
|
119
|
+
|
120
|
+
alias_method :secure?, :secure
|
121
|
+
alias_method :use_system_ssl_cert_chain?, :use_system_ssl_cert_chain
|
122
|
+
|
123
|
+
def initialize
|
124
|
+
@secure = false
|
125
|
+
@use_system_ssl_cert_chain= false
|
126
|
+
@host = 'api.honeybadger.io'
|
127
|
+
@http_open_timeout = 2
|
128
|
+
@http_read_timeout = 5
|
129
|
+
@params_filters = DEFAULT_PARAMS_FILTERS.dup
|
130
|
+
@backtrace_filters = DEFAULT_BACKTRACE_FILTERS.dup
|
131
|
+
@ignore_by_filters = []
|
132
|
+
@ignore = IGNORE_DEFAULT.dup
|
133
|
+
@ignore_user_agent = []
|
134
|
+
@development_environments = %w(development test cucumber)
|
135
|
+
@notifier_name = 'Honeybadger Notifier'
|
136
|
+
@notifier_version = VERSION
|
137
|
+
@notifier_url = 'https://github.com/honeybadger/honeybadger'
|
138
|
+
@framework = 'Standalone'
|
139
|
+
@user_information = 'Honeybadger Error {{error_id}}'
|
140
|
+
@rescue_rake_exceptions = nil
|
141
|
+
end
|
142
|
+
|
143
|
+
# Public: Takes a block and adds it to the list of backtrace filters. When
|
144
|
+
# the filters run, the block will be handed each line of the backtrace and
|
145
|
+
# can modify it as necessary.
|
146
|
+
#
|
147
|
+
# &block - The new backtrace filter.
|
148
|
+
#
|
149
|
+
# Examples:
|
150
|
+
#
|
151
|
+
# config.filter_bracktrace do |line|
|
152
|
+
# line.gsub(/^#{Rails.root}/, "[Rails.root]")
|
153
|
+
# end
|
154
|
+
#
|
155
|
+
# Yields a line in the backtrace.
|
156
|
+
def filter_backtrace(&block)
|
157
|
+
self.backtrace_filters << block
|
158
|
+
end
|
159
|
+
|
160
|
+
# Public: Takes a block and adds it to the list of ignore filters. When
|
161
|
+
# the filters run, the block will be handed the exception.
|
162
|
+
#
|
163
|
+
# &block - The new ignore filter
|
164
|
+
# If the block returns true the exception will be ignored, otherwise it
|
165
|
+
# will be processed by honeybadger.
|
166
|
+
#
|
167
|
+
# Examples:
|
168
|
+
#
|
169
|
+
# config.ignore_by_filter do |exception_data|
|
170
|
+
# true if exception_data[:error_class] == "RuntimeError"
|
171
|
+
# end
|
172
|
+
#
|
173
|
+
# Yields the the exception data given to Honeybadger.notify
|
174
|
+
def ignore_by_filter(&block)
|
175
|
+
self.ignore_by_filters << block
|
176
|
+
end
|
177
|
+
|
178
|
+
# Public: Overrides the list of default ignored errors.
|
179
|
+
#
|
180
|
+
# names - A list of exceptions to ignore.
|
181
|
+
#
|
182
|
+
# Returns nothing
|
183
|
+
def ignore_only=(names)
|
184
|
+
@ignore = [names].flatten
|
185
|
+
end
|
186
|
+
|
187
|
+
# Public: Overrides the list of default ignored user agents
|
188
|
+
#
|
189
|
+
# names - A list of user agents to ignore
|
190
|
+
#
|
191
|
+
# Returns nothing
|
192
|
+
def ignore_user_agent_only=(names)
|
193
|
+
@ignore_user_agent = [names].flatten
|
194
|
+
end
|
195
|
+
|
196
|
+
# Public: Allows config options to be read like a hash
|
197
|
+
#
|
198
|
+
# option - Key for a given attribute
|
199
|
+
#
|
200
|
+
# Returns value of requested attribute
|
201
|
+
def [](option)
|
202
|
+
send(option)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Public
|
206
|
+
# Returns a hash of all configurable options
|
207
|
+
def to_hash
|
208
|
+
OPTIONS.inject({}) do |hash, option|
|
209
|
+
hash[option.to_sym] = self.send(option)
|
210
|
+
hash
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Public
|
215
|
+
#
|
216
|
+
# hash - A set of configuration options that will take precedence over the defaults
|
217
|
+
#
|
218
|
+
# Returns a hash of all configurable options merged with +hash+
|
219
|
+
def merge(hash)
|
220
|
+
to_hash.merge(hash)
|
221
|
+
end
|
222
|
+
|
223
|
+
# Public: Determines if the notifier will send notices.
|
224
|
+
#
|
225
|
+
# Returns false if in a development environment, true otherwise.
|
226
|
+
def public?
|
227
|
+
!development_environments.include?(environment_name)
|
228
|
+
end
|
229
|
+
|
230
|
+
def port
|
231
|
+
@port || default_port
|
232
|
+
end
|
233
|
+
|
234
|
+
# Public: Determines whether protocol should be "http" or "https".
|
235
|
+
#
|
236
|
+
# Returns 'http' if you've set secure to false in
|
237
|
+
# configuration, and 'https' otherwise.
|
238
|
+
def protocol
|
239
|
+
if secure?
|
240
|
+
'https'
|
241
|
+
else
|
242
|
+
'http'
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def ca_bundle_path
|
247
|
+
if use_system_ssl_cert_chain? && File.exist?(OpenSSL::X509::DEFAULT_CERT_FILE)
|
248
|
+
OpenSSL::X509::DEFAULT_CERT_FILE
|
249
|
+
else
|
250
|
+
local_cert_path # ca-bundle.crt built from source, see resources/README.md
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def local_cert_path
|
255
|
+
File.expand_path(File.join("..", "..", "..", "resources", "ca-bundle.crt"), __FILE__)
|
256
|
+
end
|
257
|
+
|
258
|
+
private
|
259
|
+
|
260
|
+
# Private: Determines what port should we use for sending notices.
|
261
|
+
#
|
262
|
+
# Returns 443 if you've set secure to true in your
|
263
|
+
# configuration, and 80 otherwise.
|
264
|
+
def default_port
|
265
|
+
if secure?
|
266
|
+
443
|
267
|
+
else
|
268
|
+
80
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
@@ -0,0 +1,314 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module Honeybadger
|
4
|
+
class Notice
|
5
|
+
# The exception that caused this notice, if any
|
6
|
+
attr_reader :exception
|
7
|
+
|
8
|
+
# The backtrace from the given exception or hash.
|
9
|
+
attr_reader :backtrace
|
10
|
+
|
11
|
+
# The name of the class of error (such as RuntimeError)
|
12
|
+
attr_reader :error_class
|
13
|
+
|
14
|
+
# The name of the server environment (such as "production")
|
15
|
+
attr_reader :environment_name
|
16
|
+
|
17
|
+
# CGI variables such as HTTP_METHOD
|
18
|
+
attr_reader :cgi_data
|
19
|
+
|
20
|
+
# The message from the exception, or a general description of the error
|
21
|
+
attr_reader :error_message
|
22
|
+
|
23
|
+
# See Configuration#backtrace_filters
|
24
|
+
attr_reader :backtrace_filters
|
25
|
+
|
26
|
+
# See Configuration#params_filters
|
27
|
+
attr_reader :params_filters
|
28
|
+
|
29
|
+
# A hash of parameters from the query string or post body.
|
30
|
+
attr_reader :parameters
|
31
|
+
alias_method :params, :parameters
|
32
|
+
|
33
|
+
# The component (if any) which was used in this request (usually the controller)
|
34
|
+
attr_reader :component
|
35
|
+
alias_method :controller, :component
|
36
|
+
|
37
|
+
# The action (if any) that was called in this request
|
38
|
+
attr_reader :action
|
39
|
+
|
40
|
+
# A hash of session data from the request
|
41
|
+
attr_reader :session_data
|
42
|
+
|
43
|
+
# The path to the project that caused the error (usually Rails.root)
|
44
|
+
attr_reader :project_root
|
45
|
+
|
46
|
+
# The URL at which the error occurred (if any)
|
47
|
+
attr_reader :url
|
48
|
+
|
49
|
+
# See Configuration#ignore
|
50
|
+
attr_reader :ignore
|
51
|
+
|
52
|
+
# See Configuration#ignore_by_filters
|
53
|
+
attr_reader :ignore_by_filters
|
54
|
+
|
55
|
+
# The name of the notifier library sending this notice, such as "Honeybadger Notifier"
|
56
|
+
attr_reader :notifier_name
|
57
|
+
|
58
|
+
# The version number of the notifier library sending this notice, such as "2.1.3"
|
59
|
+
attr_reader :notifier_version
|
60
|
+
|
61
|
+
# A URL for more information about the notifier library sending this notice
|
62
|
+
attr_reader :notifier_url
|
63
|
+
|
64
|
+
# The host name where this error occurred (if any)
|
65
|
+
attr_reader :hostname
|
66
|
+
|
67
|
+
def initialize(args)
|
68
|
+
self.args = args
|
69
|
+
self.exception = args[:exception]
|
70
|
+
self.project_root = args[:project_root]
|
71
|
+
self.url = args[:url] || rack_env(:url)
|
72
|
+
|
73
|
+
self.notifier_name = args[:notifier_name]
|
74
|
+
self.notifier_version = args[:notifier_version]
|
75
|
+
self.notifier_url = args[:notifier_url]
|
76
|
+
|
77
|
+
self.ignore = args[:ignore] || []
|
78
|
+
self.ignore_by_filters = args[:ignore_by_filters] || []
|
79
|
+
self.backtrace_filters = args[:backtrace_filters] || []
|
80
|
+
self.params_filters = args[:params_filters] || []
|
81
|
+
self.parameters = args[:parameters] ||
|
82
|
+
action_dispatch_params ||
|
83
|
+
rack_env(:params) ||
|
84
|
+
{}
|
85
|
+
self.component = args[:component] || args[:controller] || parameters['controller']
|
86
|
+
self.action = args[:action] || parameters['action']
|
87
|
+
|
88
|
+
self.environment_name = args[:environment_name]
|
89
|
+
self.cgi_data = args[:cgi_data] || args[:rack_env]
|
90
|
+
self.backtrace = Backtrace.parse(exception_attribute(:backtrace, caller), :filters => self.backtrace_filters)
|
91
|
+
self.error_class = exception_attribute(:error_class) {|exception| exception.class.name }
|
92
|
+
self.error_message = exception_attribute(:error_message, 'Notification') do |exception|
|
93
|
+
"#{exception.class.name}: #{exception.message}"
|
94
|
+
end
|
95
|
+
|
96
|
+
self.hostname = local_hostname
|
97
|
+
|
98
|
+
also_use_rack_params_filters
|
99
|
+
find_session_data
|
100
|
+
clean_params
|
101
|
+
clean_rack_request_data
|
102
|
+
end
|
103
|
+
|
104
|
+
# Public: Template used to create JSON payload
|
105
|
+
#
|
106
|
+
# Returns JSON representation of notice
|
107
|
+
def as_json(options = {})
|
108
|
+
{
|
109
|
+
:notifier => {
|
110
|
+
:name => notifier_name,
|
111
|
+
:url => notifier_url,
|
112
|
+
:version => notifier_version
|
113
|
+
},
|
114
|
+
:error => {
|
115
|
+
:class => error_class,
|
116
|
+
:message => error_message,
|
117
|
+
:backtrace => backtrace
|
118
|
+
},
|
119
|
+
:request => {
|
120
|
+
:url => url,
|
121
|
+
:component => component,
|
122
|
+
:action => action,
|
123
|
+
:params => parameters,
|
124
|
+
:session => session_data,
|
125
|
+
:cgi_data => cgi_data
|
126
|
+
},
|
127
|
+
:server => {
|
128
|
+
:project_root => project_root,
|
129
|
+
:environment_name => environment_name,
|
130
|
+
:hostname => hostname
|
131
|
+
}
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
135
|
+
# Public: Creates JSON
|
136
|
+
#
|
137
|
+
# Returns valid JSON representation of notice
|
138
|
+
def to_json(*a)
|
139
|
+
as_json.to_json(*a)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Determines if this notice should be ignored
|
143
|
+
def ignore?
|
144
|
+
ignored_class_names.include?(error_class) ||
|
145
|
+
ignore_by_filters.any? {|filter| filter.call(self) }
|
146
|
+
end
|
147
|
+
|
148
|
+
# Public: Allows properties to be accessed using a hash-like syntax
|
149
|
+
#
|
150
|
+
# method - The given key for an attribute
|
151
|
+
#
|
152
|
+
# Examples:
|
153
|
+
#
|
154
|
+
# notice[:error_message]
|
155
|
+
#
|
156
|
+
# Returns the attribute value, or self if given +:request+
|
157
|
+
def [](method)
|
158
|
+
case method
|
159
|
+
when :request
|
160
|
+
self
|
161
|
+
else
|
162
|
+
send(method)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
attr_writer :exception, :backtrace, :error_class, :error_message,
|
169
|
+
:backtrace_filters, :parameters, :params_filters, :environment_filters,
|
170
|
+
:session_data, :project_root, :url, :ignore, :ignore_by_filters,
|
171
|
+
:notifier_name, :notifier_url, :notifier_version, :component, :action,
|
172
|
+
:cgi_data, :environment_name, :hostname
|
173
|
+
|
174
|
+
# Private: Arguments given in the initializer
|
175
|
+
attr_accessor :args
|
176
|
+
|
177
|
+
# Private: Gets a property named +attribute+ of an exception, either from an actual
|
178
|
+
# exception or a hash.
|
179
|
+
#
|
180
|
+
# If an exception is available, #from_exception will be used. Otherwise,
|
181
|
+
# a key named +attribute+ will be used from the #args.
|
182
|
+
#
|
183
|
+
# If no exception or hash key is available, +default+ will be used.
|
184
|
+
def exception_attribute(attribute, default = nil, &block)
|
185
|
+
(exception && from_exception(attribute, &block)) || args[attribute] || default
|
186
|
+
end
|
187
|
+
|
188
|
+
# Private: Gets a property named +attribute+ from an exception.
|
189
|
+
#
|
190
|
+
# If a block is given, it will be used when getting the property from an
|
191
|
+
# exception. The block should accept and exception and return the value for
|
192
|
+
# the property.
|
193
|
+
#
|
194
|
+
# If no block is given, a method with the same name as +attribute+ will be
|
195
|
+
# invoked for the value.
|
196
|
+
def from_exception(attribute)
|
197
|
+
if block_given?
|
198
|
+
yield(exception)
|
199
|
+
else
|
200
|
+
exception.send(attribute)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# Private: Removes non-serializable data from the given attribute.
|
205
|
+
# See #clean_unserializable_data
|
206
|
+
def clean_unserializable_data_from(attribute)
|
207
|
+
self.send(:"#{attribute}=", clean_unserializable_data(send(attribute)))
|
208
|
+
end
|
209
|
+
|
210
|
+
# Private: Removes non-serializable data. Allowed data types are strings, arrays,
|
211
|
+
# and hashes. All other types are converted to strings.
|
212
|
+
# TODO: move this onto Hash
|
213
|
+
def clean_unserializable_data(data, stack = [])
|
214
|
+
return "[possible infinite recursion halted]" if stack.any?{|item| item == data.object_id }
|
215
|
+
|
216
|
+
if data.respond_to?(:to_hash)
|
217
|
+
data.to_hash.inject({}) do |result, (key, value)|
|
218
|
+
result.merge(key => clean_unserializable_data(value, stack + [data.object_id]))
|
219
|
+
end
|
220
|
+
elsif data.respond_to?(:to_ary)
|
221
|
+
data.to_ary.collect do |value|
|
222
|
+
clean_unserializable_data(value, stack + [data.object_id])
|
223
|
+
end
|
224
|
+
else
|
225
|
+
data.to_s
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# Private: Replaces the contents of params that match params_filters.
|
230
|
+
# TODO: extract this to a different class
|
231
|
+
def clean_params
|
232
|
+
clean_unserializable_data_from(:parameters)
|
233
|
+
filter(parameters)
|
234
|
+
if cgi_data
|
235
|
+
clean_unserializable_data_from(:cgi_data)
|
236
|
+
filter(cgi_data)
|
237
|
+
end
|
238
|
+
if session_data
|
239
|
+
clean_unserializable_data_from(:session_data)
|
240
|
+
filter(session_data)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def clean_rack_request_data
|
245
|
+
if cgi_data
|
246
|
+
cgi_data.delete("rack.request.form_vars")
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def filter(hash)
|
251
|
+
if params_filters
|
252
|
+
hash.each do |key, value|
|
253
|
+
if filter_key?(key)
|
254
|
+
hash[key] = "[FILTERED]"
|
255
|
+
elsif value.respond_to?(:to_hash)
|
256
|
+
filter(hash[key])
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def filter_key?(key)
|
263
|
+
params_filters.any? do |filter|
|
264
|
+
key.to_s.eql?(filter.to_s)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def find_session_data
|
269
|
+
self.session_data = args[:session_data] || args[:session] || rack_session || {}
|
270
|
+
self.session_data = session_data[:data] if session_data[:data]
|
271
|
+
end
|
272
|
+
|
273
|
+
# Private: Converts the mixed class instances and class names into just names
|
274
|
+
# TODO: move this into Configuration or another class
|
275
|
+
def ignored_class_names
|
276
|
+
ignore.collect do |string_or_class|
|
277
|
+
if string_or_class.respond_to?(:name)
|
278
|
+
string_or_class.name
|
279
|
+
else
|
280
|
+
string_or_class
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def rack_env(method)
|
286
|
+
rack_request.send(method) if rack_request
|
287
|
+
end
|
288
|
+
|
289
|
+
def rack_request
|
290
|
+
@rack_request ||= if args[:rack_env]
|
291
|
+
::Rack::Request.new(args[:rack_env])
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def action_dispatch_params
|
296
|
+
args[:rack_env]['action_dispatch.request.parameters'] if args[:rack_env]
|
297
|
+
end
|
298
|
+
|
299
|
+
def rack_session
|
300
|
+
args[:rack_env]['rack.session'] if args[:rack_env]
|
301
|
+
end
|
302
|
+
|
303
|
+
def also_use_rack_params_filters
|
304
|
+
if args[:rack_env]
|
305
|
+
@params_filters ||= []
|
306
|
+
@params_filters += rack_request.env["action_dispatch.parameter_filter"] || []
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def local_hostname
|
311
|
+
Socket.gethostname
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|