fluentd 1.11.3 → 1.12.0

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  3. data/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. data/.github/workflows/stale-actions.yml +22 -0
  5. data/.travis.yml +22 -2
  6. data/CHANGELOG.md +66 -0
  7. data/README.md +1 -1
  8. data/appveyor.yml +3 -0
  9. data/bin/fluent-cap-ctl +7 -0
  10. data/bin/fluent-ctl +7 -0
  11. data/fluentd.gemspec +2 -1
  12. data/lib/fluent/capability.rb +87 -0
  13. data/lib/fluent/command/cap_ctl.rb +174 -0
  14. data/lib/fluent/command/ctl.rb +177 -0
  15. data/lib/fluent/command/plugin_config_formatter.rb +2 -1
  16. data/lib/fluent/env.rb +4 -0
  17. data/lib/fluent/plugin.rb +5 -0
  18. data/lib/fluent/plugin/buffer.rb +2 -21
  19. data/lib/fluent/plugin/formatter.rb +24 -0
  20. data/lib/fluent/plugin/formatter_csv.rb +1 -1
  21. data/lib/fluent/plugin/formatter_hash.rb +3 -1
  22. data/lib/fluent/plugin/formatter_json.rb +3 -1
  23. data/lib/fluent/plugin/formatter_ltsv.rb +5 -3
  24. data/lib/fluent/plugin/formatter_out_file.rb +6 -4
  25. data/lib/fluent/plugin/formatter_single_value.rb +4 -2
  26. data/lib/fluent/plugin/formatter_tsv.rb +4 -2
  27. data/lib/fluent/plugin/in_http.rb +23 -2
  28. data/lib/fluent/plugin/in_tail.rb +109 -41
  29. data/lib/fluent/plugin/in_tail/position_file.rb +39 -14
  30. data/lib/fluent/plugin/in_tcp.rb +1 -0
  31. data/lib/fluent/plugin/out_http.rb +20 -2
  32. data/lib/fluent/plugin/output.rb +14 -6
  33. data/lib/fluent/plugin_helper/http_server/compat/server.rb +1 -1
  34. data/lib/fluent/plugin_helper/inject.rb +4 -1
  35. data/lib/fluent/plugin_helper/retry_state.rb +4 -0
  36. data/lib/fluent/supervisor.rb +140 -43
  37. data/lib/fluent/time.rb +1 -0
  38. data/lib/fluent/version.rb +1 -1
  39. data/lib/fluent/winsvc.rb +22 -4
  40. data/test/command/test_binlog_reader.rb +22 -6
  41. data/test/command/test_cap_ctl.rb +100 -0
  42. data/test/command/test_ctl.rb +57 -0
  43. data/test/command/test_plugin_config_formatter.rb +57 -2
  44. data/test/plugin/in_tail/test_position_file.rb +45 -25
  45. data/test/plugin/test_filter_stdout.rb +6 -1
  46. data/test/plugin/test_formatter_hash.rb +6 -3
  47. data/test/plugin/test_formatter_json.rb +14 -4
  48. data/test/plugin/test_formatter_ltsv.rb +13 -5
  49. data/test/plugin/test_formatter_out_file.rb +35 -14
  50. data/test/plugin/test_formatter_single_value.rb +12 -6
  51. data/test/plugin/test_formatter_tsv.rb +12 -4
  52. data/test/plugin/test_in_http.rb +25 -0
  53. data/test/plugin/test_in_tail.rb +430 -30
  54. data/test/plugin/test_out_file.rb +23 -18
  55. data/test/plugin/test_output.rb +12 -0
  56. data/test/plugin/test_parser_syslog.rb +2 -2
  57. data/test/plugin_helper/test_compat_parameters.rb +7 -2
  58. data/test/plugin_helper/test_inject.rb +42 -0
  59. data/test/test_capability.rb +74 -0
  60. data/test/test_formatter.rb +34 -10
  61. data/test/test_output.rb +6 -1
  62. data/test/test_supervisor.rb +119 -1
  63. metadata +33 -4
