party_foul 0.6.0 → 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/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
|