fluentd 1.11.5-x64-mingw32 → 1.12.0-x64-mingw32

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 (48) 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 +38 -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 +1 -0
  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 +2 -2
  20. data/lib/fluent/plugin/formatter_csv.rb +1 -1
  21. data/lib/fluent/plugin/formatter_hash.rb +1 -1
  22. data/lib/fluent/plugin/formatter_ltsv.rb +3 -3
  23. data/lib/fluent/plugin/formatter_out_file.rb +3 -3
  24. data/lib/fluent/plugin/formatter_single_value.rb +2 -2
  25. data/lib/fluent/plugin/formatter_tsv.rb +2 -2
  26. data/lib/fluent/plugin/in_http.rb +23 -2
  27. data/lib/fluent/plugin/in_tail.rb +109 -41
  28. data/lib/fluent/plugin/in_tail/position_file.rb +39 -14
  29. data/lib/fluent/plugin/in_tcp.rb +1 -0
  30. data/lib/fluent/plugin/output.rb +7 -1
  31. data/lib/fluent/plugin_helper/http_server/compat/server.rb +1 -1
  32. data/lib/fluent/plugin_helper/inject.rb +4 -2
  33. data/lib/fluent/plugin_helper/retry_state.rb +4 -0
  34. data/lib/fluent/supervisor.rb +140 -42
  35. data/lib/fluent/time.rb +1 -0
  36. data/lib/fluent/version.rb +1 -1
  37. data/lib/fluent/winsvc.rb +22 -4
  38. data/test/command/test_cap_ctl.rb +100 -0
  39. data/test/command/test_ctl.rb +57 -0
  40. data/test/command/test_plugin_config_formatter.rb +57 -2
  41. data/test/plugin/in_tail/test_position_file.rb +45 -25
  42. data/test/plugin/test_in_http.rb +25 -0
  43. data/test/plugin/test_in_tail.rb +430 -30
  44. data/test/plugin/test_parser_syslog.rb +2 -2
  45. data/test/plugin_helper/test_inject.rb +29 -0
  46. data/test/test_capability.rb +74 -0
  47. data/test/test_supervisor.rb +102 -10
  48. metadata +31 -2
@@ -22,34 +22,40 @@ module Fluent::Plugin
22
22
  UNWATCHED_POSITION = 0xffffffffffffffff
23
23
  POSITION_FILE_ENTRY_REGEX = /^([^\t]+)\t([0-9a-fA-F]+)\t([0-9a-fA-F]+)/.freeze
24
24
 
25
- def self.load(file, logger:)
26
- pf = new(file, logger: logger)
25
+ def self.load(file, follow_inodes, existing_paths, logger:)
26
+ pf = new(file, follow_inodes, existing_paths, logger: logger)
27
27
  pf.load
28
28
  pf
29
29
  end
30
30
 
31
- def initialize(file, logger: nil)
31
+ def initialize(file, follow_inodes, existing_paths, logger: nil)
32
32
  @file = file
33
33
  @logger = logger
34
34
  @file_mutex = Mutex.new
35
35
  @map = {}
36
+ @follow_inodes = follow_inodes
37
+ @existing_paths = existing_paths
36
38
  end
37
39
 
38
- def [](path)
39
- if m = @map[path]
40
+ def [](target_info)
41
+ if m = @map[@follow_inodes ? target_info.ino : target_info.path]
40
42
  return m
41
43
  end
42
44
 
43
45
  @file_mutex.synchronize {
44
46
  @file.seek(0, IO::SEEK_END)
45
- seek = @file.pos + path.bytesize + 1
46
- @file.write "#{path}\t0000000000000000\t0000000000000000\n"
47
- @map[path] = FilePositionEntry.new(@file, @file_mutex, seek, 0, 0)
47
+ seek = @file.pos + target_info.path.bytesize + 1
48
+ @file.write "#{target_info.path}\t0000000000000000\t0000000000000000\n"
49
+ if @follow_inodes
50
+ @map[target_info.ino] = FilePositionEntry.new(@file, @file_mutex, seek, 0, 0)
51
+ else
52
+ @map[target_info.path] = FilePositionEntry.new(@file, @file_mutex, seek, 0, 0)
53
+ end
48
54
  }