@@ -0,0 +1,177 @@
1
+ #
2
+ # Fluentd
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require 'optparse'
18
+ require 'fluent/env'
19
+ require 'fluent/version'
20
+ if Fluent.windows?
21
+ require 'win32/event'
22
+ require 'win32/service'
23
+ end
24
+
25
+ module Fluent
26
+ class Ctl
27
+ DEFAULT_OPTIONS = {}
28
+
29
+ if Fluent.windows?
30
+ include Windows::ServiceConstants
31
+ include Windows::ServiceStructs
32
+ include Windows::ServiceFunctions
33
+
34
+ COMMAND_MAP = {
35
+ shutdown: "",
36
+ restart: "HUP",
37
+ flush: "USR1",
38
+ reload: "USR2",
39
+ }
40
+ WINSVC_CONTROL_CODE_MAP = {
41
+ shutdown: SERVICE_CONTROL_STOP,
42
+ # 128 - 255: user-defined control code
43
+ # See https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-controlservice
44
+ restart: 128,
45
+ flush: 129,
46
+ reload: SERVICE_CONTROL_PARAMCHANGE,
47
+ }
48
+ else
49
+ COMMAND_MAP = {
50
+ shutdown: :TERM,
51
+ restart: :HUP,
52
+ flush: :USR1,
53
+ reload: :USR2,
54
+ }
55
+ end
56
+
57
+ def initialize(argv = ARGV)
58
+ @argv = argv
59
+ @options = {}
60
+ @opt_parser = OptionParser.new
61
+ configure_option_parser
62
+ @options.merge!(DEFAULT_OPTIONS)
63
+ parse_options!
64
+ end
65
+
66
+ def help_text
67
+ text = "\n"
68
+ if Fluent.windows?
69
+ text << "Usage: #{$PROGRAM_NAME} COMMAND [PID_OR_SVCNAME]\n"
70
+ else
71
+ text << "Usage: #{$PROGRAM_NAME} COMMAND PID\n"
72
+ end
73
+ text << "\n"
74
+ text << "Commands: \n"
75
+ COMMAND_MAP.each do |key, value|
76
+ text << " #{key}\n"
77
+ end
78
+ text
79
+ end
80
+
81
+ def usage(msg = nil)
82
+ puts help_text
83
+ if msg
84
+ puts
85
+ puts "Error: #{msg}"
86
+ end
87
+ exit 1
88
+ end
89
+
90
+ def call
91
+ if Fluent.windows?
92
+ if @pid_or_svcname =~ /^[0-9]+$/
93
+ # Use as PID
94
+ return call_windows_event(@command, "fluentd_#{@pid_or_svcname}")
95
+ end
96
+
97
+ unless call_winsvc_control_code(@command, @pid_or_svcname)
98
+ puts "Cannot send control code to #{@pid_or_svcname} service, try to send an event with this name ..."
99
+ call_windows_event(@command, @pid_or_svcname)
100
+ end
101
+ else
102
+ call_signal(@command, @pid_or_svcname)
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ def call_signal(command, pid)
109
+ signal = COMMAND_MAP[command.to_sym]
110
+ Process.kill(signal, pid.to_i)
111
+ end
112
+
113
+ def call_winsvc_control_code(command, pid_or_svcname)
114
+ status = SERVICE_STATUS.new
115
+
116
+ begin
117
+ handle_scm = OpenSCManager(nil, nil, SC_MANAGER_CONNECT)
118
+ FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
119
+
120
+ handle_scs = OpenService(handle_scm, "fluentdwinsvc", SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_USER_DEFINED_CONTROL)
121
+ FFI.raise_windows_error('OpenService') if handle_scs == 0
122
+
123
+ control_code = WINSVC_CONTROL_CODE_MAP[command.to_sym]
124
+
125
+ unless ControlService(handle_scs, control_code, status)
126
+ FFI.raise_windows_error('ControlService')
127
+ end
128
+ rescue => e
129
+ puts e
130
+ state = status[:dwCurrentState]
131
+ return state == SERVICE_STOPPED || state == SERVICE_STOP_PENDING
132
+ ensure
133
+ CloseServiceHandle(handle_scs)
134
+ CloseServiceHandle(handle_scm)
135
+ end
136
+
137
+ return true
138
+ end
139
+
140
+ def call_windows_event(command, pid_or_svcname)
141
+ prefix = pid_or_svcname
142
+ event_name = COMMAND_MAP[command.to_sym]
143
+ suffix = event_name.empty? ? "" : "_#{event_name}"
144
+
145
+ begin
146
+ event = Win32::Event.open("#{prefix}#{suffix}")
147
+ event.set
148
+ event.close
149
+ rescue Errno::ENOENT => e
150
+ puts "Error: Cannot find the fluentd process with the event name: \"#{prefix}\""
151
+ end
152
+ end
153
+
154
+ def configure_option_parser
155
+ @opt_parser.banner = help_text
156
+ @opt_parser.version = Fluent::VERSION
157
+ end
158
+
159
+ def parse_options!
160
+ @opt_parser.parse!(@argv)
161
+
162
+ @command = @argv[0]
163
+ @pid_or_svcname = @argv[1] || "fluentdwinsvc"
164
+
165
+ usage("Command isn't specified!") if @command.nil? || @command.empty?
166
+ usage("Unknown command: #{@command}") unless COMMAND_MAP.has_key?(@command.to_sym)
167
+
168
+ if Fluent.windows?
169
+ usage("PID or SVCNAME isn't specified!") if @pid_or_svcname.nil? || @pid_or_svcname.empty?
170
+ else
171
+ usage("PID isn't specified!") if @pid_or_svcname.nil? || @pid_or_svcname.empty?
172
+ usage("Invalid PID: #{pid}") unless @pid_or_svcname =~ /^[0-9]+$/
173
+ end
174
+ end
175
+ end
176
+ end
177
+
@@ -29,7 +29,8 @@ class FluentPluginConfigFormatter
29
29
  AVAILABLE_FORMATS = [:markdown, :txt, :json]
