fluent-diagtool 0.1.9 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +27 -14
- data/exe/{diagtool → fluent-diagtool} +1 -0
- data/lib/fluent/diagtool/collectutils.rb +261 -81
- data/lib/fluent/diagtool/diagutils.rb +68 -33
- data/lib/fluent/diagtool/version.rb +1 -1
- metadata +5 -7
- data/bin/diagtool.rb +0 -87
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c852c088190fa51d4232c45aa7afd4013473c8089e194511a0910a8ca4794e5
|
4
|
+
data.tar.gz: 96b031de365ad1d47c71b7ffe25dfbd53f4909fe07be484944f7814cfead4939
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6c96dd92c4c8db2975c84395288817b5053b3cdb4b0897970a5e253fc2714886baf0799dd5bd4161439e043c4827a65c178adefde241e2e974de53265bebef8
|
7
|
+
data.tar.gz: e7d24f7ca1d450e70bc037c74bf1ff8a88e2d174cc77c04211ba164bf7e7e97961b0d2970c8e3e00e0ab57f357c6c13aa8640afc1ffa8b9d7b63d5867630c9e4
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Fluentd Diagnostic Tool
|
2
2
|
|
3
|
-
The diagtool
|
3
|
+
The diagtool enables users to automate the date collection which is required for troubleshooting. The data collected by diagtool include the configuration and log files of the td-agent and diagnostic information from an operating system such as network and memory status and stats. In some cases, configuration and log files contain security sensitive information, such as IP addresses and Hostname. The diagtool also provides the functions to generate masks on IP addresses, Hostname(in FQDN style) and user defined keywords described in the collected data.
|
4
4
|
The scope of data collection:
|
5
5
|
- TD Agent information
|
6
6
|
- configuration files (*)
|
@@ -15,7 +15,7 @@ The scope of data collection:
|
|
15
15
|
- maximum number of file descriptor(ulimit -n)
|
16
16
|
- kernel network parameters(sysctl)
|
17
17
|
- snapshot of current process(ps)
|
18
|
-
- network
|
18
|
+
- network connectivity status/stats(netstat -plan/netstat -s)
|
19
19
|
- memory information(/proc/meminfo)
|
20
20
|
<br>
|
21
21
|
|
@@ -33,16 +33,28 @@ Successfully installed fileutils-1.0.2
|
|
33
33
|
Fetching: json-2.1.0.gem (100%)
|
34
34
|
Building native extensions. This could take a while...
|
35
35
|
Successfully installed json-2.1.0
|
36
|
-
Fetching: fluent-diagtool-0.
|
37
|
-
Successfully installed fluent-diagtool-0.
|
36
|
+
Fetching: fluent-diagtool-1.0.0.gem (100%)
|
37
|
+
Successfully installed fluent-diagtool-1.0.0
|
38
38
|
3 gems installed
|
39
39
|
```
|
40
|
+
When you are using td-agent, fluent-adiagtool should be installed using /usr/sbin/td-agent-gem command instead of gem command.
|
41
|
+
```
|
42
|
+
# /usr/sbin/td-agent-gem install fluent-diagtool
|
43
|
+
Fetching fluent-diagtool-1.0.0.gem
|
44
|
+
Successfully installed fluent-diagtool-1.0.0
|
45
|
+
Parsing documentation for fluent-diagtool-1.0.0
|
46
|
+
Installing ri documentation for fluent-diagtool-1.0.0
|
47
|
+
Done installing documentation for fluent-diagtool after 0 seconds
|
48
|
+
1 gem installed
|
49
|
+
```
|
40
50
|
|
41
51
|
## Usage
|
52
|
+
There are a few options in Diagtool. You can check the options of Diagtool with "--help" options. Diagtool performs the validation function in the process by default but you can turn on/off the mask function depending on the use cases.
|
42
53
|
```
|
43
54
|
# diagtool --help
|
44
55
|
Usage: /usr/local/bin/diagtool -o OUTPUT_DIR -m {yes | no} -w {word1,[word2...]} -f {listfile} -s {hash seed}
|
45
56
|
--precheck Run Precheck (Optional)
|
57
|
+
-t, --type fluentd|fluentbit Select the type of Fluentd (Mandatory)
|
46
58
|
-o, --output DIR Output directory (Mandatory)
|
47
59
|
-m, --mask yes|no Enable mask function (Optional : Default=no)
|
48
60
|
-w, --word-list word1,word2 Provide a list of user-defined words which will to be masked (Optional : Default=None)
|
@@ -52,10 +64,11 @@ Usage: /usr/local/bin/diagtool -o OUTPUT_DIR -m {yes | no} -w {word1,[word2...]}
|
|
52
64
|
-l, --log log_file provide a full path of td-agent log file (Optional : Default=None)
|
53
65
|
```
|
54
66
|
### Pre-check
|
55
|
-
The diagtool automatically
|
67
|
+
The diagtool automatically parses the path of Fluentd configuration and log files from running Fluentd processes and daemon. The precheck options provides the function to confirm if the diagtool could gather the fluentd information as expected.
|
56
68
|
The following command output shows the case when the diagtool successfully gather information from daemon.
|
69
|
+
You need to specify the type of Fluentd, "fluentd" or "fluentbit".
|
57
70
|
```
|
58
|
-
# diagtool --precheck
|
71
|
+
# diagtool --precheck -t fluentd
|
59
72
|
2020-05-28 00:39:02 -0400: [Diagtool] [INFO] [Precheck] Check OS parameters...
|
60
73
|
2020-05-28 00:39:02 -0400: [Diagtool] [INFO] [Precheck] operating system = CentOS Linux 8 (Core)
|
61
74
|
2020-05-28 00:39:02 -0400: [Diagtool] [INFO] [Precheck] kernel version = Linux 4.18.0-147.el8.x86_64
|
@@ -66,10 +79,10 @@ The following command output shows the case when the diagtool successfully gathe
|
|
66
79
|
2020-05-28 00:39:02 -0400: [Diagtool] [INFO] [Precheck] td-agent log = td-agent.log
|
67
80
|
2020-05-28 00:39:02 -0400: [Diagtool] [INFO] [Precheck] Precheck completed. You can run diagtool command without -c and -l options
|
68
81
|
```
|
69
|
-
In some cases, users do not manage td-agent as daemon but use own
|
82
|
+
In some cases, users do not manage td-agent as daemon but use their own scripts to run td-agent with command line options. In that cases, users need to specify the path of td-agent configuration and log files with -c and -l options respectively.
|
70
83
|
The following example shows the precheck results when the diagtool is not able to extract the path of td-agent configuration and log files.
|
71
84
|
```
|
72
|
-
# diagtool --precheck
|
85
|
+
# diagtool --precheck -t fluentd
|
73
86
|
2020-05-28 05:45:14 +0000: [Diagtool] [INFO] [Precheck] Check OS parameters...
|
74
87
|
2020-05-28 05:45:14 +0000: [Diagtool] [INFO] [Precheck] operating system = CentOS Linux 8 (Core)
|
75
88
|
2020-05-28 05:45:14 +0000: [Diagtool] [INFO] [Precheck] kernel version = Linux 4.18.0-147.5.1.el8_1.x86_64
|
@@ -85,7 +98,7 @@ The following example shows the precheck results when the diagtool is not able t
|
|
85
98
|
### Run diagtool
|
86
99
|
|
87
100
|
#### The "@include" directive in td-agent configuration file
|
88
|
-
The "@include" directive is a function to reuse configuration defined in
|
101
|
+
The "@include" directive is a function to reuse configuration defined in other configuration files. The diagtool reads the td-agent configuration and collects the files described in "@include" directive as well. The details of "@include" directive are described in followed url:
|
89
102
|
https://docs.fluentd.org/configuration/config-file#6-re-use-your-config-the-include-directive
|
90
103
|
|
91
104
|
#### User defined words to be masked
|
@@ -100,7 +113,7 @@ NOTE: When user specified the keywork, only the exact match words will be masked
|
|
100
113
|
|
101
114
|
#### Command sample:
|
102
115
|
```
|
103
|
-
# diagtool -o /tmp/work1 -w passwd1,passwd2 -f word_list_sample -m yes
|
116
|
+
# diagtool -t fluentd -o /tmp/work1 -w passwd1,passwd2 -f word_list_sample -m yes
|
104
117
|
2020-05-12 18:21:19 -0400: [Diagtool] [INFO] Parsing command options...
|
105
118
|
2020-05-12 18:21:19 -0400: [Diagtool] [INFO] Option : Output directory = /tmp/work1
|
106
119
|
2020-05-12 18:21:19 -0400: [Diagtool] [INFO] Option : Mask = yes
|
@@ -157,8 +170,8 @@ NOTE: When user specified the keywork, only the exact match words will be masked
|
|
157
170
|
2020-05-12 18:21:22 -0400: [Diagtool] [INFO] [Collect] Generate tar file /tmp/work1/diagout-20200512182119.tar.gz
|
158
171
|
```
|
159
172
|
#### Mask Function
|
160
|
-
When run diagtool with mask option, the log of mask is also created in 'mask_{timestamp}.json' file. Users are able to confirm how the mask was generated on each
|
161
|
-
The diagtool provides hash-seed option with '-s'. When hash-seed is specified, the mask will be generated with original word and hash-seed so that users could use unique mask value.
|
173
|
+
When run diagtool with the mask option, the log of mask is also created in 'mask_{timestamp}.json' file. Users are able to confirm how the mask was generated on each file.
|
174
|
+
The diagtool provides a hash-seed option with '-s'. When hash-seed is specified, the mask will be generated with the original word and hash-seed so that users could use a unique mask value.
|
162
175
|
#### Mask sample - IP address: IPv4_{md5hash}
|
163
176
|
```
|
164
177
|
"Line112-8": {
|
@@ -184,7 +197,7 @@ The diagtool provides hash-seed option with '-s'. When hash-seed is specified, t
|
|
184
197
|
|
185
198
|
## Tested Environment
|
186
199
|
- OS : CentOS 8.1
|
187
|
-
- Fluentd : td-agent version 3
|
200
|
+
- Fluentd : td-agent version 3/4
|
188
201
|
https://docs.fluentd.org/quickstart/td-agent-v2-vs-v3
|
189
|
-
|
202
|
+
- Fluentbit : td-agent-bit
|
190
203
|
|
@@ -27,6 +27,7 @@ params = {}
|
|
27
27
|
OptionParser.new do |opt|
|
28
28
|
opt.banner = "Usage: #{$0} -o OUTPUT_DIR -m {yes | no} -w {word1,[word2...]} -f {listfile} -s {hash seed}"
|
29
29
|
opt.on('--precheck', 'Run Precheck (Optional)')
|
30
|
+
opt.on('-t','--type fluentd|fluentbit', String, 'Select the type of Fluentd (Mandatory)')
|
30
31
|
opt.on('-o','--output DIR', String, 'Output directory (Mandatory)')
|
31
32
|
opt.on('-m','--mask yes|no', String, 'Enable mask function (Optional : Default=no)')
|
32
33
|
opt.on('-w','--word-list word1,word2', Array, 'Provide a list of user-defined words which will to be masked (Optional : Default=None)')
|
@@ -17,6 +17,8 @@
|
|
17
17
|
require 'fileutils'
|
18
18
|
require 'open3'
|
19
19
|
require 'logger'
|
20
|
+
require 'net/http'
|
21
|
+
require 'uri'
|
20
22
|
|
21
23
|
module Diagtool
|
22
24
|
class CollectUtils
|
@@ -25,18 +27,29 @@ module Diagtool
|
|
25
27
|
"#{datetime}: [Collectutils] [#{severity}] #{msg}\n"
|
26
28
|
})
|
27
29
|
@precheck = conf[:precheck]
|
30
|
+
@type = conf[:type]
|
28
31
|
@time_format = conf[:time]
|
29
32
|
@basedir = conf[:basedir]
|
30
33
|
@workdir = conf[:workdir]
|
31
|
-
@outdir = conf[:outdir]
|
32
|
-
|
33
|
-
|
34
|
+
@outdir = conf[:outdir]
|
35
|
+
@tdenv = {
|
36
|
+
'FLUENT_CONF' => '',
|
37
|
+
'TD_AGENT_LOG_FILE' => ''
|
38
|
+
}
|
39
|
+
|
40
|
+
case @type
|
41
|
+
when 'fluentd'
|
42
|
+
_find_fluentd_info()
|
43
|
+
when 'fluentbit'
|
44
|
+
_find_fluentbit_info()
|
45
|
+
end
|
46
|
+
|
34
47
|
if not conf[:tdconf].empty?
|
35
|
-
|
48
|
+
@tdconf = conf[:tdconf].split('/')[-1]
|
36
49
|
@tdconf_path = conf[:tdconf].gsub(@tdconf,'')
|
37
50
|
elsif
|
38
|
-
|
39
|
-
|
51
|
+
if not @tdenv['FLUENT_CONF'].empty?
|
52
|
+
@tdconf = @tdenv['FLUENT_CONF'].split('/')[-1]
|
40
53
|
@tdconf_path = @tdenv['FLUENT_CONF'].gsub(@tdconf,'')
|
41
54
|
else
|
42
55
|
raise "The path of td-agent configuration file need to be specified." if conf[:precheck] == false
|
@@ -50,15 +63,20 @@ module Diagtool
|
|
50
63
|
@tdlog = @tdenv['TD_AGENT_LOG_FILE'].split('/')[-1]
|
51
64
|
@tdlog_path = @tdenv['TD_AGENT_LOG_FILE'].gsub(@tdlog,'')
|
52
65
|
else
|
53
|
-
|
54
|
-
|
66
|
+
case @type
|
67
|
+
when 'fluentd'
|
68
|
+
raise "The path of td-agent log file need to be specified." if conf[:precheck] == false
|
69
|
+
when 'fluentbit'
|
70
|
+
@logger.warn("FluentBit logs are redirected to the standard output interface ")
|
71
|
+
end
|
72
|
+
end
|
55
73
|
end
|
56
|
-
@osenv =
|
74
|
+
@osenv = _find_os_info()
|
57
75
|
@oslog_path = '/var/log/'
|
58
76
|
@oslog = 'messages'
|
59
77
|
@syslog = 'syslog'
|
60
78
|
@sysctl_path = '/etc/'
|
61
|
-
@sysctl = 'sysctl.conf'
|
79
|
+
@sysctl = 'sysctl.conf'
|
62
80
|
|
63
81
|
@logger.info("Loading the environment parameters...")
|
64
82
|
@logger.info(" operating system = #{@osenv['Operating System']}")
|
@@ -69,7 +87,7 @@ module Diagtool
|
|
69
87
|
@logger.info(" td-agent log = #{@tdlog}")
|
70
88
|
end
|
71
89
|
|
72
|
-
def
|
90
|
+
def _find_os_info()
|
73
91
|
stdout, stderr, status = Open3.capture3('hostnamectl')
|
74
92
|
os_dict = {}
|
75
93
|
stdout.each_line { |l|
|
@@ -84,50 +102,102 @@ module Diagtool
|
|
84
102
|
return os_dict
|
85
103
|
end
|
86
104
|
|
87
|
-
def
|
105
|
+
def _find_fluentd_info()
|
106
|
+
### check if the td-agent is run as daemon
|
88
107
|
stdout, stderr, status = Open3.capture3('systemctl cat td-agent')
|
89
|
-
env_dict = {}
|
90
108
|
if status.success?
|
91
|
-
|
109
|
+
if @precheck == false # SKip if precheck is true
|
92
110
|
File.open(@outdir+'/td-agent_env.output', 'w') do |f|
|
93
111
|
f.puts(stdout)
|
94
112
|
end
|
95
|
-
|
113
|
+
end
|
96
114
|
stdout.split().each do | l |
|
97
115
|
if l.include?('Environment')
|
98
|
-
|
116
|
+
@tdenv[l.split('=')[1]] = l.split('=')[2]
|
99
117
|
end
|
100
|
-
|
118
|
+
end
|
101
119
|
else
|
102
|
-
|
103
|
-
stdout, stderr, status = Open3.capture3(
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
+
### check if the td-agent is not run as daemon or run Fluentd with customized script
|
121
|
+
stdout, stderr, status = Open3.capture3('ps aux | grep fluentd | grep -v ".*\(grep\|diagtool\)"')
|
122
|
+
if status.success?
|
123
|
+
line = stdout.split(/\n/)
|
124
|
+
line.each do |l|
|
125
|
+
cmd = l.split.drop(10)
|
126
|
+
i = 0
|
127
|
+
if cmd[-1] != '--under-supervisor'
|
128
|
+
cmd.each do |c|
|
129
|
+
case
|
130
|
+
when c == "-c"
|
131
|
+
@tdenv['FLUENT_CONF'] = cmd[i+1]
|
132
|
+
when c == "-l"
|
133
|
+
@tdenv['TD_AGENT_LOG_FILE'] = cmd[i+1]
|
134
|
+
when c.include?("--conf")
|
135
|
+
@tdenv['FLUENT_CONF'] = c.split("=")[1]
|
136
|
+
when c.include?("--log")
|
137
|
+
@tdenv['TD_AGENT_LOG_FILE'] = c.split("=")[1]
|
138
|
+
end
|
139
|
+
i+=1
|
120
140
|
end
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
env_dict['TD_AGENT_LOG_FILE'] = log_path
|
141
|
+
end
|
142
|
+
end
|
143
|
+
else
|
144
|
+
@logger.warn("No Fluentd daemon or proccess running")
|
145
|
+
end
|
127
146
|
end
|
128
|
-
return env_dict
|
129
147
|
end
|
130
148
|
|
149
|
+
def _find_fluentbit_info()
|
150
|
+
### check if the td-agent-bit is run as daemon
|
151
|
+
stdout, stderr, status = Open3.capture3('systemctl cat td-agent-bit')
|
152
|
+
if status.success?
|
153
|
+
if @precheck == false # SKip if precheck is true
|
154
|
+
File.open(@outdir+'/td-agent-bit_env.output', 'w') do |f|
|
155
|
+
f.puts(stdout)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
stdout.split(/\n/).each do | line |
|
159
|
+
if line.start_with?("ExecStart")
|
160
|
+
cmd = line.split("=")[1]
|
161
|
+
i =0
|
162
|
+
cmd.split().each do | c |
|
163
|
+
case
|
164
|
+
when c == "-c"
|
165
|
+
@tdenv['FLUENT_CONF'] = cmd.split()[i+1]
|
166
|
+
when c == "-l"
|
167
|
+
@tdenv['TD_AGENT_LOG_FILE'] = cmd.split()[i+1]
|
168
|
+
when c.include?("--conf")
|
169
|
+
@tdenv['FLUENT_CONF'] = c.split("=")[1]
|
170
|
+
when c.include?("--log")
|
171
|
+
@tdenv['TD_AGENT_LOG_FILE'] = c.split("=")[1]
|
172
|
+
end
|
173
|
+
i+=1
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
else
|
178
|
+
### check if the td-agent-bit is not run as daemon or run FluentdBit with customized script
|
179
|
+
stdout, stderr, status = Open3.capture3('ps aux | grep fluent-bit | grep -v ".*\(grep\|diagtool\)"')
|
180
|
+
if status.success?
|
181
|
+
i = 0
|
182
|
+
stdout.split().each do | line |
|
183
|
+
case
|
184
|
+
when line.include?("--conf")
|
185
|
+
@tdenv['FLUENT_CONF'] = line.split("=")[1]
|
186
|
+
when line.include?("--log")
|
187
|
+
@tdenv['TD_AGENT_LOG_FILE'] = line.split("=")[1]
|
188
|
+
when line == "-c"
|
189
|
+
@tdenv['FLUENT_CONF'] = stdout.split()[i+1]
|
190
|
+
when line == "-l"
|
191
|
+
@tdenv['TD_AGENT_LOG_FILE'] = stdout.split()[i+1]
|
192
|
+
end
|
193
|
+
i+=1
|
194
|
+
end
|
195
|
+
else
|
196
|
+
@logger.warn("No FluentBit daemon or proccess running")
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
131
201
|
def export_env()
|
132
202
|
env = {
|
133
203
|
:os => @osenv['Operating System'],
|
@@ -146,39 +216,134 @@ module Diagtool
|
|
146
216
|
FileUtils.cp(@tdconf_path+@tdconf, target_dir)
|
147
217
|
conf = @workdir+@tdconf_path+@tdconf
|
148
218
|
conf_list = []
|
149
|
-
conf_list.push
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
219
|
+
conf_list.push conf
|
220
|
+
case @type
|
221
|
+
when 'fluentd'
|
222
|
+
conf_list = conf_list + _collect_tdconf_include(conf)
|
223
|
+
when 'fluentbit'
|
224
|
+
conf_list = conf_list + _collect_tdconf_include(conf) + _collect_tdbit_parser(conf) + _collect_tdbit_plugins(conf)
|
225
|
+
end
|
226
|
+
return conf_list
|
227
|
+
end
|
228
|
+
|
229
|
+
def _collect_tdconf_include(conf)
|
230
|
+
target_dir = @workdir+@tdconf_path
|
231
|
+
inc_list = []
|
232
|
+
File.readlines(conf).each do |line|
|
233
|
+
if line.start_with?('@include')
|
234
|
+
l = line.split()[1]
|
235
|
+
if l.start_with?('http')
|
236
|
+
uri = URI(l)
|
237
|
+
inc_http = target_dir + 'http' + uri.path.gsub('/','_')
|
238
|
+
File.open(inc_http, 'w') do |f|
|
239
|
+
f.puts(Net::HTTP.get(uri))
|
240
|
+
end
|
241
|
+
inc_list.push inc_http
|
242
|
+
else
|
243
|
+
if l.start_with?(/\//) # /tmp/work1/b.conf
|
244
|
+
if l.include?('*')
|
245
|
+
Dir.glob(l).each { |ll|
|
246
|
+
inc_conf = target_dir + ll.gsub(/\//,'-')
|
247
|
+
FileUtils.cp(ll, inc_conf)
|
248
|
+
inc_list.push inc_conf
|
249
|
+
}
|
250
|
+
else
|
251
|
+
inc_conf = target_dir+l.gsub(/\//,'-')
|
252
|
+
FileUtils.cp(l, inc_conf)
|
253
|
+
inc_list.push inc_conf
|
254
|
+
end
|
255
|
+
else
|
256
|
+
l = l.gsub('./','') if l.include?('./')
|
257
|
+
if l.include?('*')
|
258
|
+
Dir.glob(@tdconf_path+f).each{ |ll|
|
259
|
+
inc_conf = target_dir + ll.gsub(@tdconf_path,'').gsub(/\//,'-')
|
260
|
+
FileUtils.cp(ll, inc_conf)
|
261
|
+
inc_list.push inc_conf
|
262
|
+
}
|
263
|
+
else
|
264
|
+
inc_conf = target_dir+l.gsub(/\//,'-')
|
265
|
+
FileUtils.cp(@tdconf_path+l, inc_conf)
|
266
|
+
inc_list.push inc_conf
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
return inc_list
|
273
|
+
end
|
274
|
+
|
275
|
+
def _collect_tdbit_parser(conf)
|
276
|
+
target_dir = @workdir+@tdconf_path
|
277
|
+
parser_conf = []
|
278
|
+
File.readlines(conf).each do |line|
|
279
|
+
if line.strip.start_with?('parsers_file') || line.strip.start_with?('Parsers_File')
|
280
|
+
l = line.split()[1]
|
281
|
+
if l.start_with?(/\//) # /tmp/work1/b.conf
|
282
|
+
if l.include?('*')
|
283
|
+
Dir.glob(l).each { |ll|
|
284
|
+
pconf = target_dir + ll.gsub(/\//,'-')
|
285
|
+
FileUtils.cp(ll, pconf)
|
286
|
+
parser_conf.push(pconf)
|
287
|
+
}
|
288
|
+
else
|
289
|
+
pconf = target_dir+l.gsub(/\//,'-')
|
290
|
+
FileUtils.cp(l, pconf)
|
291
|
+
parser_conf.push(pconf)
|
292
|
+
end
|
293
|
+
else
|
294
|
+
l = l.gsub('./','') if l.include?('./')
|
295
|
+
if l.include?('*')
|
296
|
+
Dir.glob(@tdconf_path+f).each{ |ll|
|
297
|
+
pconf = target_dir + ll.gsub(@tdconf_path,'').gsub(/\//,'-')
|
298
|
+
FileUtils.cp(ll, pconf)
|
299
|
+
parser_conf.push(pconf)
|
300
|
+
}
|
301
|
+
else
|
302
|
+
pconf = target_dir+l.gsub(/\//,'-')
|
303
|
+
FileUtils.cp(@tdconf_path+l, pconf)
|
304
|
+
parser_conf.push(pconf)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
return parser_conf
|
310
|
+
end
|
311
|
+
|
312
|
+
def _collect_tdbit_plugins(conf)
|
313
|
+
target_dir = @workdir+@tdconf_path
|
314
|
+
plugins_conf = []
|
315
|
+
File.readlines(conf).each do |line|
|
316
|
+
if line.strip.start_with?('plugins_file') || line.strip.start_with?('Plugins_File')
|
317
|
+
l = line.split()[1]
|
318
|
+
if l.start_with?(/\//) # /tmp/work1/b.conf
|
319
|
+
if l.include?('*')
|
320
|
+
Dir.glob(l).each { |ll|
|
321
|
+
pconf = target_dir + ll.gsub(/\//,'-')
|
322
|
+
FileUtils.cp(ll, pconf)
|
323
|
+
plugins_conf.push(pconf)
|
324
|
+
}
|
325
|
+
else
|
326
|
+
pconf = target_dir+l.gsub(/\//,'-')
|
327
|
+
FileUtils.cp(l, pconf)
|
328
|
+
plugins_conf.push(pconf)
|
329
|
+
end
|
330
|
+
else
|
331
|
+
l = l.gsub('./','') if l.include?('./')
|
332
|
+
if l.include?('*')
|
333
|
+
Dir.glob(@tdconf_path+f).each{ |ll|
|
334
|
+
pconf = target_dir + ll.gsub(@tdconf_path,'').gsub(/\//,'-')
|
335
|
+
FileUtils.cp(ll, pconf)
|
336
|
+
plugins_conf.push(pconf)
|
337
|
+
}
|
338
|
+
else
|
339
|
+
pconf = target_dir+l.gsub(/\//,'-')
|
340
|
+
FileUtils.cp(@tdconf_path+l, pconf)
|
341
|
+
plugins_conf.push(pconf)
|
342
|
+
end
|
343
|
+
end
|
178
344
|
end
|
179
345
|
end
|
180
|
-
|
181
|
-
return conf_list
|
346
|
+
return plugins_conf
|
182
347
|
end
|
183
348
|
|
184
349
|
def collect_tdlog()
|
@@ -200,20 +365,35 @@ module Diagtool
|
|
200
365
|
FileUtils.cp(@oslog_path+@syslog, target_dir)
|
201
366
|
return target_dir+@syslog
|
202
367
|
else
|
203
|
-
|
368
|
+
@logger.warn("Can not find OS log file in #{oslog} or #{syslog}")
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def collect_ntp(command)
|
373
|
+
output = @outdir+'/ntp_info.output'
|
374
|
+
stdout_date, stderr_date, status_date = Open3.capture3("date")
|
375
|
+
stdout_ntp, stderr_ntp, status_ntp = Open3.capture3("chronyc sources") if command == "chrony"
|
376
|
+
stdout_ntp, stderr_ntp, status_ntp = Open3.capture3("ntpq -p") if command == "ntp"
|
377
|
+
File.open(output, 'w') do |f|
|
378
|
+
f.puts(stdout_date)
|
379
|
+
f.puts(stdout_ntp)
|
204
380
|
end
|
205
381
|
end
|
206
382
|
|
207
383
|
def collect_cmd_output(cmd)
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
384
|
+
if system(cmd + '> /dev/null 2>&1')
|
385
|
+
cmd_name = cmd.gsub(/\s/,'_').gsub(/\//,'-').gsub(',','_')
|
386
|
+
output = @outdir+'/'+cmd_name+'.txt'
|
387
|
+
stdout, stderr, status = Open3.capture3(cmd)
|
388
|
+
if status.success?
|
389
|
+
File.open(output, 'w') do |f|
|
390
|
+
f.puts(stdout)
|
391
|
+
end
|
392
|
+
else
|
393
|
+
@logger.warn("Command #{cmd} failed due to the following message - #{stderr.chomp}")
|
214
394
|
end
|
215
395
|
else
|
216
|
-
@logger.warn("Command #{cmd}
|
396
|
+
@logger.warn("Command #{cmd} does not exist - skip collecting #{cmd} output")
|
217
397
|
end
|
218
398
|
return output
|
219
399
|
end
|
@@ -27,11 +27,11 @@ module Diagtool
|
|
27
27
|
time = Time.new
|
28
28
|
@time_format = time.strftime("%Y%m%d%0k%M%0S")
|
29
29
|
@conf = parse_diagconf(params)
|
30
|
-
@cmd_list = [
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
@cmd_list = [
|
31
|
+
"ps -eo pid,ppid,stime,time,%mem,%cpu,cmd",
|
32
|
+
"cat /proc/meminfo",
|
33
|
+
"netstat -plan",
|
34
|
+
"netstat -s",
|
35
35
|
]
|
36
36
|
end
|
37
37
|
|
@@ -42,6 +42,7 @@ module Diagtool
|
|
42
42
|
loglevel = 'WARN'
|
43
43
|
c = CollectUtils.new(@conf, loglevel)
|
44
44
|
c_env = c.export_env()
|
45
|
+
prechecklog.info("[Precheck] Fluentd Type = #{@conf[:type]}")
|
45
46
|
prechecklog.info("[Precheck] Check OS parameters...")
|
46
47
|
prechecklog.info("[Precheck] operating system = #{c_env[:os]}")
|
47
48
|
prechecklog.info("[Precheck] kernel version = #{c_env[:kernel]}")
|
@@ -51,13 +52,13 @@ module Diagtool
|
|
51
52
|
prechecklog.info("[Precheck] td-agent log path = #{c_env[:tdlog_path]}")
|
52
53
|
prechecklog.info("[Precheck] td-agent log = #{c_env[:tdlog]}")
|
53
54
|
if c_env[:tdconf_path] == nil || c_env[:tdconf] == nil
|
54
|
-
|
55
|
+
prechecklog.warn("[Precheck] can not find td-agent conf path: please run diagtool command with -c /path/to/<td-agent conf file>")
|
55
56
|
end
|
56
57
|
if c_env[:tdlog_path] == nil || c_env[:tdlog] == nil
|
57
58
|
prechecklog.warn("[Precheck] can not find td-agent log path: please run diagtool command with -l /path/to/<td-agent log file>")
|
58
59
|
end
|
59
60
|
if c_env[:tdconf_path] != nil && c_env[:tdconf] != nil && c_env[:tdlog_path] != nil && c_env[:tdlog] != nil
|
60
|
-
|
61
|
+
prechecklog.info("[Precheck] Precheck completed. You can run diagtool command without -c and -l options")
|
61
62
|
end
|
62
63
|
end
|
63
64
|
|
@@ -97,8 +98,19 @@ module Diagtool
|
|
97
98
|
v = ValidUtils.new(loglevel)
|
98
99
|
|
99
100
|
diaglogger_info("[Collect] Collecting log files of td-agent...")
|
100
|
-
|
101
|
-
|
101
|
+
case @type
|
102
|
+
when 'fluentd'
|
103
|
+
tdlog = c.collect_tdlog()
|
104
|
+
diaglogger_info("[Collect] log files of td-agent are stored in #{tdlog}")
|
105
|
+
when 'fleuntbit'
|
106
|
+
if tdlog.empty?
|
107
|
+
diaglogger_info("FluentBit logs are redirected to the standard output interface ")
|
108
|
+
tdlog = ''
|
109
|
+
else
|
110
|
+
tdlog = c.collect_tdlog()
|
111
|
+
diaglogger_info("[Collect] log files of td-agent are stored in #{tdlog}")
|
112
|
+
end
|
113
|
+
end
|
102
114
|
|
103
115
|
diaglogger_info("[Collect] Collecting config file of td-agent...")
|
104
116
|
tdconf = c.collect_tdconf()
|
@@ -119,10 +131,10 @@ module Diagtool
|
|
119
131
|
diaglogger_info("[Collect] Collecting date/time information...")
|
120
132
|
if system('which chronyc > /dev/null 2>&1')
|
121
133
|
ntp = c.collect_cmd_output(command="chronyc sources")
|
122
|
-
|
134
|
+
diaglogger_info("[Collect] date/time information is stored in #{ntp}")
|
123
135
|
elsif system('which ntpq > /dev/null 2>&1')
|
124
|
-
ntp = c.
|
125
|
-
|
136
|
+
ntp = c.collect_cmd_output(command="ntpq -p")
|
137
|
+
diaglogger_info("[Collect] date/time information is stored in #{ntp}")
|
126
138
|
else
|
127
139
|
diaglogger_warn("[Collect] chrony/ntp does not exist. skip collectig date/time information")
|
128
140
|
end
|
@@ -131,13 +143,15 @@ module Diagtool
|
|
131
143
|
# Correct OS information
|
132
144
|
###
|
133
145
|
@cmd_list.each { |cmd|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
146
|
+
diaglogger_info("[Collect] Collecting command output : command = #{cmd}")
|
147
|
+
if system(cmd + '> /dev/null 2>&1')
|
148
|
+
out = c.collect_cmd_output(cmd)
|
149
|
+
if @conf[:mask] == 'yes'
|
150
|
+
diaglogger_info("[Mask] Masking command output file : #{out}...")
|
151
|
+
out = m.mask_tdlog(out, clean = true)
|
152
|
+
end
|
153
|
+
diaglogger_info("[Collect] Collecting command output #{cmd.split[0]} stored in #{out}")
|
139
154
|
end
|
140
|
-
diaglogger_info("[Collect] Collecting command output #{cmd.split[0]} stored in #{out}")
|
141
155
|
}
|
142
156
|
|
143
157
|
###
|
@@ -171,19 +185,24 @@ module Diagtool
|
|
171
185
|
end
|
172
186
|
|
173
187
|
if @conf[:mask] == 'yes'
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
188
|
+
tdconf.each { | file |
|
189
|
+
diaglogger_info("[Mask] Masking td-agent config file : #{file}...")
|
190
|
+
m.mask_tdlog(file, clean = true)
|
191
|
+
}
|
192
|
+
end
|
193
|
+
|
194
|
+
if @conf[:mask] == 'yes'
|
195
|
+
if tdlog != nil
|
196
|
+
tdlog.each { | file |
|
197
|
+
diaglogger_info("[Mask] Masking td-agent log file : #{file}...")
|
198
|
+
filename = file.split("/")[-1]
|
199
|
+
if filename.include?(".gz")
|
200
|
+
m.mask_tdlog_gz(file, clean = true)
|
201
|
+
elsif
|
202
|
+
m.mask_tdlog(file, clean = true)
|
203
|
+
end
|
204
|
+
}
|
205
|
+
end
|
187
206
|
end
|
188
207
|
|
189
208
|
if @conf[:mask] == 'yes'
|
@@ -197,8 +216,9 @@ module Diagtool
|
|
197
216
|
|
198
217
|
def parse_diagconf(params)
|
199
218
|
options = {
|
200
|
-
:precheck => '', :basedir => '', :mask => '', :words => [], :wfile => '', :seed => '', :tdconf =>'', :tdlog => ''
|
219
|
+
:precheck => '', :basedir => '', :type =>'', :mask => '', :words => [], :wfile => '', :seed => '', :tdconf =>'', :tdlog => ''
|
201
220
|
}
|
221
|
+
### Parse precheck flag
|
202
222
|
if params[:precheck]
|
203
223
|
options[:precheck] = params[:precheck]
|
204
224
|
else
|
@@ -215,6 +235,13 @@ module Diagtool
|
|
215
235
|
raise "output directory '-o' must be specified"
|
216
236
|
end
|
217
237
|
end
|
238
|
+
### Parse fluent type
|
239
|
+
if params[:type] == 'fluentd' || params[:type] == 'fluentbit'
|
240
|
+
options[:type] = params[:type]
|
241
|
+
else
|
242
|
+
raise "fluentd type '-t' must be specified (fluentd or fluentbit)"
|
243
|
+
end
|
244
|
+
### Parse mask flag
|
218
245
|
if params[:mask] == nil
|
219
246
|
options[:mask] = 'no'
|
220
247
|
else
|
@@ -224,7 +251,11 @@ module Diagtool
|
|
224
251
|
raise "invalid arguments '#{params[:mask]}' : input of '-m|--mask' should be 'yes' or 'no'"
|
225
252
|
end
|
226
253
|
end
|
254
|
+
|
255
|
+
### Parse uder-defined keyword list which will be used in the mask function
|
227
256
|
options[:words] = params[:"word-list"] if params[:"word-list"] != nil
|
257
|
+
|
258
|
+
### Parse uder-defined keyword file which will be used in the mask function
|
228
259
|
if params[:"word-file"] != nil
|
229
260
|
f = params[:"word-file"]
|
230
261
|
if File.exist?(f)
|
@@ -236,8 +267,11 @@ module Diagtool
|
|
236
267
|
end
|
237
268
|
end
|
238
269
|
options[:words] = options[:words].uniq
|
270
|
+
|
271
|
+
### Parse hash seed which will be used in the mask function
|
239
272
|
options[:seed] = params[:"hash-seed"] if params[:"hash-seed"] != nil
|
240
|
-
|
273
|
+
|
274
|
+
### Parse the path of fluentd config file
|
241
275
|
if params[:conf] != nil
|
242
276
|
f = params[:conf]
|
243
277
|
if File.exist?(f)
|
@@ -247,6 +281,7 @@ module Diagtool
|
|
247
281
|
end
|
248
282
|
end
|
249
283
|
|
284
|
+
### Parse the path of fluentd log file
|
250
285
|
if params[:log] != nil
|
251
286
|
f = params[:log]
|
252
287
|
if File.exist?(f)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-diagtool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kubotat
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fileutils
|
@@ -44,7 +44,7 @@ description: Bringing productivity of trouble shooting to the next level by aut
|
|
44
44
|
email:
|
45
45
|
- tkubota@ctc-america.com
|
46
46
|
executables:
|
47
|
-
- diagtool
|
47
|
+
- fluent-diagtool
|
48
48
|
extensions: []
|
49
49
|
extra_rdoc_files: []
|
50
50
|
files:
|
@@ -56,11 +56,10 @@ files:
|
|
56
56
|
- README.md
|
57
57
|
- Rakefile
|
58
58
|
- bin/console
|
59
|
-
- bin/diagtool.rb
|
60
59
|
- bin/setup
|
61
60
|
- bin/word_list_sample
|
62
61
|
- exclude_list01
|
63
|
-
- exe/diagtool
|
62
|
+
- exe/fluent-diagtool
|
64
63
|
- fluent-diagtool.gemspec
|
65
64
|
- lib/fluent/diagtool/collectutils.rb
|
66
65
|
- lib/fluent/diagtool/diagutils.rb
|
@@ -86,8 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
85
|
- !ruby/object:Gem::Version
|
87
86
|
version: '0'
|
88
87
|
requirements: []
|
89
|
-
|
90
|
-
rubygems_version: 2.7.6.2
|
88
|
+
rubygems_version: 3.1.2
|
91
89
|
signing_key:
|
92
90
|
specification_version: 4
|
93
91
|
summary: Diagnostic Tool for Fluentd
|
data/bin/diagtool.rb
DELETED
@@ -1,87 +0,0 @@
|
|
1
|
-
require 'optparse'
|
2
|
-
require 'logger'
|
3
|
-
require '../lib/diagutils'
|
4
|
-
include Diagtool
|
5
|
-
|
6
|
-
logger = Logger.new(STDOUT, formatter: proc {|severity, datetime, progname, msg|
|
7
|
-
"#{datetime}: [Diagtool] [#{severity}] #{msg}\n"
|
8
|
-
})
|
9
|
-
|
10
|
-
output_dir = '../output'
|
11
|
-
mask = 'yes'
|
12
|
-
exlist= Array.new
|
13
|
-
|
14
|
-
opt = OptionParser.new
|
15
|
-
opt.banner = "Usage: #{$0} -o OUTPUT_DIR -m {yes | no} -e {word1,[word2...]} -f {listfile}"
|
16
|
-
opt.on('-o','--output DIR', String, 'Output directory (Default=./output)') { |o|
|
17
|
-
output_dir = o
|
18
|
-
}
|
19
|
-
opt.on('-m','--mask YES|NO', String, 'Enable mask function (Default=True)') { |m|
|
20
|
-
if m == 'yes' || m == 'no'
|
21
|
-
mask = m
|
22
|
-
else
|
23
|
-
logger.error("Invalid value '#{m}' : -m | --mask should be yes or no")
|
24
|
-
exit!
|
25
|
-
end
|
26
|
-
}
|
27
|
-
opt.on('-e','--exclude-list LIST', Array, 'Provide a list of exclude words which will to be masked (Default=None)') { |e| exlist += e }
|
28
|
-
opt.on('-f','--exclude-file FILE', String, 'provide a file which describes a List of exclude words (Default=None)') { |f|
|
29
|
-
if File.exist?(f)
|
30
|
-
File.readlines(f).each do |l|
|
31
|
-
exlist.append(l.gsub(/\n/,''))
|
32
|
-
end
|
33
|
-
else
|
34
|
-
logger.error("No such file or directory")
|
35
|
-
exit!
|
36
|
-
end
|
37
|
-
}
|
38
|
-
opt.parse(ARGV)
|
39
|
-
exlist = exlist.uniq
|
40
|
-
|
41
|
-
logger.info("Parsing command options...")
|
42
|
-
logger.info(" Option : Output directory = #{output_dir}")
|
43
|
-
logger.info(" Option : Mask = #{mask}")
|
44
|
-
logger.info(" Option : Exclude list = #{exlist}")
|
45
|
-
|
46
|
-
logger.info("Initializing parameters...")
|
47
|
-
node1 = Diagutils.new(output_dir,exlist, 'INFO')
|
48
|
-
|
49
|
-
logger.info("Collecting log files of td-agent...")
|
50
|
-
tdlog = node1.collect_tdlog()
|
51
|
-
logger.info("log files of td-agent are stored in #{tdlog}")
|
52
|
-
|
53
|
-
logger.info("Collecting config file of td-agent...")
|
54
|
-
tdconf = node1.collect_tdconf()
|
55
|
-
logger.info("config file is stored in #{tdconf}")
|
56
|
-
|
57
|
-
logger.info("Collecting systctl information...")
|
58
|
-
sysctl = node1.collect_sysctl()
|
59
|
-
logger.info("sysctl information is stored in #{sysctl}")
|
60
|
-
|
61
|
-
logger.info("Collecting date/time information...")
|
62
|
-
ntp = node1.collect_ntp()
|
63
|
-
logger.info("date/time information is stored in #{ntp}")
|
64
|
-
|
65
|
-
logger.info("Collecting ulimit information...")
|
66
|
-
ulimit = node1.collect_ulimit()
|
67
|
-
logger.info("ulimit information is stored in #{ulimit}")
|
68
|
-
|
69
|
-
if mask == 'yes'
|
70
|
-
logger.info("Masking td-agent config file : #{tdconf}...")
|
71
|
-
node1.mask_tdconf(tdconf)
|
72
|
-
tdlog.each do | file |
|
73
|
-
logger.info("Masking td-agent log file : #{file}...")
|
74
|
-
filename = file.split("/")[-1]
|
75
|
-
if filename.include?(".gz")
|
76
|
-
node1.mask_tdlog_gz(file)
|
77
|
-
elsif
|
78
|
-
node1.mask_tdlog(file)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
tar_file = node1.compress_output()
|
84
|
-
logger.info("Generate tar file #{tar_file}")
|
85
|
-
|
86
|
-
|
87
|
-
|