airbrake_tools 0.0.4 → 0.0.5

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/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- airbrake_tools (0.0.4)
4
+ airbrake_tools (0.0.5)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
@@ -9,4 +9,5 @@ Gem::Specification.new name, AirbrakeTools::VERSION do |s|
9
9
  s.homepage = "http://github.com/jcheatham/#{name}"
10
10
  s.files = `git ls-files`.split("\n")
11
11
  s.license = "MIT"
12
+ s.executables = ["airbrake-tools"]
12
13
  end
@@ -1,3 +1,3 @@
1
1
  module AirbrakeTools
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -4,6 +4,7 @@ require "airbrake-api"
4
4
 
5
5
  module AirbrakeTools
6
6
  DEFAULT_HOT_PAGES = 1
7
+ DEFAULT_NEW_PAGES = 1
7
8
  DEFAULT_SUMMARY_PAGES = 5
8
9
  DEFAULT_COMPARE_DEPTH = 7
9
10
  DEFAULT_ENVIRONMENT = "production"
@@ -23,37 +24,31 @@ module AirbrakeTools
23
24
 
24
25
  case ARGV[2]
25
26
  when "hot"
26
- errors = hot(options)
27
- print_errors(errors)
27
+ print_errors(hot(options))
28
28
  when "list"
29
29
  list(options)
30
30
  when "summary"
31
31
  summary(ARGV[3] || raise("Need error id"), options)
32
+ when "new"
33
+ print_errors(new(options))
32
34
  else
33
- raise "Unknown command try hot/list/summary"
35
+ raise "Unknown command #{ARGV[2].inspect} try hot/new/list/summary"
34
36
  end
35
37
  return 0
36
38
  end
37
39
 
38
40
  def hot(options = {})
39
- pages = (options[:pages] || DEFAULT_HOT_PAGES).to_i
40
- errors = []
41
- pages.times do |i|
42
- errors.concat(AirbrakeAPI.errors(:page => i+1) || [])
43
- end
44
- select_env!(errors, options)
41
+ errors = errors_with_notices({:pages => DEFAULT_HOT_PAGES}.merge(options))
42
+ errors.sort_by{|e,n,f| f }.reverse
43
+ end
45
44
 
46
- errors = Parallel.map(errors, :in_threads => 10) do |error|
47
- begin
48
- notices = AirbrakeAPI.notices(error.id, :pages => 1, :raw => true).compact
49
- print "."
50
- [error, notices, frequency(notices)]
51
- rescue Faraday::Error::ParsingError
52
- $stderr.puts "Ignoring #{hot_summary(error)}, got 500 from http://#{AirbrakeAPI.account}.airbrake.io/errors/#{error.id}"
53
- end
54
- end.compact
45
+ def new(options = {})
46
+ errors = errors_with_notices({:pages => DEFAULT_NEW_PAGES}.merge(options))
47
+ errors.sort_by{|e,n,f| e.created_at }.reverse
48
+ end
55
49
 
56
- errors.sort_by{|e,n,f| f }.reverse
50
+ def errors_with_notices(options)
51
+ add_notices_to_pages(errors_from_pages(options))
57
52
  end
58
53
 
59
54
  def list(options)
@@ -77,7 +72,11 @@ module AirbrakeTools
77
72
  puts "last day: #{sparkline(notices, :slots => 24, :interval => 60 * 60)}"
78
73
 
79
74
  backtraces = notices.compact.select{|n| n.backtrace }.group_by do |notice|
80
- notice.backtrace.first[1][0..compare_depth]
75
+ if notice.backtrace.is_a?(String) # no backtrace recorded...
76
+ []
77
+ else
78
+ notice.backtrace.first[1][0..compare_depth]
79
+ end
81
80
  end
82
81
 
83
82
  backtraces.sort_by{|_,notices| notices.size }.reverse.each_with_index do |(backtrace, notices), index|
@@ -90,6 +89,27 @@ module AirbrakeTools
90
89
 
91
90
  private
92
91
 
92
+ def add_notices_to_pages(errors)
93
+ Parallel.map(errors, :in_threads => 10) do |error|
94
+ begin
95
+ notices = AirbrakeAPI.notices(error.id, :pages => 1, :raw => true).compact
96
+ print "."
97
+ [error, notices, frequency(notices)]
98
+ rescue Faraday::Error::ParsingError
99
+ $stderr.puts "Ignoring #{hot_summary(error)}, got 500 from http://#{AirbrakeAPI.account}.airbrake.io/errors/#{error.id}"
100
+ end
101
+ end.compact
102
+ end
103
+
104
+ def errors_from_pages(options)
105
+ errors = []
106
+ options[:pages].times do |i|
107
+ errors.concat(AirbrakeAPI.errors(:page => i+1) || [])
108
+ end
109
+ select_env!(errors, options)
110
+ errors
111
+ end
112
+
93
113
  def select_env!(errors, options)