30
30
  SUPPORTED_TYPES = [
31
31
  "input", "output", "filter",
32
- "buffer", "parser", "formatter", "storage"
32
+ "buffer", "parser", "formatter", "storage",
33
+ "service_discovery"
33
34
  ]
34
35
 
35
36
  DOCS_BASE_URL = "https://docs.fluentd.org/v/1.0"
@@ -28,4 +28,8 @@ module Fluent
28
28
  def self.windows?
29
29
  ServerEngine.windows?
30
30
  end
31
+
32
+ def self.linux?
33
+ /linux/ === RUBY_PLATFORM
34
+ end
31
35
  end
@@ -121,6 +121,11 @@ module Fluent
121
121
  new_impl('sd', SD_REGISTRY, type, parent)
122
122
  end
123
123
 
124
+ class << self
125
+ # This should be defined for fluent-plugin-config-formatter type arguments.
126
+ alias_method :new_service_discovery, :new_sd
127
+ end
128
+
124
129
  def self.new_parser(type, parent: nil)
125
130
  if type[0] == '/' && type[-1] == '/'
126
131
  # This usage is not recommended for new API... create RegexpParser directly
@@ -143,33 +143,14 @@ module Fluent
143
143
  end
144
144
  end
145
145
 
146
- # timekey should be unixtime as usual.
147
- # So, unixtime should be bigger than 2^30 - 1 (= 1073741823) nowadays.
148
- # We should check object_id stability to use object_id as optimization for comparing operations.
149
- # e.g.)
150
- # irb> Time.parse("2020/07/31 18:30:00+09:00").to_i
151
- # => 1596187800
152
- # irb> Time.parse("2020/07/31 18:30:00+09:00").to_i > 2**30 -1
153
- # => true
154
- def self.enable_optimize?
155
- a1 = 2**30 - 1
156
- a2 = 2**30 - 1
157
- b1 = 2**62 - 1
158
- b2 = 2**62 - 1
159
- (a1.object_id == a2.object_id) && (b1.object_id == b2.object_id)
160
- end
161
-
162
146
  # This is an optimization code. Current Struct's implementation is comparing all data.
163
147
  # https://github.com/ruby/ruby/blob/0623e2b7cc621b1733a760b72af246b06c30cf96/struct.c#L1200-L1203
164
148
  # Actually this overhead is very small but this class is generated *per chunk* (and used in hash object).
165
149
  # This means that this class is one of the most called object in Fluentd.
166
150
  # See https://github.com/fluent/fluentd/pull/2560
167
- # But, this optimization has a side effect on Windows and 32bit environment(s) due to differing object_id.
168
- # This difference causes flood of buffer files.
169
- # So, this optimization should be enabled on `enable_optimize?` as true platforms.
170
151
  def hash
171
- timekey.object_id
172
- end if enable_optimize?
152
+ timekey.hash
153
+ end
173
154
  end
174
155
 
175
156
  # for tests
@@ -46,5 +46,29 @@ module Fluent
46
46
  @proc.call(tag, time, record)
47
47
  end
48
48
  end
49
+
50
+ module Newline
51
+ module Mixin
52
+ include Fluent::Configurable
53
+
54
+ DEFAULT_NEWLINE = if Fluent.windows?
55
+ :crlf
56
+ else
57
+ :lf
58
+ end
59
+
60
+ config_param :newline, :enum, list: [:lf, :crlf], default: DEFAULT_NEWLINE
61
+
62
+ def configure(conf)
63
+ super
64
+ @newline = case newline
65
+ when :lf
66
+ "\n".freeze
67
+ when :crlf
68
+ "\r\n".freeze
69
+ end
70
+ end
71
+ end
72
+ end
49
73
  end
