rack_hoptoad 0.0.5 → 0.1.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/README.md +5 -5
- data/Rakefile +7 -10
- data/lib/rack/hoptoad.rb +63 -0
- metadata +14 -6
- data/lib/rack/hoptoad_notifier.rb +0 -130
- data/lib/rack/notice.xml.erb +0 -40
- data/lib/rack_hoptoad.rb +0 -5
data/README.md
CHANGED
|
@@ -7,15 +7,15 @@ Usage
|
|
|
7
7
|
=====
|
|
8
8
|
Throw something like this in your config.ru to enable notifications.
|
|
9
9
|
|
|
10
|
-
require '
|
|
10
|
+
require 'rack/hoptoad'
|
|
11
11
|
|
|
12
|
-
use Rack::
|
|
12
|
+
use Rack::Hoptoad, 'fd48c7d26f724503a0280f808f44b339fc65fab8'
|
|
13
13
|
|
|
14
14
|
You can also exclude certain sensitive environmental variables using the block syntax
|
|
15
15
|
|
|
16
|
-
require '
|
|
16
|
+
require 'rack/hoptoad'
|
|
17
17
|
|
|
18
|
-
use Rack::
|
|
18
|
+
use Rack::Hoptoad, 'fd48c7d26f724503a0280f808f44b339fc65fab8' do |notifier|
|
|
19
19
|
notifier.environment_filters << %w(MY_SECRET_KEY MY_SECRET_TOKEN)
|
|
20
20
|
end
|
|
21
21
|
|
|
@@ -37,4 +37,4 @@ In order for exceptions to propagate up to Rack in Sinatra you need to enable ra
|
|
|
37
37
|
enable :raise_errors
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
Note that the errors block does not execute so you'll need to handle the 500 elsewhere.
|
|
40
|
+
Note that the errors block does not execute so you'll need to handle the 500 elsewhere. Normally this is done with a 500.html in the document root.
|
data/Rakefile
CHANGED
|
@@ -3,9 +3,10 @@ require 'rake/gempackagetask'
|
|
|
3
3
|
require 'rubygems/specification'
|
|
4
4
|
require 'spec/rake/spectask'
|
|
5
5
|
require 'date'
|
|
6
|
+
require 'bundler'
|
|
6
7
|
|
|
7
8
|
GEM = "rack_hoptoad"
|
|
8
|
-
GEM_VERSION = "0.0
|
|
9
|
+
GEM_VERSION = "0.1.0"
|
|
9
10
|
AUTHOR = "Corey Donohoe"
|
|
10
11
|
EMAIL = "atmos@atmos.org"
|
|
11
12
|
HOMEPAGE = "http://github.com/atmos/rack_hoptoad"
|
|
@@ -23,11 +24,13 @@ spec = Gem::Specification.new do |s|
|
|
|
23
24
|
s.email = EMAIL
|
|
24
25
|
s.homepage = HOMEPAGE
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
manifest = Bundler::Environment.load(File.dirname(__FILE__) + '/Gemfile')
|
|
28
|
+
manifest.dependencies.each do |d|
|
|
29
|
+
next unless d.only && d.only.include?('release')
|
|
30
|
+
s.add_dependency(d.name, d.version)
|
|
31
|
+
end
|
|
28
32
|
|
|
29
33
|
s.require_path = 'lib'
|
|
30
|
-
s.autorequire = GEM
|
|
31
34
|
s.files = %w(LICENSE README.md Rakefile TODO) + Dir.glob("{lib,specs}/**/*")
|
|
32
35
|
end
|
|
33
36
|
|
|
@@ -35,11 +38,6 @@ Rake::GemPackageTask.new(spec) do |pkg|
|
|
|
35
38
|
pkg.gem_spec = spec
|
|
36
39
|
end
|
|
37
40
|
|
|
38
|
-
desc "install the gem locally"
|
|
39
|
-
task :install => [:package] do
|
|
40
|
-
sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
|
|
41
|
-
end
|
|
42
|
-
|
|
43
41
|
desc "create a gemspec file"
|
|
44
42
|
task :make_spec do
|
|
45
43
|
File.open("#{GEM}.gemspec", "w") do |file|
|
|
@@ -60,6 +58,5 @@ namespace :rack_hoptoad do
|
|
|
60
58
|
t.rcov = ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true
|
|
61
59
|
t.rcov_opts << '--text-summary'
|
|
62
60
|
t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
|
|
63
|
-
# t.rcov_opts << '--only-uncovered'
|
|
64
61
|
end
|
|
65
62
|
end
|
data/lib/rack/hoptoad.rb
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'rack'
|
|
2
|
+
require 'erb'
|
|
3
|
+
require 'toadhopper'
|
|
4
|
+
|
|
5
|
+
module Rack
|
|
6
|
+
# Catches all exceptions raised from the app it wraps and
|
|
7
|
+
# posts the results to hoptoad.
|
|
8
|
+
class Hoptoad
|
|
9
|
+
attr_accessor :api_key, :environment_filters
|
|
10
|
+
|
|
11
|
+
def initialize(app, api_key = nil)
|
|
12
|
+
@app = app
|
|
13
|
+
@api_key = api_key
|
|
14
|
+
@environment_filters = %w(AWS_ACCESS_KEY AWS_SECRET_ACCESS_KEY AWS_ACCOUNT SSH_AUTH_SOCK)
|
|
15
|
+
yield self if block_given?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call(env)
|
|
19
|
+
status, headers, body =
|
|
20
|
+
begin
|
|
21
|
+
@app.call(env)
|
|
22
|
+
rescue StandardError, LoadError, SyntaxError => boom
|
|
23
|
+
# TODO don't allow exceptions from send_notification to
|
|
24
|
+
# propogate
|
|
25
|
+
send_notification boom, env
|
|
26
|
+
raise
|
|
27
|
+
end
|
|
28
|
+
send_notification env['rack.exception'], env if env['rack.exception']
|
|
29
|
+
[status, headers, body]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def environment_filter_keys
|
|
33
|
+
@environment_filters.flatten
|
|
34
|
+
end
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def send_notification(exception, env)
|
|
38
|
+
request = Rack::Request.new(env)
|
|
39
|
+
|
|
40
|
+
options = {
|
|
41
|
+
:api_key => api_key,
|
|
42
|
+
:url => "#{request.scheme}://#{request.host}#{request.path}",
|
|
43
|
+
:request => request,
|
|
44
|
+
:framework_env => ENV['RACK_ENV'] || 'development',
|
|
45
|
+
:notifier_name => 'Rack::Hoptoad',
|
|
46
|
+
:notifier_version => '0.0.6',
|
|
47
|
+
:session => env['rack.session']
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if %w(staging production).include?(ENV['RACK_ENV'])
|
|
51
|
+
ToadHopper.new(api_key).post!(exception, options, {'X-Hoptoad-Client-Name' => 'Rack::Hoptoad'})
|
|
52
|
+
end
|
|
53
|
+
env['hoptoad.notified'] = true
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def extract_body(env)
|
|
57
|
+
if io = env['rack.input']
|
|
58
|
+
io.rewind if io.respond_to?(:rewind)
|
|
59
|
+
io.read
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
metadata
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rack_hoptoad
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Corey Donohoe
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
|
|
12
|
-
date: 2009-12-
|
|
12
|
+
date: 2009-12-15 00:00:00 -08:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
@@ -22,6 +22,16 @@ dependencies:
|
|
|
22
22
|
- !ruby/object:Gem::Version
|
|
23
23
|
version: "0"
|
|
24
24
|
version:
|
|
25
|
+
- !ruby/object:Gem::Dependency
|
|
26
|
+
name: toadhopper
|
|
27
|
+
type: :runtime
|
|
28
|
+
version_requirement:
|
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ~>
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 0.9.1
|
|
34
|
+
version:
|
|
25
35
|
description: A gem that provides hoptoad notifications from rack
|
|
26
36
|
email: atmos@atmos.org
|
|
27
37
|
executables: []
|
|
@@ -36,9 +46,7 @@ files:
|
|
|
36
46
|
- README.md
|
|
37
47
|
- Rakefile
|
|
38
48
|
- TODO
|
|
39
|
-
- lib/rack/
|
|
40
|
-
- lib/rack/notice.xml.erb
|
|
41
|
-
- lib/rack_hoptoad.rb
|
|
49
|
+
- lib/rack/hoptoad.rb
|
|
42
50
|
has_rdoc: true
|
|
43
51
|
homepage: http://github.com/atmos/rack_hoptoad
|
|
44
52
|
licenses: []
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
require 'net/http'
|
|
2
|
-
|
|
3
|
-
module Rack
|
|
4
|
-
# Catches all exceptions raised from the app it wraps and
|
|
5
|
-
# posts the results to hoptoad.
|
|
6
|
-
class HoptoadNotifier
|
|
7
|
-
attr_accessor :api_key, :environment_filters
|
|
8
|
-
|
|
9
|
-
def initialize(app, api_key = nil)
|
|
10
|
-
@app = app
|
|
11
|
-
@api_key = api_key
|
|
12
|
-
@environment_filters = %w(AWS_ACCESS_KEY AWS_SECRET_ACCESS_KEY AWS_ACCOUNT SSH_AUTH_SOCK)
|
|
13
|
-
yield self if block_given?
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def call(env)
|
|
17
|
-
status, headers, body =
|
|
18
|
-
begin
|
|
19
|
-
@app.call(env)
|
|
20
|
-
rescue StandardError, LoadError, SyntaxError => boom
|
|
21
|
-
# TODO don't allow exceptions from send_notification to
|
|
22
|
-
# propogate
|
|
23
|
-
send_notification boom, env
|
|
24
|
-
raise
|
|
25
|
-
end
|
|
26
|
-
send_notification env['rack.exception'], env if env['rack.exception']
|
|
27
|
-
[status, headers, body]
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def environment_filter_keys
|
|
31
|
-
@environment_filters.flatten
|
|
32
|
-
end
|
|
33
|
-
private
|
|
34
|
-
|
|
35
|
-
def notice_template
|
|
36
|
-
::File.read(::File.join(::File.dirname(__FILE__), 'notice.xml.erb'))
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
INPUT_FORMAT = %r{^([^:]+):(\d+)(?::in `([^']+)')?$}.freeze
|
|
40
|
-
|
|
41
|
-
class Backtrace < Struct.new(:file, :number, :method); end
|
|
42
|
-
def build_backtrace(exception)
|
|
43
|
-
exception.backtrace.map do |line|
|
|
44
|
-
_, file, number, method = line.match(INPUT_FORMAT).to_a
|
|
45
|
-
Backtrace.new(file, number, method)
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def send_notification(exception, env)
|
|
50
|
-
@error = exception
|
|
51
|
-
@api_key = api_key
|
|
52
|
-
@request = Rack::Request.new(env)
|
|
53
|
-
@request_path = @request.script_name + @request.path_info
|
|
54
|
-
@environment = clean_hoptoad_environment(ENV.to_hash.merge(env))
|
|
55
|
-
@backtrace = build_backtrace(exception)
|
|
56
|
-
|
|
57
|
-
document = ERB.new(notice_template).result(binding)
|
|
58
|
-
|
|
59
|
-
if %w(staging production).include?(ENV['RACK_ENV'])
|
|
60
|
-
send_to_hoptoad document
|
|
61
|
-
end
|
|
62
|
-
env['hoptoad.notified'] = true
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def extract_body(env)
|
|
66
|
-
if io = env['rack.input']
|
|
67
|
-
io.rewind if io.respond_to?(:rewind)
|
|
68
|
-
io.read
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def send_to_hoptoad(data) #:nodoc:
|
|
73
|
-
url = URI.parse("http://hoptoadapp.com:80/notifier_api/v2/notices")
|
|
74
|
-
|
|
75
|
-
Net::HTTP.start(url.host, url.port) do |http|
|
|
76
|
-
headers = {
|
|
77
|
-
'Content-type' => 'text/xml',
|
|
78
|
-
'Accept' => 'text/xml, application/xml'
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
http.read_timeout = 5 # seconds
|
|
82
|
-
http.open_timeout = 2 # seconds
|
|
83
|
-
# http.use_ssl = HoptoadNotifier.secure
|
|
84
|
-
response = begin
|
|
85
|
-
http.post(url.path, data, headers)
|
|
86
|
-
rescue TimeoutError => e
|
|
87
|
-
logger "Timeout while contacting the Hoptoad server."
|
|
88
|
-
nil
|
|
89
|
-
end
|
|
90
|
-
case response
|
|
91
|
-
when Net::HTTPSuccess then
|
|
92
|
-
logger "Hoptoad Success: #{response.class}"
|
|
93
|
-
else
|
|
94
|
-
logger "Hoptoad Failure: #{response.class}\n#{response.body if response.respond_to? :body}"
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def logger(str)
|
|
100
|
-
puts str if ENV['RACK_DEBUG']
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def clean_non_serializable_data(notice) #:nodoc:
|
|
104
|
-
notice.select{|k,v| serializable?(v) }.inject({}) do |h, pair|
|
|
105
|
-
h[pair.first] = pair.last.is_a?(Hash) ? clean_non_serializable_data(pair.last) : pair.last
|
|
106
|
-
h
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def clean_hoptoad_environment(environment) #:nodoc:
|
|
111
|
-
clean_non_serializable_data(environment).each do |key, value|
|
|
112
|
-
environment[key] = "[FILTERED]" if filter?(key)
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def filter?(key)
|
|
117
|
-
environment_filter_keys.any? do |filter|
|
|
118
|
-
key.to_s.match(/#{filter}/)
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def serializable?(value) #:nodoc:
|
|
123
|
-
value.is_a?(Fixnum) ||
|
|
124
|
-
value.is_a?(Array) ||
|
|
125
|
-
value.is_a?(String) ||
|
|
126
|
-
value.is_a?(Hash) ||
|
|
127
|
-
value.is_a?(Bignum)
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
end
|
data/lib/rack/notice.xml.erb
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<notice version="2.0.0">
|
|
3
|
-
<api-key><%= @api_key %></api-key>
|
|
4
|
-
<notifier>
|
|
5
|
-
<name>Rack Hoptoad Notifier</name>
|
|
6
|
-
<version>0.0.5</version>
|
|
7
|
-
<url>http://github.com//atmos/rack_hoptoad</url>
|
|
8
|
-
</notifier>
|
|
9
|
-
<error>
|
|
10
|
-
<class><%= @error.class.name %></class>
|
|
11
|
-
<message><%= @error.message %></message>
|
|
12
|
-
<backtrace>
|
|
13
|
-
<% @backtrace.each do |line| %>
|
|
14
|
-
<line method="<%= line.method %>" file="<%= line.file %>" number="<%= line.number %>"/>
|
|
15
|
-
<% end %>
|
|
16
|
-
</backtrace>
|
|
17
|
-
</error>
|
|
18
|
-
<request>
|
|
19
|
-
<url><%= @request_path %></url>
|
|
20
|
-
<component><%= @request_path %></component>
|
|
21
|
-
<% if @request.params.any? %>
|
|
22
|
-
<params>
|
|
23
|
-
<% @request.params.each do |key,value| %>
|
|
24
|
-
<var key="<%= key %>"><%= value %></var>
|
|
25
|
-
<% end %>
|
|
26
|
-
</params>
|
|
27
|
-
<% end %>
|
|
28
|
-
<% if @environment.any? %>
|
|
29
|
-
<cgi-data>
|
|
30
|
-
<% @environment.each do |key,value| %>
|
|
31
|
-
<var key="<%= key %>"><%= value %></var>
|
|
32
|
-
<% end %>
|
|
33
|
-
</cgi-data>
|
|
34
|
-
<% end %>
|
|
35
|
-
</request>
|
|
36
|
-
<server-environment>
|
|
37
|
-
<project-root><%= Dir.pwd %></project-root>
|
|
38
|
-
<environment-name><%= ENV['RACK_ENV'] || 'development' %></environment-name>
|
|
39
|
-
</server-environment>
|
|
40
|
-
</notice>
|