94
114
  errors.select!{|e| e.rails_env == options[:env] || DEFAULT_ENVIRONMENT }
95
115
  end
@@ -100,13 +120,12 @@ module AirbrakeTools
100
120
  end
101
121
  end
102
122
 
123
+ # we only have a limited sample size, so we do not know how many errors occurred in total
103
124
  def frequency(notices)
104
- hour = 60 * 60
105
- sum_of_ages = notices.map { |n| Time.now - n.created_at }.inject(&:+)
106
- average_age = sum_of_ages / notices.size
107
- time_to_error = average_age / notices.size
108
- rate = 1 / time_to_error
109
- (rate * hour).round(1)
125
+ return 0 if notices.empty?
126
+ range = Time.now.to_f - notices.map{ |n| n.created_at.to_f }.min
127
+ errors_per_second = notices.size / range
128
+ (errors_per_second * 60 * 60).round(2) # errors_per_hour
110
129
  end
111
130
 
112
131
  def hot_summary(error)
@@ -129,8 +148,8 @@ module AirbrakeTools
129
148
 
130
149
  Options:
131
150
  BANNER
132
- opts.on("-c NUM", "--compare-depth NUM", Integer, "How deep to compare backtraces in summary (default: #{DEFAULT_COMPARE_DEPTH})") {|s| options[:env] = s }
133
- opts.on("-p NUM", "--pages NUM", Integer, "How maybe pages to iterate over (default: hot:#{DEFAULT_HOT_PAGES} summary:#{DEFAULT_SUMMARY_PAGES})") {|s| options[:env] = s }
151
+ opts.on("-c NUM", "--compare-depth NUM", Integer, "How deep to compare backtraces in summary (default: #{DEFAULT_COMPARE_DEPTH})") {|s| options[:compare_depth] = s }
152
+ opts.on("-p NUM", "--pages NUM", Integer, "How maybe pages to iterate over (default: hot:#{DEFAULT_HOT_PAGES} new:#{DEFAULT_NEW_PAGES} summary:#{DEFAULT_SUMMARY_PAGES})") {|s| options[:pages] = s }
134
153
  opts.on("-e ENV", "--environment ENV", String, "Only show errors from this environment (default: #{DEFAULT_ENVIRONMENT})") {|s| options[:env] = s }
135
154
  opts.on("-h", "--help", "Show this.") { puts opts; exit }
136
155
  opts.on("-v", "--version", "Show Version"){ puts "airbrake-tools #{VERSION}"; exit }
@@ -1,4 +1,5 @@
1
1
  require 'yaml'
2
+ require 'airbrake_tools'
2
3
 
3
4
  ROOT = File.expand_path('../../', __FILE__)
4
5
 
@@ -64,5 +65,45 @@ describe "airbrake-tools" do
64
65
  output.should include("last 2 hours: ")
65
66
  end
66
67
  end
68
+
69
+ describe "newest" do
70
+ it "kinda works" do
71
+ output = airbrake_tools("#{config["subdomain"]} #{config["auth_token"]} new")
72
+ output.should =~ /#\d+\s+\d+\.\d+\/hour\s+total:\d+/
73
+ end
74
+ end
75
+
76
+ describe ".extract_options" do
77
+ it "finds nothing" do
78
+ AirbrakeTools.send(:extract_options, []).should == {}
79
+ end
80
+
81
+ it "finds pages" do
82
+ AirbrakeTools.send(:extract_options, ["--pages", "1"]).should == {:pages => 1}
83
+ end
84
+
85
+ it "finds env" do
86
+ AirbrakeTools.send(:extract_options, ["--environment", "xx"]).should == {:env => "xx"}
87
+ end
88
+
89
+ it "finds compare-depth" do
90
+ AirbrakeTools.send(:extract_options, ["--compare-depth", "1"]).should == {:compare_depth => 1}
91
+ end
92
+ end
93
+
94
+ describe ".frequency" do
95
+ it "calculates for 0" do
96
+ AirbrakeTools.send(:frequency, []).should == 0
97
+ end
98
+
99
+ it "calculates for 1" do
100
+ AirbrakeTools.send(:frequency, [stub(:created_at => Time.now - (60*60))]).should == 1
101
+ end
102
+
103
+ it "calculates for n" do
104
+ # 3 per minute => 180/hour
105
+ AirbrakeTools.send(:frequency, [stub(:created_at => Time.now-60), stub(:created_at => Time.now-40), stub(:created_at => Time.now-20)]).should == 180
106
+ end
107
+ end
67
108
  end
68
109
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: airbrake_tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-20 00:00:00.000000000 Z
12
+ date: 2012-12-27 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description:
15
15
  email: coaxis@gmail.com
16
- executables: []
16
+ executables:
17
+ - airbrake-tools
17
18
  extensions: []
18
19
  extra_rdoc_files: []
19
20
  files: