puma-status 0.1.4 → 1.1

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
  SHA256:
3
- metadata.gz: 3ca2fb63b6be20e85ac281783c213be6227b29a268d98c743ebf5f15c8ce57af
4
- data.tar.gz: 6ba41b60919c9271d47e1e1e94e4d3a951b49f413e51a7a85f9a8fc26bce1d5b
3
+ metadata.gz: 12ca14da024cebc278c903552c9bfe21afccfabec8baea2972ac4b3e7b241ab1
4
+ data.tar.gz: f738a71b63d55124ee43e921f69f9d81295360c9d43925716ed0bb3716751907
5
5
  SHA512:
6
- metadata.gz: 8233f7045c47ff7172990686b5be21178d0b2df078001ddad38572aeafd8127e2a5d0dc70604f75676a58e59fdcd2139674b2f7aa970e169e2f471b833c9af69
7
- data.tar.gz: 0ce03ee787e21a2b5f49a69d4d28089429a0f95fd993dadbb897e90883315c4bc93f2790e3490640362d0cd14c8f982618a8176e7b2111ebb1aca8630655dfe3
6
+ metadata.gz: 4b96bb8f8ebd7776dd6a3b023c533e53638bf63da61efe5e5c9abb9111ff0ad05aa615584ffa3a804e00ddc1d7cc8cabbec46be677044139e97b722a60eb7f62
7
+ data.tar.gz: e136edb4aed4f52dc06ecda5e48b43b4c92e13b79619277c0d63cc4ac960009855ec4a8d475fe8c23721e0164799b6692f4863b506ac2eb7cf5039551d723af6
@@ -1,13 +1,28 @@
1
1
  require 'yaml'
2
2
  require 'json'
3
3
  require 'net_x/http_unix'
4
+ require 'openssl'
4
5
  require 'time'
5
6
  require_relative 'stats'
6
7
 
7
8
  def get_stats(state_file_path)
8
9
  puma_state = YAML.load_file(state_file_path)
9
10
 
10
- client = NetX::HTTPUnix.new(puma_state["control_url"])
11
+ uri = URI.parse(puma_state["control_url"])
12
+
13
+ address = if uri.scheme =~ /unix/i
14
+ [uri.scheme, '://', uri.host, uri.path].join
15
+ else
16
+ [uri.host, uri.path].join
17
+ end
18
+
19
+ client = NetX::HTTPUnix.new(address, uri.port)
20
+
21
+ if uri.scheme =~ /ssl/i
22
+ client.use_ssl = true
23
+ client.verify_mode = OpenSSL::SSL::VERIFY_NONE if ENV['SSL_NO_VERIFY'] == '1'
24
+ end
25
+
11
26
  req = Net::HTTP::Get.new("/stats?token=#{puma_state["control_auth_token"]}")
12
27
  resp = client.request(req)
13
28
  raw_stats = JSON.parse(resp.body)
@@ -36,25 +51,41 @@ def hydrate_stats(stats, puma_state, state_file_path)
36
51
 
37
52
  stats.tap do |s|
38
53
  stats.workers.map do |wstats|
39
- wstats.mem = top_stats[wstats.pid][:mem]
40
- wstats.pcpu = top_stats[wstats.pid][:pcpu]
54
+ wstats.mem = top_stats.dig(wstats.pid, :mem) || 0
55
+ wstats.pcpu = top_stats.dig(wstats.pid, :pcpu) || 0
56
+ wstats.killed = !top_stats.key?(wstats.pid) || (wstats.mem <=0 && wstats.pcpu <= 0)
41
57
  end
42
58
  end
43
59
  end
44
60
 
45
- def display_stats(stats)
46
- master_line = "#{stats.pid} (#{stats.state_file_path}) Uptime: #{seconds_to_human(stats.uptime)} "
47
- master_line += "| Phase: #{stats.phase} " if stats.phase
48
- master_line += "| Load: #{color(75, 50, stats.load, asciiThreadLoad(stats.running_threads, stats.max_threads))}"
61
+ def format_stats(stats)
62
+ master_line = "#{stats.pid} (#{stats.state_file_path}) Uptime: #{seconds_to_human(stats.uptime)}"
63
+ master_line += " | Phase: #{stats.phase}" if stats.phase
49
64
 