50
74
  end
@@ -27,7 +27,7 @@ module Fluent
27
27
  helpers :record_accessor
28
28
 
29
29
  config_param :delimiter, default: ',' do |val|
30
- ['\t', 'TAB'].include?(val) ? "\t" : val
30
+ ['\t', 'TAB'].include?(val) ? "\t".freeze : val.freeze
31
31
  end
32
32
  config_param :force_quotes, :bool, default: true
33
33
  # "array" looks good for type of :fields, but this implementation removes tailing comma
@@ -19,13 +19,15 @@ require 'fluent/plugin/formatter'
19
19
  module Fluent
20
20
  module Plugin
21
21
  class HashFormatter < Formatter
22
+ include Fluent::Plugin::Newline::Mixin
23
+
22
24
  Plugin.register_formatter('hash', self)
23
25
 
24
26
  config_param :add_newline, :bool, default: true
25
27
 
26
28
  def format(tag, time, record)
27
29
  line = record.to_s
28
- line << "\n".freeze if @add_newline
30
+ line << @newline if @add_newline
29
31
  line
30
32
  end
31
33
  end
@@ -20,6 +20,8 @@ require 'fluent/env'
20
20
  module Fluent
21
21
  module Plugin
22
22
  class JSONFormatter < Formatter
23
+ include Fluent::Plugin::Newline::Mixin
24
+
23
25
  Plugin.register_formatter('json', self)
24
26
 
25
27
  config_param :json_parser, :string, default: 'oj'
@@ -44,7 +46,7 @@ module Fluent
44
46
  end
45
47
 
46
48
  def format(tag, time, record)
47
- "#{@dump_proc.call(record)}\n"
49
+ "#{@dump_proc.call(record)}#{@newline}"
48
50
  end
49
51
 
50
52
  def format_without_nl(tag, time, record)
@@ -19,12 +19,14 @@ require 'fluent/plugin/formatter'
19
19
  module Fluent
20
20
  module Plugin
21
21
  class LabeledTSVFormatter < Formatter
22
+ include Fluent::Plugin::Newline::Mixin
23
+
22
24
  Plugin.register_formatter('ltsv', self)
23
25
 
24
26
  # http://ltsv.org/
25
27
 
26
- config_param :delimiter, :string, default: "\t"
27
- config_param :label_delimiter, :string, default: ":"
28
+ config_param :delimiter, :string, default: "\t".freeze
29
+ config_param :label_delimiter, :string, default: ":".freeze
28
30
  config_param :add_newline, :bool, default: true
29
31
 
30
32
  # TODO: escaping for \t in values
@@ -34,7 +36,7 @@ module Fluent
34
36
  formatted << @delimiter if formatted.length.nonzero?
35
37
  formatted << "#{label}#{@label_delimiter}#{value}"
36
38
  end
37
- formatted << "\n".freeze if @add_newline
39
+ formatted << @newline if @add_newline
38
40
  formatted
39
41
  end
40
42
  end
@@ -21,15 +21,17 @@ require 'yajl'
21
21
  module Fluent
22
22
  module Plugin
23
23
  class OutFileFormatter < Formatter
24
+ include Fluent::Plugin::Newline::Mixin
25
+
24
26
  Plugin.register_formatter('out_file', self)
25
27
 
26
28
  config_param :output_time, :bool, default: true
27
29
  config_param :output_tag, :bool, default: true
28
30
  config_param :delimiter, default: "\t" do |val|
29
31
  case val
30
- when /SPACE/i then ' '
31
- when /COMMA/i then ','
32
- else "\t"
32
+ when /SPACE/i then ' '.freeze
33
+ when /COMMA/i then ','.freeze
34
+ else "\t".freeze
33
35
  end
34
36
  end
35
37
  config_set_default :time_type, :string
@@ -44,7 +46,7 @@ module Fluent
44
46
  header = ''
45
47
  header << "#{@timef.format(time)}#{@delimiter}" if @output_time
46
48
  header << "#{tag}#{@delimiter}" if @output_tag
47
- "#{header}#{Yajl.dump(record)}\n"
49
+ "#{header}#{Yajl.dump(record)}#{@newline}"
48
50
  end
49
51
  end
50
52
  end
@@ -19,14 +19,16 @@ require 'fluent/plugin/formatter'
19
19
  module Fluent
