fluentd 1.10.1 → 1.11.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fluentd might be problematic. Click here for more details.

Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +112 -1
  3. data/CONTRIBUTING.md +1 -1
  4. data/lib/fluent/command/debug.rb +1 -0
  5. data/lib/fluent/command/fluentd.rb +12 -1
  6. data/lib/fluent/config.rb +1 -0
  7. data/lib/fluent/log.rb +45 -6
  8. data/lib/fluent/match.rb +1 -1
  9. data/lib/fluent/plugin/in_dummy.rb +2 -2
  10. data/lib/fluent/plugin/in_forward.rb +2 -2
  11. data/lib/fluent/plugin/in_gc_stat.rb +16 -0
  12. data/lib/fluent/plugin/in_http.rb +146 -75
  13. data/lib/fluent/plugin/in_monitor_agent.rb +1 -1
  14. data/lib/fluent/plugin/in_syslog.rb +4 -4
  15. data/lib/fluent/plugin/in_tail.rb +4 -4
  16. data/lib/fluent/plugin/in_unix.rb +77 -77
  17. data/lib/fluent/plugin/out_copy.rb +1 -1
  18. data/lib/fluent/plugin/out_file.rb +1 -1
  19. data/lib/fluent/plugin/out_forward.rb +23 -18
  20. data/lib/fluent/plugin/out_forward/load_balancer.rb +1 -1
  21. data/lib/fluent/plugin/out_http.rb +15 -2
  22. data/lib/fluent/plugin/parser_multiline.rb +1 -1
  23. data/lib/fluent/plugin/parser_syslog.rb +215 -54
  24. data/lib/fluent/plugin_helper/child_process.rb +3 -2
  25. data/lib/fluent/plugin_helper/record_accessor.rb +14 -0
  26. data/lib/fluent/plugin_helper/service_discovery.rb +7 -0
  27. data/lib/fluent/plugin_helper/service_discovery/manager.rb +8 -0
  28. data/lib/fluent/plugin_helper/socket.rb +20 -2
  29. data/lib/fluent/plugin_helper/socket_option.rb +2 -2
  30. data/lib/fluent/supervisor.rb +21 -9
  31. data/lib/fluent/system_config.rb +2 -1
  32. data/lib/fluent/test/filter_test.rb +2 -2
  33. data/lib/fluent/test/output_test.rb +3 -3
  34. data/lib/fluent/version.rb +1 -1
  35. data/test/command/test_fluentd.rb +57 -10
  36. data/test/config/test_system_config.rb +2 -0
  37. data/test/plugin/out_forward/test_load_balancer.rb +46 -0
  38. data/test/plugin/test_in_gc_stat.rb +24 -1
  39. data/test/plugin/test_in_http.rb +57 -0
  40. data/test/plugin/test_in_syslog.rb +1 -1
  41. data/test/plugin/test_in_tail.rb +20 -16
  42. data/test/plugin/test_in_unix.rb +128 -73
  43. data/test/plugin/test_out_forward.rb +11 -2
  44. data/test/plugin/test_out_http.rb +38 -0
  45. data/test/plugin/test_out_null.rb +1 -1
  46. data/test/plugin/test_output_as_buffered_retries.rb +12 -4
  47. data/test/plugin/test_parser_syslog.rb +66 -29
  48. data/test/plugin_helper/data/cert/cert_chains/ca-cert-key.pem +27 -0
  49. data/test/plugin_helper/data/cert/cert_chains/ca-cert.pem +20 -0
  50. data/test/plugin_helper/data/cert/cert_chains/cert-key.pem +27 -0
  51. data/test/plugin_helper/data/cert/cert_chains/cert.pem +40 -0
  52. data/test/plugin_helper/data/cert/generate_cert.rb +38 -0
  53. data/test/plugin_helper/http_server/test_app.rb +1 -1
  54. data/test/plugin_helper/http_server/test_route.rb +1 -1
  55. data/test/plugin_helper/test_child_process.rb +15 -0
  56. data/test/plugin_helper/test_http_server_helper.rb +2 -2
  57. data/test/plugin_helper/test_record_accessor.rb +41 -0
  58. data/test/plugin_helper/test_server.rb +1 -1
  59. data/test/plugin_helper/test_service_discovery.rb +37 -4
  60. data/test/plugin_helper/test_socket.rb +131 -0
  61. data/test/test_log.rb +44 -0
  62. metadata +12 -2
@@ -104,7 +104,7 @@ module Fluent
104
104
  end
105
105
 
106
106
  def firstline?(text)
