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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e5be26bf333469e8c3c4eed5be22a33d05f8a37306d5e93067c10134a9415cb3
4
- data.tar.gz: 128d6aa7ff697b5fcae2669a8e14dffb96f52ab360d4585313942343432a89b8
3
+ metadata.gz: 8c852c088190fa51d4232c45aa7afd4013473c8089e194511a0910a8ca4794e5
4
+ data.tar.gz: 96b031de365ad1d47c71b7ffe25dfbd53f4909fe07be484944f7814cfead4939
5
5
  SHA512:
6
- metadata.gz: 4165a154d754ac8d7d338bd4adaa681059579c6f7c7410badefdb34d9ab260211e5807a54dcd602bb3f765a8265aa40a2ff8b95d5ecb82c5ccb8931be198c6a3
7
- data.tar.gz: c0f8916a1399438aa50a15deb90f59321e5e02fc5902c04d04ed1ac98701f8f1bc109de5fa79ff9167169a9e848310f7988a6a521af95bb74dade68ddbdbc4e0
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 enable users to automate the date collection which is required for trouble shooting. The data collected by diagtool include the configuration and log files of the td-agent and diagnostic information of operating system such as network and memory status and stats. In some cases, configuration and log files contains the security sensitive information, such as IP addresses and Hostname. The diagtool also provides the functions to generate mask on IP addresses, Hostname(in FQDN style) and user defined keywords described in the collected data.
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 conectivity status/stats(netstat -plan/netstat -s)
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.1.2.gem (100%)
37
- Successfully installed fluent-diagtool-0.1.2
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 extract the path of td-agent configuration and log files from td-agent daemon and use them during data collection if the td-agent is managed as daemon. The precheck options provides the function to confirm if the diagtool could gather the td-agent information as expected.
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 script to run td-agent with command line options. In that cases, users need to speccify the path of td-agent configuration and log files with -c and -l options respectively.
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 another configuration files. The diagtool read the td-agent configuration and collect the files described in "@include" directive as well. The details of "@include" directive are described in followed url:
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 files.
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
- @tdenv = gen_tdenv()
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
- @tdconf = conf[:tdconf].split('/')[-1]
48
+ @tdconf = conf[:tdconf].split('/')[-1]
36
49
  @tdconf_path = conf[:tdconf].gsub(@tdconf,'')
37
50
  elsif
38
- if not @tdenv['FLUENT_CONF'].empty?
39
- @tdconf = @tdenv['FLUENT_CONF'].split('/')[-1]
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
- raise "The path of td-agent log file need to be specified." if conf[:precheck] == false
54
- end
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 = gen_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 gen_osenv()
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 gen_tdenv()
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
- if @precheck == false # SKip if precheck is true
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
- end
113
+ end
96
114
  stdout.split().each do | l |
97
115
  if l.include?('Environment')
98
- env_dict[l.split('=')[1]] = l.split('=')[2]
116
+ @tdenv[l.split('=')[1]] = l.split('=')[2]
99
117
  end
100
- end
118
+ end
101
119
  else
