fluent-diagtool 0.1.5 → 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: 614c1b026136b0743f9c6439b6dd2349adbe9edf3cdb2d77e1ab78698a9d9e49
4
- data.tar.gz: a2228adc3509363ba6bf8f4ddee4d6b1f3feaef2088452a5c69e72057eac2336
3
+ metadata.gz: 8c852c088190fa51d4232c45aa7afd4013473c8089e194511a0910a8ca4794e5
4
+ data.tar.gz: 96b031de365ad1d47c71b7ffe25dfbd53f4909fe07be484944f7814cfead4939
5
5
  SHA512:
6
- metadata.gz: af815f59c3ed5492175d5fe988eb6eb33fae556b4a2fcd6a20cf89e7e71aa1db92438f7ec1002c08e7f00d59099f91b2d8764f91b18cfb52593c81c5ae89f8e4
7
- data.tar.gz: c7adc36937ba9e1b74a2914a6c2fdf41a6f44dfbd155aa3eb99cf59126022280461a38972eb45f0f486e639b701db6ea7d1c3ec2de73893dd6c982ea9dde8f0c
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
@@ -81,6 +94,14 @@ The following example shows the precheck results when the diagtool is not able t
81
94
  2020-05-28 05:45:14 +0000: [Diagtool] [WARN] [Precheck] can not find td-agent conf path: please run diagtool command with -c /path/to/<td-agent conf file>
82
95
  2020-05-28 05:45:14 +0000: [Diagtool] [WARN] [Precheck] can not find td-agent log path: please run diagtool command with -l /path/to/<td-agent log file>
83
96
  ```
97
+
98
+ ### Run diagtool
99
+
100
+ #### The "@include" directive in td-agent configuration file
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:
102
+ https://docs.fluentd.org/configuration/config-file#6-re-use-your-config-the-include-directive
103
+
104
+ #### User defined words to be masked
84
105
  The user-defined words can be specified both -e option and -f option and the words are merged when both options are selected.
85
106
  The format of user-defined words list file specified in -f option should be followed format.
86
107
  ```
@@ -90,10 +111,9 @@ centos8102
90
111
  ```
91
112
  NOTE: When user specified the keywork, only the exact match words will be masked. For instance, when users like to mask words like "nginx1" and "nginx2", users need to specify "nginx1" and "nginx2" respectively and "nginx*" should not work in the tool.
92
113
 
93
- ### Run diagtool
94
114
  #### Command sample:
95
115
  ```
96
- # 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
97
117
  2020-05-12 18:21:19 -0400: [Diagtool] [INFO] Parsing command options...
98
118
  2020-05-12 18:21:19 -0400: [Diagtool] [INFO] Option : Output directory = /tmp/work1
99
119
  2020-05-12 18:21:19 -0400: [Diagtool] [INFO] Option : Mask = yes
@@ -149,9 +169,9 @@ NOTE: When user specified the keywork, only the exact match words will be masked
149
169
  2020-05-12 18:21:22 -0400: [Diagtool] [INFO] [Mask] Export mask log file : ./mask_20200512182119.json
150
170
  2020-05-12 18:21:22 -0400: [Diagtool] [INFO] [Collect] Generate tar file /tmp/work1/diagout-20200512182119.tar.gz
151
171
  ```
152
- ## Mask Function
153
- 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.
154
- 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.
172
+ #### Mask Function
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.
155
175
  #### Mask sample - IP address: IPv4_{md5hash}
156
176
  ```
