Commit 44b8346b437dd2238637d4d587b157968d6de370

Authored by Jared Pace
1 parent 795b9de3
Exists in master and in 1 other branch production

Find similar errors based on a digest fingerprint of the backtrace

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
... ...