Commit 44b8346b437dd2238637d4d587b157968d6de370
1 parent
795b9de3
Exists in
master
and in
1 other branch
Find similar errors based on a digest fingerprint of the backtrace
Showing
7 changed files
with
47 additions
and
17 deletions
Show diff stats
app/models/err.rb
... | ... | @@ -3,10 +3,10 @@ class Err |
3 | 3 | include Mongoid::Timestamps |
4 | 4 | |
5 | 5 | field :klass |
6 | - field :message | |
7 | 6 | field :component |
8 | 7 | field :action |
9 | 8 | field :environment |
9 | + field :fingerprint | |
10 | 10 | field :resolved, :type => Boolean, :default => false |
11 | 11 | |
12 | 12 | referenced_in :project |
... | ... | @@ -40,4 +40,8 @@ class Err |
40 | 40 | where |
41 | 41 | end |
42 | 42 | |
43 | + def message | |
44 | + notices.first.message || klass | |
45 | + end | |
46 | + | |
43 | 47 | end |
44 | 48 | \ No newline at end of file | ... | ... |
app/models/notice.rb
... | ... | @@ -4,6 +4,7 @@ class Notice |
4 | 4 | include Mongoid::Document |
5 | 5 | include Mongoid::Timestamps |
6 | 6 | |
7 | + field :message | |
7 | 8 | field :backtrace, :type => Array |
8 | 9 | field :server_environment, :type => Hash |
9 | 10 | field :request, :type => Hash |
... | ... | @@ -23,19 +24,20 @@ class Notice |
23 | 24 | hoptoad_notice['request']['action'] = nil if hoptoad_notice['request']['action'].blank? |
24 | 25 | |
25 | 26 | error = Err.for({ |
26 | - :project => project, | |
27 | - :klass => hoptoad_notice['error']['class'], | |
28 | - :message => hoptoad_notice['error']['message'], | |
29 | - :component => hoptoad_notice['request']['component'], | |
30 | - :action => hoptoad_notice['request']['action'], | |
31 | - :environment => hoptoad_notice['server-environment']['environment-name'] | |
27 | + :project => project, | |
28 | + :klass => hoptoad_notice['error']['class'], | |
29 | + :component => hoptoad_notice['request']['component'], | |
30 | + :action => hoptoad_notice['request']['action'], | |
31 | + :environment => hoptoad_notice['server-environment']['environment-name'], | |
32 | + :fingerprint => hoptoad_notice['fingerprint'] | |
32 | 33 | }) |
33 | 34 | |
34 | 35 | error.notices.create!({ |
35 | - :backtrace => hoptoad_notice['error']['backtrace']['line'], | |
36 | + :message => hoptoad_notice['error']['message'], | |
37 | + :backtrace => hoptoad_notice['error']['backtrace']['line'], | |
36 | 38 | :server_environment => hoptoad_notice['server-environment'], |
37 | - :request => hoptoad_notice['request'], | |
38 | - :notifier => hoptoad_notice['notifier'] | |
39 | + :request => hoptoad_notice['request'], | |
40 | + :notifier => hoptoad_notice['notifier'] | |
39 | 41 | }) |
40 | 42 | end |
41 | 43 | ... | ... |
app/views/projects/index.html.haml
... | ... | @@ -12,4 +12,10 @@ |
12 | 12 | %tr |
13 | 13 | %td.name= link_to project.name, project_path(project) |
14 | 14 | %td.deploy= project.last_deploy_at ? project.last_deploy_at.to_s(:micro) : 'n/a' |
15 | - %td.count= link_to project.errs.unresolved.count, project_errs_path(project) | |
16 | 15 | \ No newline at end of file |
16 | + %td.count= link_to project.errs.unresolved.count, project_errs_path(project) | |
17 | + - if @projects.none? | |
18 | + %tr | |
19 | + %td{:colspan => 3} | |
20 | + %em | |
21 | + No projects here. | |
22 | + = link_to 'Click here to create your first one', new_project_path | |
17 | 23 | \ No newline at end of file | ... | ... |
lib/hoptoad.rb
1 | 1 | module Hoptoad |
2 | 2 | module V2 |
3 | + require 'digest/md5' | |
4 | + | |
3 | 5 | class ApiVersionError < StandardError |
4 | 6 | def initialize |
5 | 7 | super "Wrong API Version: Expecting v2.0" |
... | ... | @@ -9,7 +11,9 @@ module Hoptoad |
9 | 11 | def self.parse_xml(xml) |
10 | 12 | parsed = ActiveSupport::XmlMini.backend.parse(xml)['notice'] |
11 | 13 | raise ApiVersionError unless parsed && parsed['version'] == '2.0' |
12 | - rekey(parsed) | |
14 | + rekeyed = rekey(parsed) | |
15 | + rekeyed['fingerprint'] = Digest::MD5.hexdigest(rekeyed['error']['backtrace'].to_s) | |
16 | + rekeyed | |
13 | 17 | end |
14 | 18 | |
15 | 19 | private | ... | ... |
spec/factories/err_factories.rb
1 | 1 | Factory.define :err do |e| |
2 | 2 | e.project {|p| p.association :project} |
3 | 3 | e.klass 'FooError' |
4 | - e.message 'FooError: Too Much Bar' | |
5 | 4 | e.component 'foo' |
6 | 5 | e.action 'bar' |
7 | 6 | e.environment 'production' |
8 | 7 | end |
9 | 8 | |
10 | 9 | Factory.define :notice do |n| |
11 | - n.err {|e| e.association :err} | |
10 | + n.err {|e| e.association :err} | |
11 | + n.message 'FooError: Too Much Bar' | |
12 | 12 | n.backtrace { random_backtrace } |
13 | 13 | n.server_environment 'server-environment' => 'production' |
14 | 14 | n.notifier 'name' => 'Notifier', 'version' => '1', 'url' => 'http://toad.com' | ... | ... |
spec/models/err_spec.rb
... | ... | @@ -22,7 +22,6 @@ describe Err do |
22 | 22 | @conditions = { |
23 | 23 | :project => @project, |
24 | 24 | :klass => 'Whoops', |
25 | - :message => 'Whoops: Oopsy Daisy', | |
26 | 25 | :component => 'Foo', |
27 | 26 | :action => 'bar', |
28 | 27 | :environment => 'production' |
... | ... | @@ -59,6 +58,15 @@ describe Err do |
59 | 58 | end |
60 | 59 | end |
61 | 60 | |
61 | + context '#message' do | |
62 | + it 'returns the message from the first notice' do | |
63 | + err = Factory(:err) | |
64 | + notice1 = Factory(:notice, :err => err, :message => 'ERR 1') | |
65 | + notice2 = Factory(:notice, :err => err, :message => 'ERR 2') | |
66 | + err.message.should == notice1.message | |
67 | + end | |
68 | + end | |
69 | + | |
62 | 70 | context "#resolved?" do |
63 | 71 | it "should start out as unresolved" do |
64 | 72 | error = Err.new | ... | ... |
spec/models/notice_spec.rb
... | ... | @@ -26,6 +26,7 @@ describe Notice do |
26 | 26 | before do |
27 | 27 | @xml = Rails.root.join('spec','fixtures','hoptoad_test_notice.xml').read |
28 | 28 | @project = Factory(:project, :api_key => 'ALLGLORYTOTHEHYPNOTOAD') |
29 | + Digest::MD5.stub(:hexdigest).and_return('fingerprintdigest') | |
29 | 30 | end |
30 | 31 | |
31 | 32 | it 'finds the correct project' do |
... | ... | @@ -37,10 +38,10 @@ describe Notice do |
37 | 38 | Err.should_receive(:for).with({ |
38 | 39 | :project => @project, |
39 | 40 | :klass => 'HoptoadTestingException', |
40 | - :message => 'HoptoadTestingException: Testing hoptoad via "rake hoptoad:test". If you can see this, it works.', | |
41 | 41 | :component => 'application', |
42 | 42 | :action => 'verify', |
43 | - :environment => 'development' | |
43 | + :environment => 'development', | |
44 | + :fingerprint => 'fingerprintdigest' | |
44 | 45 | }).and_return(err = Err.new) |
45 | 46 | err.notices.stub(:create!) |
46 | 47 | @notice = Notice.from_xml(@xml) |
... | ... | @@ -56,6 +57,11 @@ describe Notice do |
56 | 57 | @notice.err.should be_a(Err) |
57 | 58 | end |
58 | 59 | |
60 | + it 'captures the error message' do | |
61 | + @notice = Notice.from_xml(@xml) | |
62 | + @notice.message.should == 'HoptoadTestingException: Testing hoptoad via "rake hoptoad:test". If you can see this, it works.' | |
63 | + end | |
64 | + | |
59 | 65 | it 'captures the backtrace' do |
60 | 66 | @notice = Notice.from_xml(@xml) |
61 | 67 | @notice.backtrace.size.should == 73 | ... | ... |