49
55
  end
50
56
 
51
- def unwatch(path)
52
- if (entry = @map.delete(path))
57
+ def unwatch(target_info)
58
+ if (entry = @map.delete(@follow_inodes ? target_info.ino : target_info.path))
53
59
  entry.update_pos(UNWATCHED_POSITION)
54
60
  end
55
61
  end
@@ -69,7 +75,11 @@ module Fluent::Plugin
69
75
  pos = m[2].to_i(16)
70
76
  ino = m[3].to_i(16)
71
77
  seek = @file.pos - line.bytesize + path.bytesize + 1
72
- map[path] = FilePositionEntry.new(@file, @file_mutex, seek, pos, ino)
78
+ if @follow_inodes
79
+ map[ino] = FilePositionEntry.new(@file, @file_mutex, seek, pos, ino)
80
+ else
81
+ map[path] = FilePositionEntry.new(@file, @file_mutex, seek, pos, ino)
82
+ end
73
83
  end
74
84
  end
75
85
 
@@ -94,8 +104,9 @@ module Fluent::Plugin
94
104
  @file.truncate(0)
95
105
  @file.write(entries.values.map(&:to_entry_fmt).join)
96
106
 
97
- entries.each do |path, val|
98
- if (m = @map[path])
107
+ # entry contains path/ino key and value.
108
+ entries.each do |key, val|
109
+ if (m = @map[key])
99
110
  m.seek = val.seek
100
111
  end
101
112
  end
@@ -139,13 +150,25 @@ module Fluent::Plugin
139
150
  @logger.warn("#{path} already exists. use latest one: deleted #{entries[path]}") if @logger
140
151
  end
141
152
 
142
- entries[path] = Entry.new(path, pos, ino, file_pos + path.size + 1)
153
+ if @follow_inodes
154
+ entries[ino] = Entry.new(path, pos, ino, file_pos + path.size + 1)
155
+ else
156
+ entries[path] = Entry.new(path, pos, ino, file_pos + path.size + 1)
157
+ end
143
158
  file_pos += line.size
144
159
  end
145
160
  end
146
161
 
162
+ entries = remove_deleted_files_entries(entries, @existing_paths) if @follow_inodes
147
163
  entries
148
164
  end
165
+
166
+ def remove_deleted_files_entries(existent_entries, existing_paths)
167
+ filtered_entries = existent_entries.select {|file_entry|
168
+ existing_paths.key?(file_entry)
169
+ }
170
+ filtered_entries
171
+ end
149
172
  end
150
173
 
151
174
  Entry = Struct.new(:path, :pos, :ino, :seek) do
@@ -224,5 +247,7 @@ module Fluent::Plugin
224
247
  @inode
225
248
  end
226
249
  end
250
+
251
+ TargetInfo = Struct.new(:path, :ino)
227
252
  end
228
253
  end
@@ -98,6 +98,7 @@ module Fluent::Plugin
98
98
  def start
99
99
  super
100
100
 
101
+ log.info "listening tcp socket", bind: @bind, port: @port
101
102
  del_size = @delimiter.length
102
103
  if @_extract_enabled && @_extract_tag_key
103
104
  server_create(:in_tcp_server_single_emit, @port, bind: @bind, resolve_name: !!@source_hostname_key) do |data, conn|
@@ -1252,7 +1252,13 @@ module Fluent
1252
1252
  log.debug "buffer queue cleared"
1253
1253
  @retry = nil
1254
1254
  else
1255
- @retry.step
1255
+ # Ensure that the current time is greater than or equal to @retry.next_time to avoid the situation when
1256
+ # @retry.step is called almost as many times as the number of flush threads in a short time.
1257
+ if Time.now >= @retry.next_time
1258
+ @retry.step
1259
+ else
1260
+ @retry.recalc_next_time # to prevent all flush threads from retrying at the same time
1261
+ end
1256
1262
  if error
1257
1263
  if using_secondary
1258
1264
  msg = "failed to flush the buffer with secondary output."
@@ -81,7 +81,7 @@ module Fluent
81
81
 
82
82
  def build_handler
83
83
  @methods.group_by(&:first).each do |(path, rest)|