107
- @firstline_regex.match(text)
107
+ @firstline_regex.match?(text)
108
108
  end
109
109
 
110
110
  private
@@ -63,16 +63,22 @@ module Fluent
63
63
  def initialize
64
64
  super
65
65
  @mutex = Mutex.new
66
- @space_count = nil
66
+ @regexp = nil
67
+ @regexp3164 = nil
68
+ @regexp5424 = nil
69
+ @regexp_parser = nil
70
+ @time_parser_rfc3164 = nil
71
+ @time_parser_rfc5424 = nil
72
+ @space_count_rfc3164 = nil
67
73
  @space_count_rfc5424 = nil
74
+ @skip_space_count_rfc3164 = false
75
+ @skip_space_count_rfc5424 = false
76
+ @time_parser_rfc5424_without_subseconds = nil
68
77
  end
69
78
 
70
79
  def configure(conf)
71
80
  super
72
81
 
73
- @time_parser_rfc3164 = @time_parser_rfc5424 = nil
74
- @time_parser_rfc5424_without_subseconds = nil
75
- @support_rfc5424_without_subseconds = false
76
82
  @regexp_parser = @parser_type == :regexp
77
83
  @regexp = case @message_format
78
84
  when :rfc3164
@@ -85,27 +91,49 @@ module Fluent
85
91
  alias_method :parse, :parse_rfc3164
86
92
  end
87
93
  end
94
+ setup_time_parser_3164(@time_format)
88
95
  RFC3164_WITHOUT_TIME_AND_PRI_REGEXP
89
96
  when :rfc5424
90
- class << self
91
- alias_method :parse, :parse_rfc5424_regex
97
+ if @regexp_parser
98
+ class << self
99
+ alias_method :parse, :parse_rfc5424_regex
100
+ end
101
+ else
102
+ class << self
103
+ alias_method :parse, :parse_rfc5424
104
+ end
92
105
  end
93
106
  @time_format = @rfc5424_time_format unless conf.has_key?('time_format')
94
- @support_rfc5424_without_subseconds = true
107
+ setup_time_parser_5424(@time_format)
95
108
  RFC5424_WITHOUT_TIME_AND_PRI_REGEXP
96
109
  when :auto
97
110
  class << self
98
111
  alias_method :parse, :parse_auto
99
112
  end
100
- @time_parser_rfc3164 = time_parser_create(format: @time_format)
101
- @time_parser_rfc5424 = time_parser_create(format: @rfc5424_time_format)
113
+ setup_time_parser_3164(@time_format)
114
+ setup_time_parser_5424(@rfc5424_time_format)
102
115
  nil
103
116
  end
104
117
 
105
- @space_count = @time_format.squeeze(' ').count(' ') + 1
106
- @space_count_rfc5424 = @rfc5424_time_format.squeeze(' ').count(' ') + 1
107
- @time_parser = time_parser_create
118
+ if @regexp_parser
119
+ @regexp3164 = RFC3164_WITHOUT_TIME_AND_PRI_REGEXP
120
+ @regexp5424 = RFC5424_WITHOUT_TIME_AND_PRI_REGEXP
121
+ end
122
+ end
123
+
124
+ def setup_time_parser_3164(time_fmt)
125
+ @time_parser_rfc3164 = time_parser_create(format: time_fmt)
126
+ if ['%b %d %H:%M:%S', '%b %d %H:%M:%S.%N'].include?(time_fmt)
127
+ @skip_space_count_rfc3164 = true
128
+ end
129
+ @space_count_rfc3164 = time_fmt.squeeze(' ').count(' ') + 1
130
+ end
131
+
132
+ def setup_time_parser_5424(time_fmt)
133
+ @time_parser_rfc5424 = time_parser_create(format: time_fmt)
108
134
  @time_parser_rfc5424_without_subseconds = time_parser_create(format: "%Y-%m-%dT%H:%M:%S%z")
135
+ @skip_space_count_rfc5424 = time_fmt.count(' ').zero?
136
+ @space_count_rfc5424 = time_fmt.squeeze(' ').count(' ') + 1
109
137
  end
110
138
 
111
139
  # this method is for tests
@@ -118,14 +146,13 @@ module Fluent
118
146
  end
119
147
 
120
148
  def parse_auto(text, &block)
121
- if REGEXP_DETECT_RFC5424.match(text)
122
- @regexp = RFC5424_WITHOUT_TIME_AND_PRI_REGEXP
123
- @time_parser = @time_parser_rfc5424
124
- @support_rfc5424_without_subseconds = true
125
- parse_rfc5424_regex(text, &block)
149
+ if REGEXP_DETECT_RFC5424.match?(text)
150
+ if @regexp_parser
151
+ parse_rfc5424_regex(text, &block)
152
+ else
153
+ parse_rfc5424(text, &block)
154
+ end
126
155
  else
