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