fluentd 1.13.3 → 1.16.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/{bug_report.yaml → bug_report.yml} +2 -0
  3. data/.github/ISSUE_TEMPLATE/config.yml +2 -2
  4. data/.github/ISSUE_TEMPLATE/{feature_request.yaml → feature_request.yml} +1 -0
  5. data/.github/workflows/stale-actions.yml +11 -9
  6. data/.github/workflows/test.yml +32 -0
  7. data/CHANGELOG.md +490 -10
  8. data/CONTRIBUTING.md +2 -2
  9. data/MAINTAINERS.md +7 -5
  10. data/README.md +3 -23
  11. data/Rakefile +1 -1
  12. data/SECURITY.md +14 -0
  13. data/fluentd.gemspec +7 -8
  14. data/lib/fluent/command/cat.rb +13 -3
  15. data/lib/fluent/command/ctl.rb +6 -3
  16. data/lib/fluent/command/fluentd.rb +73 -65
  17. data/lib/fluent/command/plugin_config_formatter.rb +1 -1
  18. data/lib/fluent/compat/output.rb +9 -6
  19. data/lib/fluent/config/dsl.rb +1 -1
  20. data/lib/fluent/config/error.rb +12 -0
  21. data/lib/fluent/config/literal_parser.rb +2 -2
  22. data/lib/fluent/config/parser.rb +1 -1
  23. data/lib/fluent/config/v1_parser.rb +3 -3
  24. data/lib/fluent/config/yaml_parser/fluent_value.rb +47 -0
  25. data/lib/fluent/config/yaml_parser/loader.rb +108 -0
  26. data/lib/fluent/config/yaml_parser/parser.rb +166 -0
  27. data/lib/fluent/config/yaml_parser/section_builder.rb +107 -0
  28. data/lib/fluent/config/yaml_parser.rb +56 -0
  29. data/lib/fluent/config.rb +14 -1
  30. data/lib/fluent/counter/server.rb +1 -1
  31. data/lib/fluent/counter/validator.rb +3 -3
  32. data/lib/fluent/daemon.rb +2 -4
  33. data/lib/fluent/engine.rb +1 -1
  34. data/lib/fluent/env.rb +4 -0
  35. data/lib/fluent/error.rb +3 -0
  36. data/lib/fluent/event.rb +8 -4
  37. data/lib/fluent/event_router.rb +47 -2
  38. data/lib/fluent/file_wrapper.rb +137 -0
  39. data/lib/fluent/log/console_adapter.rb +66 -0
  40. data/lib/fluent/log.rb +44 -5
  41. data/lib/fluent/match.rb +1 -1
  42. data/lib/fluent/msgpack_factory.rb +6 -1
  43. data/lib/fluent/oj_options.rb +1 -2
  44. data/lib/fluent/plugin/bare_output.rb +49 -8
  45. data/lib/fluent/plugin/base.rb +26 -9
  46. data/lib/fluent/plugin/buf_file.rb +34 -5
  47. data/lib/fluent/plugin/buf_file_single.rb +32 -3
  48. data/lib/fluent/plugin/buffer/file_chunk.rb +1 -1
  49. data/lib/fluent/plugin/buffer.rb +216 -70
  50. data/lib/fluent/plugin/filter.rb +35 -1
  51. data/lib/fluent/plugin/filter_record_transformer.rb +1 -1
  52. data/lib/fluent/plugin/in_forward.rb +2 -2
  53. data/lib/fluent/plugin/in_http.rb +39 -10
  54. data/lib/fluent/plugin/in_monitor_agent.rb +4 -2
  55. data/lib/fluent/plugin/in_sample.rb +1 -1
  56. data/lib/fluent/plugin/in_syslog.rb +13 -1
  57. data/lib/fluent/plugin/in_tail/group_watch.rb +204 -0
  58. data/lib/fluent/plugin/in_tail/position_file.rb +33 -33
  59. data/lib/fluent/plugin/in_tail.rb +216 -84
  60. data/lib/fluent/plugin/in_tcp.rb +47 -2
  61. data/lib/fluent/plugin/input.rb +39 -1
  62. data/lib/fluent/plugin/metrics.rb +119 -0
  63. data/lib/fluent/plugin/metrics_local.rb +96 -0
  64. data/lib/fluent/plugin/multi_output.rb +43 -6
  65. data/lib/fluent/plugin/out_copy.rb +1 -1
  66. data/lib/fluent/plugin/out_exec_filter.rb +2 -2
  67. data/lib/fluent/plugin/out_file.rb +20 -2
  68. data/lib/fluent/plugin/out_forward/ack_handler.rb +19 -4
  69. data/lib/fluent/plugin/out_forward/socket_cache.rb +2 -0
  70. data/lib/fluent/plugin/out_forward.rb +17 -9
  71. data/lib/fluent/plugin/out_secondary_file.rb +39 -22
  72. data/lib/fluent/plugin/output.rb +167 -78
  73. data/lib/fluent/plugin/parser.rb +3 -4
  74. data/lib/fluent/plugin/parser_apache2.rb +1 -1
  75. data/lib/fluent/plugin/parser_json.rb +1 -1
  76. data/lib/fluent/plugin/parser_syslog.rb +1 -1
  77. data/lib/fluent/plugin/storage_local.rb +3 -5
  78. data/lib/fluent/plugin.rb +10 -1
  79. data/lib/fluent/plugin_helper/child_process.rb +3 -0
  80. data/lib/fluent/plugin_helper/event_emitter.rb +8 -1
  81. data/lib/fluent/plugin_helper/event_loop.rb +2 -2
  82. data/lib/fluent/plugin_helper/http_server/server.rb +2 -1
  83. data/lib/fluent/plugin_helper/metrics.rb +129 -0
  84. data/lib/fluent/plugin_helper/record_accessor.rb +1 -1
  85. data/lib/fluent/plugin_helper/retry_state.rb +14 -4
  86. data/lib/fluent/plugin_helper/server.rb +35 -6
  87. data/lib/fluent/plugin_helper/service_discovery.rb +2 -2
  88. data/lib/fluent/plugin_helper/socket.rb +13 -2
  89. data/lib/fluent/plugin_helper/thread.rb +3 -3
  90. data/lib/fluent/plugin_helper.rb +1 -0
  91. data/lib/fluent/plugin_id.rb +3 -2
  92. data/lib/fluent/registry.rb +2 -1
  93. data/lib/fluent/root_agent.rb +6 -0
  94. data/lib/fluent/rpc.rb +4 -3
  95. data/lib/fluent/supervisor.rb +283 -259
  96. data/lib/fluent/system_config.rb +13 -3
  97. data/lib/fluent/test/driver/base.rb +11 -5
  98. data/lib/fluent/test/driver/filter.rb +4 -0
  99. data/lib/fluent/test/startup_shutdown.rb +6 -8
  100. data/lib/fluent/time.rb +21 -20
  101. data/lib/fluent/version.rb +1 -1
  102. data/lib/fluent/win32api.rb +38 -0
  103. data/lib/fluent/winsvc.rb +5 -8
  104. data/templates/new_gem/test/helper.rb.erb +0 -1
  105. data/test/command/test_cat.rb +31 -2
  106. data/test/command/test_ctl.rb +1 -2
  107. data/test/command/test_fluentd.rb +209 -24
  108. data/test/command/test_plugin_config_formatter.rb +0 -1
  109. data/test/compat/test_parser.rb +6 -6
  110. data/test/config/test_system_config.rb +13 -11
  111. data/test/config/test_types.rb +1 -1
  112. data/test/log/test_console_adapter.rb +110 -0
  113. data/test/plugin/in_tail/test_io_handler.rb +26 -8
  114. data/test/plugin/in_tail/test_position_file.rb +48 -59
  115. data/test/plugin/out_forward/test_ack_handler.rb +39 -0
  116. data/test/plugin/out_forward/test_socket_cache.rb +26 -1
  117. data/test/plugin/test_bare_output.rb +14 -1
  118. data/test/plugin/test_base.rb +133 -1
  119. data/test/plugin/test_buf_file.rb +62 -23
  120. data/test/plugin/test_buf_file_single.rb +65 -0
  121. data/test/plugin/test_buffer.rb +267 -3
  122. data/test/plugin/test_buffer_chunk.rb +11 -0
  123. data/test/plugin/test_filter.rb +12 -1
  124. data/test/plugin/test_filter_parser.rb +1 -1
  125. data/test/plugin/test_filter_stdout.rb +2 -2
  126. data/test/plugin/test_in_forward.rb +9 -11
  127. data/test/plugin/test_in_http.rb +65 -3
  128. data/test/plugin/test_in_monitor_agent.rb +216 -11
  129. data/test/plugin/test_in_object_space.rb +9 -3
  130. data/test/plugin/test_in_syslog.rb +35 -0
  131. data/test/plugin/test_in_tail.rb +1393 -385
  132. data/test/plugin/test_in_tcp.rb +87 -2
  133. data/test/plugin/test_in_udp.rb +28 -0
  134. data/test/plugin/test_in_unix.rb +2 -2
  135. data/test/plugin/test_input.rb +12 -1
  136. data/test/plugin/test_metrics.rb +294 -0
  137. data/test/plugin/test_metrics_local.rb +96 -0
  138. data/test/plugin/test_multi_output.rb +25 -1
  139. data/test/plugin/test_out_exec.rb +6 -4
  140. data/test/plugin/test_out_exec_filter.rb +6 -2
  141. data/test/plugin/test_out_file.rb +34 -17
  142. data/test/plugin/test_out_forward.rb +78 -77
  143. data/test/plugin/test_out_http.rb +1 -0
  144. data/test/plugin/test_out_stdout.rb +2 -2
  145. data/test/plugin/test_output.rb +297 -12
  146. data/test/plugin/test_output_as_buffered.rb +44 -44
  147. data/test/plugin/test_output_as_buffered_compress.rb +32 -18
  148. data/test/plugin/test_output_as_buffered_retries.rb +54 -7
  149. data/test/plugin/test_output_as_buffered_secondary.rb +4 -4
  150. data/test/plugin/test_parser_regexp.rb +1 -6
  151. data/test/plugin/test_parser_syslog.rb +1 -1
  152. data/test/plugin_helper/test_cert_option.rb +1 -1
  153. data/test/plugin_helper/test_child_process.rb +38 -16
  154. data/test/plugin_helper/test_event_emitter.rb +29 -0
  155. data/test/plugin_helper/test_http_server_helper.rb +1 -1
  156. data/test/plugin_helper/test_metrics.rb +137 -0
  157. data/test/plugin_helper/test_retry_state.rb +602 -38
  158. data/test/plugin_helper/test_server.rb +78 -6
  159. data/test/plugin_helper/test_timer.rb +2 -2
  160. data/test/test_config.rb +191 -24
  161. data/test/test_event_router.rb +17 -0
  162. data/test/test_file_wrapper.rb +53 -0
  163. data/test/test_formatter.rb +24 -21
  164. data/test/test_log.rb +122 -40
  165. data/test/test_msgpack_factory.rb +32 -0
  166. data/test/test_plugin_classes.rb +102 -0
  167. data/test/test_root_agent.rb +30 -1
  168. data/test/test_supervisor.rb +477 -257
  169. data/test/test_time_parser.rb +22 -0
  170. metadata +55 -34
  171. data/.drone.yml +0 -35
  172. data/.github/workflows/issue-auto-closer.yml +0 -12
  173. data/.github/workflows/linux-test.yaml +0 -36
  174. data/.github/workflows/macos-test.yaml +0 -30
  175. data/.github/workflows/windows-test.yaml +0 -46
  176. data/.gitlab-ci.yml +0 -103
  177. data/lib/fluent/plugin/file_wrapper.rb +0 -187
  178. data/test/plugin/test_file_wrapper.rb +0 -126
  179. data/test/test_logger_initializer.rb +0 -46