127
- @regexp = RFC3164_WITHOUT_TIME_AND_PRI_REGEXP
128
- @time_parser = @time_parser_rfc3164
129
156
  if @regexp_parser
130
157
  parse_rfc3164_regex(text, &block)
131
158
  else
@@ -134,6 +161,8 @@ module Fluent
134
161
  end
135
162
  end
136
163
 
164
+ SPLIT_CHAR = ' '.freeze
165
+
137
166
  def parse_rfc3164_regex(text, &block)
138
167
  idx = 0
139
168
  record = {}
@@ -151,22 +180,22 @@ module Fluent
151
180
 
152
181
  i = idx - 1
153
182
  sq = false
154
- @space_count.times do
155
- while text[i + 1] == ' '.freeze
183
+ @space_count_rfc3164.times do
184
+ while text[i + 1] == SPLIT_CHAR
156
185
  sq = true
157
186
  i += 1
158
187
  end
159
188
 
160
- i = text.index(' '.freeze, i + 1)
189
+ i = text.index(SPLIT_CHAR, i + 1)
161
190
  end
162
191
 
163
- time_str = sq ? text.slice(idx, i - idx).squeeze(' ') : text.slice(idx, i - idx)
164
- time = @mutex.synchronize { @time_parser.parse(time_str) }
192
+ time_str = sq ? text.slice(idx, i - idx).squeeze(SPLIT_CHAR) : text.slice(idx, i - idx)
193
+ time = @mutex.synchronize { @time_parser_rfc3164.parse(time_str) }
165
194
  if @keep_time_key
166
195
  record['time'] = time_str
167
196
  end
168
197
 
169
- parse_plain(time, text, i + 1, record, RFC3164_CAPTURES, &block)
198
+ parse_plain(@regexp3164, time, text, i + 1, record, RFC3164_CAPTURES, &block)
170
199
  end
171
200
 
172
201
  def parse_rfc5424_regex(text, &block)
@@ -186,40 +215,36 @@ module Fluent
186
215
  i = idx - 1
187
216
  sq = false
188
217
  @space_count_rfc5424.times {
189
- while text[i + 1] == ' '.freeze
218
+ while text[i + 1] == SPLIT_CHAR
190
219
  sq = true
191
220
  i += 1
192
221
  end
193
222
 
194
- i = text.index(' '.freeze, i + 1)
223
+ i = text.index(SPLIT_CHAR, i + 1)
195
224
  }
196
225
 
197
- time_str = sq ? text.slice(idx, i - idx).squeeze(' '.freeze) : text.slice(idx, i - idx)
226
+ time_str = sq ? text.slice(idx, i - idx).squeeze(SPLIT_CHAR) : text.slice(idx, i - idx)
198
227
  time = @mutex.synchronize do
199
228
  begin
200
- @time_parser.parse(time_str)
229
+ @time_parser_rfc5424.parse(time_str)
201
230
  rescue Fluent::TimeParser::TimeParseError => e
202
- if @support_rfc5424_without_subseconds
203
- log.trace(e)
204
- @time_parser_rfc5424_without_subseconds.parse(time_str)
205
- else
206
- raise
207
- end
231
+ log.trace(e)
232
+ @time_parser_rfc5424_without_subseconds.parse(time_str)
208
233
  end
209
234
  end
210
235
 
211
236
  if @keep_time_key
212
237
  record['time'] = time_str
213
238
  end
214
- parse_plain(time, text, i + 1, record, RFC5424_CAPTURES, &block)
239
+ parse_plain(@regexp5424, time, text, i + 1, record, RFC5424_CAPTURES, &block)
215
240
  end
216
241
 
217
242
  # @param time [EventTime]
218
243
  # @param idx [Integer] note: this argument is needed to avoid string creation
219
244
  # @param record [Hash]
220
245
  # @param capture_list [Array] for performance
221
- def parse_plain(time, text, idx, record, capture_list, &block)
222
- m = @regexp.match(text, idx)
246
+ def parse_plain(re, time, text, idx, record, capture_list, &block)
247
+ m = re.match(text, idx)
223
248
  if m.nil?
224
249
  yield nil, nil
225
250
  return
@@ -244,8 +269,6 @@ module Fluent
244
269
  yield time, record
245
270
  end
246
271
 
247
- SPLIT_CHAR = ' '.freeze
248
-
249
272
  def parse_rfc3164(text, &block)
