greenhat 0.1.5 → 0.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 +4 -4
- data/lib/greenhat.rb +1 -0
- data/lib/greenhat/accessors/disk.rb +58 -2
- data/lib/greenhat/accessors/process.rb +7 -2
- data/lib/greenhat/cli.rb +1 -5
- data/lib/greenhat/settings.rb +31 -3
- data/lib/greenhat/shell.rb +4 -4
- data/lib/greenhat/shell/color_string.rb +43 -0
- data/lib/greenhat/shell/disk.rb +4 -43
- data/lib/greenhat/shell/faststats.rb +6 -7
- data/lib/greenhat/shell/filter.rb +128 -0
- data/lib/greenhat/shell/helper.rb +93 -50
- data/lib/greenhat/shell/log.rb +8 -116
- data/lib/greenhat/shell/page.rb +33 -0
- data/lib/greenhat/shell/process.rb +45 -2
- data/lib/greenhat/shell/report.rb +36 -16
- data/lib/greenhat/thing/file_types.rb +23 -2
- data/lib/greenhat/thing/formatters/syslog.rb +39 -0
- data/lib/greenhat/tty/columns.rb +40 -0
- data/lib/greenhat/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79572c7389cb4cd519007f185a7fc249a771c08fa1fcbde8645d99786db42736
|
4
|
+
data.tar.gz: 6904a95f759a1dc0e866733f0a6220c62b47f3985998f82e2efd4619fdabe390
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d44e38727ce532ddbdcf8cc9aad1f5d7ee26a113ee589489bb5f15a99e616d06f66d304dc1e7735a679a707ed99323a45184234779066ca9f1f2cdea3e1b87e9
|
7
|
+
data.tar.gz: 6fb959d970482e244bea30f39ccd7357ce442fbc886df0084f425c3540b662a86972889213273ed5e734cace0f6b2b710c356c5585f98f76ffc4763ce2c68b10
|
data/lib/greenhat.rb
CHANGED
@@ -20,8 +20,64 @@ module GreenHat
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def self.df
|
24
|
-
Thing.where(name: 'df_h')
|
23
|
+
def self.df(args = {})
|
24
|
+
things = Thing.where(name: 'df_h')
|
25
|
+
|
26
|
+
# Host / Archive
|
27
|
+
things.select! { |x| x.archive? args.archive } if args.archive
|
28
|
+
|
29
|
+
things
|
30
|
+
end
|
31
|
+
|
32
|
+
# Unified Output Handler
|
33
|
+
# rubocop:disable Metrics/MethodLength
|
34
|
+
def self.format_output(file, name = false, limit = nil, filter = %w[tmpfs loop])
|
35
|
+
output = []
|
36
|
+
|
37
|
+
output << file.friendly_name if name
|
38
|
+
|
39
|
+
# Reject TMPFS
|
40
|
+
disks = file.data.sort_by { |x| x.use.to_i }.reverse
|
41
|
+
|
42
|
+
# Filter
|
43
|
+
disks.reject! { |x| filter.any? { |y| x.filesystem.include? y } }
|
44
|
+
|
45
|
+
disks = disks[0..limit - 1] if limit
|
46
|
+
|
47
|
+
pad_mount, pad_size, pad_used, pad_avail = GreenHat::Disk.padding(disks)
|
48
|
+
|
49
|
+
# Headers
|
50
|
+
output << [
|
51
|
+
'Mount'.ljust(pad_mount).colorize(:blue),
|
52
|
+
'Size'.ljust(pad_size).colorize(:magenta),
|
53
|
+
'Used'.ljust(pad_used).colorize(:cyan),
|
54
|
+
'Avail'.ljust(pad_avail).colorize(:white),
|
55
|
+
'% Use'.ljust(pad_avail).colorize(:green)
|
56
|
+
].join
|
57
|
+
|
58
|
+
# Table Summary
|
59
|
+
disks.map do |disk|
|
60
|
+
# Pretty Disk Use
|
61
|
+
use = [
|
62
|
+
disk.use.rjust(5).ljust(5).colorize(:green),
|
63
|
+
' ['.colorize(:blue),
|
64
|
+
('=' * (disk.use.to_i / 2)).colorize(:green),
|
65
|
+
' ' * (50 - disk.use.to_i / 2),
|
66
|
+
']'.colorize(:blue)
|
67
|
+
].join
|
68
|
+
|
69
|
+
# Whole Thing
|
70
|
+
output << [
|
71
|
+
disk.mounted_on.ljust(pad_mount).colorize(:blue),
|
72
|
+
disk[:size].to_s.ljust(pad_size).colorize(:magenta),
|
73
|
+
disk.used.to_s.ljust(pad_used).colorize(:cyan),
|
74
|
+
disk.avail.to_s.ljust(pad_avail).colorize(:white),
|
75
|
+
use
|
76
|
+
].join
|
77
|
+
end
|
78
|
+
|
79
|
+
output
|
25
80
|
end
|
81
|
+
# rubocop:enable Metrics/MethodLength
|
26
82
|
end
|
27
83
|
end
|
@@ -1,8 +1,13 @@
|
|
1
1
|
module GreenHat
|
2
2
|
# Sidekiq Log Helpers
|
3
3
|
module Ps
|
4
|
-
def self.ps
|
5
|
-
Thing.where(name: 'ps')
|
4
|
+
def self.ps(args)
|
5
|
+
things = Thing.where(name: 'ps')
|
6
|
+
|
7
|
+
# Host / Archive
|
8
|
+
things.select! { |x| x.archive? args.archive } if args.archive
|
9
|
+
|
10
|
+
things
|
6
11
|
end
|
7
12
|
end
|
8
13
|
end
|
data/lib/greenhat/cli.rb
CHANGED
data/lib/greenhat/settings.rb
CHANGED
@@ -4,13 +4,41 @@ module GreenHat
|
|
4
4
|
module Settings
|
5
5
|
def self.settings
|
6
6
|
@settings ||= {
|
7
|
-
history: []
|
7
|
+
history: [],
|
8
|
+
|
9
|
+
# round: [2],
|
10
|
+
# page: [:true] Automatic,
|
11
|
+
truncate: [TTY::Screen.width * 4]
|
12
|
+
|
8
13
|
}
|
9
14
|
end
|
10
15
|
|
16
|
+
# Load User Settings and drop them into settings
|
17
|
+
def self.settings_load
|
18
|
+
return true unless File.exist?(settings_file)
|
19
|
+
|
20
|
+
Oj.load(File.read(settings_file)).each do |key, value|
|
21
|
+
settings[key] = value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.settings_file
|
26
|
+
"#{dir}/settings.json"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Set any Log Arguments that weren't set otherwise
|
30
|
+
def self.default_log_args(args, skip_args)
|
31
|
+
args[:round] ||= settings.round unless skip_args.include?(:round)
|
32
|
+
args[:page] ||= settings.page unless skip_args.include?(:page)
|
33
|
+
args[:truncate] ||= settings.truncate unless skip_args.include?(:truncate)
|
34
|
+
end
|
35
|
+
|
11
36
|
def self.start
|
12
37
|
Dir.mkdir dir unless Dir.exist? dir
|
13
38
|
|
39
|
+
# Load User Settings
|
40
|
+
settings_load
|
41
|
+
|
14
42
|
# CMD History Loading / Tracking
|
15
43
|
File.write(cmd_file, "\n") unless File.exist? cmd_file
|
16
44
|
end
|
@@ -33,8 +61,8 @@ module GreenHat
|
|
33
61
|
File.read(cmd_file).split("\n")
|
34
62
|
end
|
35
63
|
|
36
|
-
def self.
|
37
|
-
File.write(cmd_file,
|
64
|
+
def self.cmd_history_clear
|
65
|
+
File.write(cmd_file, "\n")
|
38
66
|
end
|
39
67
|
|
40
68
|
def self.cmd_add(line)
|
data/lib/greenhat/shell.rb
CHANGED
@@ -9,8 +9,8 @@ module GreenHat
|
|
9
9
|
Disk.df
|
10
10
|
end
|
11
11
|
|
12
|
-
def self.ps
|
13
|
-
Process.ps
|
12
|
+
def self.ps(raw = {})
|
13
|
+
Process.ps raw
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.netstat
|
@@ -63,11 +63,11 @@ module GreenHat
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def self.history_clear
|
66
|
-
|
66
|
+
Settings.cmd_history_clear
|
67
67
|
end
|
68
68
|
|
69
69
|
def self.history
|
70
|
-
|
70
|
+
Settings.cmd_history_clean.each_with_index do |line, i|
|
71
71
|
puts "#{i.to_s.ljust(3).colorize(:magenta)} #{line}"
|
72
72
|
end
|
73
73
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module GreenHat
|
2
|
+
module ShellHelper
|
3
|
+
# Helper to colorize and make outtput easier to read
|
4
|
+
module StringColor
|
5
|
+
def self.do(key, entry)
|
6
|
+
LogBot.debug('Unknown Format', entry.class) if ENV['DEBUG'] && !entry.instance_of?(String)
|
7
|
+
|
8
|
+
# Other Helpful colorizers
|
9
|
+
if colorize?(key)
|
10
|
+
colorize(key, entry)
|
11
|
+
else
|
12
|
+
entry.to_s
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Add Color?
|
17
|
+
def self.colorize?(key)
|
18
|
+
[:severity].any? key
|
19
|
+
end
|
20
|
+
|
21
|
+
# General Key/Value Coloring
|
22
|
+
def self.colorize(key, value)
|
23
|
+
case key
|
24
|
+
when :severity then severity(value)
|
25
|
+
else
|
26
|
+
value.to_s
|
27
|
+
end
|
28
|
+
end
|
29
|
+
# ----
|
30
|
+
|
31
|
+
def self.severity(value)
|
32
|
+
case value.to_s.downcase.to_sym
|
33
|
+
when :debug then value.colorize(:blue)
|
34
|
+
when :info then value.colorize(:cyan)
|
35
|
+
when :warn then value.colorize(:yellow)
|
36
|
+
when :fatal, :error then value.colorize(:light_red)
|
37
|
+
else
|
38
|
+
value.to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/greenhat/shell/disk.rb
CHANGED
@@ -13,55 +13,16 @@ module GreenHat
|
|
13
13
|
ShellHelper.file_output GreenHat::Disk.df
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
GreenHat::Disk.df.each do |file|
|
19
|
-
# Reject TMPFS
|
20
|
-
puts file.friendly_name
|
21
|
-
disks = file.data.sort_by { |x| x.use.to_i }.reverse.reject { |x| x.filesystem.include? 'tmpfs' }
|
16
|
+
def self.free(raw = {})
|
17
|
+
_log_list, _1opts, args = ShellHelper.param_parse(raw)
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
# pad_used = GreenHat::Disk.max_padding(disks, :used)
|
26
|
-
# pad_avail = GreenHat::Disk.max_padding(disks, :avail)
|
27
|
-
|
28
|
-
pad_mount, pad_size, pad_used, pad_avail = GreenHat::Disk.padding(disks)
|
29
|
-
|
30
|
-
# Headers
|
31
|
-
puts [
|
32
|
-
'Mount'.ljust(pad_mount).colorize(:blue),
|
33
|
-
'Size'.ljust(pad_size).colorize(:magenta),
|
34
|
-
'Used'.ljust(pad_used).colorize(:cyan),
|
35
|
-
'Avail'.ljust(pad_avail).colorize(:white),
|
36
|
-
'% Use'.ljust(pad_avail).colorize(:green)
|
37
|
-
].join
|
38
|
-
|
39
|
-
# Table Summary
|
40
|
-
disks.map do |disk|
|
41
|
-
# Pretty Disk Use
|
42
|
-
use = [
|
43
|
-
disk.use.rjust(5).ljust(5).colorize(:green),
|
44
|
-
' ['.colorize(:blue),
|
45
|
-
('=' * (disk.use.to_i / 2)).colorize(:green),
|
46
|
-
' ' * (50 - disk.use.to_i / 2),
|
47
|
-
']'.colorize(:blue)
|
48
|
-
].join
|
49
|
-
|
50
|
-
# Whole Thing
|
51
|
-
puts [
|
52
|
-
disk.mounted_on.ljust(pad_mount).colorize(:blue),
|
53
|
-
disk[:size].to_s.ljust(pad_size).colorize(:magenta),
|
54
|
-
disk.used.to_s.ljust(pad_used).colorize(:cyan),
|
55
|
-
disk.avail.to_s.ljust(pad_avail).colorize(:white),
|
56
|
-
use
|
57
|
-
].join
|
58
|
-
end
|
19
|
+
GreenHat::Disk.df(args).each do |file|
|
20
|
+
puts GreenHat::Disk.format_output(file, true)
|
59
21
|
|
60
22
|
# File End Loop / Break
|
61
23
|
puts
|
62
24
|
end
|
63
25
|
end
|
64
|
-
# rubocop:enable Metrics/MethodLength,Metrics/BlockLength
|
65
26
|
# ------------------------------------------------------------------------
|
66
27
|
end
|
67
28
|
end
|
@@ -111,11 +111,8 @@ module GreenHat
|
|
111
111
|
# --sort=count,fail,max,median,min,p95,p99,rps,score
|
112
112
|
# ========================================================================
|
113
113
|
def self.top(log_list)
|
114
|
-
# Extract Args
|
115
|
-
log_list, opts, args = ShellHelper.param_parse(log_list)
|
116
|
-
|
117
|
-
# Default to color output
|
118
|
-
args['color-output'] ||= true
|
114
|
+
# Extract Args, No Round / Truncate for Fast Stats
|
115
|
+
log_list, opts, args = ShellHelper.param_parse(log_list, %i[page round truncate])
|
119
116
|
|
120
117
|
cmd = opts.map { |opt| "--#{opt.field}=#{opt.value}" }.join(' ')
|
121
118
|
cmd += args.keys.map { |arg| "--#{arg}" }.join(' ')
|
@@ -142,8 +139,8 @@ module GreenHat
|
|
142
139
|
# ===== [ Fast Stats - Errors ] ====================
|
143
140
|
# ========================================================================
|
144
141
|
def self.errors(log_list)
|
145
|
-
# Extract Args
|
146
|
-
log_list, opts, args = ShellHelper.param_parse(log_list)
|
142
|
+
# Extract Args, No Round / Truncate for Fast Stats
|
143
|
+
log_list, opts, args = ShellHelper.param_parse(log_list, %i[page round truncate])
|
147
144
|
|
148
145
|
# Default to color output
|
149
146
|
args['color-output'] ||= true
|
@@ -157,6 +154,8 @@ module GreenHat
|
|
157
154
|
# Convert to Things
|
158
155
|
files = ShellHelper.find_things(log_list)
|
159
156
|
|
157
|
+
LogBot.debug('FastStats CMD', cmd) if ENV['DEBUG']
|
158
|
+
|
160
159
|
results = ShellHelper.file_process(files) do |file|
|
161
160
|
[
|
162
161
|
file.friendly_name,
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module GreenHat
|
2
|
+
# CLI Helper
|
3
|
+
module ShellHelper
|
4
|
+
# Unify Filter / Filter Help
|
5
|
+
module Filter
|
6
|
+
# rubocop:disable Metrics/MethodLength
|
7
|
+
def self.help
|
8
|
+
puts "\u2500".colorize(:cyan) * 20
|
9
|
+
puts 'Filter'.colorize(:yellow)
|
10
|
+
puts "\u2500".colorize(:cyan) * 20
|
11
|
+
|
12
|
+
puts 'Options'.colorize(:blue)
|
13
|
+
puts '--raw'.colorize(:green)
|
14
|
+
puts ' Do not use less/paging'
|
15
|
+
puts
|
16
|
+
|
17
|
+
puts '--page'.colorize(:green)
|
18
|
+
puts ' Specifically enable or disable paging'
|
19
|
+
puts ' E.g. --page (default to true), --page=true, --page=false'
|
20
|
+
puts
|
21
|
+
|
22
|
+
puts '--round'.colorize(:green)
|
23
|
+
puts ' Attempt to round all integers. Default: 2.'
|
24
|
+
puts ' E.g. --round, --round=3, --round=0'
|
25
|
+
puts
|
26
|
+
|
27
|
+
puts '--truncate'.colorize(:green)
|
28
|
+
puts ' Limit output values. Defaults to 4 lines. 0 to disable'
|
29
|
+
puts ' E.g. --truncate=100 or --truncate=0'
|
30
|
+
puts
|
31
|
+
|
32
|
+
puts '--json'.colorize(:green)
|
33
|
+
puts ' Print output back into JSON'
|
34
|
+
puts
|
35
|
+
|
36
|
+
puts '--or'.colorize(:green)
|
37
|
+
puts ' Filters will use OR instead of AND'
|
38
|
+
puts
|
39
|
+
|
40
|
+
puts '--total'.colorize(:green)
|
41
|
+
puts ' Print only total count of matching entries'
|
42
|
+
puts
|
43
|
+
|
44
|
+
puts '--slice'.colorize(:green)
|
45
|
+
puts ' Extract specific fields from entries (slice multiple with comma)'
|
46
|
+
puts ' Ex: --slice=path or --slice=path,params'
|
47
|
+
puts
|
48
|
+
|
49
|
+
puts '--except'.colorize(:green)
|
50
|
+
puts ' Exclude specific fields (except multiple with comma)'
|
51
|
+
puts ' Ex: --except=params --except=params,path'
|
52
|
+
puts
|
53
|
+
|
54
|
+
puts '--stats'.colorize(:green)
|
55
|
+
puts ' Order/Count occurrances by field'
|
56
|
+
puts ' Ex: --stats=params --except=params,path'
|
57
|
+
puts
|
58
|
+
|
59
|
+
puts '--uniq'.colorize(:green)
|
60
|
+
puts ' Show unique values only'
|
61
|
+
puts ' Ex: --uniq=params --uniq=params,path'
|
62
|
+
puts
|
63
|
+
|
64
|
+
puts '--pluck'.colorize(:green)
|
65
|
+
puts ' Extract values from entries'
|
66
|
+
puts ' Ex: --pluck=params --pluck=params,path'
|
67
|
+
puts
|
68
|
+
|
69
|
+
puts '--archive'.colorize(:green)
|
70
|
+
puts ' Limit to specific archvie name (inclusive). Matching SOS tar.gz name'
|
71
|
+
puts ' Ex: --archive=dev-gitlab_20210622154626, --archive=202106,202107'
|
72
|
+
puts
|
73
|
+
|
74
|
+
puts '--sort'.colorize(:green)
|
75
|
+
puts ' Sort by multiple fields'
|
76
|
+
puts ' Ex: --sort=duration_s,db_duration_s'
|
77
|
+
puts
|
78
|
+
|
79
|
+
puts '--reverse'.colorize(:green)
|
80
|
+
puts ' Reverse all results'
|
81
|
+
puts ' Ex: --reverse'
|
82
|
+
puts
|
83
|
+
|
84
|
+
puts '--combine'.colorize(:green)
|
85
|
+
puts ' Omit archive identifier dividers. Useful with sort or time filters'
|
86
|
+
puts ' Ex: --combine'
|
87
|
+
puts
|
88
|
+
|
89
|
+
puts '--start'.colorize(:green)
|
90
|
+
puts ' Show events after specified time. Filtered by the `time` field'
|
91
|
+
puts ' Use with `--end` for between selections'
|
92
|
+
puts ' Ex: log filter --start="2021-06-22 14:44 UTC" --end="2021-06-22 14:45 UTC"'
|
93
|
+
puts
|
94
|
+
|
95
|
+
puts '--end'.colorize(:green)
|
96
|
+
puts ' Show events before specified time. Filtered by the `time` field'
|
97
|
+
puts ' Use with `--start` for between selections'
|
98
|
+
puts ' Ex: log filter --end="2021-06-22"'
|
99
|
+
puts
|
100
|
+
|
101
|
+
puts '--truncate'.colorize(:green)
|
102
|
+
puts ' Truncate field length. On by default (4 rows). Performance issues!'
|
103
|
+
puts ' Disable with --truncate=0'.colorize(:light_red)
|
104
|
+
puts ' Ex: --truncate=200, --truncate=2048"'
|
105
|
+
puts
|
106
|
+
|
107
|
+
puts 'Field Searching'.colorize(:blue)
|
108
|
+
puts ' --[key]=[value]'
|
109
|
+
puts ' Search in key for value'
|
110
|
+
puts ' Example: --path=mirror/pull'
|
111
|
+
puts
|
112
|
+
|
113
|
+
puts 'Search specific logs'.colorize(:blue)
|
114
|
+
puts ' Any non dash parameters will be the log list to search from'
|
115
|
+
puts " Ex: log filter --path=api sidekiq/current (hint: use `#{'ls'.colorize(:yellow)}` for log names"
|
116
|
+
puts
|
117
|
+
|
118
|
+
puts 'Example Queries'.colorize(:blue)
|
119
|
+
puts " Also see #{'examples'.colorize(:light_blue)} for even more examples"
|
120
|
+
puts ' log filter --class=BuildFinishedWorker sidekiq/current --slice=time,message'
|
121
|
+
puts ' log filter gitlab-rails/api_json.log --slice=ua --uniq=ua --ua=gitlab-runner'
|
122
|
+
|
123
|
+
puts
|
124
|
+
end
|
125
|
+
# rubocop:enable Metrics/MethodLength
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -3,7 +3,7 @@ module GreenHat
|
|
3
3
|
# rubocop:disable Metrics/ModuleLength
|
4
4
|
module ShellHelper
|
5
5
|
# Generic Parameter Parsing
|
6
|
-
def self.param_parse(params)
|
6
|
+
def self.param_parse(params, skip_args = [])
|
7
7
|
# Turn Params into Hash
|
8
8
|
opts = params.flat_map { |param| param_opt_scan(param) }
|
9
9
|
|
@@ -23,6 +23,9 @@ module GreenHat
|
|
23
23
|
opt_field_remove?(opts, param) || arg_field_remove?(args, param)
|
24
24
|
end
|
25
25
|
|
26
|
+
# Update other user defaults
|
27
|
+
Settings.default_log_args(args, skip_args)
|
28
|
+
|
26
29
|
[params, opts, args]
|
27
30
|
end
|
28
31
|
|
@@ -57,7 +60,7 @@ module GreenHat
|
|
57
60
|
|
58
61
|
def self.param_special_opts
|
59
62
|
%i[
|
60
|
-
slice except stats uniq pluck round archive start end sort limit
|
63
|
+
slice except stats uniq pluck round archive start end sort limit truncate page case
|
61
64
|
]
|
62
65
|
end
|
63
66
|
|
@@ -75,8 +78,10 @@ module GreenHat
|
|
75
78
|
# Arg Defaults
|
76
79
|
def self.param_arg_defaults(field)
|
77
80
|
case field
|
78
|
-
when :round then 2
|
81
|
+
when :round then [2]
|
79
82
|
when :limit then [TTY::Screen.height / 2]
|
83
|
+
when :truncate then [TTY::Screen.width * 4]
|
84
|
+
when :page, :case then [:true]
|
80
85
|
when *param_special_opts then []
|
81
86
|
else
|
82
87
|
true
|
@@ -128,15 +133,15 @@ module GreenHat
|
|
128
133
|
end
|
129
134
|
|
130
135
|
# Check if content needs to paged, or if auto_height is off
|
131
|
-
if
|
132
|
-
puts data.map { |
|
136
|
+
if Page.skip?(args, data)
|
137
|
+
puts data.map { |entry| entry_show(args, entry) }.compact.join("\n")
|
133
138
|
return true
|
134
139
|
end
|
135
140
|
|
136
141
|
# Default Pager
|
137
142
|
TTY::Pager.page do |pager|
|
138
143
|
data.each do |entry|
|
139
|
-
output = entry_show(
|
144
|
+
output = entry_show(args, entry)
|
140
145
|
|
141
146
|
next if output.blank?
|
142
147
|
|
@@ -145,63 +150,76 @@ module GreenHat
|
|
145
150
|
end
|
146
151
|
end
|
147
152
|
|
148
|
-
#
|
149
|
-
def self.
|
150
|
-
|
151
|
-
size = 0
|
152
|
-
|
153
|
-
data.each do |entry|
|
154
|
-
size += case entry
|
155
|
-
when Hash then entry.keys.count
|
156
|
-
when Array then entry.count
|
157
|
-
else
|
158
|
-
1
|
159
|
-
end
|
160
|
-
|
161
|
-
break if size > height
|
162
|
-
end
|
163
|
-
|
164
|
-
height > size
|
165
|
-
end
|
166
|
-
|
167
|
-
# Entry Shower
|
168
|
-
def self.entry_show(entry, args)
|
153
|
+
# Entry Shower / Top Level
|
154
|
+
def self.entry_show(args, entry, key = nil)
|
155
|
+
LogBot.debug('Entry Show', entry.class) if ENV['DEBUG']
|
169
156
|
case entry
|
170
157
|
when Hash then render_table(entry, args)
|
171
|
-
when
|
172
|
-
|
173
|
-
|
158
|
+
when Float, Integer, Array
|
159
|
+
format_table_entry(args, entry, key)
|
160
|
+
# Ignore Special Formatting for Strings / Usually already formatted
|
161
|
+
when String
|
162
|
+
entry
|
174
163
|
else
|
175
164
|
LogBot.warn('Shell Show', "Unknown #{entry.class}")
|
176
165
|
nil
|
177
166
|
end
|
178
167
|
end
|
179
168
|
|
169
|
+
# Format Table Entries
|
170
|
+
def self.format_table_entry(args, entry, key = nil)
|
171
|
+
formatted_entry = case entry
|
172
|
+
# Rounding
|
173
|
+
when Float, Integer || entry.numeric?
|
174
|
+
args.round ? entry.to_f.round(args.round.first.to_s.to_i).ai : entry.ai
|
175
|
+
|
176
|
+
# General Inspecting
|
177
|
+
when Hash then entry.ai(ruby19_syntax: true)
|
178
|
+
|
179
|
+
# Arrays often contain Hashes. Dangerous Recursive?
|
180
|
+
when Array
|
181
|
+
entry.map { |x| format_table_entry(args, x) }.join("\n")
|
182
|
+
|
183
|
+
when Time
|
184
|
+
entry.to_s.colorize(:light_white)
|
185
|
+
|
186
|
+
# Default String Formatting
|
187
|
+
else
|
188
|
+
StringColor.do(key, entry)
|
189
|
+
end
|
190
|
+
|
191
|
+
if args[:truncate]
|
192
|
+
entry_truncate(formatted_entry, args[:truncate].join.to_i)
|
193
|
+
else
|
194
|
+
formatted_entry
|
195
|
+
end
|
196
|
+
rescue StandardError => e
|
197
|
+
binding.pry
|
198
|
+
end
|
199
|
+
|
180
200
|
# Print the Table in a Nice way
|
181
201
|
def self.render_table(entry, args)
|
202
|
+
entry = entry.map { |k, v| [k, format_table_entry(args, v, k)] }.to_h
|
203
|
+
# Pre-format Entry
|
204
|
+
|
182
205
|
table = TTY::Table.new(header: entry.keys, rows: [entry], orientation: :vertical)
|
183
206
|
|
184
|
-
|
207
|
+
LogBot.debug('Rendering Entries') if ENV['DEBUG']
|
208
|
+
table.render(:unicode, padding: [0, 1, 0, 1], multiline: true) do |renderer|
|
185
209
|
renderer.border.style = :cyan
|
186
210
|
|
187
|
-
renderer.filter = lambda do |val, _row_index, col_index|
|
188
|
-
|
189
|
-
|
190
|
-
# TODO: Better Casting?
|
191
|
-
val = val.to_f.round(args.round.first.to_s.to_i) if args.round
|
192
|
-
val.to_s.colorize(:blue)
|
193
|
-
else
|
194
|
-
val.to_s
|
195
|
-
end
|
196
|
-
else
|
197
|
-
val.to_s
|
198
|
-
end
|
199
|
-
end
|
211
|
+
# renderer.filter = lambda do |val, _row_index, col_index|
|
212
|
+
# render_table_entry(val, col_index, args)
|
213
|
+
# end
|
200
214
|
end
|
201
215
|
|
216
|
+
# LogBot.debug('Finish Render Table') if ENV['DEBUG']
|
202
217
|
# Fall Back to Amazing Inspect
|
203
218
|
rescue StandardError => e
|
204
|
-
|
219
|
+
if ENV['DEBUG']
|
220
|
+
LogBot.warn('Table', message: e.message)
|
221
|
+
ap e.backtrace
|
222
|
+
end
|
205
223
|
|
206
224
|
[
|
207
225
|
entry.ai,
|
@@ -210,6 +228,12 @@ module GreenHat
|
|
210
228
|
].join("\n")
|
211
229
|
end
|
212
230
|
|
231
|
+
def self.render_table_entry(val, col_index, args)
|
232
|
+
return val.to_s unless col_index == 1
|
233
|
+
|
234
|
+
format_table_entry(args, val)
|
235
|
+
end
|
236
|
+
|
213
237
|
# Main Entry Point for Filtering
|
214
238
|
def self.filter_start(log_list, filter_type, args, opts)
|
215
239
|
# Convert to Things
|
@@ -239,7 +263,7 @@ module GreenHat
|
|
239
263
|
results = data.clone.flatten.compact
|
240
264
|
results.select! do |row|
|
241
265
|
opts.send(type) do |opt|
|
242
|
-
filter_row_key(row, opt)
|
266
|
+
filter_row_key(row, opt, args)
|
243
267
|
end
|
244
268
|
end
|
245
269
|
|
@@ -273,9 +297,12 @@ module GreenHat
|
|
273
297
|
# Pluck
|
274
298
|
results = filter_pluck(results, args[:pluck]) if args.key?(:pluck)
|
275
299
|
|
300
|
+
# Truncate
|
301
|
+
# filter_truncate(results, args[:truncate].join.to_i) if args.key?(:truncate)
|
302
|
+
|
276
303
|
# Limit
|
277
304
|
if args[:limit]
|
278
|
-
results[0..args[:limit].map(&:to_s).join.to_i]
|
305
|
+
results[0..args[:limit].map(&:to_s).join.to_i - 1]
|
279
306
|
else
|
280
307
|
results
|
281
308
|
end
|
@@ -332,6 +359,17 @@ module GreenHat
|
|
332
359
|
results.map { |row| row.except(*except) }
|
333
360
|
end
|
334
361
|
|
362
|
+
def self.entry_truncate(entry, truncate)
|
363
|
+
# Ignore if Truncation Off
|
364
|
+
return entry if truncate.zero?
|
365
|
+
|
366
|
+
# Only truncate large strings
|
367
|
+
return entry unless entry.instance_of?(String) && entry.size > truncate
|
368
|
+
|
369
|
+
# Include '...' to indicate truncation
|
370
|
+
"#{entry.to_s[0..truncate]} #{'...'.colorize(:light_blue)}"
|
371
|
+
end
|
372
|
+
|
335
373
|
def self.filter_slice(results, slice)
|
336
374
|
# Avoid Empty Results
|
337
375
|
if slice.empty?
|
@@ -397,13 +435,18 @@ module GreenHat
|
|
397
435
|
end
|
398
436
|
|
399
437
|
# Break out filter row logic into separate method
|
400
|
-
def self.filter_row_key(row, param)
|
438
|
+
def self.filter_row_key(row, param, args)
|
401
439
|
# Ignore Other Logic if Field isn't even included
|
402
440
|
return false unless row.key? param.field
|
403
441
|
|
404
|
-
#
|
405
|
-
included =
|
442
|
+
# Sensitivity Check / Check for Match
|
443
|
+
included = if args.key?(:case)
|
444
|
+
row[param.field].to_s.include? param.value.to_s
|
445
|
+
else
|
446
|
+
row[param.field].to_s.downcase.include? param.value.to_s.downcase
|
447
|
+
end
|
406
448
|
|
449
|
+
# Pivot of off include vs exclude
|
407
450
|
if param.bang
|
408
451
|
!included
|
409
452
|
else
|
data/lib/greenhat/shell/log.rb
CHANGED
@@ -2,7 +2,6 @@ module GreenHat
|
|
2
2
|
# CLI Helper
|
3
3
|
module Shell
|
4
4
|
# Logs
|
5
|
-
# rubocop:disable Metrics/ModuleLength
|
6
5
|
module Log
|
7
6
|
def self.help
|
8
7
|
puts "\u2500".colorize(:cyan) * 20
|
@@ -14,16 +13,17 @@ module GreenHat
|
|
14
13
|
puts ' Just print selected logs'
|
15
14
|
puts ' filter'.colorize(:green)
|
16
15
|
puts ' Key/Field Filtering'
|
16
|
+
puts ' - See `filter_help`'
|
17
17
|
puts ' search'.colorize(:green)
|
18
18
|
puts ' General text by entry searching'
|
19
|
+
puts ' - See `search_help`'
|
19
20
|
puts ' ls'.colorize(:green)
|
20
21
|
puts ' List available files'
|
21
22
|
puts
|
23
|
+
end
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
puts
|
26
|
-
search_help
|
25
|
+
def self.filter_help
|
26
|
+
ShellHelper::Filter.help
|
27
27
|
end
|
28
28
|
|
29
29
|
# List Files Helpers
|
@@ -113,122 +113,15 @@ module GreenHat
|
|
113
113
|
end
|
114
114
|
|
115
115
|
# log filter --path='cloud/gitlab-automation' --path='/pull' --all
|
116
|
-
# cloud/gitlab-automation
|
117
116
|
# log filter --project=thingy --other_filter=asdf *
|
118
117
|
rescue StandardError => e
|
119
|
-
binding.pry
|
120
118
|
LogBot.fatal('Filter', message: e.message, backtrace: e.backtrace.first)
|
121
119
|
end
|
122
120
|
# ========================================================================
|
123
121
|
|
124
|
-
# rubocop:disable Metrics/MethodLength
|
125
|
-
def self.filter_help
|
126
|
-
puts "\u2500".colorize(:cyan) * 20
|
127
|
-
puts 'Log Filter'.colorize(:yellow)
|
128
|
-
puts "\u2500".colorize(:cyan) * 20
|
129
|
-
|
130
|
-
puts 'Options'.colorize(:blue)
|
131
|
-
puts '--raw'.colorize(:green)
|
132
|
-
puts ' Do not use less/paging'
|
133
|
-
puts
|
134
|
-
|
135
|
-
puts '--round'.colorize(:green)
|
136
|
-
puts ' Attempt to round all integers. Default: 2.'
|
137
|
-
puts ' E.g. --round, --round=3, --round=0'
|
138
|
-
puts
|
139
|
-
|
140
|
-
puts '--json'.colorize(:green)
|
141
|
-
puts ' Print output back into JSON'
|
142
|
-
puts
|
143
|
-
|
144
|
-
puts '--or'.colorize(:green)
|
145
|
-
puts ' Filters will use OR instead of AND'
|
146
|
-
puts
|
147
|
-
|
148
|
-
puts '--total'.colorize(:green)
|
149
|
-
puts ' Print only total count of matching entries'
|
150
|
-
puts
|
151
|
-
|
152
|
-
puts '--slice'.colorize(:green)
|
153
|
-
puts ' Extract specific fields from entries (slice multiple with comma)'
|
154
|
-
puts ' Ex: --slice=path or --slice=path,params'
|
155
|
-
puts
|
156
|
-
|
157
|
-
puts '--except'.colorize(:green)
|
158
|
-
puts ' Exclude specific fields (except multiple with comma)'
|
159
|
-
puts ' Ex: --except=params --except=params,path'
|
160
|
-
puts
|
161
|
-
|
162
|
-
puts '--stats'.colorize(:green)
|
163
|
-
puts ' Order/Count occurrances by field'
|
164
|
-
puts ' Ex: --stats=params --except=params,path'
|
165
|
-
puts
|
166
|
-
|
167
|
-
puts '--uniq'.colorize(:green)
|
168
|
-
puts ' Show unique values only'
|
169
|
-
puts ' Ex: --uniq=params --uniq=params,path'
|
170
|
-
puts
|
171
|
-
|
172
|
-
puts '--pluck'.colorize(:green)
|
173
|
-
puts ' Extract values from entries'
|
174
|
-
puts ' Ex: --pluck=params --pluck=params,path'
|
175
|
-
puts
|
176
|
-
|
177
|
-
puts '--archive'.colorize(:green)
|
178
|
-
puts ' Limit to specific archvie name (inclusive). Matching SOS tar.gz name'
|
179
|
-
puts ' Ex: --archive=dev-gitlab_20210622154626, --archive=202106,202107'
|
180
|
-
puts
|
181
|
-
|
182
|
-
puts '--sort'.colorize(:green)
|
183
|
-
puts ' Sort by multiple fields'
|
184
|
-
puts ' Ex: --sort=duration_s,db_duration_s'
|
185
|
-
puts
|
186
|
-
|
187
|
-
puts '--reverse'.colorize(:green)
|
188
|
-
puts ' Reverse all results'
|
189
|
-
puts ' Ex: --reverse'
|
190
|
-
puts
|
191
|
-
|
192
|
-
puts '--combine'.colorize(:green)
|
193
|
-
puts ' Omit archive identifier dividers. Useful with sort or time filters'
|
194
|
-
puts ' Ex: --combine'
|
195
|
-
puts
|
196
|
-
|
197
|
-
puts '--start'.colorize(:green)
|
198
|
-
puts ' Show events after specified time. Filtered by the `time` field'
|
199
|
-
puts ' Use with `--end` for between selections'
|
200
|
-
puts ' Ex: log filter --start="2021-06-22 14:44 UTC" --end="2021-06-22 14:45 UTC"'
|
201
|
-
puts
|
202
|
-
|
203
|
-
puts '--end'.colorize(:green)
|
204
|
-
puts ' Show events before specified time. Filtered by the `time` field'
|
205
|
-
puts ' Use with `--start` for between selections'
|
206
|
-
puts ' Ex: log filter --end="2021-06-22"'
|
207
|
-
puts
|
208
|
-
|
209
|
-
puts 'Field Searching'.colorize(:blue)
|
210
|
-
puts ' --[key]=[value]'
|
211
|
-
puts ' Search in key for value'
|
212
|
-
puts ' Example: --path=mirror/pull'
|
213
|
-
puts
|
214
|
-
|
215
|
-
puts 'Search specific logs'.colorize(:blue)
|
216
|
-
puts ' Any non dash parameters will be the log list to search from'
|
217
|
-
puts " Ex: log filter --path=api sidekiq/current (hint: use `#{'ls'.colorize(:yellow)}` for log names"
|
218
|
-
puts
|
219
|
-
|
220
|
-
puts 'Example Queries'.colorize(:blue)
|
221
|
-
puts " Also see #{'filter_examples'.colorize(:light_blue)} for even more examples"
|
222
|
-
puts 'log filter --class=BuildFinishedWorker sidekiq/current --slice=time,message'
|
223
|
-
puts 'log filter gitlab-rails/api_json.log --slice=ua --uniq=ua --ua=gitlab-runner'
|
224
|
-
|
225
|
-
puts
|
226
|
-
end
|
227
|
-
# rubocop:enable Metrics/MethodLength
|
228
|
-
|
229
122
|
# rubocop:disable Layout/LineLength
|
230
123
|
# TODO: Add a lot more examples
|
231
|
-
def self.
|
124
|
+
def self.examples
|
232
125
|
puts 'Find `done` job for sidekiq, sort by duration, only duration, and show longest first'.colorize(:light_green)
|
233
126
|
puts 'log filter sidekiq/current --job_status=done --sort=duration_s,db_duration_s --slice=duration_s,db_duration_s --reverse'
|
234
127
|
puts
|
@@ -236,8 +129,8 @@ module GreenHat
|
|
236
129
|
puts 'log filter --status=500 --slice=exception.message gitlab-rails/production_json.log'
|
237
130
|
puts
|
238
131
|
end
|
239
|
-
# rubocop:enable Layout/LineLength
|
240
132
|
|
133
|
+
# rubocop:enable Layout/LineLength
|
241
134
|
# ========================================================================
|
242
135
|
# Search (Full Text / String Search)
|
243
136
|
# ========================================================================
|
@@ -325,7 +218,7 @@ module GreenHat
|
|
325
218
|
|
326
219
|
puts 'Search specific logs'.colorize(:blue)
|
327
220
|
puts ' Any non dash parameters will be the log list to search from'
|
328
|
-
puts " Ex: log filter --path=api sidekiq/current (hint: use `#{'ls'.colorize(:yellow)}` for log names"
|
221
|
+
puts " Ex: log filter --path=api sidekiq/current (hint: use `#{'ls'.colorize(:yellow)}` for log names)"
|
329
222
|
puts
|
330
223
|
|
331
224
|
puts 'Example Queries'.colorize(:blue)
|
@@ -337,7 +230,6 @@ module GreenHat
|
|
337
230
|
|
338
231
|
# ------------------------------------------------------------------------
|
339
232
|
end
|
340
|
-
# rubocop:enable Metrics/ModuleLength
|
341
233
|
end
|
342
234
|
end
|
343
235
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module GreenHat
|
2
|
+
module ShellHelper
|
3
|
+
# Helper to organize page check / methods
|
4
|
+
module Page
|
5
|
+
# Check if paging should be skipped
|
6
|
+
def self.skip?(args, data)
|
7
|
+
return count_rows(data) unless args[:page]
|
8
|
+
|
9
|
+
args[:page].first == :false
|
10
|
+
end
|
11
|
+
|
12
|
+
# Array/Hash and String pagination check. Don't unncessarily loop through everything
|
13
|
+
def self.count_rows(data)
|
14
|
+
height = TTY::Screen.height
|
15
|
+
size = 0
|
16
|
+
|
17
|
+
data.each do |entry|
|
18
|
+
size += case entry
|
19
|
+
when Hash then entry.keys.count
|
20
|
+
when Array then entry.count
|
21
|
+
else
|
22
|
+
1
|
23
|
+
end
|
24
|
+
|
25
|
+
break if size > height
|
26
|
+
end
|
27
|
+
|
28
|
+
size < height
|
29
|
+
end
|
30
|
+
end
|
31
|
+
# ----
|
32
|
+
end
|
33
|
+
end
|
@@ -4,8 +4,51 @@ module GreenHat
|
|
4
4
|
# Logs
|
5
5
|
module Process
|
6
6
|
# Easy Show All
|
7
|
-
|
8
|
-
|
7
|
+
# Filter --archive
|
8
|
+
|
9
|
+
def self.ls
|
10
|
+
help
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.help
|
14
|
+
puts "\u2500".colorize(:cyan) * 20
|
15
|
+
puts "#{'Process'.colorize(:yellow)} - ps helper"
|
16
|
+
puts "\u2500".colorize(:cyan) * 20
|
17
|
+
|
18
|
+
puts 'Command Summary'.colorize(:blue)
|
19
|
+
puts ' ps'.colorize(:green)
|
20
|
+
puts ' Raw `ps`'
|
21
|
+
puts ' filter'.colorize(:green)
|
22
|
+
puts ' Key/Field Filtering'
|
23
|
+
puts ' - See `filter_help`'
|
24
|
+
puts
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.filter_help
|
28
|
+
ShellHelper::Filter.help
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.ps(raw = {})
|
32
|
+
_log_list, _1opts, args = ShellHelper.param_parse(raw)
|
33
|
+
ShellHelper.file_output GreenHat::Ps.ps(args)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.filter(raw = {})
|
37
|
+
_log_list, opts, args = ShellHelper.param_parse(raw)
|
38
|
+
|
39
|
+
# AND / OR Filtering
|
40
|
+
filter_type = args.or ? :any? : :all?
|
41
|
+
|
42
|
+
# ShellHelper.file_output
|
43
|
+
results = ShellHelper.filter_start(GreenHat::Ps.ps(args).map(&:name), filter_type, args, opts)
|
44
|
+
|
45
|
+
# Check Search Results
|
46
|
+
if results.instance_of?(Hash) && results.values.flatten.empty?
|
47
|
+
puts 'No results'.colorize(:red)
|
48
|
+
else
|
49
|
+
# This causes the key 'colorized' output to also be included
|
50
|
+
ShellHelper.show(results.to_a.compact.flatten, args)
|
51
|
+
end
|
9
52
|
end
|
10
53
|
end
|
11
54
|
end
|
@@ -19,7 +19,7 @@ module GreenHat
|
|
19
19
|
|
20
20
|
attr_accessor :archive, :host, :os_release, :selinux_status, :cpu, :uname,
|
21
21
|
:timedatectl, :uptime, :meminfo, :gitlab_manifest, :gitlab_status,
|
22
|
-
:production_log, :api_log, :application_log, :sidekiq_log, :free_m
|
22
|
+
:production_log, :api_log, :application_log, :sidekiq_log, :free_m, :disk_free
|
23
23
|
|
24
24
|
# Find Needed Files for Report
|
25
25
|
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
@@ -40,6 +40,7 @@ module GreenHat
|
|
40
40
|
self.api_log = archive.things.find { |x| x.name == 'gitlab-rails/api_json.log' }
|
41
41
|
self.application_log = archive.things.find { |x| x.name == 'gitlab-rails/application_json.log' }
|
42
42
|
self.sidekiq_log = archive.things.find { |x| x.name == 'sidekiq/current' }
|
43
|
+
self.disk_free = archive.things.find { |x| x.name == 'df_h' }
|
43
44
|
end
|
44
45
|
|
45
46
|
def show
|
@@ -62,11 +63,17 @@ module GreenHat
|
|
62
63
|
# Memory
|
63
64
|
if meminfo || free_m
|
64
65
|
output << 'Memory'.colorize(:light_yellow)
|
65
|
-
output <<
|
66
|
+
output << memory_perc if meminfo
|
66
67
|
output << memory_free if free_m
|
67
68
|
output << ''
|
68
69
|
end
|
69
70
|
|
71
|
+
# Disk
|
72
|
+
if disk_free
|
73
|
+
output << disks
|
74
|
+
output << ''
|
75
|
+
end
|
76
|
+
|
70
77
|
# Gitlab
|
71
78
|
output << 'GitLab'.colorize(:light_yellow) if gitlab_manifest
|
72
79
|
output << gitlab_version if gitlab_manifest
|
@@ -209,6 +216,9 @@ module GreenHat
|
|
209
216
|
end
|
210
217
|
|
211
218
|
def sys_time
|
219
|
+
# Ignore if Empty
|
220
|
+
return false if timedatectl.data.nil?
|
221
|
+
|
212
222
|
ntp_statuses = timedatectl.data.slice(*ntp_keys).values.compact
|
213
223
|
|
214
224
|
enabled = %w[active yes] & ntp_statuses
|
@@ -260,26 +270,18 @@ module GreenHat
|
|
260
270
|
].join
|
261
271
|
end
|
262
272
|
|
263
|
-
def
|
273
|
+
def memory_perc
|
264
274
|
total = ShellHelper.human_size_to_number(meminfo.data['MemTotal'])
|
265
275
|
free = ShellHelper.human_size_to_number(meminfo.data['MemFree'])
|
266
276
|
used = percent((total - free), total)
|
277
|
+
|
267
278
|
[
|
268
|
-
title('
|
269
|
-
'['.colorize(:light_black),
|
279
|
+
title('Usage'),
|
280
|
+
' ['.colorize(:light_black),
|
270
281
|
'='.colorize(:green) * (used / 2),
|
271
282
|
' ' * (50 - used / 2),
|
272
283
|
']'.colorize(:light_black),
|
273
|
-
" #{100 - percent(free, total)}%".colorize(:green)
|
274
|
-
"\n",
|
275
|
-
title('Total'),
|
276
|
-
number_to_human_size(total).colorize(:light_white),
|
277
|
-
"\n",
|
278
|
-
title('Used'),
|
279
|
-
number_to_human_size(total - free),
|
280
|
-
"\n",
|
281
|
-
title('Free'),
|
282
|
-
number_to_human_size(free)
|
284
|
+
" #{100 - percent(free, total)}%".colorize(:green) # Inverse
|
283
285
|
].join
|
284
286
|
end
|
285
287
|
|
@@ -288,6 +290,8 @@ module GreenHat
|
|
288
290
|
|
289
291
|
return unless free
|
290
292
|
|
293
|
+
formatted_mem = free_m.data.map { |x| GreenHat::Memory.memory_row x }
|
294
|
+
|
291
295
|
[
|
292
296
|
title('Total', :cyan, 14),
|
293
297
|
number_to_human_size(free.total.to_i * 1024**2),
|
@@ -299,7 +303,23 @@ module GreenHat
|
|
299
303
|
number_to_human_size(free.free.to_i * 1024**2),
|
300
304
|
"\n",
|
301
305
|
title('Available', :green, 14),
|
302
|
-
number_to_human_size(free.available.to_i * 1024**2)
|
306
|
+
number_to_human_size(free.available.to_i * 1024**2),
|
307
|
+
"\n\n",
|
308
|
+
formatted_mem.map { |x| x.prepend ' ' * 2 }.join("\n")
|
309
|
+
].join
|
310
|
+
end
|
311
|
+
|
312
|
+
def disks
|
313
|
+
# GreenHat::Disk.df({archive: []})
|
314
|
+
file = GreenHat::Disk.df({ archive: [archive.name] })
|
315
|
+
|
316
|
+
disk_list = GreenHat::Disk.format_output(file.first, false, 3)
|
317
|
+
|
318
|
+
# Preapre / Indent List
|
319
|
+
[
|
320
|
+
'Disks(Top % Usage)'.colorize(:light_yellow),
|
321
|
+
"\n",
|
322
|
+
disk_list.each { |x| x.prepend(' ' * 4) }.join("\n")
|
303
323
|
].join
|
304
324
|
end
|
305
325
|
|
@@ -44,7 +44,7 @@ module GreenHat
|
|
44
44
|
]
|
45
45
|
},
|
46
46
|
'log/syslog' => {
|
47
|
-
format: :
|
47
|
+
format: :syslog,
|
48
48
|
log: true,
|
49
49
|
pattern: [
|
50
50
|
%r{log/syslog}
|
@@ -95,6 +95,13 @@ module GreenHat
|
|
95
95
|
%r{consul/current}
|
96
96
|
]
|
97
97
|
},
|
98
|
+
'consul/failover_pgbouncer.log' => {
|
99
|
+
format: :raw,
|
100
|
+
log: true,
|
101
|
+
pattern: [
|
102
|
+
%r{consul/failover_pgbouncer.log}
|
103
|
+
]
|
104
|
+
},
|
98
105
|
'pgbouncer/current' => {
|
99
106
|
format: :time_space,
|
100
107
|
log: true,
|
@@ -179,6 +186,13 @@ module GreenHat
|
|
179
186
|
%r{logrotate/current}
|
180
187
|
]
|
181
188
|
},
|
189
|
+
'gitlab-rails/grpc.log' => {
|
190
|
+
format: :raw,
|
191
|
+
log: true,
|
192
|
+
pattern: [
|
193
|
+
%r{gitlab-rails/grpc.log}
|
194
|
+
]
|
195
|
+
},
|
182
196
|
'gitlab-rails/api_json.log' => {
|
183
197
|
format: :api_json,
|
184
198
|
log: true,
|
@@ -301,6 +315,13 @@ module GreenHat
|
|
301
315
|
/mount/
|
302
316
|
]
|
303
317
|
},
|
318
|
+
'nginx/current' => {
|
319
|
+
format: :time_space,
|
320
|
+
log: true,
|
321
|
+
pattern: [
|
322
|
+
%r{nginx/current}
|
323
|
+
]
|
324
|
+
},
|
304
325
|
'nginx/gitlab_pages_access.log' => {
|
305
326
|
format: :raw,
|
306
327
|
log: true,
|
@@ -671,7 +692,7 @@ module GreenHat
|
|
671
692
|
]
|
672
693
|
},
|
673
694
|
'log/messages' => {
|
674
|
-
format: :
|
695
|
+
format: :syslog,
|
675
696
|
log: true,
|
676
697
|
pattern: [
|
677
698
|
%r{log/messages}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Top
|
2
|
+
module GreenHat
|
3
|
+
# Log
|
4
|
+
module Formatters
|
5
|
+
# Formatters for bracket logs (dmesg, sos)
|
6
|
+
def format_syslog
|
7
|
+
self.result = raw.map do |row|
|
8
|
+
next if row.empty? || row == "\n"
|
9
|
+
|
10
|
+
format_syslog_row(row)
|
11
|
+
end
|
12
|
+
|
13
|
+
result.compact!
|
14
|
+
end
|
15
|
+
|
16
|
+
# Split / Parse Time
|
17
|
+
# TODO: Better sys log parsing? Cannot find ready-made regex/ruby parser
|
18
|
+
def format_syslog_row(row)
|
19
|
+
month, day, time, device, service, message = row.split(' ', 6)
|
20
|
+
service.gsub!(':', '')
|
21
|
+
pid = service.match(/\[(.*?)\]/)&.captures&.first
|
22
|
+
|
23
|
+
{
|
24
|
+
time: format_time_parse("#{month} #{day} #{time}"),
|
25
|
+
device: device,
|
26
|
+
service: service,
|
27
|
+
message: message,
|
28
|
+
pid: pid
|
29
|
+
}
|
30
|
+
|
31
|
+
# Return everything incase of error
|
32
|
+
rescue StandardError => e
|
33
|
+
LogBot.warn('SysLog Parse', e.message)
|
34
|
+
{
|
35
|
+
message: row
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
# A module for calculating table data column widths
|
6
|
+
#
|
7
|
+
# Used by {Table} to manage column sizing.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
module Columns
|
11
|
+
# Converts column widths to array format or infers default widths
|
12
|
+
#
|
13
|
+
# @param [TTY::Table] table
|
14
|
+
#
|
15
|
+
# @param [Array, Numeric, NilClass] column_widths
|
16
|
+
#
|
17
|
+
# @return [Array[Integer]]
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
def widths_from(table, column_widths = nil)
|
21
|
+
case column_widths
|
22
|
+
when Array
|
23
|
+
assert_widths(column_widths, table.columns_size)
|
24
|
+
Array(column_widths).map(&:to_i)
|
25
|
+
when Numeric
|
26
|
+
Array.new(table.columns_size, column_widths)
|
27
|
+
when NilClass
|
28
|
+
# THE SHIM! Strings that are too large fail to render correctly to do an empty result
|
29
|
+
# Set the maximum table width to half the screen size. Can't be full size due to the table headers
|
30
|
+
LogBot.debug('TTY Column Width') if ENV['DEBUG']
|
31
|
+
extract_widths(table.data).map { |x| x >= TTY::Screen.width ? (TTY::Screen.width * 3 / 4) : x }
|
32
|
+
|
33
|
+
else
|
34
|
+
raise TypeError, 'Invalid type for column widths'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
module_function :widths_from
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/greenhat/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: greenhat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Davin Walker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-08-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: amazing_print
|
@@ -399,14 +399,17 @@ files:
|
|
399
399
|
- lib/greenhat/settings.rb
|
400
400
|
- lib/greenhat/shell.rb
|
401
401
|
- lib/greenhat/shell/cat.rb
|
402
|
+
- lib/greenhat/shell/color_string.rb
|
402
403
|
- lib/greenhat/shell/disk.rb
|
403
404
|
- lib/greenhat/shell/faststats.rb
|
405
|
+
- lib/greenhat/shell/filter.rb
|
404
406
|
- lib/greenhat/shell/gitlab.rb
|
405
407
|
- lib/greenhat/shell/help.rb
|
406
408
|
- lib/greenhat/shell/helper.rb
|
407
409
|
- lib/greenhat/shell/log.rb
|
408
410
|
- lib/greenhat/shell/memory.rb
|
409
411
|
- lib/greenhat/shell/network.rb
|
412
|
+
- lib/greenhat/shell/page.rb
|
410
413
|
- lib/greenhat/shell/process.rb
|
411
414
|
- lib/greenhat/shell/report.rb
|
412
415
|
- lib/greenhat/thing.rb
|
@@ -425,6 +428,7 @@ files:
|
|
425
428
|
- lib/greenhat/thing/formatters/multiline_json.rb
|
426
429
|
- lib/greenhat/thing/formatters/raw.rb
|
427
430
|
- lib/greenhat/thing/formatters/shellwords.rb
|
431
|
+
- lib/greenhat/thing/formatters/syslog.rb
|
428
432
|
- lib/greenhat/thing/formatters/table.rb
|
429
433
|
- lib/greenhat/thing/formatters/time_json.rb
|
430
434
|
- lib/greenhat/thing/formatters/time_shellwords.rb
|
@@ -435,6 +439,7 @@ files:
|
|
435
439
|
- lib/greenhat/thing/kind.rb
|
436
440
|
- lib/greenhat/thing/spinner.rb
|
437
441
|
- lib/greenhat/thing/super_log.rb
|
442
|
+
- lib/greenhat/tty/columns.rb
|
438
443
|
- lib/greenhat/tty/custom_line.rb
|
439
444
|
- lib/greenhat/tty/line.rb
|
440
445
|
- lib/greenhat/tty/reader.rb
|