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 +4 -4
- data/lib/core.rb +45 -14
- data/lib/helpers.rb +15 -3
- data/lib/puma-status.rb +36 -4
- data/lib/stats.rb +27 -1
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 12ca14da024cebc278c903552c9bfe21afccfabec8baea2972ac4b3e7b241ab1
|
4
|
+
data.tar.gz: f738a71b63d55124ee43e921f69f9d81295360c9d43925716ed0bb3716751907
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4b96bb8f8ebd7776dd6a3b023c533e53638bf63da61efe5e5c9abb9111ff0ad05aa615584ffa3a804e00ddc1d7cc8cabbec46be677044139e97b722a60eb7f62
|
7
|
+
data.tar.gz: e136edb4aed4f52dc06ecda5e48b43b4c92e13b79619277c0d63cc4ac960009855ec4a8d475fe8c23721e0164799b6692f4863b506ac2eb7cf5039551d723af6
|
data/lib/core.rb
CHANGED
@@ -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
|
-
|
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
|
40
|
-
wstats.pcpu = top_stats
|
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
|
46
|
-
master_line = "#{stats.pid} (#{stats.state_file_path}) Uptime: #{seconds_to_human(stats.uptime)}
|
47
|
-
master_line += "| Phase: #{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
|
-
|
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
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
87
|
+
worker_line
|
59
88
|
end
|
89
|
+
|
90
|
+
output.join("\n")
|
60
91
|
end
|
data/lib/helpers.rb
CHANGED
@@ -4,17 +4,29 @@ def debug(str)
|
|
4
4
|
puts str if ENV.key?('DEBUG')
|
5
5
|
end
|
6
6
|
|
7
|
-
def
|
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
|
-
|
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
|
-
|
29
|
+
colorize(str, color_level)
|
18
30
|
end
|
19
31
|
|
20
32
|
def asciiThreadLoad(idx, total)
|
data/lib/puma-status.rb
CHANGED
@@ -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
|
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
|
-
|
14
|
-
|
15
|
-
|
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
|
data/lib/stats.rb
CHANGED
@@ -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:
|
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:
|
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.
|
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
|