@@ -16,6 +16,7 @@
16
16
 
17
17
  require 'fileutils'
18
18
  require 'open3'
19
+ require 'pathname'
19
20
 
20
21
  require 'fluent/config'
21
22
  require 'fluent/counter'
@@ -31,12 +32,6 @@ require 'fluent/variable_store'
31
32
  require 'serverengine'
32
33
 
33
34
  if Fluent.windows?
34
- require 'windows/library'
35
- require 'windows/synchronize'
36
- require 'windows/system_info'
37
- include Windows::Library
38
- include Windows::Synchronize
39
- include Windows::SystemInfo
40
35
  require 'win32/ipc'
41
36
  require 'win32/event'
42
37
  end
@@ -49,6 +44,9 @@ module Fluent
49
44
  @rpc_server = nil
50
45
  @counter = nil
51
46
 
47
+ @fluentd_lock_dir = Dir.mktmpdir("fluentd-lock-")
48
+ ENV['FLUENTD_LOCK_DIR'] = @fluentd_lock_dir
49
+
52
50
  if config[:rpc_endpoint]
53
51
  @rpc_endpoint = config[:rpc_endpoint]
54
52
  @enable_get_dump = config[:enable_get_dump]
@@ -68,9 +66,8 @@ module Fluent
68
66
  if config[:disable_shared_socket]
