airbrake_tools 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +4 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +44 -0
- data/Rakefile +6 -0
- data/Readme.md +18 -0
- data/airbrake_tools.gemspec +12 -0
- data/lib/airbrake_tools/version.rb +3 -0
- data/lib/airbrake_tools.rb +117 -0
- data/spec/airbrake_tools_spec.rb +52 -0
- data/spec/spec_helper.rb +1 -0
- metadata +55 -0
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
airbrake_tools (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
airbrake-api (4.2.2)
|
10
|
+
faraday_middleware (~> 0.8)
|
11
|
+
hashie
|
12
|
+
mash
|
13
|
+
multi_xml
|
14
|
+
parallel (~> 0.5.0)
|
15
|
+
bump (0.3.5)
|
16
|
+
diff-lcs (1.1.3)
|
17
|
+
faraday (0.8.4)
|
18
|
+
multipart-post (~> 1.1)
|
19
|
+
faraday_middleware (0.9.0)
|
20
|
+
faraday (>= 0.7.4, < 0.9)
|
21
|
+
hashie (1.2.0)
|
22
|
+
mash (0.1.1)
|
23
|
+
multi_xml (0.5.1)
|
24
|
+
multipart-post (1.1.5)
|
25
|
+
parallel (0.5.19)
|
26
|
+
rake (0.9.2.2)
|
27
|
+
rspec (2.11.0)
|
28
|
+
rspec-core (~> 2.11.0)
|
29
|
+
rspec-expectations (~> 2.11.0)
|
30
|
+
rspec-mocks (~> 2.11.0)
|
31
|
+
rspec-core (2.11.1)
|
32
|
+
rspec-expectations (2.11.3)
|
33
|
+
diff-lcs (~> 1.1.3)
|
34
|
+
rspec-mocks (2.11.3)
|
35
|
+
|
36
|
+
PLATFORMS
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
airbrake-api (>= 4.2.2)
|
41
|
+
airbrake_tools!
|
42
|
+
bump
|
43
|
+
rake
|
44
|
+
rspec (~> 2)
|
data/Rakefile
ADDED
data/Readme.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Power tools for Airbrake
|
2
|
+
|
3
|
+
Install
|
4
|
+
=======
|
5
|
+
|
6
|
+
gem install airbrake_tools
|
7
|
+
|
8
|
+
Usage
|
9
|
+
=====
|
10
|
+
|
11
|
+
CODE EXAMPLE
|
12
|
+
|
13
|
+
Author
|
14
|
+
======
|
15
|
+
[Jonathan Cheatham](http://github.com/jcheatham)<br/>
|
16
|
+
coaxis@gmail.com<br/>
|
17
|
+
License: MIT<br/>
|
18
|
+
[![Build Status](https://travis-ci.org/jcheatham/airbrake_tools.png)](https://travis-ci.org/jcheatham/airbrake_tools)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
2
|
+
name = "airbrake_tools"
|
3
|
+
require "#{name}/version"
|
4
|
+
|
5
|
+
Gem::Specification.new name, AirbrakeTools::VERSION do |s|
|
6
|
+
s.summary = "Power tools for Airbrake"
|
7
|
+
s.authors = ["Jonathan Cheatham"]
|
8
|
+
s.email = "coaxis@gmail.com"
|
9
|
+
s.homepage = "http://github.com/jcheatham/#{name}"
|
10
|
+
s.files = `git ls-files`.split("\n")
|
11
|
+
s.license = "MIT"
|
12
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require "airbrake_tools/version"
|
2
|
+
require "airbrake-api"
|
3
|
+
|
4
|
+
module AirbrakeTools
|
5
|
+
class << self
|
6
|
+
def cli(argv)
|
7
|
+
options = extract_options(argv)
|
8
|
+
|
9
|
+
AirbrakeAPI.account = ARGV[0]
|
10
|
+
AirbrakeAPI.auth_token = ARGV[1]
|
11
|
+
AirbrakeAPI.secure = true
|
12
|
+
|
13
|
+
if AirbrakeAPI.account.to_s.empty? || AirbrakeAPI.auth_token.to_s.empty?
|
14
|
+
puts "Usage instructions: airbrake-tools --help"
|
15
|
+
return 1
|
16
|
+
end
|
17
|
+
|
18
|
+
hot(options) || 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def hot(options)
|
22
|
+
puts "Calling hot with #{AirbrakeAPI.account}, #{AirbrakeAPI.auth_token}, #{options.inspect}"
|
23
|
+
|
24
|
+
pages = 1
|
25
|
+
|
26
|
+
errors = []
|
27
|
+
pages.times do |i|
|
28
|
+
errors.concat (AirbrakeAPI.errors(:page => i+1) || []).select{|e| e.rails_env == "production" }
|
29
|
+
end
|
30
|
+
|
31
|
+
errors = Parallel.map(errors, :in_threads => 10) do |error|
|
32
|
+
begin
|
33
|
+
notices = AirbrakeAPI.notices(error.id, :pages => 1, :raw => true).compact
|
34
|
+
print "."
|
35
|
+
[error, notices]
|
36
|
+
rescue Faraday::Error::ParsingError
|
37
|
+
$stderr.puts "Ignoring #{summary(error)}, got 500 from http://#{AirbrakeAPI.account}.airbrake.io/errors/#{error.id}"
|
38
|
+
end
|
39
|
+
end.compact
|
40
|
+
|
41
|
+
$stderr.puts
|
42
|
+
|
43
|
+
errors.sort_by{|e,n| frequency(n) }.reverse.each_with_index do |(error, notices), index|
|
44
|
+
puts "##{(index+1).to_s.ljust(2)} #{frequency(notices).to_s.rjust(8)}/hour #{error.notices_count.to_s.rjust(6)}:total #{sparkline(notices, :slots => 60, :interval => 60)}"
|
45
|
+
puts " --> id: #{error.id} -- first: #{error.created_at} -- #{error.error_class} -- #{error.error_message}"
|
46
|
+
end
|
47
|
+
|
48
|
+
return 0
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def frequency(notices)
|
54
|
+
hour = 60 * 60
|
55
|
+
sum_of_ages = notices.map { |n| Time.now - n.created_at }.inject(&:+)
|
56
|
+
average_age = sum_of_ages / notices.size
|
57
|
+
time_to_error = average_age / notices.size
|
58
|
+
rate = 1 / time_to_error
|
59
|
+
(rate * hour).round(1)
|
60
|
+
end
|
61
|
+
|
62
|
+
def summary(error)
|
63
|
+
"id:#{error.id} -- first:#{error.created_at} -- #{error.error_class} -- #{error.error_message}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def extract_options(argv)
|
67
|
+
options = {
|
68
|
+
}
|
69
|
+
OptionParser.new do |opts|
|
70
|
+
opts.banner = <<-BANNER.gsub(" "*12, "")
|
71
|
+
Get the hotest airbrake errors
|
72
|
+
|
73
|
+
Usage:
|
74
|
+
airbrake-tools subdomain token [options]
|
75
|
+
token: go to airbrake -> settings, copy your auth token
|
76
|
+
|
77
|
+
Options:
|
78
|
+
BANNER
|
79
|
+
opts.on("-h", "--help", "Show this.") { puts opts; exit }
|
80
|
+
opts.on("-v", "--version", "Show Version"){ puts "airbrake-tools #{VERSION}"; exit }
|
81
|
+
end.parse!(argv)
|
82
|
+
options
|
83
|
+
end
|
84
|
+
|
85
|
+
def run(cmd)
|
86
|
+
all = ""
|
87
|
+
puts cmd
|
88
|
+
IO.popen(cmd) do |pipe|
|
89
|
+
while str = pipe.gets
|
90
|
+
all << str
|
91
|
+
puts str
|
92
|
+
end
|
93
|
+
end
|
94
|
+
[$?.success?, all]
|
95
|
+
end
|
96
|
+
|
97
|
+
def run!(command)
|
98
|
+
raise "Command failed #{command}" unless run(command).first
|
99
|
+
end
|
100
|
+
|
101
|
+
def sparkline_data(notices, options)
|
102
|
+
last = notices.last.created_at
|
103
|
+
now = Time.now
|
104
|
+
Array.new(options[:slots]).each_with_index.map do |_, i|
|
105
|
+
slot_end = now - (i * options[:interval])
|
106
|
+
slot_start = slot_end - 1 * options[:interval]
|
107
|
+
next if last > slot_end # do not show empty lines when we actually have no data
|
108
|
+
notices.select { |n| n.created_at.between?(slot_start, slot_end) }.size
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def sparkline(notices, options)
|
113
|
+
`#{File.expand_path('../../spark.sh',__FILE__)} #{sparkline_data(notices, options).join(" ")}`.strip
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
ROOT = File.expand_path('../../', __FILE__)
|
4
|
+
|
5
|
+
describe "airbrake-tools" do
|
6
|
+
def run(command, options={})
|
7
|
+
result = `#{command} 2>&1`
|
8
|
+
message = (options[:fail] ? "SUCCESS BUT SHOULD FAIL" : "FAIL")
|
9
|
+
raise "[#{message}] #{result} [#{command}]" if $?.success? == !!options[:fail]
|
10
|
+
result
|
11
|
+
end
|
12
|
+
|
13
|
+
def airbrake_tools(args, options={})
|
14
|
+
run "#{ROOT}/bin/airbrake-tools #{args}", options
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:config) { YAML.load(File.read("spec/fixtures.yml")) }
|
18
|
+
|
19
|
+
before do
|
20
|
+
Dir.chdir ROOT
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "basics" do
|
24
|
+
it "shows its usage without arguments" do
|
25
|
+
airbrake_tools("", :fail => true).should include("Usage")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "shows its usage with -h" do
|
29
|
+
airbrake_tools("-h").should include("Usage")
|
30
|
+
end
|
31
|
+
|
32
|
+
it "shows its usage with --help" do
|
33
|
+
airbrake_tools("--help").should include("Usage")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "shows its version with -v" do
|
37
|
+
airbrake_tools("-v").should =~ /^airbrake-tools \d+\.\d+\.\d+$/
|
38
|
+
end
|
39
|
+
|
40
|
+
it "shows its version with --version" do
|
41
|
+
airbrake_tools("-v").should =~ /^airbrake-tools \d+\.\d+\.\d+$/
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "hot" do
|
46
|
+
it "kinda works" do
|
47
|
+
output = airbrake_tools("#{config["subdomain"]} #{config["auth_token"]}")
|
48
|
+
output.should =~ /#\d+\s+\d+\.\d+\/hour\s+\d+:total/
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "airbrake_tools"
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: airbrake_tools
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jonathan Cheatham
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-07 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description:
|
15
|
+
email: coaxis@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- .travis.yml
|
21
|
+
- Gemfile
|
22
|
+
- Gemfile.lock
|
23
|
+
- Rakefile
|
24
|
+
- Readme.md
|
25
|
+
- airbrake_tools.gemspec
|
26
|
+
- lib/airbrake_tools.rb
|
27
|
+
- lib/airbrake_tools/version.rb
|
28
|
+
- spec/airbrake_tools_spec.rb
|
29
|
+
- spec/spec_helper.rb
|
30
|
+
homepage: http://github.com/jcheatham/airbrake_tools
|
31
|
+
licenses:
|
32
|
+
- MIT
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 1.8.23
|
52
|
+
signing_key:
|
53
|
+
specification_version: 3
|
54
|
+
summary: Power tools for Airbrake
|
55
|
+
test_files: []
|