airbrake_tools 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/airbrake_tools.gemspec +1 -0
- data/lib/airbrake_tools/version.rb +1 -1
- data/lib/airbrake_tools.rb +47 -28
- data/spec/airbrake_tools_spec.rb +41 -0
- metadata +4 -3
data/Gemfile.lock
CHANGED
data/airbrake_tools.gemspec
CHANGED
data/lib/airbrake_tools.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
40
|
-
errors
|
41
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
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.
|
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
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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[:
|
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[:
|
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 }
|
data/spec/airbrake_tools_spec.rb
CHANGED
@@ -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
|
+
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-
|
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:
|