greenhat 0.4.0 → 0.5.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/bin/greenhat +1 -2
- data/lib/greenhat/accessors/gitlab.rb +4 -2
- data/lib/greenhat/archive.rb +5 -1
- data/lib/greenhat/cli.rb +12 -9
- data/lib/greenhat/entrypoint.rb +4 -4
- data/lib/greenhat/settings.rb +29 -7
- data/lib/greenhat/shell/filter_help.rb +216 -183
- data/lib/greenhat/shell/gitlab.rb +1 -0
- data/lib/greenhat/shell/log.rb +11 -21
- data/lib/greenhat/shell/query.rb +378 -0
- data/lib/greenhat/shell/report.rb +2 -0
- data/lib/greenhat/shell/shell_helper.rb +11 -361
- data/lib/greenhat/shell.rb +9 -1
- data/lib/greenhat/thing/file_types.rb +7 -0
- data/lib/greenhat/thing/formatters/api_json.rb +4 -2
- data/lib/greenhat/thing/formatters/bracket_log.rb +1 -1
- data/lib/greenhat/thing/formatters/colon_split_strip.rb +2 -2
- data/lib/greenhat/thing/formatters/dotenv.rb +1 -1
- data/lib/greenhat/thing/formatters/format.rb +0 -11
- data/lib/greenhat/thing/formatters/free_m.rb +2 -2
- data/lib/greenhat/thing/formatters/json.rb +41 -17
- data/lib/greenhat/thing/formatters/json_shellwords.rb +3 -2
- data/lib/greenhat/thing/formatters/kube_json.rb +3 -2
- data/lib/greenhat/thing/formatters/multiline_json.rb +1 -1
- data/lib/greenhat/thing/formatters/nginx.rb +5 -1
- data/lib/greenhat/thing/formatters/table.rb +3 -3
- data/lib/greenhat/thing/helpers.rb +0 -11
- data/lib/greenhat/thing/info_format.rb +4 -4
- data/lib/greenhat/thing/super_log.rb +0 -101
- data/lib/greenhat/thing.rb +20 -25
- data/lib/greenhat/version.rb +1 -1
- metadata +3 -2
@@ -74,7 +74,7 @@ module GreenHat
|
|
74
74
|
when Hash then render_table(entry, flags)
|
75
75
|
when Float, Integer, Array
|
76
76
|
format_table_entry(flags, entry, key)
|
77
|
-
|
77
|
+
# Ignore Special Formatting for Strings / Usually already formatted
|
78
78
|
when String
|
79
79
|
entry
|
80
80
|
else
|
@@ -90,13 +90,12 @@ module GreenHat
|
|
90
90
|
when Float, Integer || entry.numeric?
|
91
91
|
flags.key?(:round) ? entry.to_f.round(flags.round).ai : entry.ai
|
92
92
|
|
93
|
-
|
93
|
+
# General Inspecting
|
94
94
|
when Hash then entry.ai(ruby19_syntax: true)
|
95
95
|
|
96
96
|
# Arrays often contain Hashes. Dangerous Recursive?
|
97
97
|
when Array
|
98
98
|
entry.map { |x| format_table_entry(flags, x) }.join("\n")
|
99
|
-
|
100
99
|
when Time
|
101
100
|
entry.to_s.pastel(:bright_white)
|
102
101
|
|
@@ -120,7 +119,7 @@ module GreenHat
|
|
120
119
|
|
121
120
|
# Print the Table in a Nice way
|
122
121
|
def self.render_table(entry, flags)
|
123
|
-
entry = entry.
|
122
|
+
entry = entry.to_h { |k, v| [k, format_table_entry(flags, v, k)] }
|
124
123
|
# Pre-format Entry
|
125
124
|
|
126
125
|
table_style = flags[:table_style]&.to_sym || :unicode
|
@@ -168,201 +167,7 @@ module GreenHat
|
|
168
167
|
# Default to everything
|
169
168
|
files = Thing.all.map(&:name) if files.empty?
|
170
169
|
|
171
|
-
|
172
|
-
end
|
173
|
-
|
174
|
-
# Main Entry Point for Filtering
|
175
|
-
def self.filter_start(files, flags = {}, args = {})
|
176
|
-
# Convert to Things
|
177
|
-
logs = ShellHelper.find_things(files, flags).select(&:processed?)
|
178
|
-
|
179
|
-
# Ignore Archive/Host Dividers
|
180
|
-
if flags[:combine]
|
181
|
-
results = logs.reject(&:blank?).map(&:data).flatten.compact
|
182
|
-
ShellHelper.filter(results, flags, args)
|
183
|
-
else
|
184
|
-
# Iterate and Preserve Archive/Host Index
|
185
|
-
logs.each_with_object({}) do |log, obj|
|
186
|
-
# Ignore Empty Results / No Thing
|
187
|
-
next if log&.blank?
|
188
|
-
|
189
|
-
# Include Total Count in Name
|
190
|
-
results = ShellHelper.filter(log.data, flags, args)
|
191
|
-
duration = calculate_duration(results)
|
192
|
-
|
193
|
-
title = [
|
194
|
-
log.friendly_name,
|
195
|
-
" #{results.count}".pastel(:bright_black)
|
196
|
-
|
197
|
-
]
|
198
|
-
|
199
|
-
# Append Duration
|
200
|
-
title.push(" #{duration.pastel(:cyan, :dim)}") unless duration.blank?
|
201
|
-
|
202
|
-
# Save unless empty
|
203
|
-
obj[title.join] = results unless results.count.zero?
|
204
|
-
|
205
|
-
obj
|
206
|
-
end
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
def self.calculate_duration(results)
|
211
|
-
# Skip for Pluck
|
212
|
-
only_with_time = results.select { |x| x.instance_of?(Hash) && x.key?(:time) }
|
213
|
-
|
214
|
-
# If slice is used ignore
|
215
|
-
return nil if only_with_time.empty?
|
216
|
-
|
217
|
-
sorted = only_with_time.map(&:time).sort
|
218
|
-
humanize_time(sorted.first, sorted.last)
|
219
|
-
end
|
220
|
-
|
221
|
-
# Replace TimeDifference with https://stackoverflow.com/a/4136485/1678507
|
222
|
-
def self.humanize_time(time_start, time_end, increments = 2)
|
223
|
-
miliseconds = (time_end - time_start) * 1000
|
224
|
-
|
225
|
-
list = [[1000, :ms], [60, :s], [60, :m], [24, :h]].map do |count, name|
|
226
|
-
next unless miliseconds.positive?
|
227
|
-
|
228
|
-
miliseconds, n = miliseconds.divmod(count)
|
229
|
-
|
230
|
-
"#{n.to_i}#{name}" unless n.to_i.zero?
|
231
|
-
end
|
232
|
-
|
233
|
-
list.compact.reverse[0..increments - 1].join(' ')
|
234
|
-
end
|
235
|
-
|
236
|
-
# Filter Logic
|
237
|
-
# TODO: Simplify
|
238
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
239
|
-
def self.filter(data, flags = {}, args = {})
|
240
|
-
# results = data.clone.flatten.compact
|
241
|
-
|
242
|
-
# Experimenting with deep clone
|
243
|
-
results = Marshal.load(Marshal.dump(data))
|
244
|
-
results.select! do |row|
|
245
|
-
args.send(flags.logic || :all?) do |arg|
|
246
|
-
filter_row_key(row, arg, flags)
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
# Ensure presecense of a specific field
|
251
|
-
results = filter_exists(results, flags[:exists]) if flags.key?(:exists)
|
252
|
-
|
253
|
-
# Time Zone
|
254
|
-
results = filter_modify_timezone(results, flags[:time_zone]) if flags.key?(:time_zone)
|
255
|
-
|
256
|
-
# Time Filtering
|
257
|
-
results = filter_time(results, flags) if flags.key?(:start) || flags.key?(:end)
|
258
|
-
|
259
|
-
# Strip Results if Slice is defined
|
260
|
-
results = filter_slice(results, flags[:slice]) if flags.key?(:slice)
|
261
|
-
|
262
|
-
# Strip Results if Except is defined
|
263
|
-
results = filter_except(results, flags[:except]) if flags.key?(:except)
|
264
|
-
|
265
|
-
# Remove Blank from either slice or except
|
266
|
-
results.reject!(&:empty?)
|
267
|
-
|
268
|
-
# Sort
|
269
|
-
results.sort_by! { |x| x.slice(*flags[:sort]).values } if flags.key?(:sort)
|
270
|
-
|
271
|
-
# JSON Formatting
|
272
|
-
results = results.map { |x| Oj.dump(x) } if flags.key?(:json)
|
273
|
-
|
274
|
-
# Show Unique Only
|
275
|
-
results = filter_uniq(results, flags[:uniq]) if flags.key?(:uniq)
|
276
|
-
|
277
|
-
# Reverse
|
278
|
-
results.reverse! if flags[:reverse]
|
279
|
-
|
280
|
-
# Count occurrences / Skip Results
|
281
|
-
return filter_stats(results, flags) if flags.key?(:stats)
|
282
|
-
|
283
|
-
# Limit before Pluck / Flattening
|
284
|
-
results = filter_limit(results, flags[:limit]) if flags.key?(:limit)
|
285
|
-
|
286
|
-
# Pluck
|
287
|
-
results = filter_pluck(results, flags[:pluck]) if flags.key?(:pluck)
|
288
|
-
|
289
|
-
results
|
290
|
-
end
|
291
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
292
|
-
|
293
|
-
# Limit / Ensure Exists and Valid Number
|
294
|
-
def self.filter_limit(results, limit)
|
295
|
-
return results unless limit.integer? && limit.positive?
|
296
|
-
|
297
|
-
results.shift limit
|
298
|
-
end
|
299
|
-
|
300
|
-
def self.filter_modify_timezone(results, time_zone)
|
301
|
-
results.each do |x|
|
302
|
-
next unless x.key? :time
|
303
|
-
|
304
|
-
x[:time] = x[:time].in_time_zone time_zone
|
305
|
-
end
|
306
|
-
|
307
|
-
results
|
308
|
-
end
|
309
|
-
|
310
|
-
# Filter Start and End Times
|
311
|
-
# TODO: This is a bit icky, simplify/dry
|
312
|
-
def self.filter_time(results, flags)
|
313
|
-
if flags.key?(:start)
|
314
|
-
begin
|
315
|
-
time_start = Time.parse(flags[:start])
|
316
|
-
|
317
|
-
results.select! do |x|
|
318
|
-
if x.time
|
319
|
-
time_start < x.time
|
320
|
-
else
|
321
|
-
true
|
322
|
-
end
|
323
|
-
end
|
324
|
-
rescue StandardError
|
325
|
-
puts 'Unable to Process Start Time Filter'.pastel(:red)
|
326
|
-
end
|
327
|
-
end
|
328
|
-
|
329
|
-
if flags.key?(:end)
|
330
|
-
begin
|
331
|
-
time_start = Time.parse(flags[:end])
|
332
|
-
|
333
|
-
results.select! do |x|
|
334
|
-
if x.time
|
335
|
-
time_start > x.time
|
336
|
-
else
|
337
|
-
true
|
338
|
-
end
|
339
|
-
end
|
340
|
-
rescue StandardError
|
341
|
-
puts 'Unable to Process End Time Filter'.pastel(:red)
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
results
|
346
|
-
end
|
347
|
-
|
348
|
-
def self.filter_except(results, except)
|
349
|
-
# Avoid Empty Results
|
350
|
-
if except.empty?
|
351
|
-
filter_empty_arg('except')
|
352
|
-
return results
|
353
|
-
end
|
354
|
-
|
355
|
-
results.map { |row| row.except(*except) }
|
356
|
-
end
|
357
|
-
|
358
|
-
def self.filter_exists(results, exists)
|
359
|
-
# Avoid Empty Results
|
360
|
-
if exists.empty?
|
361
|
-
filter_empty_arg('exists')
|
362
|
-
return results
|
363
|
-
end
|
364
|
-
|
365
|
-
results.select { |row| (exists - row.keys).empty? }
|
170
|
+
Query.start(files, flags, args)
|
366
171
|
end
|
367
172
|
|
368
173
|
def self.entry_truncate(entry, truncate)
|
@@ -376,162 +181,6 @@ module GreenHat
|
|
376
181
|
"#{entry.to_s[0..truncate]} #{'...'.pastel(:bright_blue)}"
|
377
182
|
end
|
378
183
|
|
379
|
-
def self.filter_slice(results, slice)
|
380
|
-
# Avoid Empty Results
|
381
|
-
if slice.empty?
|
382
|
-
filter_empty_arg('slice')
|
383
|
-
return results
|
384
|
-
end
|
385
|
-
|
386
|
-
results.map { |row| row.slice(*slice) }
|
387
|
-
end
|
388
|
-
|
389
|
-
def self.filter_pluck(results, pluck)
|
390
|
-
# Avoid Empty Results
|
391
|
-
if pluck.empty?
|
392
|
-
filter_empty_arg('pluck')
|
393
|
-
return results
|
394
|
-
end
|
395
|
-
|
396
|
-
results.map { |x| x.slice(*pluck).values }.flatten
|
397
|
-
end
|
398
|
-
|
399
|
-
def self.filter_uniq(results, unique)
|
400
|
-
# Avoid Empty Results
|
401
|
-
if unique.empty?
|
402
|
-
filter_empty_arg('uniq')
|
403
|
-
return results
|
404
|
-
end
|
405
|
-
|
406
|
-
unique.map do |field|
|
407
|
-
results.uniq { |x| x[field] }
|
408
|
-
end.inject(:&)
|
409
|
-
end
|
410
|
-
|
411
|
-
def self.filter_stats(results, flags)
|
412
|
-
stats = flags[:stats]
|
413
|
-
|
414
|
-
# Avoid Empty Results
|
415
|
-
if stats.empty?
|
416
|
-
filter_empty_arg('stats')
|
417
|
-
return results
|
418
|
-
end
|
419
|
-
|
420
|
-
# Loop through Stats, Separate Hash/Tables
|
421
|
-
stats.map do |field|
|
422
|
-
occurrences = filter_count_occurrences(results, field, flags)
|
423
|
-
|
424
|
-
# Use Truncate For Long Keys
|
425
|
-
occurrences.transform_keys! { |key| key.to_s[0..flags[:truncate]] } if flags[:truncate]
|
426
|
-
|
427
|
-
# Total Occurences
|
428
|
-
total = occurrences.values.sum
|
429
|
-
|
430
|
-
# Percs
|
431
|
-
occurrences.transform_values! do |count|
|
432
|
-
[
|
433
|
-
count,
|
434
|
-
" #{percent(count, total)}%".pastel(:bright_black)
|
435
|
-
]
|
436
|
-
end
|
437
|
-
|
438
|
-
# Sort by total occurances / New Variable for Total
|
439
|
-
output = occurrences.sort_by(&:last).to_h.transform_values!(&:join).to_a
|
440
|
-
|
441
|
-
# Append Header / Total with field name
|
442
|
-
output.unshift([field.to_s.pastel(:bright_black), total])
|
443
|
-
|
444
|
-
# Format
|
445
|
-
output.to_h
|
446
|
-
end
|
447
|
-
end
|
448
|
-
|
449
|
-
# Percent Helper
|
450
|
-
def self.percent(value, total)
|
451
|
-
((value / total.to_f) * 100).round
|
452
|
-
end
|
453
|
-
|
454
|
-
# Helper to Count occurrences
|
455
|
-
def self.filter_count_occurrences(results, field, flags = {})
|
456
|
-
results.each_with_object(Hash.new(0)) do |entry, counts|
|
457
|
-
if entry.key? field
|
458
|
-
# Rounding in pagination breaks stats
|
459
|
-
key = if flags.key?(:round) && entry[field].numeric?
|
460
|
-
entry[field].to_f.round(flags.round)
|
461
|
-
else
|
462
|
-
entry[field]
|
463
|
-
end
|
464
|
-
|
465
|
-
counts[key] += 1
|
466
|
-
else
|
467
|
-
counts['None'.pastel(:bright_black)] += 1
|
468
|
-
end
|
469
|
-
|
470
|
-
counts
|
471
|
-
end
|
472
|
-
end
|
473
|
-
|
474
|
-
def self.filter_empty_arg(arg)
|
475
|
-
puts [
|
476
|
-
'Ignoring'.pastel(:bright_yellow),
|
477
|
-
"--#{arg}".pastel(:cyan),
|
478
|
-
'it requires an argument'.pastel(:red)
|
479
|
-
].join(' ')
|
480
|
-
end
|
481
|
-
|
482
|
-
# Break out filter row logic into separate method
|
483
|
-
def self.filter_row_key(row, arg, flags)
|
484
|
-
# Ignore Other Logic if Field isn't even included / Full Text Searching
|
485
|
-
return false unless row.key?(arg[:field]) || arg[:field] == :text
|
486
|
-
|
487
|
-
# Sensitivity Check / Check for Match / Full Text Searching
|
488
|
-
search_data = arg[:field] == :text ? row : row[arg.field]
|
489
|
-
match = filter_row_entry(search_data.to_s, arg, flags)
|
490
|
-
|
491
|
-
# Pivot of off include vs exclude
|
492
|
-
if arg.bang
|
493
|
-
!match
|
494
|
-
else
|
495
|
-
match
|
496
|
-
end
|
497
|
-
end
|
498
|
-
|
499
|
-
# Field Partial / Case / Exact search
|
500
|
-
def self.filter_row_entry(entry, arg, flags)
|
501
|
-
# Exact Matching / Unless doing full text search
|
502
|
-
return entry == arg.value.to_s if flags.key?(:exact) && arg.field != :text
|
503
|
-
|
504
|
-
# Cast to String/Integer Helper
|
505
|
-
entry, value = filter_entry_cast(entry, arg, flags)
|
506
|
-
|
507
|
-
entry.send(arg.logic, value)
|
508
|
-
end
|
509
|
-
|
510
|
-
# Handle casting to strings or integers
|
511
|
-
def self.filter_entry_cast(entry, arg, flags)
|
512
|
-
# Cast to String
|
513
|
-
value = arg.value.to_s
|
514
|
-
|
515
|
-
case arg.logic
|
516
|
-
when :include?
|
517
|
-
|
518
|
-
# Exact Case argument
|
519
|
-
unless flags.key?(:case)
|
520
|
-
entry.downcase!
|
521
|
-
value.downcase!
|
522
|
-
end
|
523
|
-
when :>=, :<=
|
524
|
-
entry = entry.to_i if entry.numeric?
|
525
|
-
value = value.to_i if value&.numeric?
|
526
|
-
end
|
527
|
-
|
528
|
-
[entry, value]
|
529
|
-
end
|
530
|
-
|
531
|
-
def self.filter_entry_logic(entry, arg)
|
532
|
-
entry.send(arg.logic, arg.value)
|
533
|
-
end
|
534
|
-
|
535
184
|
# Total Count Helper
|
536
185
|
def self.total_count(results, flags)
|
537
186
|
results.map do |k, v|
|
@@ -541,7 +190,7 @@ module GreenHat
|
|
541
190
|
|
542
191
|
output = {}
|
543
192
|
output[:total] = v.count
|
544
|
-
output[:duration] = calculate_duration(v)
|
193
|
+
output[:duration] = Query.calculate_duration(v)
|
545
194
|
|
546
195
|
# Sort / Get first and Last
|
547
196
|
list = v.select(&:time).map(&:time)
|
@@ -594,8 +243,8 @@ module GreenHat
|
|
594
243
|
end
|
595
244
|
|
596
245
|
# Total Log List Manipulator
|
597
|
-
def self.prepare_list(log_list, base_list = nil
|
598
|
-
base_list ||=
|
246
|
+
def self.prepare_list(log_list, base_list = nil)
|
247
|
+
base_list ||= Thing.list
|
599
248
|
|
600
249
|
# Assume all
|
601
250
|
log_list.push '*' if log_list.empty?
|
@@ -606,9 +255,9 @@ module GreenHat
|
|
606
255
|
log_list
|
607
256
|
end
|
608
257
|
|
609
|
-
# Fuzzy match for things
|
258
|
+
# Fuzzy match for things / List used for processable (Hash Entries)
|
610
259
|
def self.thing_list
|
611
|
-
@thing_list ||= Thing.
|
260
|
+
@thing_list ||= Thing.list.map(&:name)
|
612
261
|
|
613
262
|
@thing_list
|
614
263
|
end
|
@@ -620,7 +269,7 @@ module GreenHat
|
|
620
269
|
return file if file.instance_of?(Thing)
|
621
270
|
|
622
271
|
if flags.fuzzy_file_match
|
623
|
-
Thing.
|
272
|
+
Thing.list.select { |x| x.name.include?(file) || x.type.include?(file) }
|
624
273
|
else
|
625
274
|
Thing.where name: file
|
626
275
|
end
|
@@ -666,5 +315,6 @@ module GreenHat
|
|
666
315
|
puts
|
667
316
|
end
|
668
317
|
end
|
318
|
+
|
669
319
|
# rubocop:enable Metrics/ModuleLength
|
670
320
|
end
|
data/lib/greenhat/shell.rb
CHANGED
@@ -7,6 +7,10 @@ module GreenHat
|
|
7
7
|
# rubocop:enable Lint/Debugger
|
8
8
|
end
|
9
9
|
|
10
|
+
def self.settings
|
11
|
+
Settings.configure_settings
|
12
|
+
end
|
13
|
+
|
10
14
|
def self.web
|
11
15
|
# Load Required Files
|
12
16
|
require 'greenhat/web'
|
@@ -18,6 +22,10 @@ module GreenHat
|
|
18
22
|
Disk.df
|
19
23
|
end
|
20
24
|
|
25
|
+
def self.quit(_raw = {})
|
26
|
+
exit 0
|
27
|
+
end
|
28
|
+
|
21
29
|
def self.ps(raw = {})
|
22
30
|
Process.ps raw
|
23
31
|
end
|
@@ -60,7 +68,7 @@ module GreenHat
|
|
60
68
|
end
|
61
69
|
|
62
70
|
def self.quiet
|
63
|
-
Cli.
|
71
|
+
Cli.quiet_toggle
|
64
72
|
|
65
73
|
if Cli.quiet
|
66
74
|
puts "GreenHat Quiet Logging #{'Off'.pastel(:red)}"
|
@@ -379,6 +379,13 @@ module GreenHat
|
|
379
379
|
%r{nginx/gitlab_pages_access.log}
|
380
380
|
]
|
381
381
|
},
|
382
|
+
'nginx/gitlab_pages_error.log' => {
|
383
|
+
format: :raw,
|
384
|
+
log: true,
|
385
|
+
pattern: [
|
386
|
+
%r{nginx/gitlab_pages_error.log}
|
387
|
+
]
|
388
|
+
},
|
382
389
|
'nginx/gitlab_access.log' => {
|
383
390
|
format: :nginx,
|
384
391
|
log: true,
|
@@ -11,9 +11,10 @@ module GreenHat
|
|
11
11
|
result = Oj.load row
|
12
12
|
|
13
13
|
# Parsing Time
|
14
|
-
|
14
|
+
format_time(result)
|
15
15
|
|
16
|
-
flatten_hash(result).sort.to_h
|
16
|
+
# flatten_hash(result).sort.to_h
|
17
|
+
result
|
17
18
|
rescue StandardError => e
|
18
19
|
# TODO: Background Logger?
|
19
20
|
e.message
|
@@ -29,6 +30,7 @@ module GreenHat
|
|
29
30
|
v.is_a?(Hash) ? a.merge(flatten_hash(v, "#{prefix}#{k}.")) : a.merge("#{prefix}#{k}".to_sym => v)
|
30
31
|
end
|
31
32
|
end
|
33
|
+
|
32
34
|
# ==========================================================================
|
33
35
|
end
|
34
36
|
end
|
@@ -4,9 +4,9 @@ module GreenHat
|
|
4
4
|
module Formatters
|
5
5
|
# Formatters for single json blobs in entire file
|
6
6
|
def format_colon_split_strip
|
7
|
-
self.result =
|
7
|
+
self.result = raw_full.to_h do |row|
|
8
8
|
row.split(':', 2).map(&:strip)
|
9
|
-
end
|
9
|
+
end
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -5,7 +5,7 @@ module GreenHat
|
|
5
5
|
# Get Split Memory Table
|
6
6
|
def format_free_m
|
7
7
|
# Headers to Readable Symbol
|
8
|
-
headers =
|
8
|
+
headers = raw_full.first.split.map(&:downcase).map do |x|
|
9
9
|
x.gsub(/\s+/, '_').gsub(/[^0-9A-Za-z_]/, '')
|
10
10
|
end.map(&:to_sym)
|
11
11
|
|
@@ -15,7 +15,7 @@ module GreenHat
|
|
15
15
|
final = []
|
16
16
|
|
17
17
|
# Put fields into a Hash based on Location/Key
|
18
|
-
|
18
|
+
raw_full[1..].map(&:split).each do |row|
|
19
19
|
result = {}
|
20
20
|
row.each_with_index do |detail, i|
|
21
21
|
result[headers[i]] = detail.split(':').first
|
@@ -14,10 +14,10 @@ module GreenHat
|
|
14
14
|
{ message: row }
|
15
15
|
end
|
16
16
|
|
17
|
-
# Parsing Time
|
18
|
-
|
17
|
+
# Parsing Time / Recusrive is really slow
|
18
|
+
format_time(result)
|
19
19
|
|
20
|
-
result
|
20
|
+
result
|
21
21
|
rescue StandardError => e
|
22
22
|
# TODO: Background Logger?
|
23
23
|
e.message
|
@@ -27,11 +27,12 @@ module GreenHat
|
|
27
27
|
|
28
28
|
:ok
|
29
29
|
end
|
30
|
+
|
30
31
|
# ==========================================================================
|
31
32
|
|
32
|
-
# Recursively Navigate
|
33
|
+
# Recursively Navigate -- Recusrive is really slow
|
33
34
|
def format_json_traverse(result)
|
34
|
-
|
35
|
+
format_time(result)
|
35
36
|
|
36
37
|
result.each do |_key, value|
|
37
38
|
next unless value.instance_of? Hash
|
@@ -40,27 +41,50 @@ module GreenHat
|
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
44
|
+
def format_time_fields
|
45
|
+
%i[time timestamp created_at enqueued_at completed_at]
|
46
|
+
end
|
47
|
+
|
48
|
+
# Loop through potential fields and parse as needed
|
49
|
+
def format_time(result)
|
50
|
+
format_time_fields.each do |field|
|
51
|
+
result[field] = format_time_parse(result[field]) if result.key?(field)
|
52
|
+
rescue StandardError => e
|
53
|
+
LogBot.warn('JSON Format Time', e.message)
|
54
|
+
next
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
43
58
|
# Check for Common Fields
|
44
|
-
def format_json_time(result)
|
45
|
-
|
59
|
+
# def format_json_time(result)
|
60
|
+
# if result.key? :time
|
61
|
+
# result.time = format_time_parse(result.time)
|
62
|
+
# elsif result.key?(:timestamp)
|
63
|
+
# result.time = format_time_parse(result.timestamp)
|
64
|
+
# end
|
46
65
|
|
47
|
-
|
48
|
-
|
66
|
+
# result.created_at = format_time_parse(result.created_at) if result.key? :created_at
|
67
|
+
# result.enqueued_at = format_time_parse(result.enqueued_at) if result.key? :enqueued_at
|
68
|
+
# rescue StandardError => e
|
69
|
+
# LogBot.warn("JSON Time Parse", e.message)
|
70
|
+
# true
|
71
|
+
# end
|
49
72
|
|
50
|
-
|
51
|
-
|
52
|
-
rescue StandardError
|
53
|
-
|
54
|
-
|
73
|
+
def format_time_parse(time)
|
74
|
+
Time.parse time
|
75
|
+
rescue StandardError
|
76
|
+
# Handle Epoch Timestamps as well as string timestamps
|
77
|
+
format_time_numeric(time)
|
55
78
|
end
|
56
79
|
|
57
|
-
|
58
|
-
def format_time_parse(time)
|
80
|
+
def format_time_numeric(time)
|
59
81
|
if time.numeric?
|
60
82
|
Time.at time
|
61
83
|
else
|
62
|
-
|
84
|
+
time
|
63
85
|
end
|
86
|
+
rescue StandardError => e
|
87
|
+
LogBot.warn('JSON Time Parse', e.message)
|
64
88
|
end
|
65
89
|
# ---
|
66
90
|
end
|
@@ -14,9 +14,9 @@ module GreenHat
|
|
14
14
|
end
|
15
15
|
|
16
16
|
# Parsing Time
|
17
|
-
|
17
|
+
format_time(result)
|
18
18
|
|
19
|
-
result
|
19
|
+
result
|
20
20
|
rescue StandardError => e
|
21
21
|
LogBot.warn('JSON Parse', e.message)
|
22
22
|
next
|
@@ -36,6 +36,7 @@ module GreenHat
|
|
36
36
|
|
37
37
|
result
|
38
38
|
end
|
39
|
+
|
39
40
|
# ==========================================================================
|
40
41
|
end
|
41
42
|
end
|