greenhat 0.3.3 → 0.3.4
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 +41 -0
- data/lib/greenhat/accessors/gitlab.rb +26 -0
- data/lib/greenhat/archive.rb +4 -0
- data/lib/greenhat/cli.rb +1 -0
- data/lib/greenhat/shell/filter_help.rb +203 -2
- data/lib/greenhat/shell/log.rb +11 -2
- data/lib/greenhat/shell/markdown.rb +356 -0
- data/lib/greenhat/shell/report.rb +34 -3
- data/lib/greenhat/shell/shell_helper.rb +12 -2
- data/lib/greenhat/thing/file_types.rb +92 -7
- data/lib/greenhat/thing/formatters/kube_json.rb +36 -0
- data/lib/greenhat/thing/formatters/kube_nginx.rb +48 -0
- data/lib/greenhat/thing/formatters/kube_webservice.rb +51 -0
- data/lib/greenhat/thing/formatters/registry.rb +47 -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: 4c055abc7397135be9ca07e7965fc88d67f1d7d16099a5cc5db1977986490361
|
4
|
+
data.tar.gz: 2e2f66a3bbc5bdc84e8b5bef9abfa28706f48841f25c7e642fe352c063745e22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d9fcd1274e4887fa332a053185ea1049e5db2f3d449714542a31084234326d48623e95e0fb8678b6cb67b20b636ec04c82db1555ff43aac09f158d6df4fc7dc0
|
7
|
+
data.tar.gz: c6fe018479b7cc90f2e70b411761fe5e3b8da5d4237064e3bb450399f966cf00a0302adc433ebf39fad57565ca11efbd95fbd0601a15f2989bc1c6455029a4e8
|
@@ -79,5 +79,46 @@ module GreenHat
|
|
79
79
|
output
|
80
80
|
end
|
81
81
|
# rubocop:enable Metrics/MethodLength
|
82
|
+
|
83
|
+
# Unified Output Handler
|
84
|
+
def self.markdown_format(file, name = false, limit = nil, filter = %w[tmpfs loop])
|
85
|
+
output = []
|
86
|
+
|
87
|
+
output << file.friendly_name if name
|
88
|
+
|
89
|
+
# Reject TMPFS
|
90
|
+
disks = file.data.sort_by { |x| x.use.to_i }.reverse
|
91
|
+
|
92
|
+
# Filter
|
93
|
+
disks.reject! { |x| filter.any? { |y| x.filesystem.include? y } }
|
94
|
+
|
95
|
+
disks = disks[0..limit - 1] if limit
|
96
|
+
|
97
|
+
pad_mount, pad_size, pad_used, pad_avail = GreenHat::Disk.padding(disks)
|
98
|
+
|
99
|
+
# Headers
|
100
|
+
output << [
|
101
|
+
'Mount'.ljust(pad_mount),
|
102
|
+
'Size'.ljust(pad_size),
|
103
|
+
'Used'.ljust(pad_used),
|
104
|
+
'Avail'.ljust(pad_avail),
|
105
|
+
'% Use'.ljust(pad_avail)
|
106
|
+
].join
|
107
|
+
|
108
|
+
# Table Summary
|
109
|
+
disks.map do |disk|
|
110
|
+
# Whole Thing
|
111
|
+
output << [
|
112
|
+
disk.mounted_on.ljust(pad_mount),
|
113
|
+
disk[:size].to_s.ljust(pad_size),
|
114
|
+
disk.used.to_s.ljust(pad_used),
|
115
|
+
disk.avail.to_s.ljust(pad_avail),
|
116
|
+
disk.use.rjust(5).ljust(5)
|
117
|
+
].join
|
118
|
+
end
|
119
|
+
|
120
|
+
output
|
121
|
+
end
|
122
|
+
# =
|
82
123
|
end
|
83
124
|
end
|
@@ -71,5 +71,31 @@ module GreenHat
|
|
71
71
|
|
72
72
|
table.render(:unicode, padding: [0, 1, 0, 1], indent: indent)
|
73
73
|
end
|
74
|
+
|
75
|
+
def self.services_markdown(archive)
|
76
|
+
# manifest = archive.things.find { |x| x.type == 'gitlab/version-manifest.json' }
|
77
|
+
gitlab_status = archive.things.find { |x| x.name == 'gitlab_status' }
|
78
|
+
|
79
|
+
list = gitlab_status.data.keys.sort.map do |service|
|
80
|
+
status = gitlab_status.data.dig(service, 0, :status) == 'run' ? '↑' : '!'
|
81
|
+
|
82
|
+
"#{service}#{status}"
|
83
|
+
end
|
84
|
+
|
85
|
+
# Keep Alphabetical Sort
|
86
|
+
groups = list.each_slice((list.size / 3.to_f).round).to_a
|
87
|
+
|
88
|
+
table = TTY::Table.new do |t|
|
89
|
+
loop do
|
90
|
+
break if groups.all?(&:empty?)
|
91
|
+
|
92
|
+
t << groups.map(&:shift)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
"```\n#{table.render(:basic)}\n```"
|
97
|
+
end
|
98
|
+
|
99
|
+
# ==
|
74
100
|
end
|
75
101
|
end
|
data/lib/greenhat/archive.rb
CHANGED
data/lib/greenhat/cli.rb
CHANGED
@@ -2,8 +2,8 @@ module GreenHat
|
|
2
2
|
# CLI Helper
|
3
3
|
module ShellHelper
|
4
4
|
# Unify Filter / Filter Help
|
5
|
+
# rubocop:disable Metrics/MethodLength,Metrics/ModuleLength
|
5
6
|
module Filter
|
6
|
-
# rubocop:disable Metrics/MethodLength
|
7
7
|
def self.help
|
8
8
|
puts "\u2500".pastel(:cyan) * 20
|
9
9
|
puts 'Filter'.pastel(:yellow)
|
@@ -156,7 +156,208 @@ module GreenHat
|
|
156
156
|
|
157
157
|
puts
|
158
158
|
end
|
159
|
-
|
159
|
+
|
160
|
+
def self.help_index
|
161
|
+
{
|
162
|
+
title: [
|
163
|
+
"\u2500".pastel(:cyan) * 20,
|
164
|
+
'Filter'.pastel(:yellow),
|
165
|
+
"\u2500".pastel(:cyan) * 20
|
166
|
+
],
|
167
|
+
options: [
|
168
|
+
'Options'.pastel(:blue)
|
169
|
+
],
|
170
|
+
|
171
|
+
raw: [
|
172
|
+
'--raw'.pastel(:green),
|
173
|
+
' Disable formatting and page/less'
|
174
|
+
],
|
175
|
+
|
176
|
+
page: [
|
177
|
+
'--page'.pastel(:green),
|
178
|
+
' Specifically enable or disable paging',
|
179
|
+
' E.g. --page (default to true), --page=true, --page=false'
|
180
|
+
],
|
181
|
+
|
182
|
+
round: ['--round'.pastel(:green),
|
183
|
+
' Attempt to round all integers. Default: 2.',
|
184
|
+
' E.g. --round, --round=3, --round=0'],
|
185
|
+
|
186
|
+
limit: [
|
187
|
+
|
188
|
+
'--limit'.pastel(:green),
|
189
|
+
' Limit total output lines. Disabled by default. Default without value is based on screen height',
|
190
|
+
' E.g. --limit, --limit=5'
|
191
|
+
],
|
192
|
+
|
193
|
+
json: [
|
194
|
+
'--json'.pastel(:green),
|
195
|
+
' Print output back into JSON'
|
196
|
+
],
|
197
|
+
|
198
|
+
or: [
|
199
|
+
'--or'.pastel(:green),
|
200
|
+
' Filters will use OR instead of AND (all match vs any match)'
|
201
|
+
],
|
202
|
+
|
203
|
+
total: [
|
204
|
+
'--total'.pastel(:green),
|
205
|
+
' Print only total count of matching entries'
|
206
|
+
],
|
207
|
+
|
208
|
+
fields: [
|
209
|
+
'--fields'.pastel(:green),
|
210
|
+
' Print only Available fields for selected files'
|
211
|
+
],
|
212
|
+
|
213
|
+
slice: [
|
214
|
+
'--slice'.pastel(:green),
|
215
|
+
' Extract specific fields from entries (slice multiple with comma)',
|
216
|
+
' Ex: --slice=path or --slice=path,params'
|
217
|
+
|
218
|
+
],
|
219
|
+
|
220
|
+
except: [
|
221
|
+
'--except'.pastel(:green),
|
222
|
+
' Exclude specific fields (except multiple with comma)',
|
223
|
+
' Ex: --except=params --except=params,path'
|
224
|
+
|
225
|
+
],
|
226
|
+
|
227
|
+
exists: [
|
228
|
+
'--exists'.pastel(:green),
|
229
|
+
' Ensure field exists regardless of contents',
|
230
|
+
' Ex: --exists=params --exists=params,path'
|
231
|
+
|
232
|
+
],
|
233
|
+
|
234
|
+
stats: [
|
235
|
+
'--stats'.pastel(:green),
|
236
|
+
' Order/Count occurrances by field. Combine with `truncate` for key names',
|
237
|
+
' Ex: --stats=params --except=params,path'
|
238
|
+
|
239
|
+
],
|
240
|
+
|
241
|
+
uniq: [
|
242
|
+
'--uniq'.pastel(:green),
|
243
|
+
' Show unique values only',
|
244
|
+
' Ex: --uniq=params --uniq=params,path'
|
245
|
+
|
246
|
+
],
|
247
|
+
|
248
|
+
pluck: [
|
249
|
+
'--pluck'.pastel(:green),
|
250
|
+
' Extract values from entries',
|
251
|
+
' Ex: --pluck=params --pluck=params,path'
|
252
|
+
|
253
|
+
],
|
254
|
+
|
255
|
+
archive: [
|
256
|
+
'--archive'.pastel(:green),
|
257
|
+
' Limit to specific archvie name (partial matching /inclusive). Matching SOS tar.gz name',
|
258
|
+
' Ex: --archive=dev-gitlab_20210622154626, --archive=202106,202107'
|
259
|
+
|
260
|
+
],
|
261
|
+
|
262
|
+
sort: [
|
263
|
+
'--sort'.pastel(:green),
|
264
|
+
' Sort by multiple fields',
|
265
|
+
' Ex: --sort=duration_s,db_duration_s'
|
266
|
+
|
267
|
+
],
|
268
|
+
|
269
|
+
reverse: [
|
270
|
+
'--reverse'.pastel(:green),
|
271
|
+
' Reverse all results',
|
272
|
+
' Ex: --reverse'
|
273
|
+
|
274
|
+
],
|
275
|
+
|
276
|
+
combine: [
|
277
|
+
'--combine'.pastel(:green),
|
278
|
+
' Omit archive identifier dividers. Useful with sort or time filters',
|
279
|
+
' Ex: --combine'
|
280
|
+
|
281
|
+
],
|
282
|
+
|
283
|
+
case: [
|
284
|
+
'--case'.pastel(:green),
|
285
|
+
' Exact case match. Defaults to case insensitive',
|
286
|
+
' Ex: --case; --name=Jon, --name=jane --case'
|
287
|
+
|
288
|
+
],
|
289
|
+
|
290
|
+
exact: [
|
291
|
+
'--exact'.pastel(:green),
|
292
|
+
' Exact parameter/value match. Defaults to partial match',
|
293
|
+
' Ex: --field=CommonPartial --exact'
|
294
|
+
|
295
|
+
],
|
296
|
+
|
297
|
+
start: [
|
298
|
+
'--start'.pastel(:green),
|
299
|
+
' Show events after specified time. Filtered by the `time` field',
|
300
|
+
' Use with `--end` for between selections',
|
301
|
+
' Ex: log filter --start="2021-06-22 14:44 UTC" --end="2021-06-22 14:45 UTC"'
|
302
|
+
|
303
|
+
],
|
304
|
+
|
305
|
+
end: [
|
306
|
+
'--end'.pastel(:green),
|
307
|
+
' Show events before specified time. Filtered by the `time` field',
|
308
|
+
' Use with `--start` for between selections',
|
309
|
+
' Ex: log filter --end="2021-06-22"'
|
310
|
+
],
|
311
|
+
|
312
|
+
time_zone: [
|
313
|
+
'--time_zone'.pastel(:green),
|
314
|
+
' Manipulate the `time` field into a specific timezone',
|
315
|
+
' Ex: log filter --time_zone=EDT'
|
316
|
+
|
317
|
+
],
|
318
|
+
|
319
|
+
text: [
|
320
|
+
'--text'.pastel(:green),
|
321
|
+
' Full entry text searching (slow)',
|
322
|
+
' --text="anything here"'
|
323
|
+
],
|
324
|
+
|
325
|
+
table_style: [
|
326
|
+
'--table_style'.pastel(:green),
|
327
|
+
' Renderer used for formatted output. basic, ascii, or unicode(default)',
|
328
|
+
' Ex: log filter --table_style=base'
|
329
|
+
],
|
330
|
+
|
331
|
+
truncate: [
|
332
|
+
'--truncate'.pastel(:green),
|
333
|
+
' Truncate field length. On by default (4 rows). Performance issues!',
|
334
|
+
' Disable with --truncate=0'.pastel(:bright_red),
|
335
|
+
' Ex: --truncate=200, --truncate=2048"'
|
336
|
+
],
|
337
|
+
|
338
|
+
field: [
|
339
|
+
'Field Searching'.pastel(:blue),
|
340
|
+
' --[key]=[value]',
|
341
|
+
' Search in key for value',
|
342
|
+
' Example: --path=mirror/pull'
|
343
|
+
],
|
344
|
+
|
345
|
+
ls: [
|
346
|
+
'Search specific logs'.pastel(:blue),
|
347
|
+
' Any non dash parameters will be the log list to search from',
|
348
|
+
" Ex: log filter --path=api sidekiq/current (hint: use `#{'ls'.pastel(:yellow)}` for log names"
|
349
|
+
],
|
350
|
+
|
351
|
+
examples: [
|
352
|
+
'Example Queries'.pastel(:blue),
|
353
|
+
" Also see #{'examples'.pastel(:bright_blue)} for even more examples",
|
354
|
+
' log filter --class=BuildFinishedWorker sidekiq/current --slice=time,message',
|
355
|
+
' log filter gitlab-rails/api_json.log --slice=ua --uniq=ua --ua=gitlab-runner'
|
356
|
+
]
|
357
|
+
|
358
|
+
}
|
359
|
+
end
|
360
|
+
# rubocop:enable Metrics/MethodLength,Metrics/ModuleLength
|
160
361
|
end
|
161
362
|
end
|
162
363
|
end
|
data/lib/greenhat/shell/log.rb
CHANGED
@@ -47,6 +47,7 @@ module GreenHat
|
|
47
47
|
puts ' filter'.pastel(:green)
|
48
48
|
puts " Primary way for log searching within greenhat. See #{'filter_help'.pastel(:blue)}"
|
49
49
|
puts ' Time, round, slice/except, and/or, stats, uniq, sort'
|
50
|
+
puts " #{'filter_help'.pastel(:blue)} supports filtering Ex: #{'filter_help stats'.pastel(:blue)}"
|
50
51
|
puts
|
51
52
|
|
52
53
|
puts ' show'.pastel(:green)
|
@@ -62,8 +63,16 @@ module GreenHat
|
|
62
63
|
puts "See #{'examples'.pastel(:bright_blue)} for query examples"
|
63
64
|
end
|
64
65
|
|
65
|
-
def self.filter_help
|
66
|
-
|
66
|
+
def self.filter_help(args = {})
|
67
|
+
if args.empty?
|
68
|
+
ShellHelper::Filter.help
|
69
|
+
else
|
70
|
+
list = ShellHelper::Filter.help_index.select do |k, _v|
|
71
|
+
k.to_s.include? args.first
|
72
|
+
end
|
73
|
+
|
74
|
+
puts list.values.map { |x| x.join("\n") }.join("\n\n")
|
75
|
+
end
|
67
76
|
end
|
68
77
|
|
69
78
|
def self.ls(args = [])
|
@@ -0,0 +1,356 @@
|
|
1
|
+
module GreenHat
|
2
|
+
# Root Level Shell / Report Helper
|
3
|
+
module Shell
|
4
|
+
def self.markdown_report(raw)
|
5
|
+
_files, flags, _args = Args.parse(raw)
|
6
|
+
|
7
|
+
archives = if flags.archive
|
8
|
+
Archive.all.select do |archive|
|
9
|
+
flags.archive.any? { |x| archive.name.include? x.to_s }
|
10
|
+
end
|
11
|
+
else
|
12
|
+
Archive.all
|
13
|
+
end
|
14
|
+
|
15
|
+
ShellHelper.show(archives.map(&:report_markdown).map(&:show).flatten, flags)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module GreenHat
|
21
|
+
# Report Generator Helper
|
22
|
+
# rubocop:disable Metrics/ClassLength
|
23
|
+
class ReportMarkdown
|
24
|
+
include ActionView::Helpers::NumberHelper
|
25
|
+
|
26
|
+
attr_accessor :archive, :host, :os_release, :selinux_status, :cpu, :uname,
|
27
|
+
:timedatectl, :uptime, :meminfo, :gitlab_manifest, :gitlab_status,
|
28
|
+
:production_log, :api_log, :application_log, :sidekiq_log,
|
29
|
+
:exceptions_log, :gitaly_log, :free_m, :disk_free
|
30
|
+
|
31
|
+
# Find Needed Files for Report
|
32
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
33
|
+
def initialize(archive)
|
34
|
+
self.archive = archive
|
35
|
+
self.host = archive.things.find { |x| x.name == 'hostname' }
|
36
|
+
self.os_release = archive.things.find { |x| x.name == 'etc/os-release' }
|
37
|
+
self.selinux_status = archive.things.find { |x| x.name == 'sestatus' }
|
38
|
+
self.cpu = archive.things.find { |x| x.name == 'lscpu' }
|
39
|
+
self.uname = archive.things.find { |x| x.name == 'uname' }
|
40
|
+
self.timedatectl = archive.things.find { |x| x.name == 'timedatectl' }
|
41
|
+
self.uptime = archive.things.find { |x| x.name == 'uptime' }
|
42
|
+
self.meminfo = archive.things.find { |x| x.name == 'meminfo' }
|
43
|
+
self.free_m = archive.things.find { |x| x.name == 'free_m' }
|
44
|
+
self.gitlab_manifest = archive.things.find { |x| x.name == 'gitlab/version-manifest.json' }
|
45
|
+
self.gitlab_status = archive.things.find { |x| x.name == 'gitlab_status' }
|
46
|
+
self.production_log = archive.things.find { |x| x.name == 'gitlab-rails/production_json.log' }
|
47
|
+
self.api_log = archive.things.find { |x| x.name == 'gitlab-rails/api_json.log' }
|
48
|
+
self.application_log = archive.things.find { |x| x.name == 'gitlab-rails/application_json.log' }
|
49
|
+
self.exceptions_log = archive.things.find { |x| x.name == 'gitlab-rails/exceptions_json.log' }
|
50
|
+
self.gitaly_log = archive.things.find { |x| x.name == 'gitaly/current' }
|
51
|
+
self.sidekiq_log = archive.things.find { |x| x.name == 'sidekiq/current' }
|
52
|
+
self.disk_free = archive.things.find { |x| x.name == 'df_h' }
|
53
|
+
end
|
54
|
+
|
55
|
+
def show
|
56
|
+
output = [
|
57
|
+
archive.friendly_name,
|
58
|
+
''
|
59
|
+
]
|
60
|
+
|
61
|
+
# OS
|
62
|
+
output << "**OS**\n"
|
63
|
+
|
64
|
+
output << collect_host
|
65
|
+
output << ''
|
66
|
+
|
67
|
+
# Memory
|
68
|
+
if meminfo || free_m
|
69
|
+
output << "**Memory**\n"
|
70
|
+
# output << memory_perc if meminfo
|
71
|
+
output << memory_free if free_m
|
72
|
+
output << ''
|
73
|
+
end
|
74
|
+
|
75
|
+
# Disk
|
76
|
+
if disk_free
|
77
|
+
output << disks
|
78
|
+
output << ''
|
79
|
+
end
|
80
|
+
|
81
|
+
# Gitlab
|
82
|
+
output << "**GitLab**\n" if gitlab_manifest || gitlab_status
|
83
|
+
output << gitlab_version if gitlab_manifest
|
84
|
+
output << gitlab_services if gitlab_status
|
85
|
+
|
86
|
+
output << ''
|
87
|
+
|
88
|
+
output << "**Errors**\n" if production_log || api_log || application_log || sidekiq_log
|
89
|
+
output << collect_errors
|
90
|
+
|
91
|
+
# Final Space / Return
|
92
|
+
output << ''
|
93
|
+
output
|
94
|
+
end
|
95
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
96
|
+
|
97
|
+
def collect_host
|
98
|
+
output = []
|
99
|
+
output << hostname if host
|
100
|
+
output << distro if os_release
|
101
|
+
output << selinux if selinux_status
|
102
|
+
# output << arch if cpu
|
103
|
+
output << kernel if uname
|
104
|
+
output << sys_time if timedatectl
|
105
|
+
output << sys_uptime if uptime
|
106
|
+
output << load_average if uptime && cpu
|
107
|
+
|
108
|
+
groups = output.each_slice((output.size / 2.to_f).round).to_a
|
109
|
+
|
110
|
+
table = TTY::Table.new do |t|
|
111
|
+
loop do
|
112
|
+
break if groups.all?(&:empty?)
|
113
|
+
|
114
|
+
t << groups.map(&:shift)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
"```\n#{table.render(:basic, padding: [0, 2, 0, 0])}\n```"
|
119
|
+
end
|
120
|
+
|
121
|
+
def collect_errors
|
122
|
+
output = []
|
123
|
+
output << production_errors if production_log
|
124
|
+
output << application_errors if application_log
|
125
|
+
output << sidekiq_errors if sidekiq_log
|
126
|
+
output << api_errors if api_log
|
127
|
+
output << exception_errors if exceptions_log
|
128
|
+
output << gitaly_errors if gitaly_log
|
129
|
+
|
130
|
+
# Keep Alphabetical Sort
|
131
|
+
groups = output.each_slice((output.size / 3.to_f).round).to_a
|
132
|
+
|
133
|
+
table = TTY::Table.new do |t|
|
134
|
+
loop do
|
135
|
+
break if groups.all?(&:empty?)
|
136
|
+
|
137
|
+
t << groups.map(&:shift)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
"```\n#{table.render(:basic, padding: [0, 2, 0, 0])}\n```"
|
142
|
+
end
|
143
|
+
|
144
|
+
def exception_errors
|
145
|
+
count = exceptions_log.data.count
|
146
|
+
|
147
|
+
"Exception: #{count}"
|
148
|
+
end
|
149
|
+
|
150
|
+
def gitaly_errors
|
151
|
+
count = gitaly_log.data.count { |x| x.level == 'error' }
|
152
|
+
|
153
|
+
"Gitaly: #{count}"
|
154
|
+
end
|
155
|
+
|
156
|
+
def production_errors
|
157
|
+
count = production_log.data.count { |x| x.status == 500 }
|
158
|
+
|
159
|
+
"Production: #{count}"
|
160
|
+
end
|
161
|
+
|
162
|
+
def api_errors
|
163
|
+
count = api_log.data.count { |x| x.status == 500 }
|
164
|
+
|
165
|
+
"API: #{count}"
|
166
|
+
end
|
167
|
+
|
168
|
+
def application_errors
|
169
|
+
results = ShellHelper.filter_internal([
|
170
|
+
'gitlab-rails/application_json.log',
|
171
|
+
'--message!="Cannot obtain an exclusive lease"',
|
172
|
+
'--severity=error'
|
173
|
+
].join(' '))
|
174
|
+
|
175
|
+
"Application: #{results.count}"
|
176
|
+
end
|
177
|
+
|
178
|
+
def sidekiq_errors
|
179
|
+
count = sidekiq_log.data.count { |x| x&.severity == 'ERROR' }
|
180
|
+
|
181
|
+
"Sidekiq: #{count}"
|
182
|
+
end
|
183
|
+
|
184
|
+
def gitlab_services
|
185
|
+
[
|
186
|
+
"**Services**\n",
|
187
|
+
"\n",
|
188
|
+
GreenHat::GitLab.services_markdown(archive)
|
189
|
+
].join
|
190
|
+
rescue StandardError => e
|
191
|
+
LogBot.fatal('GitLab Services', message: e.message, backtrace: e.backtrace.first)
|
192
|
+
end
|
193
|
+
|
194
|
+
def gitlab_version
|
195
|
+
"Version: #{gitlab_manifest.data.build_version}\n"
|
196
|
+
end
|
197
|
+
|
198
|
+
def hostname
|
199
|
+
"Hostname: #{host.data.first}"
|
200
|
+
end
|
201
|
+
|
202
|
+
def distro
|
203
|
+
[
|
204
|
+
"Distro: [#{os_release.data.ID}] ",
|
205
|
+
os_release.data.PRETTY_NAME
|
206
|
+
].join
|
207
|
+
end
|
208
|
+
|
209
|
+
def selinux
|
210
|
+
status = selinux_status.data['SELinux status']
|
211
|
+
|
212
|
+
[
|
213
|
+
'SeLinux: ',
|
214
|
+
status,
|
215
|
+
' (',
|
216
|
+
selinux_status.data['Current mode'],
|
217
|
+
')'
|
218
|
+
].join
|
219
|
+
end
|
220
|
+
|
221
|
+
def arch
|
222
|
+
[
|
223
|
+
'Arch: ',
|
224
|
+
cpu.data.Architecture
|
225
|
+
].join
|
226
|
+
end
|
227
|
+
|
228
|
+
def kernel
|
229
|
+
# TODO: Better way to consistently get uname info?
|
230
|
+
value, build = uname.data.first.split[2].split('-')
|
231
|
+
[
|
232
|
+
'Kernel: ',
|
233
|
+
value,
|
234
|
+
" (#{build})"
|
235
|
+
].join
|
236
|
+
end
|
237
|
+
|
238
|
+
# Helper for finding if NTP is enabled
|
239
|
+
def ntp_keys
|
240
|
+
[
|
241
|
+
'Network time on', 'NTP enabled', 'NTP service', 'System clock synchronized'
|
242
|
+
]
|
243
|
+
end
|
244
|
+
|
245
|
+
def sys_time
|
246
|
+
# Ignore if Empty
|
247
|
+
return false if timedatectl.data.nil?
|
248
|
+
|
249
|
+
ntp_statuses = timedatectl.data.slice(*ntp_keys).values.compact
|
250
|
+
|
251
|
+
ntp_status = ntp_statuses.first
|
252
|
+
|
253
|
+
# Fall Back
|
254
|
+
ntp_status ||= 'unknown'
|
255
|
+
|
256
|
+
[
|
257
|
+
'Sys Time: ',
|
258
|
+
timedatectl.data['Local time'],
|
259
|
+
"(ntp: #{ntp_status})"
|
260
|
+
].join
|
261
|
+
end
|
262
|
+
|
263
|
+
# Strip/Simplify Uptime
|
264
|
+
def sys_uptime
|
265
|
+
init = uptime.data.first.split(', load average').first.strip
|
266
|
+
|
267
|
+
"Uptime: #{init.split('up ', 2).last}"
|
268
|
+
end
|
269
|
+
|
270
|
+
def load_average
|
271
|
+
cpu_count = cpu.data['CPU(s)'].to_i
|
272
|
+
intervals = uptime.data.first.split('load average: ', 2).last.split(', ').map(&:to_f)
|
273
|
+
|
274
|
+
# Generate Colorized Text for Output
|
275
|
+
intervals_text = intervals.map do |interval|
|
276
|
+
value = percent(interval, cpu_count)
|
277
|
+
|
278
|
+
"#{interval} (#{value}%)"
|
279
|
+
end
|
280
|
+
|
281
|
+
[
|
282
|
+
'LoadAvg: ',
|
283
|
+
"[CPU #{cpu_count}] ",
|
284
|
+
intervals_text.join(', ')
|
285
|
+
].join
|
286
|
+
end
|
287
|
+
|
288
|
+
# def memory_perc
|
289
|
+
# total = ShellHelper.human_size_to_number(meminfo.data['MemTotal'])
|
290
|
+
# free = ShellHelper.human_size_to_number(meminfo.data['MemFree'])
|
291
|
+
# used = percent((total - free), total)
|
292
|
+
|
293
|
+
# [
|
294
|
+
# title('Usage'),
|
295
|
+
# ' ['.pastel(:bright_black),
|
296
|
+
# '='.pastel(:green) * (used / 2),
|
297
|
+
# ' ' * (50 - used / 2),
|
298
|
+
# ']'.pastel(:bright_black),
|
299
|
+
# " #{100 - percent(free, total)}%".pastel(:green) # Inverse
|
300
|
+
# ].join
|
301
|
+
# end
|
302
|
+
|
303
|
+
def memory_free
|
304
|
+
free = free_m.data.find { |x| x.kind == 'Mem' }
|
305
|
+
|
306
|
+
return unless free
|
307
|
+
|
308
|
+
pad = 6
|
309
|
+
list = [
|
310
|
+
"#{title('Total', pad)} #{number_to_human_size(free.total.to_i * 1024**2)}",
|
311
|
+
"#{title('Used', pad)} #{number_to_human_size(free.used.to_i * 1024**2)}",
|
312
|
+
"#{title('Free', pad)} #{number_to_human_size(free.free.to_i * 1024**2)}",
|
313
|
+
"#{title('Avail', pad)} #{number_to_human_size(free.available.to_i * 1024**2)}"
|
314
|
+
]
|
315
|
+
|
316
|
+
# Keep Alphabetical Sort
|
317
|
+
groups = list.each_slice((list.size / 2.to_f).round).to_a
|
318
|
+
|
319
|
+
table = TTY::Table.new do |t|
|
320
|
+
loop do
|
321
|
+
break if groups.all?(&:empty?)
|
322
|
+
|
323
|
+
t << groups.map(&:shift)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
"```\n#{table.render(:basic, padding: [0, 2, 0, 0])}\n```"
|
328
|
+
end
|
329
|
+
|
330
|
+
def disks
|
331
|
+
# GreenHat::Disk.df({archive: []})
|
332
|
+
file = GreenHat::Disk.df({ archive: [archive.name] })
|
333
|
+
|
334
|
+
disk_list = GreenHat::Disk.markdown_format(file.first, false, 3)
|
335
|
+
|
336
|
+
# Preapre / Indent List
|
337
|
+
[
|
338
|
+
'**Disks**',
|
339
|
+
"\n\n```\n#{disk_list.join("\n")}\n```"
|
340
|
+
].join
|
341
|
+
end
|
342
|
+
|
343
|
+
# ----------------------------
|
344
|
+
# Helpers
|
345
|
+
# ----------------------------
|
346
|
+
def percent(value, total)
|
347
|
+
((value / total.to_f) * 100).round
|
348
|
+
end
|
349
|
+
|
350
|
+
# Helper to Make Cyan Titles
|
351
|
+
def title(name, ljust = 16)
|
352
|
+
"#{name}:".ljust(ljust)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
# rubocop:enable Metrics/ClassLength
|
356
|
+
end
|
@@ -25,7 +25,8 @@ module GreenHat
|
|
25
25
|
|
26
26
|
attr_accessor :archive, :host, :os_release, :selinux_status, :cpu, :uname,
|
27
27
|
:timedatectl, :uptime, :meminfo, :gitlab_manifest, :gitlab_status,
|
28
|
-
:production_log, :api_log, :application_log, :sidekiq_log,
|
28
|
+
:production_log, :api_log, :application_log, :sidekiq_log,
|
29
|
+
:exceptions_log, :gitaly_log, :free_m, :disk_free
|
29
30
|
|
30
31
|
# Find Needed Files for Report
|
31
32
|
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
@@ -45,6 +46,8 @@ module GreenHat
|
|
45
46
|
self.production_log = archive.things.find { |x| x.name == 'gitlab-rails/production_json.log' }
|
46
47
|
self.api_log = archive.things.find { |x| x.name == 'gitlab-rails/api_json.log' }
|
47
48
|
self.application_log = archive.things.find { |x| x.name == 'gitlab-rails/application_json.log' }
|
49
|
+
self.exceptions_log = archive.things.find { |x| x.name == 'gitlab-rails/exceptions_json.log' }
|
50
|
+
self.gitaly_log = archive.things.find { |x| x.name == 'gitaly/current' }
|
48
51
|
self.sidekiq_log = archive.things.find { |x| x.name == 'sidekiq/current' }
|
49
52
|
self.disk_free = archive.things.find { |x| x.name == 'df_h' }
|
50
53
|
end
|
@@ -86,9 +89,11 @@ module GreenHat
|
|
86
89
|
output << gitlab_services if gitlab_status
|
87
90
|
output << title('Errors') if production_log || api_log || application_log || sidekiq_log
|
88
91
|
output << production_errors if production_log
|
89
|
-
output << api_errors if api_log
|
90
92
|
output << application_errors if application_log
|
91
93
|
output << sidekiq_errors if sidekiq_log
|
94
|
+
output << api_errors if api_log
|
95
|
+
output << exception_errors if exceptions_log
|
96
|
+
output << gitaly_errors if gitaly_log
|
92
97
|
|
93
98
|
# Final Space / Return
|
94
99
|
output << ''
|
@@ -96,6 +101,26 @@ module GreenHat
|
|
96
101
|
end
|
97
102
|
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
98
103
|
|
104
|
+
def exception_errors
|
105
|
+
count = exceptions_log.data.count
|
106
|
+
color = count.zero? ? :green : :red
|
107
|
+
|
108
|
+
[
|
109
|
+
title(' Exception', :bright_red, 18),
|
110
|
+
count.to_s.pastel(color)
|
111
|
+
].join
|
112
|
+
end
|
113
|
+
|
114
|
+
def gitaly_errors
|
115
|
+
count = gitaly_log.data.count { |x| x.level == 'error' }
|
116
|
+
color = count.zero? ? :green : :red
|
117
|
+
|
118
|
+
[
|
119
|
+
title(' Gitaly', :bright_red, 18),
|
120
|
+
count.to_s.pastel(color)
|
121
|
+
].join
|
122
|
+
end
|
123
|
+
|
99
124
|
def production_errors
|
100
125
|
count = production_log.data.count { |x| x.status == 500 }
|
101
126
|
color = count.zero? ? :green : :red
|
@@ -117,7 +142,13 @@ module GreenHat
|
|
117
142
|
end
|
118
143
|
|
119
144
|
def application_errors
|
120
|
-
|
145
|
+
results = ShellHelper.filter_internal([
|
146
|
+
'gitlab-rails/application_json.log',
|
147
|
+
'--message!="Cannot obtain an exclusive lease"',
|
148
|
+
'--severity=error'
|
149
|
+
].join(' '))
|
150
|
+
|
151
|
+
count = results.count { |x| x&.severity == 'ERROR' }
|
121
152
|
color = count.zero? ? :green : :red
|
122
153
|
|
123
154
|
[
|
@@ -150,8 +150,18 @@ module GreenHat
|
|
150
150
|
format_table_entry(flags, val)
|
151
151
|
end
|
152
152
|
|
153
|
+
# Internal Query Helper
|
154
|
+
# query = 'gitlab-rails/application_json.log --message!="Cannot obtain an exclusive lease" --severity=error'
|
155
|
+
# ShellHelper.filter_internal(query)
|
156
|
+
def self.filter_internal(search = '')
|
157
|
+
files, flags, args = Args.parse(Shellwords.split(search))
|
158
|
+
flags[:combine] = true
|
159
|
+
|
160
|
+
ShellHelper.filter_start(files, flags, args)
|
161
|
+
end
|
162
|
+
|
153
163
|
# Main Entry Point for Filtering
|
154
|
-
def self.filter_start(files, flags, args)
|
164
|
+
def self.filter_start(files, flags = {}, args = {})
|
155
165
|
# Convert to Things
|
156
166
|
logs = ShellHelper.find_things(files, flags).select(&:processed?)
|
157
167
|
|
@@ -189,7 +199,7 @@ module GreenHat
|
|
189
199
|
# Experimenting with deep clone
|
190
200
|
results = Marshal.load(Marshal.dump(data))
|
191
201
|
results.select! do |row|
|
192
|
-
args.send(flags.logic) do |arg|
|
202
|
+
args.send(flags.logic || :all?) do |arg|
|
193
203
|
filter_row_key(row, arg, flags)
|
194
204
|
end
|
195
205
|
end
|
@@ -43,6 +43,13 @@ module GreenHat
|
|
43
43
|
/dmesg/
|
44
44
|
]
|
45
45
|
},
|
46
|
+
'lets-encrypt/renewal' => {
|
47
|
+
format: :bracket_log,
|
48
|
+
log: true,
|
49
|
+
pattern: [
|
50
|
+
%r{lets-encrypt/renewal}
|
51
|
+
]
|
52
|
+
},
|
46
53
|
'repmgrd/current' => {
|
47
54
|
format: :bracket_log,
|
48
55
|
log: true,
|
@@ -225,7 +232,8 @@ module GreenHat
|
|
225
232
|
format: :json,
|
226
233
|
log: true,
|
227
234
|
pattern: [
|
228
|
-
%r{gitlab-rails/audit_json.log}
|
235
|
+
%r{gitlab-rails/audit_json.log},
|
236
|
+
%r{webservice.log/audit_json.log}
|
229
237
|
]
|
230
238
|
},
|
231
239
|
'gitlab-rails/auth.log' => {
|
@@ -336,6 +344,13 @@ module GreenHat
|
|
336
344
|
%r{nginx/current}
|
337
345
|
]
|
338
346
|
},
|
347
|
+
'nginx-ingress.log' => {
|
348
|
+
format: :kube_nginx,
|
349
|
+
log: true,
|
350
|
+
pattern: [
|
351
|
+
/nginx-ingress.log/
|
352
|
+
]
|
353
|
+
},
|
339
354
|
'nginx/gitlab_pages_access.log' => {
|
340
355
|
format: :nginx,
|
341
356
|
log: true,
|
@@ -422,7 +437,7 @@ module GreenHat
|
|
422
437
|
]
|
423
438
|
},
|
424
439
|
'registry/current' => {
|
425
|
-
format: :
|
440
|
+
format: :time_registry,
|
426
441
|
log: true,
|
427
442
|
pattern: [
|
428
443
|
%r{registry/current},
|
@@ -444,16 +459,18 @@ module GreenHat
|
|
444
459
|
},
|
445
460
|
'puma/puma_stderr.log' => {
|
446
461
|
format: :raw,
|
447
|
-
log:
|
462
|
+
log: false,
|
448
463
|
pattern: [
|
449
|
-
%r{puma/puma_stderr.log}
|
464
|
+
%r{puma/puma_stderr.log},
|
465
|
+
%r{webservice.log/puma.stderr.log}
|
450
466
|
]
|
451
467
|
},
|
452
468
|
'puma/puma_stdout.log' => {
|
453
469
|
format: :json,
|
454
470
|
log: true,
|
455
471
|
pattern: [
|
456
|
-
%r{puma/puma_stdout.log}
|
472
|
+
%r{puma/puma_stdout.log},
|
473
|
+
%r{webservice.log/puma.stdout.log}
|
457
474
|
]
|
458
475
|
},
|
459
476
|
'gitlab-pages/current' => {
|
@@ -745,13 +762,15 @@ module GreenHat
|
|
745
762
|
'gitlab-rails/application.log' => {
|
746
763
|
format: :raw,
|
747
764
|
pattern: [
|
748
|
-
%r{gitlab-rails/application.log}
|
765
|
+
%r{gitlab-rails/application.log},
|
766
|
+
%r{webservice.log/application.log}
|
749
767
|
]
|
750
768
|
},
|
751
769
|
'gitlab-rails/production.log' => {
|
752
770
|
format: :raw,
|
753
771
|
pattern: [
|
754
|
-
%r{gitlab-rails/production.log}
|
772
|
+
%r{gitlab-rails/production.log},
|
773
|
+
%r{webservice.log/production.log}
|
755
774
|
]
|
756
775
|
},
|
757
776
|
'gitlab/version-manifest.txt' => {
|
@@ -765,6 +784,72 @@ module GreenHat
|
|
765
784
|
pattern: [
|
766
785
|
%r{sidekiq/perf.data}
|
767
786
|
]
|
787
|
+
},
|
788
|
+
|
789
|
+
# ======================================================================
|
790
|
+
# KubeSoS TODO Section
|
791
|
+
# Things I am going to shortcut and set to raw for now
|
792
|
+
# ======================================================================
|
793
|
+
# Attempted Parsing
|
794
|
+
'kubesos_json' => {
|
795
|
+
log: true,
|
796
|
+
format: :kube_json,
|
797
|
+
pattern: [
|
798
|
+
/gitaly.log/
|
799
|
+
]
|
800
|
+
},
|
801
|
+
|
802
|
+
'kube_webservice' => {
|
803
|
+
log: false,
|
804
|
+
format: :kube_webservice,
|
805
|
+
pattern: [
|
806
|
+
/^webservice\.log$/
|
807
|
+
]
|
808
|
+
},
|
809
|
+
|
810
|
+
'kubesos' => {
|
811
|
+
format: :raw,
|
812
|
+
pattern: [
|
813
|
+
/all_values.yaml/,
|
814
|
+
%r{webservice.log/sidekiq_client.log},
|
815
|
+
/chart-version/,
|
816
|
+
/configmaps/,
|
817
|
+
/describe_deployments/,
|
818
|
+
/describe_ingress/,
|
819
|
+
/describe_nodes/,
|
820
|
+
/describe_pods/,
|
821
|
+
/describe_pv/,
|
822
|
+
/describe_pvc/,
|
823
|
+
/events/,
|
824
|
+
/get_deployments/,
|
825
|
+
/get_endpoints/,
|
826
|
+
/get_jobs/,
|
827
|
+
/get_pods/,
|
828
|
+
/get_pv/,
|
829
|
+
/get_pvc/,
|
830
|
+
/get_services/,
|
831
|
+
/gitaly.log/,
|
832
|
+
/gitlab-exporter.log/,
|
833
|
+
/gitlab-pages.log/,
|
834
|
+
/gitlab-shell.log/,
|
835
|
+
/grafana.log/,
|
836
|
+
/helm-version/,
|
837
|
+
/kubectl-check/,
|
838
|
+
/migrations.log/,
|
839
|
+
/minio.log/,
|
840
|
+
/nfs-client-provisioner.log/,
|
841
|
+
/operator.log/,
|
842
|
+
/postgresql.log/,
|
843
|
+
/prometheus.log/,
|
844
|
+
/redis.log/,
|
845
|
+
/registry.log/,
|
846
|
+
/secrets/,
|
847
|
+
/sidekiq.log/,
|
848
|
+
/task-runner.log/,
|
849
|
+
/top_nodes/,
|
850
|
+
/top_pods/,
|
851
|
+
/user_supplied_values.yaml/
|
852
|
+
]
|
768
853
|
}
|
769
854
|
}
|
770
855
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Top
|
2
|
+
module GreenHat
|
3
|
+
# Log
|
4
|
+
module Formatters
|
5
|
+
# ==========================================================================
|
6
|
+
# K8s logs seem to have a lot of non-json entries
|
7
|
+
# Extract all json, ignore everything else
|
8
|
+
# ==========================================================================
|
9
|
+
def format_kube_json
|
10
|
+
self.result = raw.map do |row|
|
11
|
+
# Skip all non-json
|
12
|
+
next unless row.first == '{'
|
13
|
+
|
14
|
+
result = begin
|
15
|
+
Oj.load row
|
16
|
+
rescue EncodingError
|
17
|
+
puts
|
18
|
+
next
|
19
|
+
end
|
20
|
+
|
21
|
+
# Parsing Time
|
22
|
+
format_json_traverse result
|
23
|
+
|
24
|
+
result.sort.to_h
|
25
|
+
rescue StandardError => e
|
26
|
+
# TODO: Background Logger?
|
27
|
+
e.message
|
28
|
+
LogBot.warn('JSON Parse', e.message)
|
29
|
+
next
|
30
|
+
end.compact
|
31
|
+
|
32
|
+
:ok
|
33
|
+
end
|
34
|
+
# =========================================================================
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Top
|
2
|
+
module GreenHat
|
3
|
+
# Log
|
4
|
+
module Formatters
|
5
|
+
# ==========================================================================
|
6
|
+
# Formatters
|
7
|
+
# ==========================================================================
|
8
|
+
def format_kube_nginx
|
9
|
+
self.result = raw.map do |row|
|
10
|
+
ip, _sym, remote_user, rest = row.split(' ', 4)
|
11
|
+
|
12
|
+
time, rest = rest.split(']', 2)
|
13
|
+
time = Time.strptime(time, '[%d/%b/%Y:%H:%M:%S %z')
|
14
|
+
|
15
|
+
verb, status, bytes, http_referer, http_user_agent, gzip_ratio = Shellwords.split(rest)
|
16
|
+
|
17
|
+
method, path, http_version = verb.split
|
18
|
+
|
19
|
+
{
|
20
|
+
remote_addr: ip,
|
21
|
+
remote_user: remote_user,
|
22
|
+
method: method,
|
23
|
+
path: path,
|
24
|
+
status: status,
|
25
|
+
bytes: bytes,
|
26
|
+
http_version: http_version,
|
27
|
+
http_referer: http_referer,
|
28
|
+
http_user_agent: http_user_agent,
|
29
|
+
gzip_ratio: gzip_ratio,
|
30
|
+
time: time
|
31
|
+
}
|
32
|
+
|
33
|
+
# Fall back for malformed logs
|
34
|
+
rescue StandardError
|
35
|
+
{ message: row }
|
36
|
+
end
|
37
|
+
|
38
|
+
:ok
|
39
|
+
end
|
40
|
+
|
41
|
+
# rubocop:disable Layout/LineLength
|
42
|
+
# NGINX Conf: /var/opt/gitlab/nginx/conf/nginx.conf
|
43
|
+
# log_format gitlab_access '$remote_addr - $remote_user [$time_local] "$request_method $filtered_request_uri $server_protocol" $status $body_bytes_sent "$filtered_http_referer" "$http_user_agent" $gzip_ratio';
|
44
|
+
# rubocop:enable Layout/LineLength
|
45
|
+
|
46
|
+
# ==========================================================================
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Top
|
2
|
+
module GreenHat
|
3
|
+
# Log
|
4
|
+
module Formatters
|
5
|
+
# ==========================================================================
|
6
|
+
# Gitlab Tail Formatter
|
7
|
+
# ==========================================================================
|
8
|
+
def format_kube_webservice
|
9
|
+
# Revert to raw for cats
|
10
|
+
self.kind = :raw
|
11
|
+
|
12
|
+
output = {}
|
13
|
+
current_log = nil
|
14
|
+
|
15
|
+
raw.each do |line|
|
16
|
+
next if line.blank?
|
17
|
+
|
18
|
+
if line.include? '*** '
|
19
|
+
current_log = /\*\*\* (.+?) \*\*\*/.match(line).captures.first
|
20
|
+
else
|
21
|
+
output[current_log] ||= [] unless current_log.nil?
|
22
|
+
output[current_log].push line unless current_log.nil?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Remove Empty Entries
|
27
|
+
output.reject { |_k, v| v.empty? }
|
28
|
+
|
29
|
+
# Root Dir
|
30
|
+
root_dir = "#{$TMP}/#{name}"
|
31
|
+
Dir.mkdir(root_dir)
|
32
|
+
|
33
|
+
# Write Files / Create Things
|
34
|
+
output.each do |k, v|
|
35
|
+
file_name = k.gsub('/var/log/gitlab/', '')
|
36
|
+
|
37
|
+
dir = "#{root_dir}/gitlab-rails"
|
38
|
+
Dir.mkdir(dir) unless File.exist?(dir)
|
39
|
+
|
40
|
+
File.write("#{root_dir}/#{file_name}", v.join("\n"))
|
41
|
+
|
42
|
+
# Thing Setup
|
43
|
+
archive.things_create(file: "#{root_dir}/#{file_name}").setup
|
44
|
+
end
|
45
|
+
|
46
|
+
# Link
|
47
|
+
self.result = raw
|
48
|
+
end
|
49
|
+
# ==========================================================================
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Top
|
2
|
+
module GreenHat
|
3
|
+
# Log
|
4
|
+
module Formatters
|
5
|
+
# ==========================================================================
|
6
|
+
# Registry Split
|
7
|
+
# ==========================================================================
|
8
|
+
# https://docs.docker.com/registry/configuration/#log
|
9
|
+
# Formatters text, json
|
10
|
+
# registry['log_formatter'] = "json"
|
11
|
+
# TODO: Logstash (Not working in 14.3)
|
12
|
+
|
13
|
+
def format_time_registry
|
14
|
+
self.result = raw.map do |row|
|
15
|
+
result = row[0] == '{' ? registry_json(row) : registry_shell_words(row)
|
16
|
+
|
17
|
+
# Timestamp Parsing
|
18
|
+
result.ts = Time.parse result.ts if result.key? 'ts'
|
19
|
+
result.time = Time.parse(result.time)
|
20
|
+
|
21
|
+
result
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def registry_shell_words(row)
|
26
|
+
time, msg = row.split(' ', 2)
|
27
|
+
|
28
|
+
output = Shellwords.split(msg).each_with_object({}) do |x, h|
|
29
|
+
key, value = x.split('=')
|
30
|
+
next if value.nil?
|
31
|
+
|
32
|
+
h[key.to_sym] = value.numeric? ? value.to_f : value
|
33
|
+
end
|
34
|
+
|
35
|
+
output[:time] = time
|
36
|
+
|
37
|
+
output
|
38
|
+
end
|
39
|
+
|
40
|
+
def registry_json(msg)
|
41
|
+
Oj.load(msg)
|
42
|
+
rescue EncodingError
|
43
|
+
{ message: msg }
|
44
|
+
end
|
45
|
+
# ==========================================================================
|
46
|
+
end
|
47
|
+
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.3.
|
4
|
+
version: 0.3.4
|
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-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: amazing_print
|
@@ -425,6 +425,7 @@ files:
|
|
425
425
|
- lib/greenhat/shell/help.rb
|
426
426
|
- lib/greenhat/shell/list.rb
|
427
427
|
- lib/greenhat/shell/log.rb
|
428
|
+
- lib/greenhat/shell/markdown.rb
|
428
429
|
- lib/greenhat/shell/memory.rb
|
429
430
|
- lib/greenhat/shell/network.rb
|
430
431
|
- lib/greenhat/shell/page.rb
|
@@ -444,9 +445,13 @@ files:
|
|
444
445
|
- lib/greenhat/thing/formatters/gitlab_status.rb
|
445
446
|
- lib/greenhat/thing/formatters/json.rb
|
446
447
|
- lib/greenhat/thing/formatters/json_shellwords.rb
|
448
|
+
- lib/greenhat/thing/formatters/kube_json.rb
|
449
|
+
- lib/greenhat/thing/formatters/kube_nginx.rb
|
450
|
+
- lib/greenhat/thing/formatters/kube_webservice.rb
|
447
451
|
- lib/greenhat/thing/formatters/multiline_json.rb
|
448
452
|
- lib/greenhat/thing/formatters/nginx.rb
|
449
453
|
- lib/greenhat/thing/formatters/raw.rb
|
454
|
+
- lib/greenhat/thing/formatters/registry.rb
|
450
455
|
- lib/greenhat/thing/formatters/shellwords.rb
|
451
456
|
- lib/greenhat/thing/formatters/syslog.rb
|
452
457
|
- lib/greenhat/thing/formatters/table.rb
|