greenhat 0.5.0 → 0.6.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/lib/greenhat/accessors/disk.rb +1 -1
  3. data/lib/greenhat/archive.rb +2 -0
  4. data/lib/greenhat/cli.rb +12 -2
  5. data/lib/greenhat/entrypoint.rb +36 -33
  6. data/lib/greenhat/host.rb +1 -1
  7. data/lib/greenhat/logbot.rb +1 -1
  8. data/lib/greenhat/paper/flag_helper.rb +18 -0
  9. data/lib/greenhat/paper/paper_helper.rb +118 -0
  10. data/lib/greenhat/paper.rb +34 -0
  11. data/lib/greenhat/reports/builder.rb +98 -0
  12. data/lib/greenhat/reports/helpers.rb +101 -0
  13. data/lib/greenhat/reports/internal_methods.rb +156 -0
  14. data/lib/greenhat/reports/reports/errors.rb +51 -0
  15. data/lib/greenhat/reports/reports/faststats.rb +42 -0
  16. data/lib/greenhat/reports/reports/full.rb +143 -0
  17. data/lib/greenhat/reports/runner.rb +58 -0
  18. data/lib/greenhat/reports/shared.rb +37 -0
  19. data/lib/greenhat/reports/shell_helper.rb +34 -0
  20. data/lib/greenhat/reports.rb +79 -0
  21. data/lib/greenhat/settings.rb +6 -1
  22. data/lib/greenhat/shell/args.rb +9 -9
  23. data/lib/greenhat/shell/color_string.rb +1 -1
  24. data/lib/greenhat/shell/faststats.rb +24 -5
  25. data/lib/greenhat/shell/field_helper.rb +1 -1
  26. data/lib/greenhat/shell/filter_help.rb +36 -189
  27. data/lib/greenhat/shell/log.rb +28 -16
  28. data/lib/greenhat/shell/markdown.rb +355 -352
  29. data/lib/greenhat/shell/process.rb +11 -5
  30. data/lib/greenhat/shell/query.rb +184 -28
  31. data/lib/greenhat/shell/report.rb +415 -412
  32. data/lib/greenhat/shell/reports.rb +41 -0
  33. data/lib/greenhat/shell/shell_helper.rb +172 -117
  34. data/lib/greenhat/shell.rb +13 -2
  35. data/lib/greenhat/thing/file_types.rb +38 -2
  36. data/lib/greenhat/thing/formatters/clean_raw.rb +1 -1
  37. data/lib/greenhat/thing/formatters/exporters.rb +48 -0
  38. data/lib/greenhat/thing/formatters/identify_db.rb +32 -0
  39. data/lib/greenhat/thing/formatters/runner_log.rb +70 -0
  40. data/lib/greenhat/thing/formatters/table.rb +15 -1
  41. data/lib/greenhat/thing/formatters/time_json.rb +12 -1
  42. data/lib/greenhat/thing/kind.rb +1 -1
  43. data/lib/greenhat/thing.rb +1 -0
  44. data/lib/greenhat/version.rb +1 -1
  45. data/lib/greenhat.rb +6 -8
  46. metadata +33 -4
  47. data/lib/greenhat/pry_helpers.rb +0 -51
  48. data/lib/greenhat/thing/super_log.rb +0 -1
@@ -100,6 +100,13 @@ module GreenHat
100
100
  puts "- #{log.name.pastel(:yellow)}"
101
101
  end
102
102
  end
103
+
104
+ return unless all
105
+
106
+ puts "\n#{'Other / Unknown'.pastel(:red)}"
107
+ (Thing.all - files).each do |file|
108
+ puts "- #{file.name.pastel(:yellow)}"
109
+ end
103
110
  end
104
111
 
105
112
  def self.ls(args = [])
@@ -117,15 +124,23 @@ module GreenHat
117
124
 
118
125
  LogBot.debug('FastStats CMD', cmd) if ENV['DEBUG']
119
126
 
