party_foul 0.6.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +72 -5
- data/lib/generators/party_foul/templates/party_foul.rb +7 -0
- data/lib/party_foul/exception_handler.rb +2 -2
- data/lib/party_foul/issue_renderers/base.rb +11 -0
- data/lib/party_foul/processors/delayed_job.rb +16 -0
- data/lib/party_foul/processors/resque.rb +16 -0
- data/lib/party_foul/processors/sidekiq.rb +17 -0
- data/lib/party_foul/version.rb +1 -1
- data/lib/party_foul.rb +1 -1
- data/test/party_foul/exception_handler_test.rb +61 -2
- data/test/test_helper.rb +1 -1
- data/test/tmp/config/initializers/party_foul.rb +7 -0
- metadata +7 -4
data/README.md
CHANGED
@@ -6,6 +6,14 @@
|
|
6
6
|
|
7
7
|
Rails exceptions automatically opened as issues on Github
|
8
8
|
|
9
|
+
## Looking for help? ##
|
10
|
+
|
11
|
+
If it is a bug [please open an issue on
|
12
|
+
Github](https://github.com/dockyard/party_foul/issues). If you need help using
|
13
|
+
the gem please ask the question on
|
14
|
+
[Stack Overflow](http://stackoverflow.com). Be sure to tag the
|
15
|
+
question with `DockYard` so we can find it.
|
16
|
+
|
9
17
|
## About ##
|
10
18
|
|
11
19
|
`PartyFoul` captures exceptions in your application and does the
|
@@ -99,21 +107,47 @@ curl -u <github_login> -i -d "{ \"scopes\": [\"repo\"] }" \
|
|
99
107
|
https://api.github.com/authorizations
|
100
108
|
```
|
101
109
|
|
102
|
-
Add as the very last middleware in your production `Rack` stack.
|
103
|
-
|
104
110
|
## Customization ##
|
105
111
|
|
112
|
+
### Labels ###
|
113
|
+
|
114
|
+
You can specify an additional array of labels that will be applied to the issues PartyFoul creates.
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
PartyFoul.configure do |config|
|
118
|
+
config.additional_labels = ['front-end']
|
119
|
+
end
|
120
|
+
```
|
121
|
+
|
122
|
+
You can also provide a Proc that is passed the exception and the environment.
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
PartyFoul.configure do |config|
|
126
|
+
config.additional_labels = Proc.new do |exception, env|
|
127
|
+
labels = if env["HTTP_HOST"] =~ /beta\./
|
128
|
+
['beta']
|
129
|
+
else
|
130
|
+
['production']
|
131
|
+
end
|
132
|
+
if exception.message =~ /PG::Error/
|
133
|
+
labels << 'database'
|
134
|
+
end
|
135
|
+
labels
|
136
|
+
end
|
137
|
+
end
|
138
|
+
```
|
139
|
+
|
106
140
|
### Background Processing ###
|
107
141
|
|
108
142
|
You can specify the adapter with which the exceptions should be
|
109
143
|
handled. By default, PartyFoul includes the
|
110
|
-
[`PartyFoul::
|
144
|
+
[`PartyFoul::Processors::Sync`](https://github.com/dockyard/party_foul/tree/master/lib/party_foul/processors/sync.rb)
|
111
145
|
which handles the exception synchronously. To use your own adapter,
|
112
146
|
include the following in your `PartyFoul.configure` block:
|
113
147
|
|
114
148
|
```ruby
|
115
149
|
PartyFoul.configure do |config|
|
116
|
-
config.
|
150
|
+
config.processor = PartyFoul::Processors::MyBackgroundProcessor
|
117
151
|
end
|
118
152
|
|
119
153
|
class PartyFoul::Processors::MyBackgroundProcessor
|
@@ -125,8 +159,41 @@ end
|
|
125
159
|
|
126
160
|
```
|
127
161
|
|
128
|
-
|
162
|
+
`PartyFoul` comes with the following background processing adapters:
|
163
|
+
|
164
|
+
* [PartyFoul::Processors::Sidekiq](https://github.com/dockyard/party_foul/blob/master/lib/party_foul/processors/sidekiq.rb)
|
165
|
+
* [PartyFoul::Processors::Resque](https://github.com/dockyard/party_foul/blob/master/lib/party_foul/processors/resque.rb)
|
166
|
+
* [PartyFoul::Processors::DelayedJob](https://github.com/dockyard/party_foul/blob/master/lib/party_foul/processors/delayed_job.rb)
|
167
|
+
|
168
|
+
These adapters are not loaded by default. You must explicitly require if
|
169
|
+
you want to use:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
require 'party_foul/processors/sidekiq'
|
173
|
+
|
174
|
+
PartyFoul.configure do |config|
|
175
|
+
config.processor = PartyFoul::Processors::Sidekiq
|
176
|
+
end
|
177
|
+
```
|
178
|
+
|
179
|
+
## Tracking errors outside of an HTTP request
|
180
|
+
|
181
|
+
You may want to track errors outside of a reqular HTTP stack. In that
|
182
|
+
case you will need to make sure of the
|
183
|
+
`PartyFoul::RacklessExceptionHandler`.
|
184
|
+
|
185
|
+
The code that you want to handle should be wrapped like so:
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
begin
|
189
|
+
... # some code that might raise an error
|
190
|
+
rescue => e
|
191
|
+
PartyFoul::RacklessExceptionHandler.handle(e, {class: class_name, method: method_name, params: message)
|
192
|
+
raise e
|
193
|
+
end
|
194
|
+
```
|
129
195
|
|
196
|
+
### Tracking errors in a Sidekiq worker
|
130
197
|
In order to use PartyFoul for exception handling with Sidekiq you will need to create an initializer with some middleware configuration. The following example is based on using [Sidekiq with another exception notifiier server](https://github.com/bugsnag/bugsnag-ruby/blob/master/lib/bugsnag/sidekiq.rb).
|
131
198
|
|
132
199
|
File: config/initializers/partyfoul_sidekiq.rb
|
@@ -22,4 +22,11 @@ PartyFoul.configure do |config|
|
|
22
22
|
|
23
23
|
# The branch for your deployed code
|
24
24
|
# config.branch = 'master'
|
25
|
+
|
26
|
+
# Additional labels to add to issues created
|
27
|
+
# config.additional_labels = ['production']
|
28
|
+
# or
|
29
|
+
# config.additional_labels = Proc.new do |exception, env|
|
30
|
+
# []
|
31
|
+
# end
|
25
32
|
end
|
@@ -46,7 +46,7 @@ class PartyFoul::ExceptionHandler
|
|
46
46
|
# Will create a new issue and a comment with the proper details. All issues are labeled as 'bug'.
|
47
47
|
def create_issue
|
48
48
|
self.sha = PartyFoul.github.git_data.references.get(PartyFoul.owner, PartyFoul.repo, "heads/#{PartyFoul.branch}").object.sha
|
49
|
-
issue = PartyFoul.github.issues.create(PartyFoul.owner, PartyFoul.repo, title: rendered_issue.title, body: rendered_issue.body, labels: ['bug'])
|
49
|
+
issue = PartyFoul.github.issues.create(PartyFoul.owner, PartyFoul.repo, title: rendered_issue.title, body: rendered_issue.body, labels: ['bug'] + rendered_issue.labels)
|
50
50
|
PartyFoul.github.issues.comments.create(PartyFoul.owner, PartyFoul.repo, issue['number'], body: rendered_issue.comment)
|
51
51
|
end
|
52
52
|
|
@@ -58,7 +58,7 @@ class PartyFoul::ExceptionHandler
|
|
58
58
|
params = {body: rendered_issue.update_body(issue['body']), state: 'open'}
|
59
59
|
|
60
60
|
if issue['state'] == 'closed'
|
61
|
-
params[:labels] = ['bug', 'regression']
|
61
|
+
params[:labels] = (['bug', 'regression'] + issue['labels']).uniq
|
62
62
|
end
|
63
63
|
|
64
64
|
self.sha = PartyFoul.github.git_data.references.get(PartyFoul.owner, PartyFoul.repo, "heads/#{PartyFoul.branch}").object.sha
|
@@ -127,6 +127,17 @@ BODY
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
|
+
# Provides additional labels using the configured options
|
131
|
+
#
|
132
|
+
# @return [Array]
|
133
|
+
def labels
|
134
|
+
if PartyFoul.additional_labels.respond_to? :call
|
135
|
+
PartyFoul.additional_labels.call(self.exception, self.env) || []
|
136
|
+
else
|
137
|
+
PartyFoul.additional_labels || []
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
130
141
|
private
|
131
142
|
|
132
143
|
def app_root
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'party_foul/processors/base'
|
2
|
+
|
3
|
+
class PartyFoul::Processors::DelayedJob < PartyFoul::Processors::Base
|
4
|
+
@queue = 'party_foul'
|
5
|
+
|
6
|
+
# Passes the exception and rack env data to DelayedJob to be processed later
|
7
|
+
#
|
8
|
+
# @param [Exception, Hash]
|
9
|
+
def self.handle(exception, env)
|
10
|
+
new.delay(queue: @queue).perform(Marshal.dump(exception), Marshal.dump(env))
|
11
|
+
end
|
12
|
+
|
13
|
+
def perform(exception, env)
|
14
|
+
PartyFoul::ExceptionHandler.new(Marshal.load(exception), Marshal.load(env)).run
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'party_foul/processors/base'
|
2
|
+
|
3
|
+
class PartyFoul::Processors::Resque < PartyFoul::Processors::Base
|
4
|
+
@queue = :party_foul
|
5
|
+
|
6
|
+
# Passes the exception and rack env data to Resque to be processed later
|
7
|
+
#
|
8
|
+
# @param [Exception, Hash]
|
9
|
+
def self.handle(exception, env)
|
10
|
+
Resque.enqueue(PartyFoul::Processors::Resque, Marshal.dump(exception), Marshal.dump(env))
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.perform(exception, env)
|
14
|
+
PartyFoul::ExceptionHandler.new(Marshal.load(exception), Marshal.load(env)).run
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'party_foul/processors/base'
|
2
|
+
|
3
|
+
class PartyFoul::Processors::Sidekiq < PartyFoul::Processors::Base
|
4
|
+
include Sidekiq::Worker
|
5
|
+
sidekiq_options queue: 'party_foul'
|
6
|
+
|
7
|
+
# Passes the exception and rack env data to Sidekiq to be processed later
|
8
|
+
#
|
9
|
+
# @param [Exception, Hash]
|
10
|
+
def self.handle(exception, env)
|
11
|
+
perform_async(Marshal.dump(exception), Marshal.dump(env))
|
12
|
+
end
|
13
|
+
|
14
|
+
def perform(exception, env)
|
15
|
+
PartyFoul::ExceptionHandler.new(Marshal.load(exception), Marshal.load(env)).run
|
16
|
+
end
|
17
|
+
end
|
data/lib/party_foul/version.rb
CHANGED
data/lib/party_foul.rb
CHANGED
@@ -2,7 +2,7 @@ require 'github_api'
|
|
2
2
|
|
3
3
|
module PartyFoul
|
4
4
|
class << self
|
5
|
-
attr_accessor :github, :oauth_token, :endpoint, :owner, :repo, :blacklisted_exceptions, :processor, :web_url, :branch, :whitelisted_rack_variables
|
5
|
+
attr_accessor :github, :oauth_token, :endpoint, :owner, :repo, :blacklisted_exceptions, :processor, :web_url, :branch, :whitelisted_rack_variables, :additional_labels
|
6
6
|
end
|
7
7
|
|
8
8
|
def self.whitelisted_rack_variables
|
@@ -31,6 +31,65 @@ describe 'Party Foul Exception Handler' do
|
|
31
31
|
PartyFoul.github.issues.comments.expects(:create).with('test_owner', 'test_repo', 1, body: 'Test Comment')
|
32
32
|
PartyFoul::ExceptionHandler.new(nil, {}).run
|
33
33
|
end
|
34
|
+
|
35
|
+
context 'when additional labels are configured' do
|
36
|
+
before do
|
37
|
+
PartyFoul.configure do |config|
|
38
|
+
config.additional_labels = ['custom', 'label']
|
39
|
+
end
|
40
|
+
end
|
41
|
+
after do
|
42
|
+
clean_up_party
|
43
|
+
end
|
44
|
+
it 'will open a new error on Github with the additional labels' do
|
45
|
+
PartyFoul::IssueRenderers::Rails.any_instance.stubs(:body).returns('Test Body')
|
46
|
+
PartyFoul::IssueRenderers::Rails.any_instance.stubs(:comment).returns('Test Comment')
|
47
|
+
PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: []))
|
48
|
+
PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'closed').returns(Hashie::Mash.new(issues: []))
|
49
|
+
PartyFoul.github.issues.expects(:create).with('test_owner', 'test_repo', title: 'Test Title', body: 'Test Body', :labels => ['bug', 'custom', 'label']).returns(Hashie::Mash.new('number' => 1))
|
50
|
+
PartyFoul.github.git_data.references.expects(:get).with('test_owner', 'test_repo', 'heads/deploy').returns(Hashie::Mash.new(object: Hashie::Mash.new(sha: 'abcdefg1234567890')))
|
51
|
+
PartyFoul.github.issues.comments.expects(:create).with('test_owner', 'test_repo', 1, body: 'Test Comment')
|
52
|
+
PartyFoul::ExceptionHandler.new(nil, {}).run
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when a proc for additional labels are configured' do
|
57
|
+
before do
|
58
|
+
PartyFoul.configure do |config|
|
59
|
+
config.additional_labels = Proc.new do |exception, env|
|
60
|
+
if env[:http_host] =~ /beta\./
|
61
|
+
['beta']
|
62
|
+
elsif exception.message =~ /Database/
|
63
|
+
['database_error']
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
PartyFoul::IssueRenderers::Rails.any_instance.stubs(:body).returns('Test Body')
|
68
|
+
PartyFoul::IssueRenderers::Rails.any_instance.stubs(:comment).returns('Test Comment')
|
69
|
+
PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: []))
|
70
|
+
PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'closed').returns(Hashie::Mash.new(issues: []))
|
71
|
+
PartyFoul.github.git_data.references.expects(:get).with('test_owner', 'test_repo', 'heads/deploy').returns(Hashie::Mash.new(object: Hashie::Mash.new(sha: 'abcdefg1234567890')))
|
72
|
+
PartyFoul.github.issues.comments.expects(:create).with('test_owner', 'test_repo', 1, body: 'Test Comment')
|
73
|
+
end
|
74
|
+
after do
|
75
|
+
clean_up_party
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'will open a new error on Github with the default labels if no additional labels are returned from the proc' do
|
79
|
+
PartyFoul.github.issues.expects(:create).with('test_owner', 'test_repo', title: 'Test Title', body: 'Test Body', :labels => ['bug']).returns(Hashie::Mash.new('number' => 1))
|
80
|
+
PartyFoul::ExceptionHandler.new(stub(:message => ''), {}).run
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'will open a new error on Github with the additional labels based on the exception message' do
|
84
|
+
PartyFoul.github.issues.expects(:create).with('test_owner', 'test_repo', title: 'Test Title', body: 'Test Body', :labels => ['bug', 'database_error']).returns(Hashie::Mash.new('number' => 1))
|
85
|
+
PartyFoul::ExceptionHandler.new(stub(:message => 'Database'), {}).run
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'will open a new error on Github with the additional labels based on the env' do
|
89
|
+
PartyFoul.github.issues.expects(:create).with('test_owner', 'test_repo', title: 'Test Title', body: 'Test Body', :labels => ['bug', 'beta']).returns(Hashie::Mash.new('number' => 1))
|
90
|
+
PartyFoul::ExceptionHandler.new(stub(:message => ''), {:http_host => 'beta.example.com'}).run
|
91
|
+
end
|
92
|
+
end
|
34
93
|
end
|
35
94
|
|
36
95
|
context 'when error is not new' do
|
@@ -52,8 +111,8 @@ describe 'Party Foul Exception Handler' do
|
|
52
111
|
context 'and closed' do
|
53
112
|
it 'will update the count on the body and re-open the issue' do
|
54
113
|
PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: []))
|
55
|
-
PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'closed').returns(Hashie::Mash.new(issues: [{title: 'Test Title', body: 'Test Body', state: 'closed', number: 1}]))
|
56
|
-
PartyFoul.github.issues.expects(:edit).with('test_owner', 'test_repo', 1, body: 'New Body', state: 'open', labels: ['bug', 'regression'])
|
114
|
+
PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'closed').returns(Hashie::Mash.new(issues: [{title: 'Test Title', body: 'Test Body', state: 'closed', number: 1, labels: ['staging']}]))
|
115
|
+
PartyFoul.github.issues.expects(:edit).with('test_owner', 'test_repo', 1, body: 'New Body', state: 'open', labels: ['bug', 'regression', 'staging'])
|
57
116
|
PartyFoul.github.issues.comments.expects(:create).with('test_owner', 'test_repo', 1, body: 'Test Comment')
|
58
117
|
PartyFoul.github.git_data.references.expects(:get).with('test_owner', 'test_repo', 'heads/deploy').returns(Hashie::Mash.new(object: Hashie::Mash.new(sha: 'abcdefg1234567890')))
|
59
118
|
PartyFoul::ExceptionHandler.new(nil, {}).run
|
data/test/test_helper.rb
CHANGED
@@ -24,7 +24,7 @@ module MiniTest::Expectations
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def clean_up_party
|
27
|
-
%w{github oauth_token endpoint owner repo blacklisted_exceptions processor web_url branch}.each do |attr|
|
27
|
+
%w{github oauth_token endpoint owner repo blacklisted_exceptions processor web_url branch additional_labels}.each do |attr|
|
28
28
|
PartyFoul.send("#{attr}=", nil)
|
29
29
|
end
|
30
30
|
end
|
@@ -22,4 +22,11 @@ PartyFoul.configure do |config|
|
|
22
22
|
|
23
23
|
# The branch for your deployed code
|
24
24
|
# config.branch = 'master'
|
25
|
+
|
26
|
+
# Additional labels to add to issues created
|
27
|
+
# config.additional_labels = ['production']
|
28
|
+
# or
|
29
|
+
# config.additional_labels = Proc.new do |exception, env|
|
30
|
+
# []
|
31
|
+
# end
|
25
32
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: party_foul
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-02-
|
13
|
+
date: 2013-02-06 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: github_api
|
@@ -190,6 +190,9 @@ files:
|
|
190
190
|
- lib/party_foul/issue_renderers.rb
|
191
191
|
- lib/party_foul/middleware.rb
|
192
192
|
- lib/party_foul/processors/base.rb
|
193
|
+
- lib/party_foul/processors/delayed_job.rb
|
194
|
+
- lib/party_foul/processors/resque.rb
|
195
|
+
- lib/party_foul/processors/sidekiq.rb
|
193
196
|
- lib/party_foul/processors/sync.rb
|
194
197
|
- lib/party_foul/processors.rb
|
195
198
|
- lib/party_foul/rackless_exception_handler.rb
|
@@ -222,7 +225,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
222
225
|
version: '0'
|
223
226
|
segments:
|
224
227
|
- 0
|
225
|
-
hash:
|
228
|
+
hash: 764634010609561347
|
226
229
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
227
230
|
none: false
|
228
231
|
requirements:
|
@@ -231,7 +234,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
231
234
|
version: '0'
|
232
235
|
segments:
|
233
236
|
- 0
|
234
|
-
hash:
|
237
|
+
hash: 764634010609561347
|
235
238
|
requirements: []
|
236
239
|
rubyforge_project:
|
237
240
|
rubygems_version: 1.8.23
|