84
- klass = Fluent::PluginHelper::HttpServer::Compat::WebrickHandler.build(Hash[rest.map { |e| [e[1], e[2]] }])
84
+ klass = Fluent::PluginHelper::HttpServer::Compat::WebrickHandler.build(**Hash[rest.map { |e| [e[1], e[2]] }])
85
85
  @server.mount(path, klass)
86
86
  end
87
87
  end
@@ -76,7 +76,7 @@ module Fluent
76
76
  config_param :time_key, :string, default: nil
77
77
 
78
78
  # To avoid defining :time_type twice
79
- config_param :time_type, :enum, list: [:float, :unixtime, :unixtime_millis, :string], default: :float
79
+ config_param :time_type, :enum, list: [:float, :unixtime, :unixtime_millis, :unixtime_micros, :unixtime_nanos, :string], default: :float
80
80
 
81
81
  Fluent::TimeMixin::TIME_PARAMETERS.each do |name, type, opts|
82
82
  config_param(name, type, **opts)
@@ -132,7 +132,9 @@ module Fluent
132
132
  if @_inject_time_key
133
133
  @_inject_time_formatter = case @inject_config.time_type
134
134
  when :float then ->(time){ time.to_r.truncate(+6).to_f } # microsecond floating point value
135
- when :unixtime_millis then ->(time) { time.to_f.floor(3) * 1000 }
135
+ when :unixtime_millis then ->(time) { time.respond_to?(:nsec) ? time.to_i * 1_000 + time.nsec / 1_000_000 : (time * 1_000).floor }
136
+ when :unixtime_micros then ->(time) { time.respond_to?(:nsec) ? time.to_i * 1_000_000 + time.nsec / 1_000 : (time * 1_000_000).floor }
137
+ when :unixtime_nanos then ->(time) { time.respond_to?(:nsec) ? time.to_i * 1_000_000_000 + time.nsec : (time * 1_000_000_000).floor }
136
138
  when :unixtime then ->(time){ time.to_i }
137
139
  else
138
140
  localtime = @inject_config.localtime && !@inject_config.utc
@@ -127,6 +127,10 @@ module Fluent
127
127
  nil
128
128
  end
129
129
 
130
+ def recalc_next_time
131
+ @next_time = calc_next_time
132
+ end
133
+
130
134
  def limit?
131
135
  if @forever
132
136
  false
@@ -53,11 +53,11 @@ module Fluent
53
53
  @enable_get_dump = config[:enable_get_dump]
54
54
  run_rpc_server
55
55
  end
56
- install_supervisor_signal_handlers
57
56
 
58
- if config[:signame]
59
- @signame = config[:signame]
57
+ if Fluent.windows?
60
58
  install_windows_event_handler
59
+ else
60
+ install_supervisor_signal_handlers
61
61
  end
62
62
 
63
63
  if counter = config[:counter_server]
@@ -70,6 +70,7 @@ module Fluent
70
70
  end
71
71
 
72
72
  def after_run
73
+ stop_windows_event_thread if Fluent.windows?
73
74
  stop_rpc_server if @rpc_endpoint
74
75
  stop_counter_server if @counter
75
76
  Fluent::Supervisor.cleanup_resources
@@ -92,7 +93,8 @@ module Fluent
92
93
  @rpc_server.mount_proc('/api/processes.flushBuffersAndKillWorkers') { |req, res|
93
94
  $log.debug "fluentd RPC got /api/processes.flushBuffersAndKillWorkers request"
94
95
  if Fluent.windows?
95
- $log.warn "operation 'flushBuffersAndKillWorkers' is not supported on Windows now."
96
+ supervisor_sigusr1_handler
97
+ stop(true)
96
98
  else
97
99
  Process.kill :USR1, $$
98
100
  Process.kill :TERM, $$
@@ -101,7 +103,9 @@ module Fluent
101
103
  }