50
- puts master_line
65
+ if stats.booting?
66
+ master_line += " #{warn("booting")}"
67
+ else
68
+ master_line += " | Load: #{color(75, 50, stats.load, asciiThreadLoad(stats.running_threads, stats.max_threads))}"
69
+ master_line += " | Req: #{stats.requests_count}" if stats.requests_count
70
+ end
71
+
72
+ output = [master_line] + stats.workers.map do |wstats|
73
+ worker_line = " └ #{wstats.pid.to_s.rjust(5, ' ')} CPU: #{color(75, 50, wstats.pcpu, wstats.pcpu.to_s.rjust(5, ' '))}% Mem: #{color(1000, 750, wstats.mem, wstats.mem.to_s.rjust(4, ' '))} MB Uptime: #{seconds_to_human(wstats.uptime)}"
51
74
 
52
- stats.workers.each do |wstats|
53
- worker_line = " #{wstats.pid.to_s.rjust(5, ' ')} CPU: #{color(75, 50, wstats.pcpu, wstats.pcpu.to_s.rjust(5, ' '))}% Mem: #{color(1000, 750, wstats.mem, wstats.mem.to_s.rjust(4, ' '))} MB Uptime: #{seconds_to_human(wstats.uptime)} | Load: #{color(75, 50, wstats.load, asciiThreadLoad(wstats.running_threads, wstats.max_threads))}"
54
- worker_line += " #{("Queue: " + wstats.backlog.to_s).colorize(:red)}" if wstats.backlog > 0
55
- worker_line += " Last checkin: #{wstats.last_checkin}" if wstats.last_checkin >= 10
56
- worker_line += " Phase: #{wstats.phase}" if wstats.phase != stats.phase
75
+ if wstats.booting?
76
+ worker_line += " #{warn("booting")}"
77
+ elsif wstats.killed?
78
+ worker_line += " #{error("killed")}"
79
+ else
80
+ worker_line += " | Load: #{color(75, 50, wstats.load, asciiThreadLoad(wstats.running_threads, wstats.max_threads))}"
81
+ worker_line += " | Phase: #{error(wstats.phase)}" if wstats.phase != stats.phase
82
+ worker_line += " | Req: #{wstats.requests_count}" if wstats.requests_count
83
+ worker_line += " Queue: #{error(wstats.backlog.to_s)}" if wstats.backlog > 0
84
+ worker_line += " Last checkin: #{error(wstats.last_checkin)}" if wstats.last_checkin >= 10
85
+ end
57
86
 
58
- puts worker_line
87
+ worker_line
59
88
  end
89
+
90
+ output.join("\n")
60
91
  end
@@ -4,17 +4,29 @@ def debug(str)
4
4
  puts str if ENV.key?('DEBUG')
5
5
  end
6
6
 
7
- def color(critical, warn, value, str)
7
+ def warn(str)
8
+ colorize(str, :yellow)
9
+ end
10
+
11
+ def error(str)
12
+ colorize(str, :red)
13
+ end
14
+
15
+ def colorize(str, color_name)
8
16
  return str if ENV.key?('NO_COLOR')
17
+ str.to_s.colorize(color_name)
18
+ end
9
19
 
10
- color = if value >= critical
20
+ def color(critical, warn, value, str = nil)
21
+ str = value unless str
22
+ color_level = if value >= critical
11
23
  :red
12
24
  elsif value < critical && value >= warn
13
25
  :yellow
14
26
  else
15
27
  :green
16
28
  end
17
- str.to_s.colorize(color)
29
+ colorize(str, color_level)
18
30
  end
19
31
 
20
32
  def asciiThreadLoad(idx, total)
@@ -1,16 +1,48 @@
1
1
  require_relative './helpers'
2
2
  require_relative './core.rb'
3
+ require 'parallel'
3
4
 
4
5
  def run
5
6
  debug "puma-status"
6
7
 
7
- if ARGV.count != 1
8
+ if ARGV.count < 1
8
9
  puts "Call with:"
9
10
  puts "\tpuma-status path/to/puma.state"
10
11
  exit -1
11
12
  end
12
13
 