102
- exe = 'fluentd'
103
- stdout, stderr, status = Open3.capture3("ps aux | grep #{exe} | grep -v grep")
104
- line = stdout.split(/\n/)
105
- log_path = ''
106
- conf_path = ''
107
- line.each { |l|
108
- cmd = l.split.drop(10)
109
- i = 0
110
- log_pos = 0
111
- conf_pos = 0
112
- if cmd[-1] != '--under-supervisor'
113
- cmd.each { |c|
114
- if c.include?("--log") || c.include?("-l")
115
- log_pos = i + 1
116
- log_path = cmd[log_pos]
117
- elsif c.include?("--conf") || c.include?("-c")
118
- conf_pos = i + 1
119
- conf_path = cmd[conf_pos]
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
- i+=1
122
- }
123
- end
124
- }
125
- env_dict['FLUENT_CONF'] = conf_path
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 target_dir + @tdconf
150
- File.readlines(conf).each { |line|
151
- if line.include? '@include'
152
- f = line.split()[1]
153
- if f.start_with?(/\//) # /tmp/work1/b.conf
154
- if f.include?('*')
155
- Dir.glob(f).each { |ff|
156
- conf_inc = target_dir + ff.gsub(/\//,'__')
157
- FileUtils.cp(ff, conf_inc)
158
- conf_list.push conf_inc
159
- }
160
- else
161
- conf_inc = target_dir+f.gsub(/\//,'__')
162
- FileUtils.cp(f, conf_inc)
163
- conf_list.push conf_inc
164
- end
165
- else
166
- f = f.gsub('./','') if f.include?('./')
167
- if f.include?('*')
168
- Dir.glob(@tdconf_path+f).each{ |ff|
169
- conf_inc = target_dir + ff.gsub(@tdconf_path,'').gsub(/\//,'__')
170
- FileUtils.cp(ff, conf_inc)
171
- conf_list.push conf_inc
172
- }
173
- else
174
- conf_inc = target_dir+f.gsub(/\//,'__')
175
- FileUtils.cp(@tdconf_path+f, conf_inc)
176
- conf_list.push conf_inc
177
- end
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
- @logger.warn("Can not find OS log file in #{oslog} or #{syslog}")
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
- cmd_name = cmd.gsub(/\s/,'_').gsub(/\//,'-').gsub(',','_')
209
- output = @outdir+'/'+cmd_name+'.txt'
210
- stdout, stderr, status = Open3.capture3(cmd)
211
- if status.success?
212
- File.open(output, 'w') do |f|
213
- f.puts(stdout)
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} failed due to the following message - #{stderr.chomp}")
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
- "ps -eo pid,ppid,stime,time,%mem,%cpu,cmd",
32
- "cat /proc/meminfo",
33
- "netstat -plan",
34
- "netstat -s",
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
- prechecklog.warn("[Precheck] can not find td-agent conf path: please run diagtool command with -c /path/to/<td-agent conf file>")
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
- prechecklog.info("[Precheck] Precheck completed. You can run diagtool command without -c and -l options")
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
- tdlog = c.collect_tdlog()
101
- diaglogger_info("[Collect] log files of td-agent are stored in #{tdlog}")
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
- diaglogger_info("[Collect] date/time information is stored in #{ntp}")
134
+ diaglogger_info("[Collect] date/time information is stored in #{ntp}")
123
135
  elsif system('which ntpq > /dev/null 2>&1')
124
- ntp = c.collect_ntp(command="ntpq -p")
125
- diaglogger_info("[Collect] date/time information is stored in #{ntp}")
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
- diaglogger_info("[Collect] Collecting command output : command = #{cmd}")
135
- out = c.collect_cmd_output(cmd)
136
- if @conf[:mask] == 'yes'
137
- diaglogger_info("[Mask] Masking netstat file : #{out}...")
138
- out = m.mask_tdlog(out, clean = true)
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
- tdconf.each { | file |
175
- diaglogger_info("[Mask] Masking td-agent config file : #{file}...")
176
- m.mask_tdlog(file, clean = true)
177
- }
178
- tdlog.each { | file |
179
- diaglogger_info("[Mask] Masking td-agent log file : #{file}...")
180
- filename = file.split("/")[-1]
181
- if filename.include?(".gz")
182
- m.mask_tdlog_gz(file, clean = true)
183
- elsif
184
- m.mask_tdlog(file, clean = true)
185
- end
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)
@@ -1,5 +1,5 @@
1
1
  module Fluent
2
2
  module Diagtool
3
- VERSION = "0.1.9"
3
+ VERSION = "1.0.0"
4
4
  end
5
5
  end
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.1.9
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-08-13 00:00:00.000000000 Z
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
- rubyforge_project:
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
@@ -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
-