69
67
  $log.info "shared socket for multiple workers is disabled"
70
68
  else
71
- socket_manager_path = ServerEngine::SocketManager::Server.generate_path
72
- ServerEngine::SocketManager::Server.open(socket_manager_path)
73
- ENV['SERVERENGINE_SOCKETMANAGER_PATH'] = socket_manager_path.to_s
69
+ server = ServerEngine::SocketManager::Server.open
70
+ ENV['SERVERENGINE_SOCKETMANAGER_PATH'] = server.path.to_s
74
71
  end
75
72
  end
76
73
 
@@ -78,21 +75,27 @@ module Fluent
78
75
  stop_windows_event_thread if Fluent.windows?
79
76
  stop_rpc_server if @rpc_endpoint
80
77
  stop_counter_server if @counter
78
+ cleanup_lock_dir
81
79
  Fluent::Supervisor.cleanup_resources
82
80
  end
83
81
 
82
+ def cleanup_lock_dir
83
+ FileUtils.rm(Dir.glob(File.join(@fluentd_lock_dir, "fluentd-*.lock")))
84
+ FileUtils.rmdir(@fluentd_lock_dir)
85
+ end
86
+
84
87
  def run_rpc_server
85
88
  @rpc_server = RPC::Server.new(@rpc_endpoint, $log)
86
89
 
87
90
  # built-in RPC for signals
88
91
  @rpc_server.mount_proc('/api/processes.interruptWorkers') { |req, res|
89
92
  $log.debug "fluentd RPC got /api/processes.interruptWorkers request"
90
- Process.kill :INT, $$
93
+ Process.kill :INT, Process.pid
91
94
  nil
92
95
  }
93
96
  @rpc_server.mount_proc('/api/processes.killWorkers') { |req, res|
94
97
  $log.debug "fluentd RPC got /api/processes.killWorkers request"
95
- Process.kill :TERM, $$
98
+ Process.kill :TERM, Process.pid
96
99
  nil
97
100
  }
98
101
  @rpc_server.mount_proc('/api/processes.flushBuffersAndKillWorkers') { |req, res|
@@ -101,8 +104,8 @@ module Fluent
101
104
  supervisor_sigusr1_handler
102
105
  stop(true)
103
106
  else
104
- Process.kill :USR1, $$
105
- Process.kill :TERM, $$
107
+ Process.kill :USR1, Process.pid
108
+ Process.kill :TERM, Process.pid
106
109
  end
107
110
  nil
108
111
  }
@@ -111,7 +114,7 @@ module Fluent
111
114
  if Fluent.windows?
112
115
  supervisor_sigusr1_handler
113
116
  else
114
- Process.kill :USR1, $$
117
+ Process.kill :USR1, Process.pid
115
118
  end
116
119
  nil
117
120
  }
@@ -121,7 +124,7 @@ module Fluent
121
124
  # restart worker with auto restarting by killing
122
125
  kill_worker
123
126
  else
124
- Process.kill :HUP, $$
127
+ Process.kill :HUP, Process.pid
125
128
  end
126
129
  nil
127
130
  }
@@ -137,7 +140,7 @@ module Fluent
137
140
  if Fluent.windows?
138
141
  supervisor_sigusr2_handler
139
142
  else
140
- Process.kill :USR2, $$
143
+ Process.kill :USR2, Process.pid
141
144
  end
142
145
 
143
146
  nil
@@ -209,50 +212,57 @@ module Fluent
209
212
  def install_windows_event_handler
210
213
  return unless Fluent.windows?
211
214
 
212
- @pid_signame = "fluentd_#{$$}"
215
+ @pid_signame = "fluentd_#{Process.pid}"
213
216
  @signame = config[:signame]
214
217
 
215
218
  Thread.new do
216
219
  ipc = Win32::Ipc.new(nil)
217
220
  events = [
218
- Win32::Event.new("#{@pid_signame}_STOP_EVENT_THREAD"),
219
- Win32::Event.new("#{@pid_signame}"),
220
- Win32::Event.new("#{@pid_signame}_HUP"),
221
- Win32::Event.new("#{@pid_signame}_USR1"),
222
- Win32::Event.new("#{@pid_signame}_USR2"),
221
+ {win32_event: Win32::Event.new("#{@pid_signame}_STOP_EVENT_THREAD"), action: :stop_event_thread},
222
+ {win32_event: Win32::Event.new("#{@pid_signame}"), action: :stop},
223
+ {win32_event: Win32::Event.new("#{@pid_signame}_HUP"), action: :hup},
224
+ {win32_event: Win32::Event.new("#{@pid_signame}_USR1"), action: :usr1},
225
+ {win32_event: Win32::Event.new("#{@pid_signame}_USR2"), action: :usr2},
226
+ {win32_event: Win32::Event.new("#{@pid_signame}_CONT"), action: :cont},
223
227
  ]
224
228
  if @signame
225
229
  signame_events = [
226
- Win32::Event.new("#{@signame}"),
227
- Win32::Event.new("#{@signame}_HUP"),
228
- Win32::Event.new("#{@signame}_USR1"),
229
- Win32::Event.new("#{@signame}_USR2"),
230
+ {win32_event: Win32::Event.new("#{@signame}"), action: :stop},
231
+ {win32_event: Win32::Event.new("#{@signame}_HUP"), action: :hup},
232
+ {win32_event: Win32::Event.new("#{@signame}_USR1"), action: :usr1},
233
+ {win32_event: Win32::Event.new("#{@signame}_USR2"), action: :usr2},
234
+ {win32_event: Win32::Event.new("#{@signame}_CONT"), action: :cont},
230
235
  ]