13
- state_file_path = ARGV[0]
14
- debug "State file: #{state_file_path}"
15
- display_stats(get_stats(state_file_path))
14
+ errors = []
15
+
16
+ outputs = Parallel.map(ARGV, in_threads: ARGV.count) do |state_file_path|
17
+ begin
18
+ debug "State file: #{state_file_path}"
19
+ format_stats(get_stats(state_file_path))
20
+ rescue Errno::ENOENT => e
21
+ if e.message =~ /#{state_file_path}/
22
+ errors << "#{warn(state_file_path)} doesn't exists"
23
+ elsif e.message =~ /connect\(2\) for [^\/]/
24
+ errors << "#{warn("Relative Unix socket")}: the Unix socket of the control app has a relative path. Please, ensure you are running from the same folder has puma."
25
+ else
26
+ errors << "#{error(state_file_path)} an unhandled error occured: #{e.inspect}"
27
+ end
28
+ nil
29
+ rescue Errno::EISDIR => e
30
+ if e.message =~ /#{state_file_path}/
31
+ errors << "#{warn(state_file_path)} isn't a state file"
32
+ else
33
+ errors << "#{error(state_file_path)} an unhandled error occured: #{e.inspect}"
34
+ end
35
+ nil
36
+ rescue => e
37
+ errors << "#{error(state_file_path)} an unhandled error occured: #{e.inspect}"
38
+ nil
39
+ end
40
+ end
41
+
42
+ outputs.compact.each { |output| puts output }
43
+
44
+ if errors.any?
45
+ puts ""
46
+ errors.each { |error| puts error }
47
+ end
16
48
  end
@@ -9,6 +9,14 @@ class Stats
9
9
  @wstats['pid']
10
10
  end
11
11
 
12
+ def killed=(killed)
13
+ @wstats['killed'] = killed
14
+ end
15
+
16
+ def killed?
17
+ !!@wstats['killed']
18
+ end
19
+
12
20
  def mem=(mem)
13
21
  @wstats['mem'] = mem
14
22
  end
@@ -25,6 +33,10 @@ class Stats
25
33
  @wstats['pcpu']
26
34
  end
27
35
 
36
+ def booting?
37
+ @wstats.key?('last_status') && @wstats['last_status'].empty?
38
+ end
39
+
28
40
  def running
29
41
  @wstats.dig('last_status', 'running') || @wstats['running'] || 0
30
42
  end
@@ -55,6 +67,10 @@ class Stats
55
67
  (Time.now - Time.parse(@wstats['started_at'])).to_i
56
68
  end
57
69
 
70
+ def requests_count
71
+ @wstats.dig('last_status', 'requests_count') || @wstats['requests_count']
72
+ end
73
+
58
74
  def backlog
59
75
  @wstats.dig('last_status', 'backlog') || 0
60
76
  end
@@ -71,7 +87,7 @@ class Stats
71
87
  end
72
88
 
73
89
  def workers
74
- (@stats['worker_status'] || [@stats]).map { |wstats| Worker.new(wstats) }
90
+ @workers ||= (@stats['worker_status'] || [@stats]).map { |wstats| Worker.new(wstats) }
75
91
  end
76
92
 
77
93
  def pid=(pid)
@@ -95,6 +111,10 @@ class Stats
95
111
  (Time.now - Time.parse(@stats['started_at'])).to_i
96
112
  end
97
113
 
114
+ def booting?
115
+ workers.all?(&:booting?)
116
+ end
117
+
98
118
  def total_threads
99
119
  workers.reduce(0) { |total, wstats| total + wstats.max_threads }
100
120
  end
@@ -107,6 +127,12 @@ class Stats
107
127
  workers.reduce(0) { |total, wstats| total + wstats.max_threads }
108
128
  end
109
129
 
130
+ def requests_count
131
+ workers_with_requests_count = workers.select(&:requests_count)
132
+ return if workers_with_requests_count.none?
133
+ workers_with_requests_count.reduce(0) { |total, wstats| total + wstats.requests_count }
134
+ end
135
+
110
136
  def running
111
137
  @stats['running'] || 0
112
138
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma-status
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: '1.1'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yoann Lecuyer
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: parallel
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rspec
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -105,14 +119,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
105
119
  requirements:
106
120
  - - ">="
107
121
  - !ruby/object:Gem::Version
108
- version: '0'
122
+ version: 2.3.0
109
123
  required_rubygems_version: !ruby/object:Gem::Requirement
110
124
  requirements:
111
125
  - - ">="
112
126
  - !ruby/object:Gem::Version
113
127
  version: '0'
114
128
  requirements: []
115
- rubygems_version: 3.0.3
129
+ rubygems_version: 3.0.0
116
130
  signing_key:
117
131
  specification_version: 4
118
132
  summary: Command-line tool for puma to display information about running request/process