greenhat 0.1.5 → 0.3.2
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/accessors/disk.rb +58 -2
- data/lib/greenhat/accessors/gitlab.rb +75 -0
- data/lib/greenhat/accessors/memory.rb +10 -10
- data/lib/greenhat/accessors/process.rb +10 -1
- data/lib/greenhat/cli.rb +148 -63
- data/lib/greenhat/color.rb +27 -0
- data/lib/greenhat/logbot.rb +9 -9
- data/lib/greenhat/settings.rb +51 -3
- data/lib/greenhat/shell/args.rb +146 -0
- data/lib/greenhat/shell/cat.rb +25 -73
- data/lib/greenhat/shell/color_string.rb +43 -0
- data/lib/greenhat/shell/disk.rb +30 -42
- data/lib/greenhat/shell/faststats.rb +104 -58
- data/lib/greenhat/shell/field_helper.rb +75 -0
- data/lib/greenhat/shell/filter_help.rb +162 -0
- data/lib/greenhat/shell/gitlab.rb +61 -2
- data/lib/greenhat/shell/help.rb +98 -15
- data/lib/greenhat/shell/list.rb +46 -0
- data/lib/greenhat/shell/log.rb +115 -209
- data/lib/greenhat/shell/page.rb +39 -0
- data/lib/greenhat/shell/process.rb +57 -2
- data/lib/greenhat/shell/report.rb +70 -60
- data/lib/greenhat/shell/shell_helper.rb +654 -0
- data/lib/greenhat/shell.rb +27 -13
- data/lib/greenhat/thing/file_types.rb +54 -7
- data/lib/greenhat/thing/formatters/json_shellwords.rb +0 -3
- data/lib/greenhat/thing/formatters/nginx.rb +44 -0
- data/lib/greenhat/thing/formatters/syslog.rb +39 -0
- data/lib/greenhat/thing/helpers.rb +4 -4
- data/lib/greenhat/thing/kind.rb +9 -2
- data/lib/greenhat/thing/spinner.rb +3 -3
- data/lib/greenhat/thing.rb +25 -3
- data/lib/greenhat/tty/columns.rb +44 -0
- data/lib/greenhat/version.rb +1 -1
- data/lib/greenhat.rb +16 -14
- metadata +42 -17
- data/lib/greenhat/shell/helper.rb +0 -541
data/lib/greenhat/shell/help.rb
CHANGED
@@ -1,15 +1,98 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
1
|
+
module GreenHat
|
2
|
+
# Root Level Shell / Splitting Help into its own file
|
3
|
+
module Shell
|
4
|
+
# rubocop:disable Layout/LineLength
|
5
|
+
def self.help
|
6
|
+
puts 'Quickstart'.pastel(:blue)
|
7
|
+
puts " Commands are organized by submodule: #{'log'.pastel(:blue)}, #{'cat'.pastel(:blue)}, #{'faststats'.pastel(:blue)}"
|
8
|
+
puts " Use #{'help'.pastel(:bright_blue)} for available commands in each module"
|
9
|
+
puts
|
10
|
+
|
11
|
+
puts ' Example Commands'
|
12
|
+
puts ' log filter sidekiq/current'.pastel(:yellow)
|
13
|
+
puts ' disk free'.pastel(:yellow)
|
14
|
+
puts
|
15
|
+
|
16
|
+
puts 'Top Level Commands'.pastel(:blue)
|
17
|
+
puts ' report'.pastel(:green)
|
18
|
+
puts ' Show summary report of SOS Report. OS, CPU, Memory, Disk, and etc'
|
19
|
+
puts ' --raw, no pagination'
|
20
|
+
puts ' --archive=<redis/archive>, filter by archive name'
|
21
|
+
puts
|
22
|
+
|
23
|
+
puts ' ps,df,netstat,free,uptime,uname'.pastel(:green)
|
24
|
+
puts ' Show common files from archives / Emulate terminal commands'
|
25
|
+
puts
|
26
|
+
|
27
|
+
puts ' Noisy Output'.pastel(:green)
|
28
|
+
puts " Use #{'quiet'.pastel(:blue)} or to #{'debug'.pastel(:blue)} to toggle greenhat logging"
|
29
|
+
puts
|
30
|
+
|
31
|
+
cli_shortcuts
|
32
|
+
|
33
|
+
puts "See #{'about'.pastel(:bright_blue)} for more details about GreenHat"
|
34
|
+
end
|
35
|
+
# rubocop:enable Layout/LineLength
|
36
|
+
|
37
|
+
def self.cli_shortcuts
|
38
|
+
puts "\u2500".pastel(:cyan) * 25
|
39
|
+
puts 'Nav / Keyboard Shortcuts'.pastel(:blue)
|
40
|
+
puts "\u2500".pastel(:cyan) * 25
|
41
|
+
puts <<~BLOCK
|
42
|
+
| Hotkey | Description |
|
43
|
+
| ------------------- | ----------------------- |
|
44
|
+
| Ctrl + U | Clear Input |
|
45
|
+
| Ctrl + A | Go to beginning |
|
46
|
+
| Ctrl + E | Go to End |
|
47
|
+
| Ctrl + Left/Right | Move left/right by word |
|
48
|
+
| Ctrl + D, Ctrl + Z | Exit |
|
49
|
+
| Ctrl + C, Shift Tab | Up one module |
|
50
|
+
BLOCK
|
51
|
+
puts
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.about
|
55
|
+
puts "\u2500".pastel(:cyan) * 20
|
56
|
+
puts "About GreenHat #{GreenHat::VERSION}".pastel(:yellow)
|
57
|
+
puts "\u2500".pastel(:cyan) * 20
|
58
|
+
|
59
|
+
puts 'TLDR; Put in SOS reports, run commands, and find stuffs'.pastel(:green)
|
60
|
+
puts
|
61
|
+
|
62
|
+
puts <<~BLOCK
|
63
|
+
General overview (OS, Memory, Disk, GitLab)
|
64
|
+
#{'report'.pastel(:bright_cyan)}
|
65
|
+
|
66
|
+
Log Searching
|
67
|
+
#{'log filter sidekiq/current --job_status=done --sort=duration_s,db_duration_s --slice=duration_s,db_duration_s --reverse'.pastel(:bright_cyan)}
|
68
|
+
|
69
|
+
Read File(s) across SOS archives
|
70
|
+
#{'cat uptime'.pastel(:bright_cyan)} or #{'cat mount etc/fstab'.pastel(:bright_cyan)}
|
71
|
+
|
72
|
+
BLOCK
|
73
|
+
|
74
|
+
puts 'What it does / How it works'.pastel(:blue)
|
75
|
+
puts
|
76
|
+
puts <<~BLOCK
|
77
|
+
GreenHat is a support utility to enhance troubleshooting with GitLabSOS Reports and log files. Make it easy to find stuff
|
78
|
+
|
79
|
+
Supplied input files are staged, unpacked, identified, and normalized.
|
80
|
+
This enables other utilities to automatically find and present data. (Faststats, report, and etc)
|
81
|
+
|
82
|
+
BLOCK
|
83
|
+
|
84
|
+
puts 'Commands and Submodules'.pastel(:blue)
|
85
|
+
puts
|
86
|
+
puts <<~BLOCK
|
87
|
+
Greenhat is broken down into different "modules". Each module has its own commands. For example: log, cat, and faststats.
|
88
|
+
You can "cd" into or execute commands directly against with their names.
|
89
|
+
|
90
|
+
- Direct: #{'log filter sidekiq/current'.pastel(:cyan)}
|
91
|
+
- Or within: First #{'log'.pastel(:cyan)}, then #{'filter sidekiq/current'.pastel(:cyan)}
|
92
|
+
|
93
|
+
You can find the list of commands and submodules of each with #{'help'.pastel(:yellow)}
|
94
|
+
|
95
|
+
BLOCK
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module GreenHat
|
2
|
+
module ShellHelper
|
3
|
+
# Helper to handle listing of files
|
4
|
+
module List
|
5
|
+
# List Files Helpers
|
6
|
+
def self.list(raw = [], files)
|
7
|
+
filter, flags, _args = Args.parse(raw)
|
8
|
+
|
9
|
+
# Sort
|
10
|
+
files.sort_by!(&:name)
|
11
|
+
|
12
|
+
# Simplified vs Full. Full file name/path / or just file kinds
|
13
|
+
all = flags.key?(:all) || flags.key?(:a)
|
14
|
+
|
15
|
+
# Short & Uniq
|
16
|
+
files.uniq!(&:name) unless all
|
17
|
+
|
18
|
+
# Filter / Pattern
|
19
|
+
files.select! { |f| filter.any? { |x| f.name.include? x } } unless filter.empty?
|
20
|
+
|
21
|
+
# Print
|
22
|
+
files.each do |log|
|
23
|
+
if all
|
24
|
+
puts "- #{log.friendly_name}"
|
25
|
+
else
|
26
|
+
puts "- #{log.name.pastel(:yellow)}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Unified Help
|
32
|
+
def self.help
|
33
|
+
puts ' ls'.pastel(:green)
|
34
|
+
puts ' List available files'
|
35
|
+
puts ' Options'.pastel(:cyan)
|
36
|
+
puts ' -a, --all, show full file name/path including source'
|
37
|
+
puts ' <string> filter available'
|
38
|
+
puts ' Examples'.pastel(:cyan)
|
39
|
+
puts ' ls -a rails'
|
40
|
+
puts ' ls sys'
|
41
|
+
puts
|
42
|
+
end
|
43
|
+
# ----
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/greenhat/shell/log.rb
CHANGED
@@ -2,280 +2,187 @@ module GreenHat
|
|
2
2
|
# CLI Helper
|
3
3
|
module Shell
|
4
4
|
# Logs
|
5
|
-
# rubocop:disable Metrics/ModuleLength
|
6
5
|
module Log
|
6
|
+
def self.auto_complete(list, word)
|
7
|
+
# Argument Parsing
|
8
|
+
files, flags, _args = Args.parse(list)
|
9
|
+
|
10
|
+
# Don't try to autocomplete anything else
|
11
|
+
return nil unless word =~ /^-/
|
12
|
+
|
13
|
+
# Clean Up
|
14
|
+
word.delete!('-')
|
15
|
+
matches = FieldHelper.filter_flags(word)
|
16
|
+
|
17
|
+
if matches.count == 1
|
18
|
+
"--#{matches.first}"
|
19
|
+
elsif matches.count > 1
|
20
|
+
puts "#{'Filter Options:'.pastel(:bright_blue)} #{matches.join(' ').pastel(:bright_green)}"
|
21
|
+
"--#{Cli.common_substr(matches)}"
|
22
|
+
# -----------------------------------
|
23
|
+
# TODO: Fix Icky Double Nesting
|
24
|
+
elsif files.count.nonzero?
|
25
|
+
matches = FieldHelper.fields_find(files, word, flags)
|
26
|
+
|
27
|
+
return nil if matches.nil?
|
28
|
+
|
29
|
+
if matches.count == 1
|
30
|
+
"--#{matches.first}"
|
31
|
+
elsif matches.count > 1
|
32
|
+
puts "#{'Field Options:'.pastel(:bright_blue)} #{matches.join(' ').pastel(:bright_green)}"
|
33
|
+
"--#{Cli.common_substr(matches.map(&:to_s))}"
|
34
|
+
elsif FieldHelper.field_auto_complete?(word)
|
35
|
+
FieldHelper.field_auto_complete(word, files, flags)
|
36
|
+
end
|
37
|
+
# -----------------------------------
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
7
41
|
def self.help
|
8
|
-
puts "\u2500".
|
9
|
-
puts "#{'Logs'.
|
10
|
-
puts "\u2500".
|
42
|
+
puts "\u2500".pastel(:cyan) * 20
|
43
|
+
puts "#{'Logs'.pastel(:yellow)} find stuff"
|
44
|
+
puts "\u2500".pastel(:cyan) * 20
|
45
|
+
|
46
|
+
puts 'Command Summary'.pastel(:blue)
|
47
|
+
puts ' filter'.pastel(:green)
|
48
|
+
puts " Primary way for log searching within greenhat. See #{'filter_help'.pastel(:blue)}"
|
49
|
+
puts ' Time, round, slice/except, and/or, stats, uniq, sort'
|
50
|
+
puts
|
11
51
|
|
12
|
-
puts '
|
13
|
-
puts ' show'.colorize(:green)
|
52
|
+
puts ' show'.pastel(:green)
|
14
53
|
puts ' Just print selected logs'
|
15
|
-
puts ' filter'.colorize(:green)
|
16
|
-
puts ' Key/Field Filtering'
|
17
|
-
puts ' search'.colorize(:green)
|
18
|
-
puts ' General text by entry searching'
|
19
|
-
puts ' ls'.colorize(:green)
|
20
|
-
puts ' List available files'
|
21
54
|
puts
|
22
55
|
|
23
|
-
|
24
|
-
|
56
|
+
puts ' search'.pastel(:green)
|
57
|
+
puts " General full text by file searching. See #{'search_help'.pastel(:blue)}"
|
25
58
|
puts
|
26
|
-
search_help
|
27
|
-
end
|
28
|
-
|
29
|
-
# List Files Helpers
|
30
|
-
def self.list(args = [])
|
31
|
-
all = false
|
32
|
-
all = true if args.include?('-a') || args.include?('--all')
|
33
|
-
|
34
|
-
files = ShellHelper::Log.list
|
35
59
|
|
36
|
-
|
37
|
-
files.sort_by!(&:name)
|
60
|
+
puts ShellHelper::List.help
|
38
61
|
|
39
|
-
#
|
40
|
-
|
62
|
+
puts "See #{'examples'.pastel(:bright_blue)} for query examples"
|
63
|
+
end
|
41
64
|
|
42
|
-
|
43
|
-
|
44
|
-
if all
|
45
|
-
puts "- #{log.friendly_name}"
|
46
|
-
else
|
47
|
-
puts "- #{log.name.colorize(:yellow)}"
|
48
|
-
end
|
49
|
-
end
|
65
|
+
def self.filter_help
|
66
|
+
ShellHelper::Filter.help
|
50
67
|
end
|
51
68
|
|
52
69
|
def self.ls(args = [])
|
53
|
-
list(args)
|
70
|
+
ShellHelper::List.list(args, ShellHelper::Log.list)
|
54
71
|
end
|
55
72
|
|
56
|
-
def self.show(
|
57
|
-
#
|
58
|
-
|
59
|
-
|
60
|
-
# Convert to Things
|
61
|
-
logs = ShellHelper.find_things(log_list)
|
73
|
+
def self.show(raw = {})
|
74
|
+
# Extract Args
|
75
|
+
files_list, flags, _args = Args.parse(raw)
|
62
76
|
|
63
|
-
|
77
|
+
# Collect Files
|
78
|
+
files = ShellHelper.files(files_list, Thing.all, flags)
|
64
79
|
|
65
|
-
ShellHelper.show
|
80
|
+
ShellHelper.show files.map(&:data).flatten
|
66
81
|
end
|
67
82
|
|
68
83
|
# ========================================================================
|
69
|
-
# Filter
|
84
|
+
# Filter (See Filter Help)
|
70
85
|
# ========================================================================
|
71
|
-
# Supported Params
|
72
|
-
# --or (Filter OR instead of AND)
|
73
|
-
# --total Total Count Entries Only
|
74
|
-
# --project=thingy --exclude_this!=asdf *
|
75
|
-
# --slice: only grab specific fields --slice=path (slice multiple with comma)
|
76
|
-
# --slice=time,path (E.g. log filter --path='mirror/pull' --slice=path,time )
|
77
|
-
# --except: Exclude specific fields (except multiple with comma)
|
78
|
-
# Example: log filter --path='mirror/pull' --except=params
|
79
86
|
def self.default(raw_list)
|
80
87
|
filter(raw_list)
|
81
88
|
end
|
82
89
|
|
83
|
-
def self.filter(
|
90
|
+
def self.filter(raw)
|
84
91
|
# Print Helper
|
85
|
-
if
|
92
|
+
if raw == ['help']
|
86
93
|
filter_help
|
87
94
|
return true
|
88
95
|
end
|
89
96
|
|
90
|
-
#
|
91
|
-
|
97
|
+
# Argument Parsing
|
98
|
+
files, flags, args = Args.parse(raw)
|
92
99
|
|
93
100
|
# Prepare Log List
|
94
|
-
|
95
|
-
|
96
|
-
# AND / OR Filtering
|
97
|
-
filter_type = args.or ? :any? : :all?
|
101
|
+
files = ShellHelper.prepare_list(files, ShellHelper::Log.list, flags)
|
98
102
|
|
99
|
-
results = ShellHelper.filter_start(
|
103
|
+
results = ShellHelper.filter_start(files, flags, args)
|
100
104
|
|
101
|
-
#
|
102
|
-
if
|
105
|
+
# Skip and Print Total if set
|
106
|
+
if flags[:total]
|
103
107
|
ShellHelper.total_count(results)
|
104
108
|
return true
|
105
109
|
end
|
106
110
|
|
111
|
+
# Skip and Print Total if set
|
112
|
+
if flags[:fields]
|
113
|
+
ShellHelper.fields_print(results)
|
114
|
+
return true
|
115
|
+
end
|
116
|
+
|
107
117
|
# Check Search Results
|
108
118
|
if results.instance_of?(Hash) && results.values.flatten.empty?
|
109
|
-
puts 'No results'.
|
119
|
+
puts 'No results'.pastel(:red)
|
110
120
|
else
|
111
121
|
# This causes the key 'colorized' output to also be included
|
112
|
-
ShellHelper.show(results.to_a.compact.flatten,
|
122
|
+
ShellHelper.show(results.to_a.compact.flatten, flags)
|
113
123
|
end
|
114
124
|
|
115
125
|
# log filter --path='cloud/gitlab-automation' --path='/pull' --all
|
116
|
-
# cloud/gitlab-automation
|
117
126
|
# log filter --project=thingy --other_filter=asdf *
|
118
127
|
rescue StandardError => e
|
119
|
-
|
120
|
-
|
128
|
+
LogBot.fatal('Filter', message: e.message)
|
129
|
+
ap e.backtrace
|
121
130
|
end
|
122
131
|
# ========================================================================
|
123
132
|
|
124
|
-
# rubocop:disable
|
125
|
-
|
126
|
-
|
127
|
-
puts '
|
128
|
-
puts
|
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"'
|
133
|
+
# rubocop:disable Layout/LineLength
|
134
|
+
# TODO: Add a lot more examples
|
135
|
+
def self.examples
|
136
|
+
puts 'Find `done` job for sidekiq, sort by duration, only duration, and show longest first'.pastel(:bright_green)
|
137
|
+
puts 'log filter sidekiq/current --job_status=done --sort=duration_s,db_duration_s --slice=duration_s,db_duration_s --reverse'
|
201
138
|
puts
|
202
139
|
|
203
|
-
puts '
|
204
|
-
puts '
|
205
|
-
puts ' Use with `--start` for between selections'
|
206
|
-
puts ' Ex: log filter --end="2021-06-22"'
|
140
|
+
puts 'Find 500s only show exceptions'.pastel(:bright_green)
|
141
|
+
puts 'log filter --status=500 --slice=exception.message gitlab-rails/production_json.log'
|
207
142
|
puts
|
208
143
|
|
209
|
-
puts '
|
210
|
-
puts '
|
211
|
-
puts ' Search in key for value'
|
212
|
-
puts ' Example: --path=mirror/pull'
|
144
|
+
puts 'Show unique sidekiq queue namespaces. Exclude Specifics'.pastel(:bright_green)
|
145
|
+
puts 'filter sidekiq/current --slice=queue_namespace --uniq=queue_namespace --queue_namespace!=jira_connect --queue_namespace!=hashed_storage'
|
213
146
|
puts
|
214
147
|
|
215
|
-
puts '
|
216
|
-
puts '
|
217
|
-
puts " Ex: log filter --path=api sidekiq/current (hint: use `#{'ls'.colorize(:yellow)}` for log names"
|
148
|
+
puts 'Show user,ip from API logs where `meta.user` field is present '.pastel(:bright_green)
|
149
|
+
puts 'gitlab-rails/api_json.log --slice=meta.user,meta.remote_ip --exists=meta.user'
|
218
150
|
puts
|
219
151
|
|
220
|
-
puts '
|
221
|
-
puts
|
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
|
-
|
152
|
+
puts 'Count/% occurences for both user and remote ip fields'.pastel(:bright_green)
|
153
|
+
puts 'gitlab-rails/api_json.log --stats=meta.user,meta.remote_ip --exists=meta.user'
|
225
154
|
puts
|
226
155
|
end
|
227
|
-
# rubocop:enable Metrics/MethodLength
|
228
156
|
|
229
|
-
# rubocop:disable Layout/LineLength
|
230
|
-
# TODO: Add a lot more examples
|
231
|
-
def self.filter_examples
|
232
|
-
puts 'Find `done` job for sidekiq, sort by duration, only duration, and show longest first'.colorize(:light_green)
|
233
|
-
puts 'log filter sidekiq/current --job_status=done --sort=duration_s,db_duration_s --slice=duration_s,db_duration_s --reverse'
|
234
|
-
puts
|
235
|
-
puts 'Find 500s only show exceptions'.colorize(:light_green)
|
236
|
-
puts 'log filter --status=500 --slice=exception.message gitlab-rails/production_json.log'
|
237
|
-
puts
|
238
|
-
end
|
239
157
|
# rubocop:enable Layout/LineLength
|
240
|
-
|
241
158
|
# ========================================================================
|
242
159
|
# Search (Full Text / String Search)
|
243
160
|
# ========================================================================
|
244
|
-
|
245
|
-
# --text='asdf'
|
246
|
-
# --text!='asdf'
|
247
|
-
# --regex='' # TODO?
|
248
|
-
# --slice=time,path (E.g. log filter --path='mirror/pull' --slice=path,time )
|
249
|
-
# --except: Exclude specific fields (except multiple with comma)
|
250
|
-
|
251
|
-
# --total Total Count Entries Only
|
252
|
-
def self.search(initial_param)
|
161
|
+
def self.search(raw)
|
253
162
|
# Extract Args
|
254
|
-
|
163
|
+
files_list, flags, args = Args.parse(raw)
|
255
164
|
|
256
165
|
# Prepare Log List
|
257
|
-
|
258
|
-
|
259
|
-
# AND / OR Filtering
|
260
|
-
filter_type = args.or ? :any? : :all?
|
166
|
+
files = ShellHelper.prepare_list(files_list)
|
261
167
|
|
262
|
-
results = ShellHelper.search_start(
|
168
|
+
results = ShellHelper.search_start(files, flags, args)
|
263
169
|
|
264
|
-
#
|
265
|
-
if
|
170
|
+
# Skip and Print Total if set
|
171
|
+
if flags[:total]
|
266
172
|
ShellHelper.total_count(results)
|
267
173
|
return true
|
268
174
|
end
|
269
175
|
|
270
176
|
# Check Search Results
|
271
177
|
if results.values.flatten.empty?
|
272
|
-
puts 'No results'.
|
178
|
+
puts 'No results'.pastel(:red)
|
273
179
|
else
|
274
180
|
# This causes the key 'colorized' output to also be included
|
275
|
-
ShellHelper.show(results.to_a.compact.flatten,
|
181
|
+
ShellHelper.show(results.to_a.compact.flatten, flags)
|
276
182
|
end
|
277
183
|
rescue StandardError => e
|
278
|
-
LogBot.fatal('
|
184
|
+
LogBot.fatal('Search', message: e.message)
|
185
|
+
ap e.backtrace
|
279
186
|
end
|
280
187
|
# ========================================================================
|
281
188
|
|
@@ -287,48 +194,48 @@ module GreenHat
|
|
287
194
|
|
288
195
|
# rubocop:disable Metrics/MethodLength
|
289
196
|
def self.search_help
|
290
|
-
puts "\u2500".
|
291
|
-
puts 'Log Search'.
|
292
|
-
puts "\u2500".
|
197
|
+
puts "\u2500".pastel(:cyan) * 20
|
198
|
+
puts 'Log Search'.pastel(:yellow)
|
199
|
+
puts "\u2500".pastel(:cyan) * 20
|
293
200
|
|
294
201
|
puts 'Search will do a full line include or exclude text search'
|
295
202
|
|
296
|
-
puts 'Options'.
|
297
|
-
puts '--text'.
|
203
|
+
puts 'Options'.pastel(:blue)
|
204
|
+
puts '--text'.pastel(:green)
|
298
205
|
puts ' Primary parameter for searching. Include or ! to exclude'
|
299
206
|
puts ' Ex: --text=BuildHooksWorker --text!=start sidekiq/current'
|
300
207
|
puts
|
301
208
|
|
302
|
-
puts '--total'.
|
209
|
+
puts '--total'.pastel(:green)
|
303
210
|
puts ' Print only total count of matching entries'
|
304
211
|
puts
|
305
212
|
|
306
|
-
puts '--slice'.
|
213
|
+
puts '--slice'.pastel(:green)
|
307
214
|
puts ' Extract specific fields from entries (slice multiple with comma)'
|
308
215
|
puts ' Ex: --slice=path or --slice=path,params'
|
309
216
|
puts
|
310
217
|
|
311
|
-
puts '--except'.
|
218
|
+
puts '--except'.pastel(:green)
|
312
219
|
puts ' Exclude specific fields (except multiple with comma)'
|
313
220
|
puts ' Ex: --except=params --except=params,path'
|
314
221
|
puts
|
315
222
|
|
316
|
-
puts '--archive'.
|
317
|
-
puts ' Limit to specific
|
223
|
+
puts '--archive'.pastel(:green)
|
224
|
+
puts ' Limit to specific archive name (inclusive). Matching SOS tar.gz name'
|
318
225
|
puts ' Ex: --archive=dev-gitlab_20210622154626, --archive=202106,202107'
|
319
226
|
puts
|
320
227
|
|
321
|
-
puts '--limit'.
|
228
|
+
puts '--limit'.pastel(:green)
|
322
229
|
puts ' Limit total number of results. Default to half total screen size'
|
323
230
|
puts ' Ex: --limit; --limit=10'
|
324
231
|
puts
|
325
232
|
|
326
|
-
puts 'Search specific logs'.
|
233
|
+
puts 'Search specific logs'.pastel(:blue)
|
327
234
|
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'.
|
235
|
+
puts " Ex: log filter --path=api sidekiq/current (hint: use `#{'ls'.pastel(:yellow)}` for log names)"
|
329
236
|
puts
|
330
237
|
|
331
|
-
puts 'Example Queries'.
|
238
|
+
puts 'Example Queries'.pastel(:blue)
|
332
239
|
puts 'log search --text=BuildHooksWorker --text!=start sidekiq/current --total'
|
333
240
|
puts 'log search --text=BuildHooksWorker --text!=start --slice=enqueued_at sidekiq/current'
|
334
241
|
puts
|
@@ -337,7 +244,6 @@ module GreenHat
|
|
337
244
|
|
338
245
|
# ------------------------------------------------------------------------
|
339
246
|
end
|
340
|
-
# rubocop:enable Metrics/ModuleLength
|
341
247
|
end
|
342
248
|
end
|
343
249
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module GreenHat
|
2
|
+
module ShellHelper
|
3
|
+
# Helper to organize page check / methods
|
4
|
+
module Page
|
5
|
+
# Check if paging should be skipped
|
6
|
+
# True / False / Not Set
|
7
|
+
def self.skip?(flags, data)
|
8
|
+
# Pass if Explicitly Set / Inverse for skip
|
9
|
+
return !flags[:page] if flags.key? :page
|
10
|
+
|
11
|
+
LogBot.debug('Page', count_rows(data, flags)) if ENV['DEBUG']
|
12
|
+
|
13
|
+
count_rows(data, flags)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Array/Hash and String pagination check. Don't unncessarily loop through everything
|
17
|
+
def self.count_rows(data, flags)
|
18
|
+
height = TTY::Screen.height
|
19
|
+
size = 0
|
20
|
+
|
21
|
+
data.each do |entry|
|
22
|
+
size += case entry
|
23
|
+
when Hash then entry.keys.count
|
24
|
+
when Array then entry.count
|
25
|
+
else
|
26
|
+
# Each Boxed Entry is 3 Lines
|
27
|
+
flags.key?(:row_size) ? flags[:row_size] : 1
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
break if size > height
|
32
|
+
end
|
33
|
+
|
34
|
+
size < height
|
35
|
+
end
|
36
|
+
end
|
37
|
+
# ----
|
38
|
+
end
|
39
|
+
end
|