157
177
  "Line112-8": {
@@ -177,7 +197,7 @@ The diagtool provides hash-seed option with '-s'. When hash-seed is specified, t
177
197
 
178
198
  ## Tested Environment
179
199
  - OS : CentOS 8.1
180
- - Fluentd : td-agent version 3
200
+ - Fluentd : td-agent version 3/4
181
201
  https://docs.fluentd.org/quickstart/td-agent-v2-vs-v3
182
-
202
+ - Fluentbit : td-agent-bit
183
203
 
@@ -0,0 +1,2 @@
1
+ centos8101
2
+ centos8102
@@ -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,26 +17,39 @@
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
23
25
  def initialize(conf, log_level)
24
26
  @logger = Logger.new(STDOUT, level: log_level, formatter: proc {|severity, datetime, progname, msg|
25
- "#{datetime}: [Diagutils] [#{severity}] #{msg}\n"
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 = get_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,14 +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 = get_osenv()
74
+ @osenv = _find_os_info()
57
75
  @oslog_path = '/var/log/'
58
76
  @oslog = 'messages'
77
+ @syslog = 'syslog'
59
78
  @sysctl_path = '/etc/'
60
- @sysctl = 'sysctl.conf'
79
+ @sysctl = 'sysctl.conf'
61
80
 
62
81
  @logger.info("Loading the environment parameters...")
63
82
  @logger.info(" operating system = #{@osenv['Operating System']}")
@@ -68,7 +87,7 @@ module Diagtool
68
87
  @logger.info(" td-agent log = #{@tdlog}")
69
88
  end
70
89
 
71
- def get_osenv()
90
+ def _find_os_info()
72
91
  stdout, stderr, status = Open3.capture3('hostnamectl')
73
92
  os_dict = {}
74
93
  stdout.each_line { |l|
@@ -83,50 +102,102 @@ module Diagtool
83
102
  return os_dict
84
103
  end
85
104
 
86
- def get_tdenv()
105
+ def _find_fluentd_info()
106
+ ### check if the td-agent is run as daemon
87
107
  stdout, stderr, status = Open3.capture3('systemctl cat td-agent')
88
- env_dict = {}
89
108
  if status.success?
90
- if @precheck == false # SKip if precheck is true
109
+ if @precheck == false # SKip if precheck is true
91
110
  File.open(@outdir+'/td-agent_env.output', 'w') do |f|
92
111
  f.puts(stdout)
93
112
  end
94
- end
113
+ end
95
114
  stdout.split().each do | l |
96
115
  if l.include?('Environment')
97
- env_dict[l.split('=')[1]] = l.split('=')[2]
116
+ @tdenv[l.split('=')[1]] = l.split('=')[2]
98
117
  end
99
- end
118
+ end
100
119
  else
101
- exe = 'fluentd'
102
- stdout, stderr, status = Open3.capture3("ps aux | grep #{exe} | grep -v grep")
103
- line = stdout.split(/\n/)
104
- log_path = ''
105
- conf_path = ''
106
- line.each { |l|
107
- cmd = l.split.drop(10)
108
- i = 0
109
- log_pos = 0
110
- conf_pos = 0
111
- if cmd[-1] != '--under-supervisor'
112
- cmd.each { |c|
113
- if c.include?("--log") || c.include?("-l")
114
- log_pos = i + 1
115
- log_path = cmd[log_pos]
116
- elsif c.include?("--conf") || c.include?("-c")
117
- conf_pos = i + 1
118
- 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
119
140
  end
120
- i+=1
121
- }
122
- end
123
- }
124
- env_dict['FLUENT_CONF'] = conf_path
125
- 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
126
146
  end
127
- return env_dict
128
147
  end
129
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
+
130
201
  def export_env()
131
202
  env = {
132
203
  :os => @osenv['Operating System'],
@@ -143,12 +214,140 @@ module Diagtool
143
214
  target_dir = @workdir+@tdconf_path
144
215
  FileUtils.mkdir_p(target_dir)
145
216
  FileUtils.cp(@tdconf_path+@tdconf, target_dir)
146
- return target_dir+@tdconf
217
+ conf = @workdir+@tdconf_path+@tdconf
218
+ conf_list = []
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
147
227
  end
148
-
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
344
+ end
345
+ end
346
+ return plugins_conf
347
+ end
348
+
149
349
  def collect_tdlog()
150
350
  target_dir = @workdir+@tdlog_path
151
- p target_dir
152
351
  FileUtils.mkdir_p(target_dir)
153
352
  Dir.glob(@tdlog_path+@tdlog+'*').each{ |f|
154
353
  FileUtils.cp(f, target_dir)
@@ -156,65 +355,20 @@ module Diagtool
156
355
  return Dir.glob(target_dir+@tdlog+'*')
157
356
  end
158
357
 
159
- def collect_sysctl()
160
- target_dir = @workdir+@sysctl_path
161
- FileUtils.mkdir_p(target_dir)
162
- FileUtils.cp(@sysctl_path+@sysctl, target_dir)
163
- return target_dir+@sysctl
164
- end
165
-
166
358
  def collect_oslog()
167
359
  target_dir = @workdir+@oslog_path
168
360
  FileUtils.mkdir_p(target_dir)
169
- FileUtils.cp(@oslog_path+@oslog, target_dir)
170
- return target_dir+@oslog
171
- end
172
-
173
- def collect_ulimit()
174
- output = @outdir+'/ulimit_n.output'
175
- stdout, stderr, status = Open3.capture3("ulimit -n")
176
- File.open(output, 'w') do |f|
177
- f.puts(stdout)
178
- end
179
- return output
180
- end
181
-
182
- def collect_ps_eo()
183
- output = @outdir+'/ps_eo.output'
184
- stdout, stderr, status = Open3.capture3("ps -eo pid,ppid,stime,time,%mem,%cpu,cmd")
185
- File.open(output, 'w') do |f|
186
- f.puts(stdout)
361
+ if File.exist? @oslog_path+@oslog
362
+ FileUtils.cp(@oslog_path+@oslog, target_dir)
363
+ return target_dir+@oslog
364
+ elsif File.exist? @oslog_path+@syslog
365
+ FileUtils.cp(@oslog_path+@syslog, target_dir)
366
+ return target_dir+@syslog
367
+ else
368
+ @logger.warn("Can not find OS log file in #{oslog} or #{syslog}")
187
369
  end
188
- return output
189
370
  end
190
371
 
191
- def collect_meminfo()
192
- output = @outdir+'/meminfo.output'
193
- stdout, stderr, status = Open3.capture3("cat /proc/meminfo")
194
- File.open(output, 'w') do |f|
195
- f.puts(stdout)
196
- end
197
- return output
198
- end
199
-
200
- def collect_netstat_plan()
201
- output = @outdir+'/netstat_plan.output'
202
- stdout, stderr, status = Open3.capture3("netstat -plan")
203
- File.open(output, 'w') do |f|
204
- f.puts(stdout)
205
- end
206
- return output
207
- end
208
-
209
- def collect_netstat_s()
210
- output = @outdir+'/netstat_s.output'
211
- stdout, stderr, status = Open3.capture3("netstat -s")
212
- File.open(output, 'w') do |f|
213
- f.puts(stdout)
214
- end
215
- return output
216
- end
217
-
218
372
  def collect_ntp(command)
219
373
  output = @outdir+'/ntp_info.output'
220
374
  stdout_date, stderr_date, status_date = Open3.capture3("date")
@@ -224,9 +378,26 @@ module Diagtool
224
378
  f.puts(stdout_date)
225
379
  f.puts(stdout_ntp)
226
380
  end
381
+ end
382
+
383
+ def collect_cmd_output(cmd)
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}")
394
+ end
395
+ else
396
+ @logger.warn("Command #{cmd} does not exist - skip collecting #{cmd} output")
397
+ end
227
398
  return output
228
399
  end
229
-
400
+
230
401
  def collect_tdgems()
231
402
  output = @outdir+'/tdgem_list.output'
232
403
  stdout, stderr, status = Open3.capture3("td-agent-gem list | grep fluent")
@@ -27,26 +27,12 @@ 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
- #@conf[:time] = @time_format
31
- #@conf[:workdir] = @conf[:basedir] + '/' + @time_format
32
- #@conf[:outdir] = @conf[:workdir] + '/output'
33
-
34
- #FileUtils.mkdir_p(@conf[:workdir])
35
- #FileUtils.mkdir_p(@conf[:outdir])
36
-
37
- #diaglog = @conf[:workdir] + '/diagtool.output'
38
- #@masklog = './mask_' + @time_format + '.json'
39
- #@logger = Logger.new(STDOUT, formatter: proc {|severity, datetime, progname, msg|
40
- # "#{datetime}: [Diagtool] [#{severity}] #{msg}\n"
41
- #})
42
- #@logger_file = Logger.new(diaglog, formatter: proc {|severity, datetime, progname, msg|
43
- # "#{datetime}: [Diagtool] [#{severity}] #{msg}\n"
44
- #})
45
- #diaglogger_info("Parsing command options...")
46
- #diaglogger_info(" Option : Output directory = #{@conf[:basedir]}")
47
- #diaglogger_info(" Option : Mask = #{@conf[:mask]}")
48
- #diaglogger_info(" Option : Word list = #{@conf[:words]}")
49
- #diaglogger_info(" Option : Hash Seed = #{@conf[:seed]}")
30
+ @cmd_list = [
31
+ "ps -eo pid,ppid,stime,time,%mem,%cpu,cmd",
32
+ "cat /proc/meminfo",
33
+ "netstat -plan",
34
+ "netstat -s",
35
+ ]
50
36
  end
51
37
 
52
38
  def run_precheck()
@@ -56,6 +42,7 @@ module Diagtool
56
42
  loglevel = 'WARN'
57
43
  c = CollectUtils.new(@conf, loglevel)
58
44
  c_env = c.export_env()
45
+ prechecklog.info("[Precheck] Fluentd Type = #{@conf[:type]}")
59
46
  prechecklog.info("[Precheck] Check OS parameters...")
60
47
  prechecklog.info("[Precheck] operating system = #{c_env[:os]}")
61
48
  prechecklog.info("[Precheck] kernel version = #{c_env[:kernel]}")
@@ -65,13 +52,13 @@ module Diagtool
65
52
  prechecklog.info("[Precheck] td-agent log path = #{c_env[:tdlog_path]}")
66
53
  prechecklog.info("[Precheck] td-agent log = #{c_env[:tdlog]}")
67
54
  if c_env[:tdconf_path] == nil || c_env[:tdconf] == nil
68
- 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>")
69
56
  end
70
57
  if c_env[:tdlog_path] == nil || c_env[:tdlog] == nil
71
58
  prechecklog.warn("[Precheck] can not find td-agent log path: please run diagtool command with -l /path/to/<td-agent log file>")
72
59
  end
73
60
  if c_env[:tdconf_path] != nil && c_env[:tdconf] != nil && c_env[:tdlog_path] != nil && c_env[:tdlog] != nil
74
- 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")
75
62
  end
76
63
  end
77
64
 
@@ -111,8 +98,19 @@ module Diagtool
111
98
  v = ValidUtils.new(loglevel)
112
99
 
113
100
  diaglogger_info("[Collect] Collecting log files of td-agent...")
114
- tdlog = c.collect_tdlog()
115
- 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
116
114
 
117
115
  diaglogger_info("[Collect] Collecting config file of td-agent...")
118
116
  tdconf = c.collect_tdconf()
@@ -130,39 +128,37 @@ module Diagtool
130
128
  end
131
129
  diaglogger_info("[Collect] config file is stored in #{oslog}")
132
130
 
133
- diaglogger_info("[Collect] Collecting process information...")
134
- meminfo = c.collect_ps_eo()
135
- diaglogger_info("[Collect] process informationis stored in #{meminfo}")
136
-
137
- diaglogger_info("[Collect] Collecting OS memory information...")
138
- meminfo = c.collect_meminfo()
139
- diaglogger_info("[Collect] OS memory information is stored in #{meminfo}")
140
-
141
131
  diaglogger_info("[Collect] Collecting date/time information...")
142
132
  if system('which chronyc > /dev/null 2>&1')
143
- ntp = c.collect_ntp(command="chrony")
133
+ ntp = c.collect_cmd_output(command="chronyc sources")
134
+ diaglogger_info("[Collect] date/time information is stored in #{ntp}")
144
135
  elsif system('which ntpq > /dev/null 2>&1')
145
- ntp = c.collect_ntp(command="ntp")
136
+ ntp = c.collect_cmd_output(command="ntpq -p")
137
+ diaglogger_info("[Collect] date/time information is stored in #{ntp}")
146
138
  else
147
139
  diaglogger_warn("[Collect] chrony/ntp does not exist. skip collectig date/time information")
148
140
  end
149
- diaglogger_info("[Collect] date/time information is stored in #{ntp}")
150
-
151
- diaglogger_info("[Collect] Collecting netstat information...")
152
- if system('which netstat > /dev/null 2>&1')
153
- netstat_n = c.collect_netstat_plan()
154
- netstat_s = c.collect_netstat_s()
155
- if @conf[:mask] == 'yes'
156
- diaglogger_info("[Mask] Masking netstat file : #{netstat_n}...")
157
- netstat_n = m.mask_tdlog(netstat_n, clean = true)
158
- end
159
- diaglogger_info("[Collect] netstat information is stored in #{netstat_n} and #{netstat_s}")
160
- else
161
- diaglogger_warn("[Collect] netstat does not exist. skip collectig netstat")
162
- end
163
141
 
142
+ ###
143
+ # Correct OS information
144
+ ###
145
+ @cmd_list.each { |cmd|
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}")
154
+ end
155
+ }
156
+
157
+ ###
158
+ # Correct information to be validated
159
+ ###
164
160
  diaglogger_info("[Collect] Collecting systctl information...")
165
- sysctl = c.collect_sysctl()
161
+ sysctl = c.collect_cmd_output("sysctl -a")
166
162
  diaglogger_info("[Collect] sysctl information is stored in #{sysctl}")
167
163
 
168
164
  diaglogger_info("[Valid] Validating systctl information...")
@@ -177,7 +173,7 @@ module Diagtool
177
173
  end
178
174
 
179
175
  diaglogger_info("[Collect] Collecting ulimit information...")
180
- ulimit = c.collect_ulimit()
176
+ ulimit = c.collect_cmd_output(cmd="sh -c 'ulimit -n'")
181
177
  diaglogger_info("[Collect] ulimit information is stored in #{ulimit}")
182
178
 
183
179
  diaglogger_info("[Valid] Validating ulimit information...")
@@ -189,16 +185,23 @@ module Diagtool
189
185
  end
190
186
 
191
187
  if @conf[:mask] == 'yes'
192
- diaglogger_info("[Mask] Masking td-agent config file : #{tdconf}...")
193
- m.mask_tdlog(tdconf, clean = true)
194
- tdlog.each do | file |
195
- diaglogger_info("[Mask] Masking td-agent log file : #{file}...")
196
- filename = file.split("/")[-1]
197
- if filename.include?(".gz")
198
- m.mask_tdlog_gz(file, clean = true)
199
- elsif
200
- m.mask_tdlog(file, clean = true)
201
- end
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
+ }
202
205
  end
203
206
  end
204
207
 
@@ -206,15 +209,16 @@ module Diagtool
206
209
  diaglogger_info("[Mask] Export mask log file : #{@masklog}")
207
210
  m.export_masklog(@masklog)
208
211
  end
209
-
212
+
210
213
  tar_file = c.compress_output()
211
214
  diaglogger_info("[Collect] Generate tar file #{tar_file}")
212
215
  end
213
216
 
214
217
  def parse_diagconf(params)
215
218
  options = {
216
- :precheck => '', :basedir => '', :mask => '', :words => [], :wfile => '', :seed => '', :tdconf =>'', :tdlog => ''
219
+ :precheck => '', :basedir => '', :type =>'', :mask => '', :words => [], :wfile => '', :seed => '', :tdconf =>'', :tdlog => ''
217
220
  }
221
+ ### Parse precheck flag
218
222
  if params[:precheck]
219
223
  options[:precheck] = params[:precheck]
220
224
  else
@@ -231,6 +235,13 @@ module Diagtool
231
235
  raise "output directory '-o' must be specified"
232
236
  end
233
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
234
245
  if params[:mask] == nil
235
246
  options[:mask] = 'no'
236
247
  else
@@ -240,7 +251,11 @@ module Diagtool
240
251
  raise "invalid arguments '#{params[:mask]}' : input of '-m|--mask' should be 'yes' or 'no'"
241
252
  end
242
253
  end
254
+
255
+ ### Parse uder-defined keyword list which will be used in the mask function
243
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
244
259
  if params[:"word-file"] != nil
245
260
  f = params[:"word-file"]
246
261
  if File.exist?(f)
@@ -252,8 +267,11 @@ module Diagtool
252
267
  end
253
268
  end
254
269
  options[:words] = options[:words].uniq
270
+
271
+ ### Parse hash seed which will be used in the mask function
255
272
  options[:seed] = params[:"hash-seed"] if params[:"hash-seed"] != nil
256
-
273
+
274
+ ### Parse the path of fluentd config file
257
275
  if params[:conf] != nil
258
276
  f = params[:conf]
259
277
  if File.exist?(f)
@@ -263,6 +281,7 @@ module Diagtool
263
281
  end
264
282
  end
265
283
 
284
+ ### Parse the path of fluentd log file
266
285
  if params[:log] != nil
267
286
  f = params[:log]
268
287
  if File.exist?(f)
@@ -33,7 +33,8 @@ module Diagtool
33
33
  :net_ipv4_tcp_max_syn_backlog => "8096",
34
34
  :net_ipv4_tcp_slow_start_after_idle => "0",
35
35
  :net_ipv4_tcp_tw_reuse => "1",
36
- :net_ipv4_ip_local_port_range => ["10240", "65535"]}
36
+ :net_ipv4_ip_local_port_range => ["10240", "65535"]
37
+ }
37
38
  @logger.debug("Initialize Validation Utils:")
38
39
  @logger.debug(" Default ulimit: #{@def_ulimit}")
39
40
  @logger.debug(" Default sysctl: #{@def_sysctl}")
@@ -57,7 +58,7 @@ module Diagtool
57
58
  v = Hash.new { |i,j| i[j] = Hash.new(&h.default_proc) }
58
59
  @logger.info("Loading sysctl file: #{sysctl_file}")
59
60
  File.readlines(sysctl_file).each{ |line|
60
- if line.include?("net")
61
+ if line.include? "net"
61
62
  line_net = line.chomp.gsub(".","_").split("=")
62
63
  key = line_net[0].strip.to_sym
63
64
  if line_net[1].strip! =~ /\s/
@@ -66,17 +67,19 @@ module Diagtool
66
67
  value= line_net[1]
67
68
  end
68
69
  h[key] = value
69
- if @def_sysctl[key] == value
70
- @logger.info("#{key} => #{value} is correct")
71
- v[key]['value'] = value
72
- v[key]['recommend'] = @def_sysctl[key]
73
- v[key]['result'] = "correct"
74
- else
75
- @logger.warn("#{key} => #{value} is incorrect, should be #{@def_sysctl[key]}")
76
- v[key]['value'] = value
77
- v[key]['recommend'] = @def_sysctl[key]
78
- v[key]['result'] = "incorrect"
79
- end
70
+ if @def_sysctl.key? key
71
+ if @def_sysctl[key] == value
72
+ @logger.info("#{key} => #{value} is correct")
73
+ v[key]['value'] = value
74
+ v[key]['recommend'] = @def_sysctl[key]
75
+ v[key]['result'] = "correct"
76
+ else
77
+ @logger.warn("#{key} => #{value} is incorrect, should be #{@def_sysctl[key]}")
78
+ v[key]['value'] = value
79
+ v[key]['recommend'] = @def_sysctl[key]
80
+ v[key]['result'] = "incorrect"
81
+ end
82
+ end
80
83
  end
81
84
  }
82
85
  if h == @sysctl
@@ -1,5 +1,5 @@
1
1
  module Fluent
2
2
  module Diagtool
3
- VERSION = "0.1.5"
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.5
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-05-28 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:
@@ -58,7 +58,8 @@ files:
58
58
  - bin/console
59
59
  - bin/setup
60
60
  - bin/word_list_sample
61
- - exe/diagtool
61
+ - exclude_list01
62
+ - exe/fluent-diagtool
62
63
  - fluent-diagtool.gemspec
63
64
  - lib/fluent/diagtool/collectutils.rb
64
65
  - lib/fluent/diagtool/diagutils.rb
@@ -84,8 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
85
  - !ruby/object:Gem::Version
85
86
  version: '0'
86
87
  requirements: []
87
- rubyforge_project:
88
- rubygems_version: 2.7.6.2
88
+ rubygems_version: 3.1.2
89
89
  signing_key:
90
90
  specification_version: 4
91
91
  summary: Diagnostic Tool for Fluentd