250
273
  pri = nil
251
274
  cursor = 0
@@ -264,20 +287,35 @@ module Fluent
264
287
  end
265
288
  end
266
289
 
267
- # header part
268
- time_size = 15 # skip Mmm dd hh:mm:ss
269
- time_end = text[cursor + time_size]
270
- if time_end == SPLIT_CHAR
271
- time_str = text.slice(cursor, time_size)
272
- cursor += 16 # time + ' '
273
- elsif time_end == '.'.freeze
274
- # support subsecond time
275
- i = text.index(SPLIT_CHAR, time_size)
276
- time_str = text.slice(cursor, i - cursor)
277
- cursor = i + 1
290
+ if @skip_space_count_rfc3164
291
+ # header part
292
+ time_size = 15 # skip Mmm dd hh:mm:ss
293
+ time_end = text[cursor + time_size]
294
+ if time_end == SPLIT_CHAR
295
+ time_str = text.slice(cursor, time_size)
296
+ cursor += 16 # time + ' '
297
+ elsif time_end == '.'.freeze
298
+ # support subsecond time
299
+ i = text.index(SPLIT_CHAR, time_size)
300
+ time_str = text.slice(cursor, i - cursor)
301
+ cursor = i + 1
302
+ else
303
+ yield nil, nil
304
+ return
305
+ end
278
306
  else
279
- yield nil, nil
280
- return
307
+ i = cursor - 1
308
+ sq = false
309
+ @space_count_rfc3164.times do
310
+ while text[i + 1] == SPLIT_CHAR
311
+ sq = true
312
+ i += 1
313
+ end
314
+ i = text.index(SPLIT_CHAR, i + 1)
315
+ end
316
+
317
+ time_str = sq ? text.slice(idx, i - cursor).squeeze(SPLIT_CHAR) : text.slice(cursor, i - cursor)
318
+ cursor = i + 1
281
319
  end
282
320
 
283
321
  i = text.index(SPLIT_CHAR, cursor)
@@ -325,7 +363,130 @@ module Fluent
325
363
  msg.chomp!
326
364
  record['message'] = msg
327
365
 
328
- time = @time_parser.parse(time_str)
366
+ time = @time_parser_rfc3164.parse(time_str)
367
+ record['time'] = time_str if @keep_time_key
368
+
369
+ yield time, record
370
+ end
371
+
372
+ NILVALUE = '-'.freeze
373
+
374
+ def parse_rfc5424(text, &block)
375
+ pri = nil
376
+ cursor = 0
377
+ if @with_priority
378
+ if text.start_with?('<'.freeze)
379
+ i = text.index('>'.freeze, 1)
380
+ if i < 2
381
+ yield nil, nil
382
+ return
383
+ end
384
+ pri = text.slice(1, i - 1).to_i
385
+ i = text.index(SPLIT_CHAR, i)
386
+ cursor = i + 1
387
+ else
388
+ yield nil, nil
389
+ return
390
+ end
391
+ end
392
+
393
+ # timestamp part
394
+ if @skip_space_count_rfc5424
395
+ i = text.index(SPLIT_CHAR, cursor)
396
+ time_str = text.slice(cursor, i - cursor)
397
+ cursor = i + 1
398
+ else
399
+ i = cursor - 1
400
+ sq = false
401
+ @space_count_rfc5424.times do
402
+ while text[i + 1] == SPLIT_CHAR
403
+ sq = true
404
+ i += 1
405
+ end
406
+ i = text.index(SPLIT_CHAR, i + 1)
407
+ end
408
+
409
+ time_str = sq ? text.slice(idx, i - cursor).squeeze(SPLIT_CHAR) : text.slice(cursor, i - cursor)
410
+ cursor = i + 1
411
+ end
412
+
413
+ # Repeat same code for the performance
414
+
415
+ # host part
416
+ i = text.index(SPLIT_CHAR, cursor)
417
+ unless i
418
+ yield nil, nil
419
+ return
420
+ end
421
+ slice_size = i - cursor
422
+ host = text.slice(cursor, slice_size)
423
+ cursor += slice_size + 1
424
+
425
+ # ident part
426
+ i = text.index(SPLIT_CHAR, cursor)
427
+ unless i
428
+ yield nil, nil
429
+ return
430
+ end
431
+ slice_size = i - cursor
432
+ ident = text.slice(cursor, slice_size)
433
+ cursor += slice_size + 1
434
+
435
+ # pid part
436
+ i = text.index(SPLIT_CHAR, cursor)
437
+ unless i
438
+ yield nil, nil
439
+ return
440
+ end
441
+ slice_size = i - cursor
442
+ pid = text.slice(cursor, slice_size)
443
+ cursor += slice_size + 1
444
+
445
+ # msgid part
446
+ i = text.index(SPLIT_CHAR, cursor)
447
+ unless i
448
+ yield nil, nil
449
+ return
450
+ end
451
+ slice_size = i - cursor
452
+ msgid = text.slice(cursor, slice_size)
453
+ cursor += slice_size + 1
454
+
455
+ record = {'host' => host, 'ident' => ident, 'pid' => pid, 'msgid' => msgid}
456
+ record['pri'] = pri if pri
457
+
458
+ # extradata part
459
+ ed_start = text[cursor]
460
+ if ed_start == NILVALUE
461
+ record['extradata'] = NILVALUE
462
+ cursor += 1
463
+ else
464
+ start = cursor
465
+ i = text.index('] '.freeze, cursor)
466
+ extradata = if i
467
+ diff = i + 1 - start # calculate ']' position
468
+ cursor += diff
469
+ text.slice(start, diff)
470
+ else # No message part case
471
+ cursor = text.bytesize
472
+ text.slice(start, cursor)
473
+ end
474
+ extradata.tr!("\\".freeze, ''.freeze)
475
+ record['extradata'] = extradata
476
+ end
477
+
478
+ # message part
479
+ if cursor != text.bytesize
480
+ msg = text.slice(cursor + 1, text.bytesize)
481
+ msg.chomp!
482
+ record['message'] = msg
483
+ end
484
+
485
+ time = begin
486
+ @time_parser_rfc5424.parse(time_str)
487
+ rescue Fluent::TimeParser::TimeParseError => e
488
+ @time_parser_rfc5424_without_subseconds.parse(time_str)
489
+ end
329
490
  record['time'] = time_str if @keep_time_key