231
236
  events.concat(signame_events)
232
237
  end
233
238
  begin
234
239
  loop do
235
- idx = ipc.wait_any(events, Windows::Synchronize::INFINITE)
236
- if idx > 0 && idx <= events.length
237
- $log.debug("Got Win32 event \"#{events[idx - 1].name}\"")
240
+ infinite = 0xFFFFFFFF
241
+ ipc_idx = ipc.wait_any(events.map {|e| e[:win32_event]}, infinite)
242
+ event_idx = ipc_idx - 1
243
+
244
+ if event_idx >= 0 && event_idx < events.length
245
+ $log.debug("Got Win32 event \"#{events[event_idx][:win32_event].name}\"")
238
246
  else
239
- $log.warn("Unexpected reutrn value of Win32::Ipc#wait_any: #{idx}")
247
+ $log.warn("Unexpected return value of Win32::Ipc#wait_any: #{ipc_idx}")
240
248
  end
241
- case idx
242
- when 2, 6
249
+ case events[event_idx][:action]
250
+ when :stop
243
251
  stop(true)
244
- when 3, 7
252
+ when :hup
245
253
  supervisor_sighup_handler
246
- when 4, 8
254
+ when :usr1
247
255
  supervisor_sigusr1_handler
248
- when 5, 9
256
+ when :usr2
249
257
  supervisor_sigusr2_handler
250
- when 1
258
+ when :cont
259
+ supervisor_dump_handler_for_windows
260
+ when :stop_event_thread
251
261
  break
252
262
  end
253
263
  end
254
264
  ensure
255
- events.each { |event| event.close }
265
+ events.each { |event| event[:win32_event].close }
256
266
  end
257
267
  end
258
268
  end
@@ -302,6 +312,26 @@ module Fluent
302
312
  $log.error "Failed to reload config file: #{e}"
303
313
  end
304
314
 
315
+ def supervisor_dump_handler_for_windows
316
+ # As for UNIX-like, SIGCONT signal to each process makes the process output its dump-file,
317
+ # and it is implemented before the implementation of the function for Windows.
318
+ # It is possible to trap SIGCONT and handle it here also on UNIX-like,
319
+ # but for backward compatibility, this handler is currently for a Windows-only.
320
+ raise "[BUG] This function is for Windows ONLY." unless Fluent.windows?
321
+
322
+ Thread.new do
323
+ begin
324
+ FluentSigdump.dump_windows
325
+ rescue => e
326
+ $log.error "failed to dump: #{e}"
327
+ end
328
+ end
329
+
330
+ send_signal_to_workers(:CONT)
331
+ rescue => e
332
+ $log.error "failed to dump: #{e}"
333
+ end
334
+
305
335
  def kill_worker
306
336
  if config[:worker_pid]
307
337
  pids = config[:worker_pid].clone
@@ -324,14 +354,18 @@ module Fluent
324
354
  { conf: @fluentd_conf }
325
355
  end
326
356
 
357
+ def dump
358
+ super unless @stop
359
+ end
360
+
327
361
  private
328
362
 
329
363
  def reopen_log
330
- if (log = config[:logger_initializer])
364
+ if $log
331
365
  # Creating new thread due to mutex can't lock
332
366
  # in main thread during trap context
333
367
  Thread.new do
334
- log.reopen!
368
+ $log.reopen!
335
369
  end
336
370
  end
337
371
  end
@@ -358,6 +392,14 @@ module Fluent
358
392
  restart(true)
359
393
  when :USR2
360
394
  reload
395
+ when :CONT
396
+ dump_all_windows_workers
397
+ end
398
+ end
399
+
400
+ def dump_all_windows_workers
401
+ @monitors.each do |m|
402
+ m.send_command("DUMP\n")
361
403
  end
362
404
  end
363
405
  end
@@ -374,46 +416,14 @@ module Fluent
374
416
  def after_start
375
417
  (config[:worker_pid] ||= {})[@worker_id] = @pm.pid
376
418
  end
419
+
420
+ def dump
421
+ super unless @stop
422
+ end
377
423
  end
378
424
 
379
425
  class Supervisor
380
- def self.load_config(path, params = {})
381
- pre_loadtime = 0
382
- pre_loadtime = params['pre_loadtime'].to_i if params['pre_loadtime']
383
- pre_config_mtime = nil
384
- pre_config_mtime = params['pre_config_mtime'] if params['pre_config_mtime']
385
- config_mtime = File.mtime(path)
386
-
387
- # reuse previous config if last load time is within 5 seconds and mtime of the config file is not changed
388
- if (Time.now - Time.at(pre_loadtime) < 5) && (config_mtime == pre_config_mtime)
389
- return params['pre_conf']
390
- end
391
-
392
- log_level = params['log_level']
393
- suppress_repeated_stacktrace = params['suppress_repeated_stacktrace']
394
- ignore_repeated_log_interval = params['ignore_repeated_log_interval']
395
- ignore_same_log_interval = params['ignore_same_log_interval']
396
-
397
- log_path = params['log_path']
398
- chuser = params['chuser']
399
- chgroup = params['chgroup']
400
- log_rotate_age = params['log_rotate_age']
401
- log_rotate_size = params['log_rotate_size']
402
-
403
- log_opts = {suppress_repeated_stacktrace: suppress_repeated_stacktrace, ignore_repeated_log_interval: ignore_repeated_log_interval,
404
- ignore_same_log_interval: ignore_same_log_interval}
405
- logger_initializer = Supervisor::LoggerInitializer.new(
406
- log_path, log_level, chuser, chgroup, log_opts,
407
- log_rotate_age: log_rotate_age,
408
- log_rotate_size: log_rotate_size
409
- )
410
- # this #init sets initialized logger to $log
411
- logger_initializer.init(:supervisor, 0)
412
- logger_initializer.apply_options(format: params['log_format'], time_format: params['log_time_format'])
413
- logger = $log
414
-
415
- command_sender = Fluent.windows? ? "pipe" : "signal"
416
-
426
+ def self.serverengine_config(params = {})
417
427
  # ServerEngine's "daemonize" option is boolean, and path of pid file is brought by "pid_path"
418
428
  pid_path = params['daemonize']
419
429
  daemonize = !!params['daemonize']
@@ -429,17 +439,12 @@ module Fluent
429
439
  unrecoverable_exit_codes: [2],
