airbrake_tools 1.1.6 → 1.2.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f6e685b460c2432e490b9a535f28aba359672c81
4
- data.tar.gz: a1e69905a9b4534d24bf595cbada9ac18cc1a103
3
+ metadata.gz: 36fac1e64067233ec999c77a709664e713fb3d1e
4
+ data.tar.gz: a4ab31edbfca13e3c5bda4a7f5e4bce73662db05
5
5
  SHA512:
6
- metadata.gz: 661d9e8ab2d8fcd3fa17896c31a32ea1af4e852f2551f2748fa0ca2e381569f9002dad7133f3b41bc5ddfa04aea07d5f504a8145df1b3efeb6b8d22bcb485555
7
- data.tar.gz: 71a7e83db45ae534f9e034821b4ded8d3e689e458112488adca1d4c113eb7b3fcef3042ec9674d6487438323e18793e3945a67a54266e3ef6601500c81043c02
6
+ metadata.gz: 91ca4a6fd975468cce389a972ce4823d0e1011d6165e49abc14edbd9adcd418051e98d17df9f8fb5c44dc40528d371948a801e033a72ef3e1083ca2c9e8d4e84
7
+ data.tar.gz: 0ebcf50c02b4652fb2da1e4f834ff00fc7c9ec5ac0ea72f9b4238ad7ba81b15a6069af1436fe8b50adfaadb64928c73262d9a76c30fbed24d6b4c53ac6d5a7c4
@@ -1,18 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- airbrake_tools (1.1.6)
5
- airbrake-api (>= 4.5.1)
4
+ airbrake_tools (1.2.0)
5
+ json
6
+ parallel
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
10
11
  addressable (2.3.6)
11
- airbrake-api (4.6.0)
12
- faraday_middleware
13
- hashie
14
- multi_xml
15
- parallel
16
12
  bump (0.3.8)
17
13
  byebug (3.5.1)
18
14
  columnize (~> 0.8)
@@ -21,16 +17,10 @@ GEM
21
17
  columnize (0.8.9)
22
18
  debugger-linecache (1.2.0)
23
19
  diff-lcs (1.1.3)
24
- faraday (0.9.1)
25
- multipart-post (>= 1.2, < 3)
26
- faraday_middleware (0.9.1)
27
- faraday (>= 0.7.4, < 0.10)
28
- hashie (3.4.0)
20
+ json (1.8.2)
29
21
  launchy (2.4.2)
30
22
  addressable (~> 2.3)
31
- multi_xml (0.5.5)
32
- multipart-post (2.0.0)
33
- parallel (1.4.1)
23
+ parallel (1.6.0)
34
24
  rake (0.9.2.2)
35
25
  rspec (2.11.0)
36
26
  rspec-core (~> 2.11.0)
@@ -11,5 +11,6 @@ Gem::Specification.new name, AirbrakeTools::VERSION do |s|
11
11
  s.bindir = 'bin'
12
12
  s.license = "MIT"
13
13
  s.executables = ["airbrake-tools"]
14
- s.add_runtime_dependency "airbrake-api", ">= 4.5.1"
14
+ s.add_runtime_dependency "json"
15
+ s.add_runtime_dependency "parallel"
15
16
  end
@@ -1,13 +1,19 @@
1
1
  require "airbrake_tools/version"
2
- require "airbrake-api"
2
+ require "json"
3
+ require "ostruct"
4
+ require "net/http"
5
+ require "net/https"
6
+ require "time"
7
+ require "parallel"
3
8
 
4
9
  module AirbrakeTools
5
10
  DEFAULT_HOT_PAGES = 1
6
11
  DEFAULT_NEW_PAGES = 1
7
- DEFAULT_LIST_PAGES = 10
8
- DEFAULT_SUMMARY_PAGES = 10
12
+ DEFAULT_LIST_PAGES = 1 # TODO 10 once pagination is not broken :/
13
+ DEFAULT_SUMMARY_PAGES = 1 # TODO 10 once pagination is not broken :/
9
14
  DEFAULT_COMPARE_DEPTH_ADDITION = 3 # first line in project is 6 -> compare at 6 + x depth
10
15
  DEFAULT_ENVIRONMENT = "production"