330
491
 
331
492
  yield time, record
@@ -259,6 +259,9 @@ module Fluent
259
259
 
260
260
  if !mode.include?(:stderr) && !mode.include?(:read_with_stderr)
261
261
  spawn_opts[:err] = IO::NULL if stderr == :discard
262
+ if !mode.include?(:read) && !mode.include?(:read_with_stderr)
263
+ spawn_opts[:out] = IO::NULL
264
+ end
262
265
  writeio, readio, wait_thread = *Open3.popen2(*spawn_args, spawn_opts)
263
266
  elsif mode.include?(:read_with_stderr)
264
267
  writeio, readio, wait_thread = *Open3.popen2e(*spawn_args, spawn_opts)
@@ -275,8 +278,6 @@ module Fluent
275
278
  if mode.include?(:read) || mode.include?(:read_with_stderr)
276
279
  readio.set_encoding(external_encoding, internal_encoding, **encoding_options)
277
280
  readio_in_use = true
278
- else
279
- readio.reopen(IO::NULL) if readio
280
281
  end
281
282
  if mode.include?(:stderr)
282
283
  stderrio.set_encoding(external_encoding, internal_encoding, **encoding_options)
@@ -41,9 +41,11 @@ module Fluent
41
41
  else
42
42
  mcall = method(:call_dig)
43
43
  mdelete = method(:delete_nest)
44
+ mset = method(:set_nest)
44
45
  singleton_class.module_eval do
45
46
  define_method(:call, mcall)
46
47
  define_method(:delete, mdelete)
48
+ define_method(:set, mset)
47
49
  end
48
50
  end
49
51
  end
@@ -75,6 +77,18 @@ module Fluent
75
77
  nil
76
78
  end
77
79
 
80
+ def set(r, v)
81
+ r[@keys] = v
82
+ end
83
+
84
+ # set_nest doesn't create intermediate object. If key doesn't exist, no effect.
85
+ # See also: https://bugs.ruby-lang.org/issues/11747
86
+ def set_nest(r, v)
87
+ r.dig(*@dig_keys)&.[]=(@last_key, v)
88
+ rescue
89
+ nil
90
+ end
91
+
78
92
  def self.parse_parameter(param)
79
93
  if param.start_with?('$.')
80
94
  parse_dot_notation(param)
@@ -43,6 +43,13 @@ module Fluent
43
43
  super
44
44
  end
45
45
 
46
+ %i[after_start stop before_shutdown shutdown after_shutdown close terminate].each do |mth|
47
+ define_method(mth) do
48
+ @discovery_manager&.__send__(mth)
49
+ super()
50
+ end
51
+ end
52
+
46
53
  private
47
54
 
48
55
  # @param title [Symbol] the thread name. this value should be unique.