127
+ # binding.pry
128
+
129
+ # Ignore Unknown Errors
120
130
  results = ShellHelper.file_process(files) do |file|
131
+ output = `fast-stats #{cmd} #{file.file} 2>&1`
132
+ result = $CHILD_STATUS.success?
133
+
134
+ next unless result
135
+
121
136
  [
122
137
  file.friendly_name,
123
- `fast-stats #{cmd} #{file.file}`.split("\n"),
138
+ output.split("\n"),
124
139
  "\n"
125
140
  ]
126
141
  end
127
142
 
128
- ShellHelper.show(results.flatten, flags)
143
+ ShellHelper.show(results.compact.flatten, flags)
129
144
  end
130
145
 
131
146
  # ========================================================================
@@ -219,11 +234,15 @@ module GreenHat
219
234
  end
220
235
  end.join(' ')
221
236
 
222
- # Prepare Log List
223
- file_list = ShellHelper.prepare_list(file_list, ShellHelper::Faststats.things, flags)
237
+ # Prepare Log List / Allow attempting of parsing everything
238
+ file_list = if file_list == ['*']
239
+ Thing.all.map(&:name)
240
+ else
241
+ ShellHelper.prepare_list(file_list, ShellHelper::Faststats.things)
242
+ end
224
243
 
225
244
  # Convert to Things
226
- files = ShellHelper.find_things(file_list)
245
+ files = ShellHelper.find_things(file_list, fuzzy_file_match: true)
227
246
 
228
247
  [files, flags, cmd]
229
248
  end
@@ -34,7 +34,7 @@ module GreenHat
34
34
  %w[
35
35
  archive case combine end exact except exists json limit or page pluck
36
36
  raw reverse round slice sort start stats table_style text time_zone
37
- total truncate uniq
37
+ total truncate uniq percentile transform
38
38
  ]
39
39
  end
40
40
 
@@ -2,198 +2,22 @@ module GreenHat
2
2
  # CLI Helper
3
3
  module ShellHelper
4
4
  # Unify Filter / Filter Help
5
- # rubocop:disable Metrics/MethodLength,Layout/LineLength
5
+ # rubocop:disable Metrics/MethodLength,Layout/LineLength,Metrics/ModuleLength
6
6
  module Filter
7
7
  def self.help(args = '')
8
- if args.blank?
9
- help_index.values.map { |x| x.push '' }
10
- else
11
- list = help_index.select do |k, _v|
12
- k.to_s.include? args
13
- end
8
+ output = if args.blank?
9
+ help_index.values.map { |x| x.push '' }
10
+ else
11
+ list = help_index.select do |k, _v|
12
+ k.to_s.include? args
13
+ end
14
14
 
15
- list.values
16
- end
15
+ list.values
16
+ end
17
17
 
18
- # .map { |x| x.join("\n") }.join("\n\n")
18
+ output.flatten(2)
19
19
  end
20
20
 