16
+ PER_PAGE = 20
11
17
  COLORS = {
12
18
  :gray => "\e[0;37m",
13
19
  :green => "\e[0;32m",
@@ -20,17 +26,15 @@ module AirbrakeTools
20
26
  def cli(argv)
21
27
  options = extract_options(argv)
22
28
 
23
- AirbrakeAPI.account = ARGV[0]
24
- AirbrakeAPI.auth_token = ARGV[1]
25
- AirbrakeAPI.secure = true
26
-
27
- options[:project_id] = project_id(options.delete(:project_name)) if options[:project_name]
28
-
29
- if AirbrakeAPI.account.to_s.empty? || AirbrakeAPI.auth_token.to_s.empty?
29
+ # TODO get rid of argument 0
30
+ @token = ARGV[1]
31
+ if @token.to_s.empty?
30
32
  puts "Usage instructions: airbrake-tools --help"
31
33
  return 1
32
34
  end
33
35
 
36
+ options[:project_id] = project_id(options.delete(:project_name)) if options[:project_name]
37
+
34
38
  case ARGV[2]
35
39
  when "hot"
36
40
  print_errors(hot(options))
@@ -52,7 +56,7 @@ module AirbrakeTools
52
56
  errors = Array(options[:project_id] || projects.map(&:id)).flat_map do |project_id|
53
57
  errors_with_notices({pages: DEFAULT_HOT_PAGES, project_id: project_id}.merge(options))
54
58
  end
55
- errors.sort_by{|_,_,f| f }.reverse[0...AirbrakeAPI::Client::PER_PAGE]
59
+ errors.sort_by{|_,_,f| f }.reverse[0...PER_PAGE]
56
60
  end
57
61
 
58
62
  def new(options = {})
@@ -62,15 +66,15 @@ module AirbrakeTools
62
66
  end
63
67
 
64
68
  def errors_with_notices(options)
65
- add_notices_to_pages(errors_from_pages(options))
69
+ add_notices_to_pages(options.fetch(:project_id), errors_from_pages(options))
66
70
  end
67
71
 
68
72
  def list(options)
69
73
  need_project_id!(options)
70
74
  list_pages = (options[:pages] ? options[:pages] : DEFAULT_LIST_PAGES)
71
75
  page = 1
72
- while page <= list_pages && errors = AirbrakeAPI.errors(page: page, project_id: options.fetch(:project_id))
73
- select_env(errors, options).each do |error|
76
+ while page <= list_pages && errors = airbrake_errors(options.fetch(:project_id), page, options)
77
+ errors.each do |error|
74
78
  puts "#{error.id} -- #{error.error_class} -- #{error.error_message} -- #{error.created_at}"
75
79
  end
76
80
  $stderr.puts "Page #{page} ----------\n"
@@ -79,7 +83,7 @@ module AirbrakeTools
79
83
  end
80
84
 
81
85
  def summary(error_id, options)
82
- notices = AirbrakeAPI.notices(error_id, :pages => options[:pages] || DEFAULT_SUMMARY_PAGES)
86
+ notices = notices_from_pages(options.fetch(:project_id), error_id, options[:pages] || DEFAULT_SUMMARY_PAGES)
83
87
 
84
88
  puts "last retrieved notice: #{((Time.now - notices.last.created_at) / (60 * 60)).round} hours ago at #{notices.last.created_at}"
85
89
  puts "last 2 hours: #{sparkline(notices, :slots => 60, :interval => 120)}"
@@ -143,25 +147,20 @@ module AirbrakeTools
143
147
  end
144
148
 
145
149
  def grouped_backtraces(notices, options)
146
- notices = notices.compact.select { |n| backtrace(n) }
150
+ notices = notices.compact.select { |n| n.backtrace.any? }
147
151
 
148
152
  compare_depth = if options[:compare_depth]
149
153
  options[:compare_depth]
150
154
  else
151
- average_first_project_line(notices.map { |n| backtrace(n) }) +
155
+ average_first_project_line(notices.map { |n| n.backtrace }) +
152
156
  DEFAULT_COMPARE_DEPTH_ADDITION
153
157
  end
154
158
 
155
159
  notices.group_by do |notice|
156
- backtrace(notice)[0..compare_depth]
160
+ notice.backtrace[0..compare_depth]
157
161
  end
158
162
  end
159
163
 
160
- def backtrace(notice)
161
- return if notice.backtrace.is_a?(String)
162
- [*notice.backtrace.first[1]] # can be string or array
163
- end
164
-
165
164
  def average_first_project_line(backtraces)
166
165
  depths = backtraces.map do |backtrace|
167
166
  backtrace.index { |line| custom_file?(line) }
@@ -174,15 +173,13 @@ module AirbrakeTools
174
173
  line.start_with?("[PROJECT_ROOT]") && !line.start_with?("[PROJECT_ROOT]/vendor/")
175
174
  end
176
175
 
177
- def add_notices_to_pages(errors)
176
+ def add_notices_to_pages(project_id, errors)
178
177
  Parallel.map(errors, :in_threads => 10) do |error|
179
178
  begin
180
179
  pages = 1
181
- notices = AirbrakeAPI.notices(error.id, pages: pages, raw: true).compact
180
+ notices = notices_from_pages(project_id, error.id, pages).compact
182
181
  print "."
183
- [error, notices, frequency(notices, pages * AirbrakeAPI::Client::PER_PAGE)]
184
- rescue Faraday::Error::ParsingError
185
- $stderr.puts "Ignoring #{hot_summary(error)}, got 500 from http://#{AirbrakeAPI.account}.airbrake.io/errors/#{error.id}"
182
+ [error, notices, frequency(notices, pages * PER_PAGE)]
186
183
  rescue Exception => e
187
184
  puts "Ignoring exception <<#{e}>>, most likely bad data from airbrake"
188
185
  end
@@ -192,13 +189,17 @@ module AirbrakeTools
192
189
  def errors_from_pages(options)
193
190
  errors = []
194
191
  options[:pages].times do |i|
195
- errors.concat(AirbrakeAPI.errors(:page => i+1, :project_id => options[:project_id]) || [])
192
+ errors.concat(airbrake_errors(options[:project_id], i+1, options))
196
193
  end
197
- select_env(errors, options)
194
+ errors
198
195
  end
199
196
 
200
- def select_env(errors, options)
201
- errors.select{|e| e.rails_env == (options[:env] || DEFAULT_ENVIRONMENT) }
197
+ def notices_from_pages(project_id, error_id, pages)
198
+ notices = []
199
+ pages.times do |i|
200
+ notices.concat(airbrake_notices(project_id, error_id, i+1))
201
+ end
202
+ notices
202
203
  end
203
204
 
204
205
  def print_errors(hot)
@@ -273,15 +274,77 @@ module AirbrakeTools
273
274
  `#{File.expand_path('../../spark.sh',__FILE__)} #{sparkline_data(notices, options).join(" ")}`.strip
274
275
  end
275
276
 
276
- def projects
277
- @projects ||= AirbrakeAPI.projects
278
- end
279
-
280
277
  def project_id(project_name)
281
278
  return project_name.to_i if project_name =~ /^\d+$/
282
279
  project = projects.detect { |p| p.name == project_name }
283
280
  raise "project with name #{project_name} not found try #{projects.map(&:name).join(", ")}" unless project
284
281
  project.id
285
282
  end
283
+
284
+ def projects
285
+ @projects ||= begin
286
+ response = make_request("https://airbrake.io/api/v3/projects?key=#{@token}")
287
+ case response.code.to_i
288
+ when 200..299
289
+ JSON.parse(response.body)["projects"].compact.map do |raw|
290
+ OpenStruct.new(
291
+ :id => raw["id"].to_s,
292
+ :name => raw["name"]
293
+ )
294
+ end.sort_by{|p| p[:name].to_s.downcase }
295
+ else
296
+ raise "ERROR - Bad response for http://airbrake.io/api/v3/projects - #{response.code} - #{response.message}"
297
+ end
298
+ end
299
+ end
300
+
301
+ def airbrake_errors(project_id, page, options)
302
+ response = make_request("https://airbrake.io/api/v3/projects/#{project_id}/groups?key=#{@token}&page=#{page}&environment=#{options[:env] || DEFAULT_ENVIRONMENT}&resolved=false")
303
+ case response.code.to_i
304
+ when 200..299
305
+ JSON.parse(response.body)["groups"].compact.map do |raw|
306
+ OpenStruct.new(
307
+ :id => raw["id"].to_s,
308
+ :project_id => raw["projectId"].to_s,
309
+ :env => raw["environment"],
310
+ :count => raw["noticeCount"],
311
+ :created_at => Time.parse(raw["createdAt"]),
312
+ :most_recent => Time.parse(raw["lastNoticeAt"]),
313
+ :error_message => raw["errors"][0]["message"].to_s,
314
+ :error_class => raw["errors"][0]["type"].to_s
315
+ )
316
+ end
317
+ else
318
+ puts "ERROR - Bad response for http://airbrake.io/api/v3/projects/#{project_id}/groups - #{response.code} - #{response.message}"
319
+ end
320
+ end
321
+
322
+ def airbrake_notices(project_id, error_id, page=1)
323
+ response = make_request("https://airbrake.io/api/v3/projects/#{project_id}/groups/#{error_id}/notices?key=#{@token}&page=#{page}")
324
+ case response.code.to_i
325
+ when 200..299
326
+ JSON.parse(response.body)["notices"].compact.map do |raw|
327
+ OpenStruct.new(
328
+ :id => raw["id"].to_s,
329
+ :created_at => Time.parse(raw["createdAt"]),
330
+ :message => raw["errors"][0]["message"].to_s,
331
+ :backtrace => (raw["errors"].first['backtrace'] || []).map { |l| "#{l["file"]}:#{l["line"]}" }
332
+ )
333
+ end
334
+ else
335
+ raise "ERROR - Bad response for http://airbrake.io/api/v3/projects/#{project_id}/groups/#{error_id}/notices - #{response.code} - #{response.message}"
336
+ end
337
+ end
338
+
339
+ def make_request(url)
340
+ # stolen from https://github.com/bf4/airbrake_client/blob/master/airbrake_client.rb
341
+ uri = URI(url)
342
+ http = Net::HTTP.new(uri.host, uri.port)
343
+ if http.use_ssl = (uri.scheme == 'https')
344
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
345
+ end
346
+ request = Net::HTTP::Get.new(uri.request_uri)
347
+ http.request(request)
348
+ end
286
349
  end
287
350
  end
@@ -1,3 +1,3 @@
1
1
  module AirbrakeTools
2
- VERSION = "1.1.6"
2
+ VERSION = "1.2.0"
3
3
  end
metadata CHANGED
@@ -1,29 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: airbrake_tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.6
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Cheatham
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-14 00:00:00.000000000 Z
11
+ date: 2015-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: airbrake-api
14
+ name: json
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 4.5.1
19
+ version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 4.5.1
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: parallel
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  description:
28
42
  email: coaxis@gmail.com
29
43
  executables: