From ce75029fc2621a11e0d2cf7e4559549017d89c72 Mon Sep 17 00:00:00 2001 From: Yoann Lecuyer Date: Sat, 25 Jan 2020 16:03:31 +0100 Subject: [PATCH 1/7] Better output + request count --- lib/core.rb | 30 +++++++++++++++++++++++------- lib/helpers.rb | 3 ++- lib/stats.rb | 20 ++++++++++++++++++++ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/lib/core.rb b/lib/core.rb index 613eca1..783920e 100644 --- a/lib/core.rb +++ b/lib/core.rb @@ -36,8 +36,9 @@ def hydrate_stats(stats, puma_state, state_file_path) stats.tap do |s| stats.workers.map do |wstats| - wstats.mem = top_stats[wstats.pid][:mem] - wstats.pcpu = top_stats[wstats.pid][:pcpu] + wstats.mem = top_stats.dig(wstats.pid, :mem) || 0 + wstats.pcpu = top_stats.dig(wstats.pid, :pcpu) || 0 + wstats.killed = !top_stats.key?(wstats.pid) || (wstats.mem <=0 && wstats.pcpu <= 0) end end end @@ -45,13 +46,28 @@ def hydrate_stats(stats, puma_state, state_file_path) def format_stats(stats) master_line = "#{stats.pid} (#{stats.state_file_path}) Uptime: #{seconds_to_human(stats.uptime)} " master_line += "| Phase: #{stats.phase} " if stats.phase - master_line += "| Load: #{color(75, 50, stats.load, asciiThreadLoad(stats.running_threads, stats.max_threads))}" + + if stats.booting? + master_line += " #{warn("booting")}" + else + master_line += "| Load: #{color(75, 50, stats.load, asciiThreadLoad(stats.running_threads, stats.max_threads))}" + end output = [master_line] + stats.workers.map do |wstats| - 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))}" - worker_line += " #{("Queue: " + wstats.backlog.to_s).colorize(:red)}" if wstats.backlog > 0 - worker_line += " Last checkin: #{wstats.last_checkin}" if wstats.last_checkin >= 10 - worker_line += " Phase: #{wstats.phase}" if wstats.phase != stats.phase + 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)}" + + if wstats.booting? + worker_line += " #{warn("booting")}" + elsif wstats.killed? + worker_line += " #{error("killed")}" + else + worker_line += " | Load: #{color(75, 50, wstats.load, asciiThreadLoad(wstats.running_threads, wstats.max_threads))}" + worker_line += " | Rqs: #{color(10000, 8000, wstats.requests_count)}" if wstats.requests_count + worker_line += " Phase: #{error(wstats.phase)}" if wstats.phase != stats.phase + worker_line += " Queue: #{error(wstats.backlog.to_s)}" if wstats.backlog > 0 + worker_line += " Last checkin: #{error(wstats.last_checkin)}" if wstats.last_checkin >= 10 + end + worker_line end diff --git a/lib/helpers.rb b/lib/helpers.rb index 51e4aa0..8b17ef7 100644 --- a/lib/helpers.rb +++ b/lib/helpers.rb @@ -17,7 +17,8 @@ def colorize(str, color_name) str.to_s.colorize(color_name) end -def color(critical, warn, value, str) +def color(critical, warn, value, str = nil) + str = value unless str color_level = if value >= critical :red elsif value < critical && value >= warn diff --git a/lib/stats.rb b/lib/stats.rb index d4e8f5d..d8d8212 100644 --- a/lib/stats.rb +++ b/lib/stats.rb @@ -9,6 +9,14 @@ def pid @wstats['pid'] end + def killed=(killed) + @wstats['killed'] = killed + end + + def killed? + @wstats['killed'] + end + def mem=(mem) @wstats['mem'] = mem end @@ -25,6 +33,10 @@ def pcpu @wstats['pcpu'] end + def booting? + @wstats['last_status'] && @wstats['last_status'].empty? + end + def running @wstats.dig('last_status', 'running') || @wstats['running'] || 0 end @@ -55,6 +67,10 @@ def uptime (Time.now - Time.parse(@wstats['started_at'])).to_i end + def requests_count + @wstats.dig('last_status', 'requests_count') || @wstats['requests_count'] || 0 + end + def backlog @wstats.dig('last_status', 'backlog') || 0 end @@ -95,6 +111,10 @@ def uptime (Time.now - Time.parse(@stats['started_at'])).to_i end + def booting? + workers.all?(&:booting?) + end + def total_threads workers.reduce(0) { |total, wstats| total + wstats.max_threads } end From a146d83681f8f8ec0442a2668e8bbfed1b5b9cdc Mon Sep 17 00:00:00 2001 From: Yoann Lecuyer Date: Sun, 26 Jan 2020 19:07:35 +0100 Subject: [PATCH 2/7] Remove color and change name to Req --- lib/core.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core.rb b/lib/core.rb index 783920e..4bdd767 100644 --- a/lib/core.rb +++ b/lib/core.rb @@ -62,7 +62,7 @@ def format_stats(stats) worker_line += " #{error("killed")}" else worker_line += " | Load: #{color(75, 50, wstats.load, asciiThreadLoad(wstats.running_threads, wstats.max_threads))}" - worker_line += " | Rqs: #{color(10000, 8000, wstats.requests_count)}" if wstats.requests_count + worker_line += " | Req: #{wstats.requests_count}" if wstats.requests_count worker_line += " Phase: #{error(wstats.phase)}" if wstats.phase != stats.phase worker_line += " Queue: #{error(wstats.backlog.to_s)}" if wstats.backlog > 0 worker_line += " Last checkin: #{error(wstats.last_checkin)}" if wstats.last_checkin >= 10 From 635d4df6db236a1e21589131c857af281d7cf1bc Mon Sep 17 00:00:00 2001 From: Yoann Lecuyer Date: Tue, 11 Feb 2020 23:14:23 +0100 Subject: [PATCH 3/7] Better handling of requests --- lib/core.rb | 9 +++++---- lib/stats.rb | 8 +++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/core.rb b/lib/core.rb index cbfb19f..d8a4eef 100644 --- a/lib/core.rb +++ b/lib/core.rb @@ -59,13 +59,14 @@ def hydrate_stats(stats, puma_state, state_file_path) end def format_stats(stats) - master_line = "#{stats.pid} (#{stats.state_file_path}) Uptime: #{seconds_to_human(stats.uptime)} " - master_line += "| Phase: #{stats.phase} " if stats.phase + master_line = "#{stats.pid} (#{stats.state_file_path}) Uptime: #{seconds_to_human(stats.uptime)}" + master_line += " | Phase: #{stats.phase}" if stats.phase if stats.booting? master_line += warn("booting") else - master_line += "| Load: #{color(75, 50, stats.load, asciiThreadLoad(stats.running_threads, stats.max_threads))}" + master_line += " | Load: #{color(75, 50, stats.load, asciiThreadLoad(stats.running_threads, stats.max_threads))}" + master_line += " | Req: #{stats.requests_count}" if stats.requests_count end output = [master_line] + stats.workers.map do |wstats| @@ -77,8 +78,8 @@ def format_stats(stats) worker_line += " #{error("killed")}" else worker_line += " | Load: #{color(75, 50, wstats.load, asciiThreadLoad(wstats.running_threads, wstats.max_threads))}" + worker_line += " | Phase: #{error(wstats.phase)}" if wstats.phase != stats.phase worker_line += " | Req: #{wstats.requests_count}" if wstats.requests_count - worker_line += " Phase: #{error(wstats.phase)}" if wstats.phase != stats.phase worker_line += " Queue: #{error(wstats.backlog.to_s)}" if wstats.backlog > 0 worker_line += " Last checkin: #{error(wstats.last_checkin)}" if wstats.last_checkin >= 10 end diff --git a/lib/stats.rb b/lib/stats.rb index df164ed..5f76ca2 100644 --- a/lib/stats.rb +++ b/lib/stats.rb @@ -68,7 +68,7 @@ def uptime end def requests_count - @wstats.dig('last_status', 'requests_count') || @wstats['requests_count'] || 0 + @wstats.dig('last_status', 'requests_count') || @wstats['requests_count'] end def backlog @@ -127,6 +127,12 @@ def max_threads workers.reduce(0) { |total, wstats| total + wstats.max_threads } end + def requests_count + workers_with_requests_count = workers.select(&:requests_count) + return unless workers_with_requests_count.any? + workers_with_requests_count.reduce(0) { |total, wstats| total + wstats.requests_count } + end + def running @stats['running'] || 0 end From 2e2da3b0df7b8e6927f1437b4d8aa16ab4014b00 Mon Sep 17 00:00:00 2001 From: Yoann Lecuyer Date: Tue, 11 Feb 2020 23:27:34 +0100 Subject: [PATCH 4/7] fix existing tests --- lib/core.rb | 2 +- spec/core_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core.rb b/lib/core.rb index d8a4eef..e558f7f 100644 --- a/lib/core.rb +++ b/lib/core.rb @@ -63,7 +63,7 @@ def format_stats(stats) master_line += " | Phase: #{stats.phase}" if stats.phase if stats.booting? - master_line += warn("booting") + master_line += " #{warn("booting")}" else master_line += " | Load: #{color(75, 50, stats.load, asciiThreadLoad(stats.running_threads, stats.max_threads))}" master_line += " | Req: #{stats.requests_count}" if stats.requests_count diff --git a/spec/core_spec.rb b/spec/core_spec.rb index 4c9fced..0149395 100644 --- a/spec/core_spec.rb +++ b/spec/core_spec.rb @@ -81,8 +81,8 @@ ClimateControl.modify NO_COLOR: '1' do expect(format_stats(Stats.new(stats))).to eq( %Q{12328 (../testpuma/tmp/puma.state) Uptime: 0m 0s | Phase: 1 | Load: 0[░░░░░░░░░░░░░░░░]16 - └ 12362 CPU: 0.0% Mem: 64 MB Uptime: 0m 0s | Load: 0[░░░░]4 Phase: 0 - └ 12366 CPU: 0.0% Mem: 64 MB Uptime: 0m 0s | Load: 0[░░░░]4 Phase: 0 + └ 12362 CPU: 0.0% Mem: 64 MB Uptime: 0m 0s | Load: 0[░░░░]4 | Phase: 0 + └ 12366 CPU: 0.0% Mem: 64 MB Uptime: 0m 0s | Load: 0[░░░░]4 | Phase: 0 └ 12370 CPU: 0.0% Mem: 64 MB Uptime: 0m 0s | Load: 0[░░░░]4 └ 12372 CPU: 0.0% Mem: 64 MB Uptime: 0m 0s | Load: 0[░░░░]4}) end From c668a6a5ded4bff9123a8449dc6e57d4c6fc1353 Mon Sep 17 00:00:00 2001 From: Yoann Lecuyer Date: Thu, 19 Mar 2020 22:34:43 +0100 Subject: [PATCH 5/7] positivity --- lib/stats.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stats.rb b/lib/stats.rb index 5f76ca2..8bebdc5 100644 --- a/lib/stats.rb +++ b/lib/stats.rb @@ -129,7 +129,7 @@ def max_threads def requests_count workers_with_requests_count = workers.select(&:requests_count) - return unless workers_with_requests_count.any? + return if workers_with_requests_count.none? workers_with_requests_count.reduce(0) { |total, wstats| total + wstats.requests_count } end From 609e19831ddf0d509039f4344c5c42f9e3c7f0b6 Mon Sep 17 00:00:00 2001 From: Yoann Lecuyer Date: Thu, 19 Mar 2020 23:08:07 +0100 Subject: [PATCH 6/7] spec stats --- spec/stats_spec.rb | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/spec/stats_spec.rb b/spec/stats_spec.rb index 0fcacbb..650e328 100644 --- a/spec/stats_spec.rb +++ b/spec/stats_spec.rb @@ -34,6 +34,11 @@ expect(stats.booting?).to eq(true) end + it 'gives the number of requests' do + stats = Stats.new({"workers"=>4, "phase"=>0, "booted_workers"=>4, "old_workers"=>0, "worker_status"=>[{"pid"=>28909, "index"=>0, "phase"=>0, "booted"=>true, "last_checkin"=>"2019-07-14T14:33:54Z", "last_status"=>{"requests_count" => 150}}, {"pid"=>28911, "index"=>1, "phase"=>0, "booted"=>true, "last_checkin"=>"2019-07-14T14:33:54Z", "last_status"=>{"backlog"=>0, "running"=>4, "pool_capacity"=>0, "max_threads"=>4, "requests_count" => 300}}]}) + expect(stats.requests_count).to eq(450) + end + context 'workers' do it 'gives running threads first worker' do expect(stats.workers.first.running_threads).to eq(4) @@ -63,11 +68,17 @@ worker = stats.workers.first expect(worker.booting?).to eq(true) end + + it 'gives the number of requests' do + stats = Stats.new({"workers"=>4, "phase"=>0, "booted_workers"=>4, "old_workers"=>0, "worker_status"=>[{"pid"=>28909, "index"=>0, "phase"=>0, "booted"=>true, "last_checkin"=>"2019-07-14T14:33:54Z", "last_status"=>{"requests_count" => 150}}, {"pid"=>28911, "index"=>1, "phase"=>0, "booted"=>true, "last_checkin"=>"2019-07-14T14:33:54Z", "last_status"=>{"backlog"=>0, "running"=>4, "pool_capacity"=>0, "max_threads"=>4, "requests_count" => 300}}]}) + worker = stats.workers.first + expect(worker.requests_count).to eq(150) + end end end context 'single stats' do - let(:stats) { Stats.new({"started_at"=>"2019-07-14T15:07:15Z", "backlog"=>0, "running"=>4, "pool_capacity"=>2, "max_threads"=>4}) } + let(:stats) { Stats.new({"started_at"=>"2019-07-14T15:07:15Z", "backlog"=>0, "running"=>4, "pool_capacity"=>2, "max_threads"=>4, "requests_count"=>150}) } it 'returns uptime 0 for older version of puma' do stats = Stats.new({"backlog"=>0, "running"=>4, "pool_capacity"=>2, "max_threads"=>4}) @@ -86,6 +97,10 @@ expect(stats.total_threads).to eq(4) end + it 'gives the number of requests' do + expect(stats.requests_count).to eq(150) + end + context 'workers' do it 'gives running threads' do expect(stats.workers.first.running_threads).to eq(2) @@ -94,6 +109,10 @@ it 'gives total threads' do expect(stats.workers.first.total_threads).to eq(4) end + + it 'gives the number of requests' do + expect(stats.workers.first.requests_count).to eq(150) + end end end From 277f8ffefff909a134a3c3e19daf845be8c2eb2e Mon Sep 17 00:00:00 2001 From: Yoann Lecuyer Date: Thu, 19 Mar 2020 23:11:40 +0100 Subject: [PATCH 7/7] add core specs --- spec/core_spec.rb | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/spec/core_spec.rb b/spec/core_spec.rb index 0149395..820210e 100644 --- a/spec/core_spec.rb +++ b/spec/core_spec.rb @@ -137,6 +137,19 @@ end end + it 'show the number of request when present in clustered mode' do + stats = {"started_at"=>"2019-07-14T10:49:24Z", "workers"=>4, "phase"=>0, "booted_workers"=>4, "old_workers"=>0, "worker_status"=>[{"started_at"=>"2019-07-14T10:49:24Z", "pid"=>12362, "index"=>0, "phase"=>0, "booted"=>true, "last_checkin"=>"2019-07-14T13:09:00Z", "last_status"=>{"backlog"=>0, "running"=>4, "pool_capacity"=>4, "max_threads"=>4, "requests_count"=>150}, "mem"=>64, "pcpu"=>0.0}, {"started_at"=>"2019-07-14T10:49:24Z", "pid"=>12366, "index"=>1, "phase"=>0, "booted"=>true, "last_checkin"=>"2019-07-14T13:09:00Z", "last_status"=>{"backlog"=>0, "running"=>4, "pool_capacity"=>4, "max_threads"=>4, "requests_count"=>223}, "mem"=>64, "pcpu"=>0.0}, {"started_at"=>"2019-07-14T10:49:24Z", "pid"=>12370, "index"=>2, "phase"=>0, "booted"=>true, "last_checkin"=>"2019-07-14T13:09:00Z", "last_status"=>{"backlog"=>0, "running"=>4, "pool_capacity"=>4, "max_threads"=>4, "requests_count"=>450}, "mem"=>64, "pcpu"=>0.0}, {"started_at"=>"2019-07-14T10:49:24Z", "pid"=>12372, "index"=>3, "phase"=>0, "booted"=>true, "last_checkin"=>"2019-07-14T13:09:00Z", "last_status"=>{"backlog"=>0, "running"=>4, "pool_capacity"=>4, "max_threads"=>4, "requests_count"=>10}, "mem"=>64, "pcpu"=>0.0}], "pid"=>12328, "state_file_path"=>"../testpuma/tmp/puma.state"} + + ClimateControl.modify NO_COLOR: '1' do + expect(format_stats(Stats.new(stats))).to eq( +%Q{12328 (../testpuma/tmp/puma.state) Uptime: 0m 0s | Phase: 0 | Load: 0[░░░░░░░░░░░░░░░░]16 | Req: 833 + └ 12362 CPU: 0.0% Mem: 64 MB Uptime: 0m 0s | Load: 0[░░░░]4 | Req: 150 + └ 12366 CPU: 0.0% Mem: 64 MB Uptime: 0m 0s | Load: 0[░░░░]4 | Req: 223 + └ 12370 CPU: 0.0% Mem: 64 MB Uptime: 0m 0s | Load: 0[░░░░]4 | Req: 450 + └ 12372 CPU: 0.0% Mem: 64 MB Uptime: 0m 0s | Load: 0[░░░░]4 | Req: 10}) + end + end + it 'works in single mode' do stats = {"started_at"=>"2019-07-14T10:49:24Z", "backlog"=>0, "running"=>4, "pool_capacity"=>4, "max_threads"=>4, "pid"=>21725, "state_file_path"=>"../testpuma/tmp/puma.state", "pcpu"=>10, "mem"=>64} @@ -146,6 +159,15 @@ └ 21725 CPU: 10% Mem: 64 MB Uptime: 0m 0s | Load: 0[░░░░]4}) end end - end + it 'show the number of request when present in single mode' do + stats = {"started_at"=>"2019-07-14T10:49:24Z", "backlog"=>0, "running"=>4, "pool_capacity"=>4, "max_threads"=>4, "pid"=>21725, "state_file_path"=>"../testpuma/tmp/puma.state", "pcpu"=>10, "mem"=>64, "requests_count"=> 150} + + ClimateControl.modify NO_COLOR: '1' do + expect(format_stats(Stats.new(stats))).to eq( +%Q{21725 (../testpuma/tmp/puma.state) Uptime: 0m 0s | Load: 0[░░░░]4 | Req: 150 + └ 21725 CPU: 10% Mem: 64 MB Uptime: 0m 0s | Load: 0[░░░░]4 | Req: 150}) + end + end + end end