430
440
  stop_immediately_at_unrecoverable_exit: true,
431
441
  root_dir: params['root_dir'],
432
- logger: logger,
433
- log: logger.out,
434
- log_path: log_path,
435
- log_level: log_level,
436
- logger_initializer: logger_initializer,
437
- chuser: chuser,
438
- chgroup: chgroup,
439
- chumask: 0,
440
- suppress_repeated_stacktrace: suppress_repeated_stacktrace,
441
- ignore_repeated_log_interval: ignore_repeated_log_interval,
442
- ignore_same_log_interval: ignore_same_log_interval,
442
+ logger: $log,
443
+ log: $log.out,
444
+ log_level: params['log_level'],
445
+ chuser: params['chuser'],
446
+ chgroup: params['chgroup'],
447
+ chumask: params['chumask'],
443
448
  daemonize: daemonize,
444
449
  rpc_endpoint: params['rpc_endpoint'],
445
450
  counter_server: params['counter_server'],
@@ -448,112 +453,22 @@ module Fluent
448
453
  File.join(File.dirname(__FILE__), 'daemon.rb'),
449
454
  ServerModule.name,
450
455
  WorkerModule.name,
451
- path,
452
456
  JSON.dump(params)],
453
- command_sender: command_sender,
457
+ command_sender: Fluent.windows? ? "pipe" : "signal",
458
+ config_path: params['fluentd_conf_path'],
454
459
  fluentd_conf: params['fluentd_conf'],
455
460
  conf_encoding: params['conf_encoding'],
456
461
  inline_config: params['inline_config'],
457
- config_path: path,
458
462
  main_cmd: params['main_cmd'],
459
463
  signame: params['signame'],
460
- disable_shared_socket: params['disable_shared_socket']
464
+ disable_shared_socket: params['disable_shared_socket'],
465
+ restart_worker_interval: params['restart_worker_interval'],
461
466
  }
462
- if daemonize
463
- se_config[:pid_path] = pid_path
464
- end
465
- pre_params = params.dup
466
- params['pre_loadtime'] = Time.now.to_i
467
- params['pre_config_mtime'] = config_mtime
468
- params['pre_conf'] = se_config
469
- # prevent pre_conf from being too big by reloading many times.
470
- pre_params['pre_conf'] = nil
471
- params['pre_conf'][:windows_daemon_cmdline][5] = JSON.dump(pre_params)
467
+ se_config[:pid_path] = pid_path if daemonize
472
468
 
473
469
  se_config
474
470
  end
475
471
 
476
- class LoggerInitializer
477
- def initialize(path, level, chuser, chgroup, opts, log_rotate_age: nil, log_rotate_size: nil)
478
- @path = path
479
- @level = level
480
- @chuser = chuser
481
- @chgroup = chgroup
482
- @opts = opts
483
- @log_rotate_age = log_rotate_age
484
- @log_rotate_size = log_rotate_size
485
- end
486
-
487
- def worker_id_suffixed_path(worker_id, path)
488
- require 'pathname'
489
-
490
- Pathname(path).sub_ext("-#{worker_id}#{Pathname(path).extname}").to_s
491
- end
492
-
493
- def init(process_type, worker_id)
494
- @opts[:process_type] = process_type
495
- @opts[:worker_id] = worker_id
496
-
497
- if @path && @path != "-"
498
- unless File.exist?(@path)
499
- FileUtils.mkdir_p(File.dirname(@path))
500
- end
501
-
502
- @logdev = if @log_rotate_age || @log_rotate_size
503
- Fluent::LogDeviceIO.new(Fluent.windows? ?
504
- worker_id_suffixed_path(worker_id, @path) : @path,
505
- shift_age: @log_rotate_age, shift_size: @log_rotate_size)
506
- else
507
- File.open(@path, "a")
508
- end
509
- if @chuser || @chgroup
510
- chuid = @chuser ? ServerEngine::Privilege.get_etc_passwd(@chuser).uid : nil
511
- chgid = @chgroup ? ServerEngine::Privilege.get_etc_group(@chgroup).gid : nil
512
- File.chown(chuid, chgid, @path)
513
- end
514
- else
515
- @logdev = STDOUT
516
- end
517
-
518
- dl_opts = {}
519
- # subtract 1 to match serverengine daemon logger side logging severity.
520
- dl_opts[:log_level] = @level - 1
521
- dl_opts[:log_rotate_age] = @log_rotate_age if @log_rotate_age
522
- dl_opts[:log_rotate_size] = @log_rotate_size if @log_rotate_size
523
- logger = ServerEngine::DaemonLogger.new(@logdev, dl_opts)
524
- $log = Fluent::Log.new(logger, @opts)
525
- $log.enable_color(false) if @path
526
- $log.enable_debug if @level <= Fluent::Log::LEVEL_DEBUG
527
- end
528
-
529
- def stdout?
530
- @logdev == STDOUT
531
- end
532
-
533
- def reopen!
534
- if @path && @path != "-"
535
- @logdev.reopen(@path, "a")
536
- end
537
- self
538
- end
539
-
540
- def apply_options(format: nil, time_format: nil, log_dir_perm: nil, ignore_repeated_log_interval: nil, ignore_same_log_interval: nil)
541
- $log.format = format if format
542
- $log.time_format = time_format if time_format
543
- $log.ignore_repeated_log_interval = ignore_repeated_log_interval if ignore_repeated_log_interval
544
- $log.ignore_same_log_interval = ignore_same_log_interval if ignore_same_log_interval
545
-
546
- if @path && log_dir_perm
547
- File.chmod(log_dir_perm || 0755, File.dirname(@path))
548
- end
549
- end
550
-
551
- def level=(level)
552
- @level = level
553
- $log.level = level
554
- end
555
- end
556
-
557
472
  def self.default_options
558
473
  {
559
474
  config_path: Fluent::DEFAULT_CONFIG_PATH,
@@ -565,18 +480,22 @@ module Fluent
565
480
  setup_path: nil,
566
481
  chuser: nil,
567
482
  chgroup: nil,
483
+ chumask: "0",
568
484
  root_dir: nil,
569
485
  suppress_interval: 0,
570
486
  suppress_repeated_stacktrace: true,
571
487
  ignore_repeated_log_interval: nil,
572
488
  without_source: nil,
489
+ enable_input_metrics: nil,
490
+ enable_size_metrics: nil,
573
491
  use_v1_config: true,
574
492
  strict_config_value: nil,
575
493
  supervise: true,
576
494
  standalone_worker: false,
577
495
  signame: nil,
578
496
  conf_encoding: 'utf-8',
579
- disable_shared_socket: nil
497
+ disable_shared_socket: nil,
498
+ config_file_type: :guess,
580
499
  }