102
104
  @rpc_server.mount_proc('/api/plugins.flushBuffers') { |req, res|
103
105
  $log.debug "fluentd RPC got /api/plugins.flushBuffers request"
104
- unless Fluent.windows?
106
+ if Fluent.windows?
107
+ supervisor_sigusr1_handler
108
+ else
105
109
  Process.kill :USR1, $$
106
110
  end
107
111
  nil
@@ -125,7 +129,9 @@ module Fluent
125
129
 
126
130
  @rpc_server.mount_proc('/api/config.gracefulReload') { |req, res|
127
131
  $log.debug "fluentd RPC got /api/config.gracefulReload request"
128
- unless Fluent.windows?
132
+ if Fluent.windows?
133
+ supervisor_sigusr2_handler
134
+ else
129
135
  Process.kill :USR2, $$
130
136
  end
131
137
 
@@ -159,37 +165,101 @@ module Fluent
159
165
  end
160
166
 
161
167
  def install_supervisor_signal_handlers
168
+ return if Fluent.windows?
169
+
162
170
  trap :HUP do
163
171
  $log.debug "fluentd supervisor process get SIGHUP"
164
172
  supervisor_sighup_handler
165
- end unless Fluent.windows?
173
+ end
166
174
 
167
175
  trap :USR1 do
168
176
  $log.debug "fluentd supervisor process get SIGUSR1"
169
177
  supervisor_sigusr1_handler
170
- end unless Fluent.windows?
178
+ end
171
179
 
172
180
  trap :USR2 do
173
181
  $log.debug 'fluentd supervisor process got SIGUSR2'
174
182
  supervisor_sigusr2_handler
175
- end unless Fluent.windows?
183
+ end
184
+ end
185
+
186
+ if Fluent.windows?
187
+ # Override some methods of ServerEngine::MultiSpawnWorker
188
+ # Since Fluentd's Supervisor doesn't use ServerEngine's HUP, USR1 and USR2
189
+ # handlers (see install_supervisor_signal_handlers), they should be
190
+ # disabled also on Windows, just send commands to workers instead.
191
+ def restart(graceful)
192
+ @monitors.each do |m|
193
+ m.send_command(graceful ? "GRACEFUL_RESTART\n" : "IMMEDIATE_RESTART\n")
194
+ end
195
+ end
196
+
197
+ def reload
198
+ @monitors.each do |m|
199
+ m.send_command("RELOAD\n")
200
+ end
201
+ end
176
202
  end
177
203
 
178
204
  def install_windows_event_handler
205
+ return unless Fluent.windows?
206
+
207
+ @pid_signame = "fluentd_#{$$}"
208
+ @signame = config[:signame]
209
+
179
210
  Thread.new do
180
- ev = Win32::Event.new(@signame)
211
+ ipc = Win32::Ipc.new(nil)
212
+ events = [
213
+ Win32::Event.new("#{@pid_signame}_STOP_EVENT_THREAD"),
214
+ Win32::Event.new("#{@pid_signame}"),
215
+ Win32::Event.new("#{@pid_signame}_HUP"),
216
+ Win32::Event.new("#{@pid_signame}_USR1"),
217
+ Win32::Event.new("#{@pid_signame}_USR2"),
218
+ ]
219
+ if @signame
220
+ signame_events = [
221
+ Win32::Event.new("#{@signame}"),
222
+ Win32::Event.new("#{@signame}_HUP"),
223
+ Win32::Event.new("#{@signame}_USR1"),
224
+ Win32::Event.new("#{@signame}_USR2"),
225
+ ]
226
+ events.concat(signame_events)
227
+ end
181
228
  begin
182
- ev.reset
183
- until WaitForSingleObject(ev.handle, 0) == WAIT_OBJECT_0
184
- sleep 1
229
+ loop do
230
+ idx = ipc.wait_any(events, Windows::Synchronize::INFINITE)
231
+ if idx > 0 && idx <= events.length
232
+ $log.debug("Got Win32 event \"#{events[idx - 1].name}\"")
233
+ else
234
+ $log.warn("Unexpected reutrn value of Win32::Ipc#wait_any: #{idx}")
235
+ end
236
+ case idx
237
+ when 2, 6
238
+ stop(true)
239
+ when 3, 7
240
+ supervisor_sighup_handler
241
+ when 4, 8
242
+ supervisor_sigusr1_handler
243
+ when 5, 9
244
+ supervisor_sigusr2_handler
245
+ when 1
246
+ break
247
+ end
185
248
  end
186
- stop(true)
187
249
  ensure
188
- ev.close
250
+ events.each { |event| event.close }
189
251
  end
190
252
  end
191
253
  end
192
254
 
255
+ def stop_windows_event_thread
256
+ if Fluent.windows?
257
+ ev = Win32::Event.open("#{@pid_signame}_STOP_EVENT_THREAD")
258
+ ev.set
259
+ ev.close
260
+ end
261
+ end
262
+
193
263
  def supervisor_sighup_handler
194
264
  kill_worker
195
265
  end
@@ -264,9 +334,25 @@ module Fluent
264
334
  def send_signal_to_workers(signal)
265
335
  return unless config[:worker_pid]
266
336
 
267
- config[:worker_pid].each_value do |pid|
268
- # don't rescue Errno::ESRCH here (invalid status)
269
- Process.kill(signal, pid)
337
+ if Fluent.windows?
338
+ send_command_to_workers(signal)
339
+ else
340
+ config[:worker_pid].each_value do |pid|
341
+ # don't rescue Errno::ESRCH here (invalid status)
342
+ Process.kill(signal, pid)
343
+ end
344
+ end
345
+ end
346
+
347
+ def send_command_to_workers(signal)
348
+ # Use SeverEngine's CommandSender on Windows
349
+ case signal
350
+ when :HUP
351
+ restart(false)
352
+ when :USR1
353
+ restart(true)
354
+ when :USR2
355
+ reload
270
356
  end
271
357
  end
272
358
  end
@@ -745,33 +831,45 @@ module Fluent
745
831
  end
746
832
  end
747
833
 
748
- trap :USR1 do
749
- flush_buffer
750
- end unless Fluent.windows?
834
+ if Fluent.windows?
835
+ install_main_process_command_handlers
836
+ else
837
+ trap :USR1 do
838
+ flush_buffer
839
+ end
751
840
 
752
- trap :USR2 do
753
- reload_config
754
- end unless Fluent.windows?
841
+ trap :USR2 do
842
+ reload_config
843
+ end
844
+ end
845
+ end
755
846
 
756
- if Fluent.windows?
757
- command_pipe = STDIN.dup
758
- STDIN.reopen(File::NULL, "rb")
759
- command_pipe.binmode
760
- command_pipe.sync = true
847
+ def install_main_process_command_handlers
848
+ command_pipe = $stdin.dup
849
+ $stdin.reopen(File::NULL, "rb")
850
+ command_pipe.binmode
851
+ command_pipe.sync = true
761
852
 
762
- Thread.new do
763
- loop do
764
- cmd = command_pipe.gets.chomp
765
- case cmd
766
- when "GRACEFUL_STOP", "IMMEDIATE_STOP"
767
- $log.debug "fluentd main process get #{cmd} command"
768
- @finished = true
769
- $log.debug "getting start to shutdown main process"
770
- Fluent::Engine.stop
771
- break
772
- else
773
- $log.warn "fluentd main process get unknown command [#{cmd}]"
774
- end
853
+ Thread.new do
854
+ loop do
855
+ cmd = command_pipe.gets
856
+ break unless cmd
857
+
858
+ case cmd.chomp!
859
+ when "GRACEFUL_STOP", "IMMEDIATE_STOP"
860
+ $log.debug "fluentd main process get #{cmd} command"
861
+ @finished = true
862
+ $log.debug "getting start to shutdown main process"
863
+ Fluent::Engine.stop
864
+ break
865
+ when "GRACEFUL_RESTART"
866
+ $log.debug "fluentd main process get #{cmd} command"
867
+ flush_buffer
868
+ when "RELOAD"
869
+ $log.debug "fluentd main process get #{cmd} command"
870
+ reload_config
871
+ else
872
+ $log.warn "fluentd main process get unknown command [#{cmd}]"
775
873
  end
776
874
  end
777
875
  end
@@ -50,6 +50,7 @@ module Fluent
50
50
  def to_int
51
51
  @sec
52
52
  end
53
+ alias :to_i :to_int
53
54
 
54
55
  def to_f
55
56
  @sec + @nsec / 1_000_000_000.0
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '1.11.5'
19
+ VERSION = '1.12.0'
20
20
 
21
21
  end