markbates-hoptoad_notifier 1.2
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/INSTALL +55 -0
- data/README +171 -0
- data/Rakefile +30 -0
- data/lib/hoptoad_notifier.rb +386 -0
- data/tasks/hoptoad_notifier_tasks.rake +63 -0
- metadata +57 -0
data/INSTALL
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
HoptoadNotifier
|
2
|
+
===============
|
3
|
+
|
4
|
+
This is the notifier plugin for integrating apps with Hoptoad.
|
5
|
+
|
6
|
+
When an uncaught exception occurs, HoptoadNotifier will POST the relevant data
|
7
|
+
to the Hoptoad server specified in your environment.
|
8
|
+
|
9
|
+
|
10
|
+
INSTALLATION
|
11
|
+
------------
|
12
|
+
|
13
|
+
REMOVE EXCEPTION_NOTIFIER
|
14
|
+
|
15
|
+
In your ApplicationController, REMOVE this line:
|
16
|
+
|
17
|
+
include ExceptionNotifiable
|
18
|
+
|
19
|
+
In your config/environment* files, remove all references to ExceptionNotifier
|
20
|
+
|
21
|
+
Remove the vendor/plugins/exception_notifier directory.
|
22
|
+
|
23
|
+
INSTALL HOPTOAD_NOTIFIER
|
24
|
+
|
25
|
+
From your project's RAILS_ROOT, run:
|
26
|
+
|
27
|
+
script/plugin install git://github.com/thoughtbot/hoptoad_notifier.git
|
28
|
+
|
29
|
+
CONFIGURATION
|
30
|
+
|
31
|
+
You should have something like this in config/initializers/hoptoad.rb.
|
32
|
+
|
33
|
+
HoptoadNotifier.configure do |config|
|
34
|
+
config.api_key = '1234567890abcdef'
|
35
|
+
end
|
36
|
+
|
37
|
+
(Please note that this configuration should be in a global configuration, and
|
38
|
+
is *not* enrivonment-specific. Hoptoad is smart enough to know what errors are
|
39
|
+
caused by what environments, so your staging errors don't get mixed in with
|
40
|
+
your production errors.)
|
41
|
+
|
42
|
+
After this is in place, all exceptions will be logged to Hoptoad where they can
|
43
|
+
be aggregated, filtered, sorted, analyzed, massaged, and searched.
|
44
|
+
|
45
|
+
** NOTE FOR RAILS 1.2.* USERS: **
|
46
|
+
You will need to copy the hoptoad_notifier_tasks.rake file into your
|
47
|
+
RAILS_ROOT/lib/tasks directory in order for the following to work:
|
48
|
+
|
49
|
+
You can test that hoptoad is working in your production environment by using
|
50
|
+
this rake task (from RAILS_ROOT):
|
51
|
+
|
52
|
+
rake hoptoad:test
|
53
|
+
|
54
|
+
If everything is configured properly, that task will send a notice to hoptoad
|
55
|
+
which will be visible immediately.
|
data/README
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
HoptoadNotifier
|
2
|
+
===============
|
3
|
+
|
4
|
+
This is the notifier plugin for integrating apps with Hoptoad.
|
5
|
+
|
6
|
+
When an uncaught exception occurs, HoptoadNotifier will POST the relevant data
|
7
|
+
to the Hoptoad server specified in your environment.
|
8
|
+
|
9
|
+
INSTALLATION
|
10
|
+
------------
|
11
|
+
|
12
|
+
REMOVE EXCEPTION_NOTIFIER
|
13
|
+
|
14
|
+
In your ApplicationController, REMOVE this line:
|
15
|
+
|
16
|
+
include ExceptionNotifiable
|
17
|
+
|
18
|
+
In your config/environment* files, remove all references to ExceptionNotifier
|
19
|
+
|
20
|
+
Remove the vendor/plugins/exception_notifier directory.
|
21
|
+
|
22
|
+
INSTALL HOPTOAD_NOTIFIER
|
23
|
+
|
24
|
+
From your project's RAILS_ROOT, run:
|
25
|
+
|
26
|
+
script/plugin install git://github.com/thoughtbot/hoptoad_notifier.git
|
27
|
+
|
28
|
+
CONFIGURATION
|
29
|
+
|
30
|
+
You should have something like this in config/initializers/hoptoad.rb.
|
31
|
+
|
32
|
+
HoptoadNotifier.configure do |config|
|
33
|
+
config.api_key = '1234567890abcdef'
|
34
|
+
end
|
35
|
+
|
36
|
+
(Please note that this configuration should be in a global configuration, and
|
37
|
+
is *not* environment-specific. Hoptoad is smart enough to know what errors are
|
38
|
+
caused by what environments, so your staging errors don't get mixed in with
|
39
|
+
your production errors.)
|
40
|
+
|
41
|
+
That should be it! Now all exceptions will be logged to Hoptoad where they can
|
42
|
+
be aggregated, filtered, sorted, analyzed, massaged, and searched. In previous
|
43
|
+
releases you had to include HoptoadNotifier::Catcher into your
|
44
|
+
ApplicationController, but the plugin takes care of that now.
|
45
|
+
|
46
|
+
** NOTE FOR RAILS 1.2.* USERS: **
|
47
|
+
You will need to copy the hoptoad_notifier_tasks.rake file into your
|
48
|
+
RAILS_ROOT/lib/tasks directory in order for the following to work:
|
49
|
+
|
50
|
+
You can test that hoptoad is working in your production environment by using
|
51
|
+
this rake task (from RAILS_ROOT):
|
52
|
+
|
53
|
+
rake hoptoad:test
|
54
|
+
|
55
|
+
If everything is configured properly, that task will send a notice to hoptoad
|
56
|
+
which will be visible immediately.
|
57
|
+
|
58
|
+
USAGE
|
59
|
+
|
60
|
+
For the most part, hoptoad works for itself. Once you've included the notifier
|
61
|
+
in your ApplicationController (which is now done automatically by the plugin),
|
62
|
+
all errors will be rescued by the #rescue_action_in_public provided by the plugin.
|
63
|
+
|
64
|
+
If you want to log arbitrary things which you've rescued yourself from a
|
65
|
+
controller, you can do something like this:
|
66
|
+
|
67
|
+
...
|
68
|
+
rescue => ex
|
69
|
+
notify_hoptoad(ex)
|
70
|
+
flash[:failure] = 'Encryptions could not be rerouted, try again.'
|
71
|
+
end
|
72
|
+
...
|
73
|
+
|
74
|
+
The #notify_hoptoad call will send the notice over to hoptoad for later
|
75
|
+
analysis.
|
76
|
+
|
77
|
+
GOING BEYOND EXCEPTIONS
|
78
|
+
|
79
|
+
You can also pass a hash to notify_hoptoad method and store whatever you want, not just an exception. And you can also use it anywhere, not just in controllers:
|
80
|
+
|
81
|
+
begin
|
82
|
+
params = {
|
83
|
+
# params that you pass to a method that can throw an exception
|
84
|
+
}
|
85
|
+
my_unpredicable_method(params)
|
86
|
+
rescue => e
|
87
|
+
HoptoadNotifier.notify(
|
88
|
+
:error_class => "Special Error",
|
89
|
+
:error_message => "Special Error: #{e.message}",
|
90
|
+
:request => { :params => params }
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
While in your controllers you use the notify_hoptoad method, anywhere else in your code, use HoptoadNotifier.notify. Hoptoad will get all the information about the error itself. As for a hash, these are the keys you should pass:
|
95
|
+
|
96
|
+
* :error_class – Use this to group similar errors together. When Hoptoad catches an exception it sends the class name of that exception object.
|
97
|
+
* :error_message – This is the title of the error you see in the errors list. For exceptions it is "#{exception.class.name}: #{exception.message}"
|
98
|
+
* :request – While there are several ways to send additional data to Hoptoad, passing a Hash with :params key as :request as in the example above is the most common use case. When Hoptoad catches an exception in a controller, the actual HTTP client request is being sent using this key.
|
99
|
+
|
100
|
+
Hoptoad merges the hash you pass with these default options:
|
101
|
+
|
102
|
+
def default_notice_options
|
103
|
+
{
|
104
|
+
:api_key => HoptoadNotifier.api_key,
|
105
|
+
:error_message => 'Notification',
|
106
|
+
:backtrace => caller,
|
107
|
+
:request => {},
|
108
|
+
:session => {},
|
109
|
+
:environment => ENV.to_hash
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
You can override any of those parameters.
|
114
|
+
|
115
|
+
FILTERING
|
116
|
+
|
117
|
+
You can specify a whitelist of errors, that Hoptoad will not report on. Use
|
118
|
+
this feature when you are so apathetic to certain errors that you don't want
|
119
|
+
them even logged.
|
120
|
+
|
121
|
+
This filter will only be applied to automatic notifications, not manual
|
122
|
+
notifications (when #notify is called directly).
|
123
|
+
|
124
|
+
Hoptoad ignores the following exceptions by default:
|
125
|
+
ActiveRecord::RecordNotFound
|
126
|
+
ActionController::RoutingError
|
127
|
+
ActionController::InvalidAuthenticityToken
|
128
|
+
CGI::Session::CookieStore::TamperedWithCookie
|
129
|
+
|
130
|
+
To ignore errors in addition to those, specify their names in your Hoptoad
|
131
|
+
configuration block.
|
132
|
+
|
133
|
+
HoptoadNotifier.configure do |config|
|
134
|
+
config.api_key = '1234567890abcdef'
|
135
|
+
config.ignore << ActiveRecord::IgnoreThisError
|
136
|
+
end
|
137
|
+
|
138
|
+
To ignore *only* certain errors (and override the defaults), use the
|
139
|
+
#ignore_only attribute.
|
140
|
+
|
141
|
+
HoptoadNotifier.configure do |config|
|
142
|
+
config.api_key = '1234567890abcdef'
|
143
|
+
config.ignore_only = [ActiveRecord::IgnoreThisError]
|
144
|
+
end
|
145
|
+
|
146
|
+
To ignore certain user agents, add in the #ignore_user_agent attribute as a
|
147
|
+
string or regexp:
|
148
|
+
|
149
|
+
HoptoadNotifier.configure do |config|
|
150
|
+
config.api_key = '1234567890abcdef'
|
151
|
+
config.ignore_user_agent << /Ignored/
|
152
|
+
config.ignore_user_agent << 'IgnoredUserAgent'
|
153
|
+
end
|
154
|
+
|
155
|
+
TESTING
|
156
|
+
|
157
|
+
When you run your tests, you might notice that the hoptoad service is recording
|
158
|
+
notices generated using #notify when you don't expect it to. You can
|
159
|
+
use code like this in your test_helper.rb to redefine that method so those
|
160
|
+
errors are not reported while running tests.
|
161
|
+
|
162
|
+
module HoptoadNotifier::Catcher
|
163
|
+
def notify(thing)
|
164
|
+
# do nothing.
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
THANKS
|
169
|
+
|
170
|
+
Thanks to Eugene Bolshakov for the excellent write-up on GOING BEYOND EXCEPTIONS, which we have included above.
|
171
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the hoptoad_notifier plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation for the hoptoad_notifier plugin.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'HoptoadNotifier'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Run ginger tests'
|
25
|
+
task :ginger do
|
26
|
+
$LOAD_PATH << File.join(*%w[vendor ginger lib])
|
27
|
+
ARGV.clear
|
28
|
+
ARGV << 'test'
|
29
|
+
load File.join(*%w[vendor ginger bin ginger])
|
30
|
+
end
|
@@ -0,0 +1,386 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'active_support'
|
5
|
+
|
6
|
+
# Plugin for applications to automatically post errors to the Hoptoad of their choice.
|
7
|
+
module HoptoadNotifier
|
8
|
+
|
9
|
+
IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound',
|
10
|
+
'ActionController::RoutingError',
|
11
|
+
'ActionController::InvalidAuthenticityToken',
|
12
|
+
'CGI::Session::CookieStore::TamperedWithCookie',
|
13
|
+
'ActionController::UnknownAction']
|
14
|
+
|
15
|
+
# Some of these don't exist for Rails 1.2.*, so we have to consider that.
|
16
|
+
IGNORE_DEFAULT.map!{|e| eval(e) rescue nil }.compact!
|
17
|
+
IGNORE_DEFAULT.freeze
|
18
|
+
|
19
|
+
IGNORE_USER_AGENT_DEFAULT = []
|
20
|
+
|
21
|
+
class << self
|
22
|
+
attr_accessor :host, :port, :secure, :api_key, :http_open_timeout, :http_read_timeout,
|
23
|
+
:proxy_host, :proxy_port, :proxy_user, :proxy_pass, :application_path,
|
24
|
+
:public_environments, :current_environment
|
25
|
+
|
26
|
+
def backtrace_filters
|
27
|
+
@backtrace_filters ||= []
|
28
|
+
end
|
29
|
+
|
30
|
+
# Takes a block and adds it to the list of backtrace filters. When the filters
|
31
|
+
# run, the block will be handed each line of the backtrace and can modify
|
32
|
+
# it as necessary. For example, by default a path matching the RAILS_ROOT
|
33
|
+
# constant will be transformed into "[RAILS_ROOT]"
|
34
|
+
def filter_backtrace &block
|
35
|
+
self.backtrace_filters << block
|
36
|
+
end
|
37
|
+
|
38
|
+
# The port on which your Hoptoad server runs.
|
39
|
+
def port
|
40
|
+
@port || (secure ? 443 : 80)
|
41
|
+
end
|
42
|
+
|
43
|
+
# The host to connect to.
|
44
|
+
def host
|
45
|
+
@host ||= 'hoptoadapp.com'
|
46
|
+
end
|
47
|
+
|
48
|
+
# The file path of the application. If RAILS_ROOT is defined it will default
|
49
|
+
# to that, otherwise the default will be FileUtils.pwd.
|
50
|
+
def application_path
|
51
|
+
@application_path ||= defined?(RAILS_ROOT) ? RAILS_ROOT : FileUtils.pwd
|
52
|
+
end
|
53
|
+
|
54
|
+
# An array of environments that are considered 'public' Default is:
|
55
|
+
# ['production', 'staging']
|
56
|
+
def public_environments
|
57
|
+
@public_environments ||= ['production', 'staging']
|
58
|
+
end
|
59
|
+
|
60
|
+
def current_environment=(environment)
|
61
|
+
@current_environment = environment
|
62
|
+
ENV['RAILS_ENV'] = environment unless ENV['RAILS_ENV']
|
63
|
+
end
|
64
|
+
|
65
|
+
def current_environment
|
66
|
+
@current_environment ||= defined?(RAILS_ENV) ? RAILS_ENV : 'development'
|
67
|
+
end
|
68
|
+
|
69
|
+
# The HTTP open timeout (defaults to 2 seconds).
|
70
|
+
def http_open_timeout
|
71
|
+
@http_open_timeout ||= 2
|
72
|
+
end
|
73
|
+
|
74
|
+
# The HTTP read timeout (defaults to 5 seconds).
|
75
|
+
def http_read_timeout
|
76
|
+
@http_read_timeout ||= 5
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns the list of errors that are being ignored. The array can be appended to.
|
80
|
+
def ignore
|
81
|
+
@ignore ||= (HoptoadNotifier::IGNORE_DEFAULT.dup)
|
82
|
+
@ignore.flatten!
|
83
|
+
@ignore
|
84
|
+
end
|
85
|
+
|
86
|
+
# Sets the list of ignored errors to only what is passed in here. This method
|
87
|
+
# can be passed a single error or a list of errors.
|
88
|
+
def ignore_only=(names)
|
89
|
+
@ignore = [names].flatten
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns the list of user agents that are being ignored. The array can be appended to.
|
93
|
+
def ignore_user_agent
|
94
|
+
@ignore_user_agent ||= (HoptoadNotifier::IGNORE_USER_AGENT_DEFAULT.dup)
|
95
|
+
@ignore_user_agent.flatten!
|
96
|
+
@ignore_user_agent
|
97
|
+
end
|
98
|
+
|
99
|
+
# Sets the list of ignored user agents to only what is passed in here. This method
|
100
|
+
# can be passed a single user agent or a list of user agents.
|
101
|
+
def ignore_user_agent_only=(names)
|
102
|
+
@ignore_user_agent = [names].flatten
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns a list of parameters that should be filtered out of what is sent to Hoptoad.
|
106
|
+
# By default, all "password" attributes will have their contents replaced.
|
107
|
+
def params_filters
|
108
|
+
@params_filters ||= %w(password)
|
109
|
+
end
|
110
|
+
|
111
|
+
def environment_filters
|
112
|
+
@environment_filters ||= %w()
|
113
|
+
end
|
114
|
+
|
115
|
+
# Call this method to modify defaults in your initializers.
|
116
|
+
#
|
117
|
+
# HoptoadNotifier.configure do |config|
|
118
|
+
# config.api_key = '1234567890abcdef'
|
119
|
+
# config.secure = false
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# NOTE: secure connections are not yet supported.
|
123
|
+
def configure
|
124
|
+
add_default_filters
|
125
|
+
yield self
|
126
|
+
if defined?(ActionController::Base) && !ActionController::Base.include?(HoptoadNotifier::Catcher)
|
127
|
+
ActionController::Base.send(:include, HoptoadNotifier::Catcher)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def protocol #:nodoc:
|
132
|
+
secure ? "https" : "http"
|
133
|
+
end
|
134
|
+
|
135
|
+
def url #:nodoc:
|
136
|
+
URI.parse("#{protocol}://#{host}:#{port}/notices/")
|
137
|
+
end
|
138
|
+
|
139
|
+
def default_notice_options #:nodoc:
|
140
|
+
{
|
141
|
+
:api_key => HoptoadNotifier.api_key,
|
142
|
+
:error_message => 'Notification',
|
143
|
+
:backtrace => caller,
|
144
|
+
:request => {},
|
145
|
+
:session => {},
|
146
|
+
:environment => ENV.to_hash
|
147
|
+
}
|
148
|
+
end
|
149
|
+
|
150
|
+
# You can send an exception manually using this method, even when you are not in a
|
151
|
+
# controller. You can pass an exception or a hash that contains the attributes that
|
152
|
+
# would be sent to Hoptoad:
|
153
|
+
# * api_key: The API key for this project. The API key is a unique identifier that Hoptoad
|
154
|
+
# uses for identification.
|
155
|
+
# * error_message: The error returned by the exception (or the message you want to log).
|
156
|
+
# * backtrace: A backtrace, usually obtained with +caller+.
|
157
|
+
# * request: The controller's request object.
|
158
|
+
# * session: The contents of the user's session.
|
159
|
+
# * environment: ENV merged with the contents of the request's environment.
|
160
|
+
def notify notice = {}
|
161
|
+
Sender.new.notify_hoptoad( notice )
|
162
|
+
end
|
163
|
+
|
164
|
+
def add_default_filters
|
165
|
+
self.backtrace_filters.clear
|
166
|
+
|
167
|
+
filter_backtrace do |line|
|
168
|
+
line.gsub(/#{application_path}/, "[RAILS_ROOT]")
|
169
|
+
end
|
170
|
+
|
171
|
+
filter_backtrace do |line|
|
172
|
+
line.gsub(/^\.\//, "")
|
173
|
+
end
|
174
|
+
|
175
|
+
filter_backtrace do |line|
|
176
|
+
if defined?(Gem)
|
177
|
+
Gem.path.inject(line) do |line, path|
|
178
|
+
line.gsub(/#{path}/, "[GEM_ROOT]")
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
filter_backtrace do |line|
|
184
|
+
line if line !~ /lib\/#{File.basename(__FILE__)}/
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Include this module in Controllers in which you want to be notified of errors.
|
190
|
+
module Catcher
|
191
|
+
|
192
|
+
def self.included(base) #:nodoc:
|
193
|
+
if base.instance_methods.include? 'rescue_action_in_public' and !base.instance_methods.include? 'rescue_action_in_public_without_hoptoad'
|
194
|
+
base.send(:alias_method, :rescue_action_in_public_without_hoptoad, :rescue_action_in_public)
|
195
|
+
base.send(:alias_method, :rescue_action_in_public, :rescue_action_in_public_with_hoptoad)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Overrides the rescue_action method in ActionController::Base, but does not inhibit
|
200
|
+
# any custom processing that is defined with Rails 2's exception helpers.
|
201
|
+
def rescue_action_in_public_with_hoptoad exception
|
202
|
+
notify_hoptoad(exception) unless ignore?(exception) || ignore_user_agent?
|
203
|
+
rescue_action_in_public_without_hoptoad(exception)
|
204
|
+
end
|
205
|
+
|
206
|
+
# This method should be used for sending manual notifications while you are still
|
207
|
+
# inside the controller. Otherwise it works like HoptoadNotifier.notify.
|
208
|
+
def notify_hoptoad hash_or_exception
|
209
|
+
if public_environment?
|
210
|
+
notice = normalize_notice(hash_or_exception)
|
211
|
+
notice = clean_notice(notice)
|
212
|
+
send_to_hoptoad(:notice => notice)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
alias_method :inform_hoptoad, :notify_hoptoad
|
217
|
+
|
218
|
+
# Returns the default logger or a logger that prints to STDOUT. Necessary for manual
|
219
|
+
# notifications outside of controllers.
|
220
|
+
def logger
|
221
|
+
ActiveRecord::Base.logger
|
222
|
+
rescue
|
223
|
+
@logger ||= Logger.new(STDERR)
|
224
|
+
end
|
225
|
+
|
226
|
+
private
|
227
|
+
|
228
|
+
def public_environment? #nodoc:
|
229
|
+
HoptoadNotifier.public_environments.include?(HoptoadNotifier.current_environment)
|
230
|
+
end
|
231
|
+
|
232
|
+
def ignore?(exception) #:nodoc:
|
233
|
+
ignore_these = HoptoadNotifier.ignore.flatten
|
234
|
+
ignore_these.include?(exception.class) || ignore_these.include?(exception.class.name)
|
235
|
+
end
|
236
|
+
|
237
|
+
def ignore_user_agent? #:nodoc:
|
238
|
+
HoptoadNotifier.ignore_user_agent.flatten.any? { |ua| ua === request.user_agent }
|
239
|
+
end
|
240
|
+
|
241
|
+
def exception_to_data exception #:nodoc:
|
242
|
+
data = {
|
243
|
+
:api_key => HoptoadNotifier.api_key,
|
244
|
+
:error_class => exception.class.name,
|
245
|
+
:error_message => "#{exception.class.name}: #{exception.message}",
|
246
|
+
:backtrace => exception.backtrace,
|
247
|
+
:environment => ENV.to_hash
|
248
|
+
}
|
249
|
+
|
250
|
+
if self.respond_to? :request
|
251
|
+
data[:request] = {
|
252
|
+
:params => request.parameters.to_hash,
|
253
|
+
:rails_root => File.expand_path(HoptoadNotifier.application_path),
|
254
|
+
:url => "#{request.protocol}#{request.host}#{request.request_uri}"
|
255
|
+
}
|
256
|
+
data[:environment].merge!(request.env.to_hash)
|
257
|
+
end
|
258
|
+
|
259
|
+
if self.respond_to? :session
|
260
|
+
data[:session] = {
|
261
|
+
:key => session.instance_variable_get("@session_id"),
|
262
|
+
:data => session.instance_variable_get("@data")
|
263
|
+
}
|
264
|
+
end
|
265
|
+
|
266
|
+
data
|
267
|
+
end
|
268
|
+
|
269
|
+
def normalize_notice(notice) #:nodoc:
|
270
|
+
case notice
|
271
|
+
when Hash
|
272
|
+
HoptoadNotifier.default_notice_options.merge(notice)
|
273
|
+
when Exception
|
274
|
+
HoptoadNotifier.default_notice_options.merge(exception_to_data(notice))
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def clean_notice(notice) #:nodoc:
|
279
|
+
notice[:backtrace] = clean_hoptoad_backtrace(notice[:backtrace])
|
280
|
+
if notice[:request].is_a?(Hash) && notice[:request][:params].is_a?(Hash)
|
281
|
+
notice[:request][:params] = filter_parameters(notice[:request][:params]) if respond_to?(:filter_parameters)
|
282
|
+
notice[:request][:params] = clean_hoptoad_params(notice[:request][:params])
|
283
|
+
end
|
284
|
+
if notice[:environment].is_a?(Hash)
|
285
|
+
notice[:environment] = filter_parameters(notice[:environment]) if respond_to?(:filter_parameters)
|
286
|
+
notice[:environment] = clean_hoptoad_environment(notice[:environment])
|
287
|
+
end
|
288
|
+
clean_non_serializable_data(notice)
|
289
|
+
end
|
290
|
+
|
291
|
+
def send_to_hoptoad data #:nodoc:
|
292
|
+
headers = {
|
293
|
+
'Content-type' => 'application/x-yaml',
|
294
|
+
'Accept' => 'text/xml, application/xml'
|
295
|
+
}
|
296
|
+
|
297
|
+
url = HoptoadNotifier.url
|
298
|
+
|
299
|
+
http = Net::HTTP::Proxy(HoptoadNotifier.proxy_host,
|
300
|
+
HoptoadNotifier.proxy_port,
|
301
|
+
HoptoadNotifier.proxy_user,
|
302
|
+
HoptoadNotifier.proxy_pass).new(url.host, url.port)
|
303
|
+
|
304
|
+
http.use_ssl = true
|
305
|
+
http.read_timeout = HoptoadNotifier.http_read_timeout
|
306
|
+
http.open_timeout = HoptoadNotifier.http_open_timeout
|
307
|
+
http.use_ssl = !!HoptoadNotifier.secure
|
308
|
+
|
309
|
+
response = begin
|
310
|
+
http.post(url.path, stringify_keys(data).to_yaml, headers)
|
311
|
+
rescue TimeoutError => e
|
312
|
+
logger.error "Timeout while contacting the Hoptoad server." if logger
|
313
|
+
nil
|
314
|
+
end
|
315
|
+
|
316
|
+
case response
|
317
|
+
when Net::HTTPSuccess then
|
318
|
+
logger.info "Hoptoad Success: #{response.class}" if logger
|
319
|
+
else
|
320
|
+
logger.error "Hoptoad Failure: #{response.class}\n#{response.body if response.respond_to? :body}" if logger
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
def clean_hoptoad_backtrace backtrace #:nodoc:
|
325
|
+
if backtrace.to_a.size == 1
|
326
|
+
backtrace = backtrace.to_a.first.split(/\n\s*/)
|
327
|
+
end
|
328
|
+
|
329
|
+
filtered = backtrace.to_a.map do |line|
|
330
|
+
HoptoadNotifier.backtrace_filters.inject(line) do |line, proc|
|
331
|
+
proc.call(line)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
filtered.compact
|
336
|
+
end
|
337
|
+
|
338
|
+
def clean_hoptoad_params params #:nodoc:
|
339
|
+
params.each do |k, v|
|
340
|
+
params[k] = "[FILTERED]" if HoptoadNotifier.params_filters.any? do |filter|
|
341
|
+
k.to_s.match(/#{filter}/)
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
def clean_hoptoad_environment env #:nodoc:
|
347
|
+
env.each do |k, v|
|
348
|
+
env[k] = "[FILTERED]" if HoptoadNotifier.environment_filters.any? do |filter|
|
349
|
+
k.to_s.match(/#{filter}/)
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
def clean_non_serializable_data(notice) #:nodoc:
|
355
|
+
notice.select{|k,v| serializable?(v) }.inject({}) do |h, pair|
|
356
|
+
h[pair.first] = pair.last.is_a?(Hash) ? clean_non_serializable_data(pair.last) : pair.last
|
357
|
+
h
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def serializable?(value) #:nodoc:
|
362
|
+
value.is_a?(Fixnum) ||
|
363
|
+
value.is_a?(Array) ||
|
364
|
+
value.is_a?(String) ||
|
365
|
+
value.is_a?(Hash) ||
|
366
|
+
value.is_a?(Bignum)
|
367
|
+
end
|
368
|
+
|
369
|
+
def stringify_keys(hash) #:nodoc:
|
370
|
+
hash.inject({}) do |h, pair|
|
371
|
+
h[pair.first.to_s] = pair.last.is_a?(Hash) ? stringify_keys(pair.last) : pair.last
|
372
|
+
h
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
end
|
377
|
+
|
378
|
+
# A dummy class for sending notifications manually outside of a controller.
|
379
|
+
class Sender
|
380
|
+
def rescue_action_in_public(exception)
|
381
|
+
end
|
382
|
+
|
383
|
+
include HoptoadNotifier::Catcher
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
@@ -0,0 +1,63 @@
|
|
1
|
+
namespace :hoptoad do
|
2
|
+
desc "Notify Hoptoad of a new deploy."
|
3
|
+
task :deploy => :environment do
|
4
|
+
require 'hoptoad_tasks'
|
5
|
+
HoptoadTasks.deploy_to(ENV['TO'])
|
6
|
+
end
|
7
|
+
|
8
|
+
desc "Verify your plugin installation by sending a test exception to the hoptoad service"
|
9
|
+
task :test => :environment do
|
10
|
+
require 'action_controller/test_process'
|
11
|
+
|
12
|
+
request = ActionController::TestRequest.new
|
13
|
+
|
14
|
+
response = ActionController::TestResponse.new
|
15
|
+
|
16
|
+
class HoptoadTestingException < RuntimeError; end
|
17
|
+
|
18
|
+
unless HoptoadNotifier.api_key
|
19
|
+
puts "Hoptoad needs an API key configured! Check the README to see how to add it."
|
20
|
+
exit
|
21
|
+
end
|
22
|
+
|
23
|
+
in_controller = ApplicationController.included_modules.include? HoptoadNotifier::Catcher
|
24
|
+
in_base = ActionController::Base.included_modules.include? HoptoadNotifier::Catcher
|
25
|
+
if !in_controller || !in_base
|
26
|
+
puts "HoptoadNotifier::Catcher must be included inside your ApplicationController class."
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
|
30
|
+
puts 'Setting up the Controller.'
|
31
|
+
class ApplicationController
|
32
|
+
# This is to bypass any filters that may prevent access to the action.
|
33
|
+
prepend_before_filter :test_hoptoad
|
34
|
+
def test_hoptoad
|
35
|
+
puts "Raising '#{exception_class.name}' to simulate application failure."
|
36
|
+
raise exception_class.new, 'Testing hoptoad via "rake hoptoad:test". If you can see this, it works.'
|
37
|
+
end
|
38
|
+
|
39
|
+
def rescue_action exception
|
40
|
+
rescue_action_in_public exception
|
41
|
+
end
|
42
|
+
|
43
|
+
def public_environment?
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
# Ensure we actually have an action to go to.
|
48
|
+
def verify; end
|
49
|
+
|
50
|
+
def exception_class
|
51
|
+
exception_name = ENV['EXCEPTION'] || "HoptoadTestingException"
|
52
|
+
Object.const_get(exception_name)
|
53
|
+
rescue
|
54
|
+
Object.const_set(exception_name, Class.new(Exception))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
puts 'Processing request.'
|
59
|
+
class HoptoadVerificationController < ApplicationController; end
|
60
|
+
HoptoadVerificationController.new.process(request, response)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: markbates-hoptoad_notifier
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "1.2"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Thoughtbot
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-26 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Rails plugin that reports exceptions to Hoptoad.
|
17
|
+
email: info@thoughtbot.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- INSTALL
|
26
|
+
- lib/hoptoad_notifier.rb
|
27
|
+
- Rakefile
|
28
|
+
- README
|
29
|
+
- tasks/hoptoad_notifier_tasks.rake
|
30
|
+
has_rdoc: true
|
31
|
+
homepage: http://github.com/thoughtbot/hoptoad_notifier
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: "0"
|
42
|
+
version:
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: "0"
|
48
|
+
version:
|
49
|
+
requirements: []
|
50
|
+
|
51
|
+
rubyforge_project:
|
52
|
+
rubygems_version: 1.2.0
|
53
|
+
signing_key:
|
54
|
+
specification_version: 2
|
55
|
+
summary: Rails plugin that reports exceptions to Hoptoad.
|
56
|
+
test_files: []
|
57
|
+
|