21
- # TODO: Confirm Remove
22
- # def self.legacy
23
- # puts "\u2500".pastel(:cyan) * 20
24
- # puts 'Filter'.pastel(:yellow)
25
- # puts "\u2500".pastel(:cyan) * 20
26
-
27
- # puts 'Options'.pastel(:blue)
28
- # puts '--raw'.pastel(:green)
29
- # puts ' Disable formatting and page/less'
30
- # puts
31
-
32
- # puts '--page'.pastel(:green)
33
- # puts ' Specifically enable or disable paging'
34
- # puts ' E.g. --page (default to true), --page=true, --page=false'
35
- # puts
36
-
37
- # puts '--round'.pastel(:green)
38
- # puts ' Attempt to round all integers. Default: 2.'
39
- # puts ' E.g. --round, --round=3, --round=0'
40
- # puts
41
-
42
- # puts '--limit'.pastel(:green)
43
- # puts ' Limit total output lines. Disabled by default. Default without value is based on screen height'
44
- # puts ' E.g. --limit, --limit=5'
45
- # puts
46
-
47
- # puts '--json'.pastel(:green)
48
- # puts ' Print output back into JSON'
49
- # puts
50
-
51
- # puts '--or'.pastel(:green)
52
- # puts ' Filters will use OR instead of AND (all match vs any match)'
53
- # puts
54
-
55
- # puts '--total'.pastel(:green)
56
- # puts ' Show total count, duration, start/end time for matching entries'
57
- # puts
58
-
59
- # puts '--fields'.pastel(:green)
60
- # puts ' Print only Available fields for selected files'
61
- # puts
62
-
63
- # puts '--slice'.pastel(:green)
64
- # puts ' Extract specific fields from entries (slice multiple with comma)'
65
- # puts ' Ex: --slice=path or --slice=path,params'
66
- # puts
67
-
68
- # puts '--except'.pastel(:green)
69
- # puts ' Exclude specific fields (except multiple with comma)'
70
- # puts ' Ex: --except=params --except=params,path'
71
- # puts
72
-
73
- # puts '--exists'.pastel(:green)
74
- # puts ' Ensure field exists regardless of contents'
75
- # puts ' Ex: --exists=params --exists=params,path'
76
- # puts
77
-
78
- # puts '--stats'.pastel(:green)
79
- # puts ' Order/Count occurrances by field. Combine with `truncate` for key names'
80
- # puts ' Ex: --stats=params --except=params,path'
81
- # puts
82
-
83
- # puts '--uniq'.pastel(:green)
84
- # puts ' Show unique values only'
85
- # puts ' Ex: --uniq=params --uniq=params,path'
86
- # puts
87
-
88
- # puts '--pluck'.pastel(:green)
89
- # puts ' Extract values from entries'
90
- # puts ' Ex: --pluck=params --pluck=params,path'
91
- # puts
92
-
93
- # puts '--archive'.pastel(:green)
94
- # puts ' Limit to specific archvie name (partial matching /inclusive). Matching SOS tar.gz name'
95
- # puts ' Ex: --archive=dev-gitlab_20210622154626, --archive=202106,202107'
96
- # puts
97
-
98
- # puts '--sort'.pastel(:green)
99
- # puts ' Sort by multiple fields'
100
- # puts ' Ex: --sort=duration_s,db_duration_s'
101
- # puts
102
-
103
- # puts '--reverse'.pastel(:green)
104
- # puts ' Reverse all results'
105
- # puts ' Ex: --reverse'
106
- # puts
107
-
108
- # puts '--combine'.pastel(:green)
109
- # puts ' Omit archive identifier dividers. Useful with sort or time filters'
110
- # puts ' Ex: --combine'
111
- # puts
112
-
113
- # puts '--case'.pastel(:green)
114
- # puts ' Exact case match. Defaults to case insensitive'
115
- # puts ' Ex: --case; --name=Jon, --name=jane --case'
116
- # puts
117
-
118
- # puts '--exact'.pastel(:green)
119
- # puts ' Exact parameter/value match. Defaults to partial match'
120
- # puts ' Ex: --field=CommonPartial --exact'
121
- # puts
122
-
123
- # puts '--start'.pastel(:green)
124
- # puts ' Show events after specified time. Filtered by the `time` field'
125
- # puts ' Use with `--end` for between selections'
126
- # puts ' Ex: log filter --start="2021-06-22 14:44 UTC" --end="2021-06-22 14:45 UTC"'
127
- # puts
128
-
129
- # puts '--end'.pastel(:green)
130
- # puts ' Show events before specified time. Filtered by the `time` field'
131
- # puts ' Use with `--start` for between selections'
132
- # puts ' Ex: log filter --end="2021-06-22"'
133
- # puts
134
-
135
- # puts '--time_zone'.pastel(:green)
136
- # puts ' Manipulate the `time` field into a specific timezone'
137
- # puts ' Ex: log filter --time_zone=EDT'
138
- # puts
139
-
140
- # puts '--text'.pastel(:green)
141
- # puts ' Full entry text searching (slow)'
142
- # puts ' --text="anything here"'
143
- # puts
144
-
145
- # puts '--table_style'.pastel(:green)
146
- # puts ' Renderer used for formatted output. basic, ascii, or unicode(default)'
147
- # puts ' Ex: log filter --table_style=base'
148
- # puts
149
-
150
- # puts '--truncate'.pastel(:green)
151
- # puts ' Truncate field length. On by default (4 rows). Performance issues!'
152
- # puts ' Disable with --truncate=0'.pastel(:bright_red)
153
- # puts ' Ex: --truncate=200, --truncate=2048"'
154
- # puts
155
-
156
- # puts 'Search specific logs'.pastel(:blue)
157
- # puts ' Any non dash parameters will be the log list to search from'
158
- # puts " Ex: log filter --path=api sidekiq/current (hint: use `#{'ls'.pastel(:yellow)}` for log names"
159
- # puts
160
-
161
- # puts 'General (Field) Searching'.pastel(:blue)
162
- # puts " There are different ways filter within specific keys/fields within log. The baseline is #{'--[key]=[value]'.pastel(:green)}"
163
- # puts ' This will, by default, select any entries with any partial match (case insensitive) of [value] in the entry [key]'
164
- # puts ' Example: --path=mirror/pull'
165
- # puts
166
-
167
- # puts " You can modify logic with params like: #{'case'.pastel(:green)}, #{'exact'.pastel(:green)}"
168
- # puts ' Ex Case turn on case sensitivity, exact will change from the inclusive to only exact matching.'
169
- # puts
170
-
171
- # puts " You can also use multiple #{'--[key]=[value]'.pastel(:green)} to narrow down, or combine it with #{'or'.pastel(:green)} to be more inclusive"
172
- # puts ' Ex nginx/gitlab_access.log --http_user_agent=gitlab-runner --path=/api/v4/jobs/request'
173
- # puts ' This will look through the access logs for anything that is a runner (user agent) and hitting the request endpoint'
174
- # puts
175
-
176
- # puts " You can also modify whether a field search excluded or a integer (and technically string) comparison by changing the operator: #{'(!= >= <=)'.pastel(:green)}"
177
- # puts ' Ex "nginx/gitlab_access.log --http_user_agent=gitlab-runner --http_user_agent!=13.12.0" will exclude any entries with 13.12.0/13.12 runners'
178
- # puts ' Ex "sidekiq/current --duration_s>=5 --class!=LdapSyncWorker" Duration over 5 that is not an ldap job'
179
- # puts
180
-
181
- # puts 'Pipe to Shell'.pastel(:blue)
182
- # puts ' This is very experimental! The idea is to take all the output from the search and allow a pipe into a traditional shell command.'
183
- # puts ' Due to the arg parsing its not a straight send to the terminal. You may need to escape quotes.'
184
- # puts ' Order is relevant, no arguments after the pipe'
185
- # puts
186
- # puts ' <files> <params> | grep path'
187
- # puts
188
-
189
- # puts 'Example Queries'.pastel(:blue)
190
- # puts " Also see #{'examples'.pastel(:bright_blue)} for even more examples"
191
- # puts ' log filter --class=BuildFinishedWorker sidekiq/current --slice=time,message'
192
- # puts ' log filter gitlab-rails/api_json.log --slice=ua --uniq=ua --ua=gitlab-runner'
193
-
194
- # puts
195
- # end
196
-
197
21
  def self.help_index
