diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 131d0be..440cb84 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,6 +1,6 @@ module ApplicationHelper def message_graph(problem) - create_percentage_table_for(problem) {|notice| notice.message} + create_percentage_table_for(problem.messages) end def generate_problem_ical(notices) @@ -35,35 +35,19 @@ module ApplicationHelper end def user_agent_graph(problem) - create_percentage_table_for(problem) {|notice| pretty_user_agent(notice.user_agent)} - end - - def pretty_user_agent(user_agent) - (user_agent.nil? || user_agent.none?) ? "N/A" : "#{user_agent.browser} #{user_agent.version}" + create_percentage_table_for(problem.user_agents) end def tenant_graph(problem) - create_percentage_table_for(problem) {|notice| get_host(notice.request['url'])} - end - - def get_host(url) - begin - uri = url && URI.parse(url) - uri.blank? ? "N/A" : uri.host - rescue URI::InvalidURIError - "N/A" - end + create_percentage_table_for(problem.hosts) end - - def create_percentage_table_for(problem, &block) - tallies = tally(problem.notices, &block) - create_percentage_table_from_tallies(tallies, :total => problem.notices.count) + def create_percentage_table_for(collection) + create_percentage_table_from_tallies(tally(collection)) end - def tally(collection, &block) - collection.inject({}) do |tallies, item| - value = yield item + def tally(collection) + collection.inject({}) do |tallies, value| tallies[value] = (tallies[value] || 0) + 1 tallies end diff --git a/app/models/notice.rb b/app/models/notice.rb index 509c8b4..5d60e44 100644 --- a/app/models/notice.rb +++ b/app/models/notice.rb @@ -19,7 +19,7 @@ class Notice after_create :increase_counter_cache, :cache_attributes_on_problem, :unresolve_problem after_create :deliver_notification, :if => :should_notify? before_save :sanitize - before_destroy :decrease_counter_cache + before_destroy :decrease_counter_cache, :remove_cached_attributes_from_problem validates_presence_of :backtrace, :server_environment, :notifier @@ -33,6 +33,10 @@ class Notice agent_string.blank? ? nil : UserAgent.parse(agent_string) end + def user_agent_string + (user_agent.nil? || user_agent.none?) ? "N/A" : "#{user_agent.browser} #{user_agent.version}" + end + def environment_name server_environment['server-environment'] || server_environment['environment-name'] end @@ -59,6 +63,17 @@ class Notice read_attribute(:request) || {} end + def url + request['url'] + end + + def host + uri = url && URI.parse(url) + uri.blank? ? "N/A" : uri.host + rescue URI::InvalidURIError + "N/A" + end + def env_vars request['cgi-data'] || {} end @@ -94,11 +109,14 @@ class Notice problem.inc(:notices_count, -1) if err end + def remove_cached_attributes_from_problem + problem.remove_cached_notice_attribures(self) + end + def unresolve_problem problem.update_attribute(:resolved, false) if problem.resolved? end - def cache_attributes_on_problem problem.cache_notice_attributes(self) end diff --git a/app/models/problem.rb b/app/models/problem.rb index 5e0e676..fb4760a 100644 --- a/app/models/problem.rb +++ b/app/models/problem.rb @@ -18,6 +18,9 @@ class Problem field :environment field :klass field :where + field :user_agents, :type => Array, :default => [] + field :messages, :type => Array, :default => [] + field :hosts, :type => Array, :default => [] index :app_id index :app_name @@ -123,8 +126,20 @@ class Problem :message => notice.message, :environment => notice.environment_name, :klass => notice.klass, - :where => notice.where) if notice + :where => notice.where, + :messages => messages.push(notice.message), + :hosts => hosts.push(notice.host), + :user_agents => user_agents.push(notice.user_agent_string) + ) if notice update_attributes!(attrs) end + + def remove_cached_notice_attribures(notice) + messages.delete_at(messages.index(notice.message)) + hosts.delete_at(hosts.index(notice.host)) + user_agents.delete_at(user_agents.index(notice.user_agent_string)) + save! + end + end diff --git a/app/views/errs/show.html.haml b/app/views/errs/show.html.haml index 0d69934..c4ba60b 100644 --- a/app/views/errs/show.html.haml +++ b/app/views/errs/show.html.haml @@ -56,7 +56,7 @@ - if @notice #summary %h3 Summary - = render 'notices/summary', :notice => @notice + = render 'notices/summary', :notice => @notice, :problem => @problem #backtrace %h3 Backtrace diff --git a/app/views/notices/_summary.html.haml b/app/views/notices/_summary.html.haml index d628d14..5eb0265 100644 --- a/app/views/notices/_summary.html.haml +++ b/app/views/notices/_summary.html.haml @@ -2,7 +2,7 @@ %table.summary %tr %th Message - %td.main.nowrap= message_graph(notice.problem) + %td.main.nowrap= message_graph(problem) - if notice.request['url'].present? %tr %th URL @@ -15,13 +15,13 @@ %td= notice.created_at.to_s(:micro) %tr %th Similar - %td= notice.problem.notices.count - 1 + %td= problem.notices_count - 1 %tr %th Browser - %td= user_agent_graph(notice.problem) + %td= user_agent_graph(problem) %tr %th Tenant - %td= tenant_graph(notice.problem) + %td= tenant_graph(problem) %tr %th App Server %td= notice.server_environment && notice.server_environment["hostname"] diff --git a/db/migrate/20111019064503_cache_problem_statistics.rb b/db/migrate/20111019064503_cache_problem_statistics.rb new file mode 100644 index 0000000..515db58 --- /dev/null +++ b/db/migrate/20111019064503_cache_problem_statistics.rb @@ -0,0 +1,18 @@ +class CacheProblemStatistics < Mongoid::Migration + def self.up + Problem.all.each do |problem| + problem.notices.each do |notice| + problem.messages << notice.message + problem.hosts << notice.host + problem.user_agents << notice.user_agent_string + end + problem.save! + end + end + + def self.down + Problem.all.each do |problem| + problem.update_attributes(:messages => [], :hosts => [], :user_agents => []) + end + end +end \ No newline at end of file diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 46d88ae..028facc 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -10,18 +10,3 @@ require 'spec_helper' # end # end # end -describe ApplicationHelper do - describe "get_host" do - it "returns host if url is valid" do - helper.get_host("http://example.com/resource/12").should == 'example.com' - end - - it "returns 'N/A' when url is not valid" do - helper.get_host("some string").should == 'N/A' - end - - it "returns 'N/A' when url is empty" do - helper.get_host({}).should == 'N/A' - end - end -end diff --git a/spec/models/notice_spec.rb b/spec/models/notice_spec.rb index 3914dcd..330c1e2 100644 --- a/spec/models/notice_spec.rb +++ b/spec/models/notice_spec.rb @@ -82,6 +82,36 @@ describe Notice do end end + describe "user agent string" do + it "should be parsed and human-readable" do + notice = Factory.build(:notice, :request => {'cgi-data' => {'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16'}}) + notice.user_agent_string.should == 'Chrome 10.0.648.204' + end + + it "should be nil if HTTP_USER_AGENT is blank" do + notice = Factory.build(:notice) + notice.user_agent_string.should == "N/A" + end + end + + describe "host" do + it "returns host if url is valid" do + notice = Factory.build(:notice, :request => {'url' => "http://example.com/resource/12"}) + notice.host.should == 'example.com' + end + + it "returns 'N/A' when url is not valid" do + notice = Factory.build(:notice, :request => {'url' => "some string"}) + notice.host.should == 'N/A' + end + + it "returns 'N/A' when url is empty" do + notice = Factory.build(:notice, :request => {}) + notice.host.should == 'N/A' + end + + end + describe "email notifications (configured individually for each app)" do custom_thresholds = [2, 4, 8, 16, 32, 64] diff --git a/spec/models/problem_spec.rb b/spec/models/problem_spec.rb index 26f4798..e26b6c8 100644 --- a/spec/models/problem_spec.rb +++ b/spec/models/problem_spec.rb @@ -197,5 +197,84 @@ describe Problem do }.should change(problem, :last_deploy_at).from(@last_deploy).to(next_deploy) end end + + context "notice messages cache" do + before do + @app = Factory(:app) + @problem = Factory(:problem, :app => @app) + @err = Factory(:err, :problem => @problem) + end + + it "#messages returns [] by default" do + @problem.messages.should == [] + end + + it "adding a notice adds a string to #messages" do + lambda { + Factory(:notice, :err => @err, :message => 'ERR 1') + }.should change(@problem, :messages).from([]).to(['ERR 1']) + end + + it "removing a notice removes string from #messages" do + notice1 = Factory(:notice, :err => @err, :message => 'ERR 1') + lambda { + @err.notices.first.destroy + @problem.reload + }.should change(@problem, :messages).from(['ERR 1']).to([]) + end + end + + context "notice hosts cache" do + before do + @app = Factory(:app) + @problem = Factory(:problem, :app => @app) + @err = Factory(:err, :problem => @problem) + end + + it "#hosts returns [] by default" do + @problem.hosts.should == [] + end + + it "adding a notice adds a string to #hosts" do + lambda { + Factory(:notice, :err => @err, :request => {'url' => "http://example.com/resource/12"}) + }.should change(@problem, :hosts).from([]).to(['example.com']) + end + + it "removing a notice removes string from #hosts" do + notice1 = Factory(:notice, :err => @err, :request => {'url' => "http://example.com/resource/12"}) + lambda { + @err.notices.first.destroy + @problem.reload + }.should change(@problem, :hosts).from(['example.com']).to([]) + end + end + + context "notice user_agents cache" do + before do + @app = Factory(:app) + @problem = Factory(:problem, :app => @app) + @err = Factory(:err, :problem => @problem) + end + + it "#user_agents returns [] by default" do + @problem.user_agents.should == [] + end + + it "adding a notice adds a string to #user_agents" do + lambda { + Factory(:notice, :err => @err, :request => {'cgi-data' => {'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16'}}) + }.should change(@problem, :user_agents).from([]).to(['Chrome 10.0.648.204']) + end + + it "removing a notice removes string from #user_agents" do + notice1 = Factory(:notice, :err => @err, :request => {'cgi-data' => {'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16'}}) + lambda { + @err.notices.first.destroy + @problem.reload + }.should change(@problem, :user_agents).from(['Chrome 10.0.648.204']).to([]) + end + end + end -- libgit2 0.21.2