581
500
  end
582
501
 
@@ -588,7 +507,11 @@ module Fluent
588
507
  end
589
508
  end
590
509
 
591
- def initialize(opt)
510
+ def initialize(cl_opt)
511
+ @cl_opt = cl_opt
512
+ opt = self.class.default_options.merge(cl_opt)
513
+
514
+ @config_file_type = opt[:config_file_type]
592
515
  @daemonize = opt[:daemonize]
593
516
  @standalone_worker= opt[:standalone_worker]
594
517
  @config_path = opt[:config_path]
@@ -601,34 +524,19 @@ module Fluent
601
524
  @plugin_dirs = opt[:plugin_dirs]
602
525
  @chgroup = opt[:chgroup]
603
526
  @chuser = opt[:chuser]
527
+ @chumask = opt[:chumask]
528
+ @signame = opt[:signame]
604
529
 
530
+ # TODO: `@log_rotate_age` and `@log_rotate_size` should be removed
531
+ # since it should be merged with SystemConfig in `build_system_config()`.
532
+ # We should always use `system_config.log.rotate_age` and `system_config.log.rotate_size`.
533
+ # However, currently, there is a bug that `system_config.log` parameters
534
+ # are not in `Fluent::SystemConfig::SYSTEM_CONFIG_PARAMETERS`, and these
535
+ # parameters are not merged in `build_system_config()`.
536
+ # Until we fix the bug of `Fluent::SystemConfig`, we need to use these instance variables.
605
537
  @log_rotate_age = opt[:log_rotate_age]
606
538
  @log_rotate_size = opt[:log_rotate_size]
607
- @signame = opt[:signame]
608
539
 
609
- @cl_opt = opt
610
- @conf = nil
611
- # parse configuration immediately to initialize logger in early stage
612
- if @config_path and File.exist?(@config_path)
613
- @conf = Fluent::Config.build(config_path: @config_path,
614
- encoding: @conf_encoding ? @conf_encoding : 'utf-8',
615
- additional_config: @inline_config ? @inline_config : nil,
616
- use_v1_config: !!@use_v1_config)
617
- @system_config = build_system_config(@conf)
618
- if @system_config.log
619
- @log_rotate_age ||= @system_config.log.rotate_age
620
- @log_rotate_size ||= @system_config.log.rotate_size
621
- end
622
- @conf = nil
623
- end
624
-
625
- log_opts = {suppress_repeated_stacktrace: opt[:suppress_repeated_stacktrace], ignore_repeated_log_interval: opt[:ignore_repeated_log_interval],
626
- ignore_same_log_interval: opt[:ignore_same_log_interval]}
627
- @log = LoggerInitializer.new(
628
- @log_path, opt[:log_level], @chuser, @chgroup, log_opts,
629
- log_rotate_age: @log_rotate_age,
630
- log_rotate_size: @log_rotate_size
631
- )
632
540
  @finished = false
633
541
  end
634
542
 
@@ -649,7 +557,7 @@ module Fluent
649
557
  end
650
558
  else
651
559
  begin
652
- FileUtils.mkdir_p(root_dir, mode: @system_config.dir_permission || 0755)
560
+ FileUtils.mkdir_p(root_dir, mode: @system_config.dir_permission || Fluent::DEFAULT_DIR_PERMISSION)
653
561
  rescue => e
654
562
  raise Fluent::InvalidRootDirectory, "failed to create root directory:#{root_dir}, #{e.inspect}"
655
563
  end
@@ -686,12 +594,6 @@ module Fluent
686
594
  end
687
595
 
688
596
  def run_worker
689
- begin
690
- require 'sigdump/setup'
691
- rescue Exception
692
- # ignore LoadError and others (related with signals): it may raise these errors in Windows
693
- end
694
-
695
597
  Process.setproctitle("worker:#{@system_config.process_name}") if @process_name
696
598
 
697
599
  if @standalone_worker && @system_config.workers != 1
@@ -707,7 +609,7 @@ module Fluent
707
609
  create_socket_manager if @standalone_worker
708
610
  if @standalone_worker
709
611
  ServerEngine::Privilege.change(@chuser, @chgroup)
710
- File.umask(0)
612
+ File.umask(@chumask.to_i(8))
711
613
  end
712
614
  MessagePackFactory.init(enable_time_support: @system_config.enable_msgpack_time_support)
713
615
  Fluent::Engine.init(@system_config)
@@ -719,17 +621,7 @@ module Fluent
719
621
  end
720
622
 
721
623
  def configure(supervisor: false)
722
- if supervisor
723
- @log.init(:supervisor, 0)
724
- else
725
- worker_id = ENV['SERVERENGINE_WORKER_ID'].to_i
726
- process_type = case
727
- when @standalone_worker then :standalone
728
- when worker_id == 0 then :worker0
729
- else :workers
730
- end
731
- @log.init(process_type, worker_id)
732
- end
624
+ setup_global_logger(supervisor: supervisor)
733
625
 
734
626
  if @show_plugin_config
735
627
  show_plugin_config
@@ -739,17 +631,14 @@ module Fluent
739
631
  $log.warn('the value "-" for `inline_config` is deprecated. See https://github.com/fluent/fluentd/issues/2711')
740
632
  @inline_config = STDIN.read
741
633
  end