198
22
  {
199
23
  title: [
@@ -238,7 +62,8 @@ module GreenHat
238
62
 
239
63
  total: [
240
64
  '--total'.pastel(:green),
241
- ' Show total count, duration, start/end time for matching entries'
65
+ ' Show total count, duration, start/end time for matching entries',
66
+ ' Add `simple` to only return counts. e.g. --total=simple'
242
67
  ],
243
68
 
244
69
  fields: [
@@ -250,14 +75,20 @@ module GreenHat
250
75
  '--slice'.pastel(:green),
251
76
  ' Extract specific fields from entries (slice multiple with comma)',
252
77
  ' Ex: --slice=path or --slice=path,params'
253
-
254
78
  ],
255
79
 
256
80
  except: [
257
81
  '--except'.pastel(:green),
258
82
  ' Exclude specific fields (except multiple with comma)',
259
83
  ' Ex: --except=params --except=params,path'
84
+ ],
260
85
 
86
+ transform: [
87
+ '--transform'.pastel(:green),
88
+ ' Copy key to another key within results. Useful for queries that require (timestamp => time)',
89
+ ' Available Transforms, to_i, default is to copy',
90
+ ' Ex: --transform=timestamp,time',
91
+ ' --transform=cpu,cpu,to_i'
261
92
  ],
262
93
 
263
94
  exists: [
@@ -371,6 +202,22 @@ module GreenHat
371
202
  ' Ex: --truncate=200, --truncate=2048"'
372
203
  ],
373
204
 
205
+ interval: [
206
+ '--interval'.pastel(:green),
207
+ ' Split results down into a interval (based on the time field)',
208
+ ' Example Params: 5m, 1d, 3h, "30 minutes" (chronic duration)',
209
+ ' Ex: --interval=15m, --interval=1h"'
210
+ ],
211
+
212
+ percentile: [
213
+ '--percentile'.pastel(:green),
214
+ ' Stats breakdown on any integer value within the results. No params',
215
+ ' Best when combined with slice/round to limit/filter results',
216
+ ' No parameters, Returns 99,95,mean,min,max and entry count',
217
+ ' Ex: --percentile --round --slice=field',
218
+ ' Query: gitlab-rails/api_json.log --percentile --round=2 --slice=cpu_s'
219
+ ],
220
+
374
221
  field_search: [
375
222
  'General (Field) Searching'.pastel(:blue),
376
223
  " There are different ways filter within specific keys/fields within log. The baseline is #{'--[key]=[value]'.pastel(:green)}",
@@ -412,7 +259,7 @@ module GreenHat
412
259
 
413
260
  }
414
261
  end
415
- # rubocop:enable Metrics/MethodLength,Layout/LineLength
262
+ # rubocop:enable Metrics/MethodLength,Layout/LineLength,Metrics/ModuleLength
416
263
  end
417
264
  end
418
265
  end
@@ -4,7 +4,6 @@ module GreenHat
4
4
  # Logs
5
5
  # rubocop:disable Metrics/ModuleLength
6
6
  module Log
7
- extend Spinner # TODO: DEBUGGING REMOVE
8
7
  def self.auto_complete(list, word)
9
8
  # Argument Parsing
10
9
  files, flags, _args = Args.parse(list)
@@ -79,11 +78,12 @@ module GreenHat
79
78
 
80
79
  def self.filter_help(raw = {})
81
80
  args, flags, _args = Args.parse(raw)
81
+
82
82
  ShellHelper.show(ShellHelper::Filter.help(args.first), flags)
83
83
  end
84
84
 
85
85
  def self.ls(args = [])
86
- ShellHelper::List.list(args, Thing.list)
86
+ ShellHelper::List.list(args, ShellHelper::Log.list)
87
87
  end
88
88
 
89
89
  def self.show(raw = {})
@@ -106,7 +106,7 @@ module GreenHat
106
106
  end
107
107
 
108
108
  name = if raw.empty?
109
- Cli.prompt.ask('Log/name to save the results to? '.pastel(:yellow))
109
+ Cli.prompt.ask('Log entry to save the results to? '.pastel(:yellow))
110
110
  else
111
111
  raw.first
112
112
  end
@@ -125,10 +125,18 @@ module GreenHat
125
125
  return false
126
126
  end
127
127
 
128
- Thing.new.query_save(results, name)
128
+ Thing.new.query_save(save_greenhat_query_info + results, name)
129
129
  puts "#{name.pastel(:green)} Saved!"
130
130
  end
131
131
 
132
+ def self.save_greenhat_query_info
133
+ [{
134
+ time: Time.now,
135
+ query: ShellHelper::Log.last,
136
+ msg: 'GreenHat Save/Write'
137
+ }]
138
+ end
139
+
132
140
  def self.write(raw = [])
133
141
  if ShellHelper::Log.last.nil?
134
142
  puts 'No previous query found'.pastel(:red)
@@ -139,7 +147,7 @@ module GreenHat
139
147
  end
140
148
 
141
149
  name = if raw.empty?
142
- Cli.prompt.ask('Log/name to save the results to? '.pastel(:yellow))
150
+ Cli.prompt.ask('Filename to save the results to? '.pastel(:yellow))
143
151
  else
144
152
  raw.first
145
153
  end
@@ -158,7 +166,7 @@ module GreenHat
158
166
  return false
159
167
  end
160
168
 
161
- all = results.map { |row| Oj.dump(row) }
169
+ all = (save_greenhat_query_info + results).map { |row| Oj.dump(row) }
162
170
  File.write(name, all.join("\n"))
163
171
  puts "#{name.pastel(:green)} File Written!"
164
172
  end
@@ -205,16 +213,10 @@ module GreenHat
205
213
  files, flags, args = Args.parse(raw)
206
214
 
207
215
  # Prepare Log List
208
- files = ShellHelper.prepare_list(files)
216
+ files = ShellHelper.prepare_list(files, ShellHelper::Log.list)
209
217
 
210
218
  results = Query.start(files, flags, args)
211
219
 
212
- # Skip and Print Total if set
213
- if flags[:total]
214
- ShellHelper.total_count(results, flags)
215
- return true
216
- end
217
-
218
220
  # Skip and Print Total if set
219
221
  if flags[:fields]
220
222
  ShellHelper.fields_print(results)
@@ -222,14 +224,15 @@ module GreenHat
222
224
  end
223
225
 
224
226
  # Check Search Results
225
- if results.instance_of?(Hash) && results.values.flatten.empty?
227
+ if results.empty?
226
228
  puts 'No results'.pastel(:red)
227
229
  ShellHelper::Log.no_files_warning(files) if ShellHelper.find_things(files, flags).count.zero?
228
230
  elsif flags[:pipe]
229
231
  Pipe.show(results, flags[:pipe])
230
232
  else
233
+ # Allow for array / multiple table outputs
231
234
  # This causes the key 'colorized' output to also be included
232
- ShellHelper.show(results.to_a.compact.flatten, flags)
235
+ ShellHelper.show(results, flags)
233
236
  end
234
237
  rescue StandardError => e
235
238
  LogBot.fatal('Filter', message: e.message)
@@ -275,6 +278,10 @@ module GreenHat
275
278
  puts 'Show workhorse duration/URI. Filter by duration bounds'.pastel(:bright_green)
276
279
  puts 'gitlab-workhorse/current --duration_ms>=30000 --duration_ms<=45000 --slice=duration_ms,uri'
277
280
  puts
281
+
282
+ puts 'Show runner statistics'.pastel(:bright_green)
283
+ puts 'gitlab-rails/api_json.log --path=/api/v4/jobs/request --percentile --round=2'
284
+ puts
278
285
  end
279
286
  # rubocop:enable Layout/LineLength
280
287
 
@@ -286,7 +293,7 @@ module GreenHat
286
293
  files_list, flags, args = Args.parse(raw)
287
294
 
288
295
  # Prepare Log List
289
- files = ShellHelper.prepare_list(files_list)
296
+ files = ShellHelper.prepare_list(files_list, ShellHelper::Log.list)
290
297
 
291
298
  results = ShellHelper.search_start(files, flags, args)
292
299
 
@@ -387,6 +394,11 @@ module GreenHat
387
394
  puts "No matching files found for pattern #{files.to_s.pastel(:yellow)}"
388
395
  puts "See #{'ls'.pastel(:blue)} for available files"
389
396
  end
397
+
398
+ # Limit to Log Files
399
+ def self.list
400
+ Thing.all.select(&:log)
401
+ end
390
402
  end
391
403
 
392
404
  # --------