20
20
  module Plugin
21
21
  class SingleValueFormatter < Formatter
22
+ include Fluent::Plugin::Newline::Mixin
23
+
22
24
  Plugin.register_formatter('single_value', self)
23
25
 
24
- config_param :message_key, :string, default: 'message'
26
+ config_param :message_key, :string, default: 'message'.freeze
25
27
  config_param :add_newline, :bool, default: true
26
28
 
27
29
  def format(tag, time, record)
28
30
  text = record[@message_key].to_s.dup
29
- text << "\n" if @add_newline
31
+ text << @newline if @add_newline
30
32
  text
31
33
  end
32
34
  end
@@ -19,18 +19,20 @@ require 'fluent/plugin/formatter'
19
19
  module Fluent
20
20
  module Plugin
21
21
  class TSVFormatter < Formatter
22
+ include Fluent::Plugin::Newline::Mixin
23
+
22
24
  Plugin.register_formatter('tsv', self)
23
25
 
24
26
  desc 'Field names included in each lines'
25
27
  config_param :keys, :array, value_type: :string
26
28
  desc 'The delimiter character (or string) of TSV values'
27
- config_param :delimiter, :string, default: "\t"
29
+ config_param :delimiter, :string, default: "\t".freeze
28
30
  desc 'The parameter to enable writing to new lines'
29
31
  config_param :add_newline, :bool, default: true
30
32
 
31
33
  def format(tag, time, record)
32
34
  formatted = @keys.map{|k| record[k].to_s }.join(@delimiter)
33
- formatted << "\n".freeze if @add_newline
35
+ formatted << @newline if @add_newline
34
36
  formatted
35
37
  end
36
38
  end
@@ -80,6 +80,8 @@ module Fluent::Plugin
80
80
  config_param :use_204_response, :bool, default: false
81
81
  desc 'Dump error log or not'
82
82
  config_param :dump_error_log, :bool, default: true
83
+ desc 'Add QUERY_ prefix query params to record'
84
+ config_param :add_query_params, :bool, default: false
83
85
 
84
86
  config_section :parse do
85
87
  config_set_default :@type, 'in_http'
@@ -277,7 +279,7 @@ module Fluent::Plugin
277
279
  private
278
280
 
279
281
  def on_server_connect(conn)
280
- handler = Handler.new(conn, @km, method(:on_request), @body_size_limit, @format_name, log, @cors_allow_origins)
282
+ handler = Handler.new(conn, @km, method(:on_request), @body_size_limit, @format_name, log, @cors_allow_origins, @add_query_params)
281
283
 
282
284
  conn.on(:data) do |data|
283
285
  handler.on_read(data)
@@ -326,6 +328,14 @@ module Fluent::Plugin
326
328
  }
327
329
  end
328
330
 
331
+ if @add_query_params
332
+ params.each_pair { |k, v|
333
+ if k.start_with?("QUERY_".freeze)
334
+ record[k] = v
335
+ end
336
+ }
337
+ end
338
+
329
339
  if @add_remote_addr
330
340
  record['REMOTE_ADDR'] = params['REMOTE_ADDR']
331
341
  end
@@ -346,7 +356,7 @@ module Fluent::Plugin
346
356
  class Handler
347
357
  attr_reader :content_type
348
358
 
349
- def initialize(io, km, callback, body_size_limit, format_name, log, cors_allow_origins)
359
+ def initialize(io, km, callback, body_size_limit, format_name, log, cors_allow_origins, add_query_params)
350
360
  @io = io
351
361
  @km = km
352
362
  @callback = callback
@@ -356,6 +366,7 @@ module Fluent::Plugin
356
366
  @log = log
357
367
  @cors_allow_origins = cors_allow_origins
358
368
  @idle = 0
369
+ @add_query_params = add_query_params
359
370
  @km.add(self)
360
371
 
361
372
  @remote_port, @remote_addr = io.remote_port, io.remote_addr
@@ -533,7 +544,17 @@ module Fluent::Plugin
533
544
  end
534
545
  path_info = uri.path
535
546
 
547
+ if (@add_query_params)
548
+
549
+ query_params = WEBrick::HTTPUtils.parse_query(uri.query)
550
+
551
+ query_params.each_pair {|k,v|
552
+ params["QUERY_#{k.gsub('-','_').upcase}"] = v
553
+ }
554
+ end
555
+
536
556
  params.merge!(@env)
557
+
537
558
  @env.clear
538
559
 
539
560
  code, header, body = @callback.call(path_info, params)