742
- @conf = Fluent::Config.build(config_path: @config_path, encoding: @conf_encoding, additional_config: @inline_config, use_v1_config: @use_v1_config)
743
- @system_config = build_system_config(@conf)
744
-
745
- @log.level = @system_config.log_level
746
- @log.apply_options(
747
- format: @system_config.log.format,
748
- time_format: @system_config.log.time_format,
749
- log_dir_perm: @system_config.dir_permission,
750
- ignore_repeated_log_interval: @system_config.ignore_repeated_log_interval,
751
- ignore_same_log_interval: @system_config.ignore_same_log_interval
634
+ @conf = Fluent::Config.build(
635
+ config_path: @config_path,
636
+ encoding: @conf_encoding,
637
+ additional_config: @inline_config,
638
+ use_v1_config: @use_v1_config,
639
+ type: @config_file_type,
752
640
  )
641
+ @system_config = build_system_config(@conf)
753
642
 
754
643
  $log.info :supervisor, 'parsing config file is succeeded', path: @config_path
755
644
 
@@ -774,10 +663,93 @@ module Fluent
774
663
 
775
664
  private
776
665
 
666
+ def setup_global_logger(supervisor: false)
667
+ if supervisor
668
+ worker_id = 0
669
+ process_type = :supervisor
670
+ else
671
+ worker_id = ENV['SERVERENGINE_WORKER_ID'].to_i
672
+ process_type = case
673
+ when @standalone_worker then :standalone
674
+ when worker_id == 0 then :worker0
675
+ else :workers
676
+ end
677
+ end
678
+
679
+ # Parse configuration immediately to initialize logger in early stage.
680
+ # Since we can't confirm the log messages in this parsing process,
681
+ # we must parse the config again after initializing logger.
682
+ conf = Fluent::Config.build(
683
+ config_path: @config_path,
684
+ encoding: @conf_encoding,
685
+ additional_config: @inline_config,
686
+ use_v1_config: @use_v1_config,
687
+ type: @config_file_type,
688
+ )
689
+ system_config = build_system_config(conf)
690
+
691
+ # TODO: we should remove this logic. This merging process should be done
692
+ # in `build_system_config()`.
693
+ @log_rotate_age ||= system_config.log.rotate_age
694
+ @log_rotate_size ||= system_config.log.rotate_size
695
+
696
+ rotate = @log_rotate_age || @log_rotate_size
697
+ actual_log_path = @log_path
698
+
699
+ # We need to prepare a unique path for each worker since Windows locks files.
700
+ if Fluent.windows? && rotate && @log_path && @log_path != "-"
701
+ actual_log_path = Fluent::Log.per_process_path(@log_path, process_type, worker_id)
702
+ end
703
+
704
+ if actual_log_path && actual_log_path != "-"
705
+ FileUtils.mkdir_p(File.dirname(actual_log_path)) unless File.exist?(actual_log_path)
706
+ if rotate
707
+ logdev = Fluent::LogDeviceIO.new(
708
+ actual_log_path,
709
+ shift_age: @log_rotate_age,
710
+ shift_size: @log_rotate_size,
711
+ )
712
+ else
713
+ logdev = File.open(actual_log_path, "a")
714
+ end
715
+
716
+ if @chuser || @chgroup
717
+ chuid = @chuser ? ServerEngine::Privilege.get_etc_passwd(@chuser).uid : nil
718
+ chgid = @chgroup ? ServerEngine::Privilege.get_etc_group(@chgroup).gid : nil
719
+ File.chown(chuid, chgid, actual_log_path)
720
+ end
721
+
722
+ if system_config.dir_permission
723
+ File.chmod(system_config.dir_permission || Fluent::DEFAULT_DIR_PERMISSION, File.dirname(actual_log_path))
724
+ end
725
+ else
726
+ logdev = STDOUT
727
+ end
728
+
729
+ $log = Fluent::Log.new(
730
+ # log_level: subtract 1 to match serverengine daemon logger side logging severity.
731
+ ServerEngine::DaemonLogger.new(logdev, log_level: system_config.log_level - 1),
732
+ path: actual_log_path,
733
+ process_type: process_type,
734
+ worker_id: worker_id,
735
+ format: system_config.log.format,
736
+ time_format: system_config.log.time_format,
737
+ suppress_repeated_stacktrace: system_config.suppress_repeated_stacktrace,
738
+ ignore_repeated_log_interval: system_config.ignore_repeated_log_interval,
739
+ ignore_same_log_interval: system_config.ignore_same_log_interval,
740
+ )
741
+ $log.enable_color(false) if actual_log_path
742
+ $log.enable_debug if system_config.log_level <= Fluent::Log::LEVEL_DEBUG
743
+
744
+ $log.info "init #{process_type} logger",
745
+ path: actual_log_path,
746
+ rotate_age: @log_rotate_age,
747
+ rotate_size: @log_rotate_size
748
+ end
749
+
777
750
  def create_socket_manager
778
- socket_manager_path = ServerEngine::SocketManager::Server.generate_path
779
- ServerEngine::SocketManager::Server.open(socket_manager_path)
780
- ENV['SERVERENGINE_SOCKETMANAGER_PATH'] = socket_manager_path.to_s
751
+ server = ServerEngine::SocketManager::Server.open
752
+ ENV['SERVERENGINE_SOCKETMANAGER_PATH'] = server.path.to_s
781
753
  end
782
754
 
783
755
  def show_plugin_config
@@ -797,32 +769,30 @@ module Fluent
797
769
  'main_cmd' => fluentd_spawn_cmd,
798
770
  'daemonize' => @daemonize,
799
771
  'inline_config' => @inline_config,
800
- 'log_path' => @log_path,
801
- 'log_rotate_age' => @log_rotate_age,
802
- 'log_rotate_size' => @log_rotate_size,
803
772
  'chuser' => @chuser,
804
773
  'chgroup' => @chgroup,
774
+ 'fluentd_conf_path' => @config_path,
775
+ 'fluentd_conf' => @conf.to_s,
805
776
  'use_v1_config' => @use_v1_config,
806
777
  'conf_encoding' => @conf_encoding,
807
778
  'signame' => @signame,
808
- 'fluentd_conf' => @conf.to_s,
809
779
 
810
780
  'workers' => @system_config.workers,
811
781
  'root_dir' => @system_config.root_dir,
812
782
  'log_level' => @system_config.log_level,
813
- 'suppress_repeated_stacktrace' => @system_config.suppress_repeated_stacktrace,
814
- 'ignore_repeated_log_interval' => @system_config.ignore_repeated_log_interval,
815
783
  'rpc_endpoint' => @system_config.rpc_endpoint,
816
784
  'enable_get_dump' => @system_config.enable_get_dump,
817
785
  'counter_server' => @system_config.counter_server,
818
- 'log_format' => @system_config.log.format,
819
- 'log_time_format' => @system_config.log.time_format,
820
- 'disable_shared_socket' => @system_config.disable_shared_socket
786
+ 'disable_shared_socket' => @system_config.disable_shared_socket,
787
+ 'restart_worker_interval' => @system_config.restart_worker_interval,
821
788
  }
822
789
 
823
- se = ServerEngine.create(ServerModule, WorkerModule){
824
- Fluent::Supervisor.load_config(@config_path, params)
790
+ se = ServerEngine.create(ServerModule, WorkerModule) {
791
+ # Note: This is called only at the initialization of ServerEngine, since
792
+ # Fluentd overwrites all related SIGNAL(HUP,USR1,USR2) and have own reloading feature.
793
+ Fluent::Supervisor.serverengine_config(params)
825
794
  }
795
+
826
796
  se.run
827
797
  end
828
798
 
@@ -864,6 +834,10 @@ module Fluent
864
834
  trap :USR2 do
865
835
  reload_config
866
836
  end
837
+
838
+ trap :CONT do
839
+ dump_non_windows
840
+ end
867
841
  end
868
842
  end
869
843
 
@@ -891,6 +865,9 @@ module Fluent
891
865
  when "RELOAD"
892
866
  $log.debug "fluentd main process get #{cmd} command"
893
867
  reload_config
868
+ when "DUMP"
869
+ $log.debug "fluentd main process get #{cmd} command"
870
+ dump_windows
894
871
  else
895
872
  $log.warn "fluentd main process get unknown command [#{cmd}]"
896
873
  end
@@ -905,7 +882,7 @@ module Fluent
905
882
  begin
906
883
  $log.debug "fluentd main process get SIGUSR1"
907
884
  $log.info "force flushing buffered events"
908
- @log.reopen!
885
+ $log.reopen!
909
886
  Fluent::Engine.flush!
910
887
  $log.debug "flushing thread: flushed"
911
888
  rescue Exception => e
@@ -924,6 +901,7 @@ module Fluent
924
901
  encoding: @conf_encoding,
925
902
  additional_config: @inline_config,
926
903
  use_v1_config: @use_v1_config,
904
+ type: @config_file_type,
927
905
  )
928
906
 
929
907
  Fluent::VariableStore.try_to_reset do
@@ -940,9 +918,27 @@ module Fluent
940
918
  end
941
919
  end
942
920
 
921
+ def dump_non_windows
922
+ begin
923
+ Sigdump.dump unless @finished
924
+ rescue => e
925
+ $log.error("failed to dump: #{e}")
926
+ end
927
+ end
928
+
929
+ def dump_windows
930
+ Thread.new do
931
+ begin
932
+ FluentSigdump.dump_windows
933
+ rescue => e
934
+ $log.error("failed to dump: #{e}")
935
+ end
936
+ end
937
+ end
938
+
943
939
  def logging_with_console_output
944
940
  yield $log
945
- unless @log.stdout?
941
+ unless $log.stdout?
946
942
  logger = ServerEngine::DaemonLogger.new(STDOUT)
947
943
  log = Fluent::Log.new(logger)
948
944
  log.level = @system_config.log_level
@@ -998,15 +994,15 @@ module Fluent
998
994
 
999
995
  def build_system_config(conf)
1000
996
  system_config = SystemConfig.create(conf, @cl_opt[:strict_config_value])
997
+ # Prefer the options explicitly specified in the command line
998
+ #
999
+ # TODO: There is a bug that `system_config.log.rotate_age/rotate_size` are
1000
+ # not merged with the command line options since they are not in
1001
+ # `SYSTEM_CONFIG_PARAMETERS`.
1002
+ # We have to fix this bug.
1001
1003
  opt = {}
1002
1004
  Fluent::SystemConfig::SYSTEM_CONFIG_PARAMETERS.each do |param|
1003
1005
  if @cl_opt.key?(param) && !@cl_opt[param].nil?
1004
- if param == :log_level && @cl_opt[:log_level] == Fluent::Log::LEVEL_INFO
1005
- # info level can't be specified via command line option.
1006
- # log_level is info here, it is default value and <system>'s log_level should be applied if exists.
1007
- next
1008
- end
1009
-
1010
1006
  opt[param] = @cl_opt[param]
1011
1007
  end
1012
1008
  end
@@ -1034,6 +1030,11 @@ module Fluent
1034
1030
  fluentd_spawn_cmd << '-Eascii-8bit:ascii-8bit'
1035
1031
  end
1036
1032
 
1033
+ if @system_config.enable_jit
1034
+ $log.info "enable Ruby JIT for workers (--jit)"
1035
+ fluentd_spawn_cmd << '--jit'
1036
+ end
1037
+
1037
1038
  # Adding `-h` so that it can avoid ruby's command blocking
1038
1039
  # e.g. `ruby -Eascii-8bit:ascii-8bit` will block. but `ruby -Eascii-8bit:ascii-8bit -h` won't.
1039
1040
  _, e, s = Open3.capture3(*fluentd_spawn_cmd, "-h")
@@ -1049,4 +1050,27 @@ module Fluent
1049
1050
  fluentd_spawn_cmd
1050
1051
  end
1051
1052
  end
1053
+
1054
+ module FluentSigdump
1055
+ def self.dump_windows
1056
+ raise "[BUG] WindowsSigdump::dump is for Windows ONLY." unless Fluent.windows?
1057
+
1058
+ # Sigdump outputs under `/tmp` dir without `SIGDUMP_PATH` specified,
1059
+ # but `/tmp` dir may not exist on Windows by default.
1060
+ # So use the systemroot-temp-dir instead.
1061
+ dump_filepath = ENV['SIGDUMP_PATH'].nil? || ENV['SIGDUMP_PATH'].empty? \
1062
+ ? "#{ENV['windir']}/Temp/fluentd-sigdump-#{Process.pid}.log"
1063
+ : get_path_with_pid(ENV['SIGDUMP_PATH'])
1064
+
1065
+ require 'sigdump'
1066
+ Sigdump.dump(dump_filepath)
1067
+
1068
+ $log.info "dump to #{dump_filepath}."
1069
+ end
1070
+
1071
+ def self.get_path_with_pid(raw_path)
1072
+ path = Pathname.new(raw_path)
1073
+ path.sub_ext("-#{Process.pid}#{path.extname}").to_s
1074
+ end
1075
+ end
1052
1076
  end