fluentd 0.12.40 → 0.14.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 (252) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +6 -0
  3. data/.gitignore +2 -0
  4. data/.travis.yml +33 -21
  5. data/CONTRIBUTING.md +1 -0
  6. data/ChangeLog +810 -237
  7. data/README.md +0 -25
  8. data/Rakefile +2 -1
  9. data/Vagrantfile +17 -0
  10. data/appveyor.yml +35 -0
  11. data/example/filter_stdout.conf +5 -5
  12. data/example/in_forward.conf +2 -2
  13. data/example/in_http.conf +2 -2
  14. data/example/in_out_forward.conf +17 -0
  15. data/example/in_syslog.conf +2 -2
  16. data/example/in_tail.conf +2 -2
  17. data/example/in_tcp.conf +2 -2
  18. data/example/in_udp.conf +2 -2
  19. data/example/out_copy.conf +4 -4
  20. data/example/out_file.conf +2 -2
  21. data/example/out_forward.conf +2 -2
  22. data/example/out_forward_buf_file.conf +23 -0
  23. data/example/v0_12_filter.conf +8 -8
  24. data/fluent.conf +29 -0
  25. data/fluentd.gemspec +18 -11
  26. data/lib/fluent/agent.rb +60 -58
  27. data/lib/fluent/command/cat.rb +1 -1
  28. data/lib/fluent/command/debug.rb +7 -5
  29. data/lib/fluent/command/fluentd.rb +97 -2
  30. data/lib/fluent/compat/call_super_mixin.rb +67 -0
  31. data/lib/fluent/compat/filter.rb +50 -0
  32. data/lib/fluent/compat/formatter.rb +109 -0
  33. data/lib/fluent/compat/input.rb +50 -0
  34. data/lib/fluent/compat/output.rb +617 -0
  35. data/lib/fluent/compat/output_chain.rb +60 -0
  36. data/lib/fluent/compat/parser.rb +163 -0
  37. data/lib/fluent/compat/propagate_default.rb +62 -0
  38. data/lib/fluent/config.rb +23 -20
  39. data/lib/fluent/config/configure_proxy.rb +119 -70
  40. data/lib/fluent/config/dsl.rb +5 -18
  41. data/lib/fluent/config/element.rb +72 -8
  42. data/lib/fluent/config/error.rb +0 -3
  43. data/lib/fluent/config/literal_parser.rb +0 -2
  44. data/lib/fluent/config/parser.rb +4 -4
  45. data/lib/fluent/config/section.rb +39 -28
  46. data/lib/fluent/config/types.rb +2 -13
  47. data/lib/fluent/config/v1_parser.rb +1 -3
  48. data/lib/fluent/configurable.rb +48 -16
  49. data/lib/fluent/daemon.rb +15 -0
  50. data/lib/fluent/engine.rb +26 -52
  51. data/lib/fluent/env.rb +6 -4
  52. data/lib/fluent/event.rb +58 -11
  53. data/lib/fluent/event_router.rb +5 -5
  54. data/lib/fluent/filter.rb +2 -50
  55. data/lib/fluent/formatter.rb +4 -293
  56. data/lib/fluent/input.rb +2 -32
  57. data/lib/fluent/label.rb +2 -2
  58. data/lib/fluent/load.rb +3 -2
  59. data/lib/fluent/log.rb +107 -38
  60. data/lib/fluent/match.rb +0 -36
  61. data/lib/fluent/mixin.rb +117 -7
  62. data/lib/fluent/msgpack_factory.rb +62 -0
  63. data/lib/fluent/output.rb +7 -612
  64. data/lib/fluent/output_chain.rb +23 -0
  65. data/lib/fluent/parser.rb +4 -800
  66. data/lib/fluent/plugin.rb +100 -121
  67. data/lib/fluent/plugin/bare_output.rb +63 -0
  68. data/lib/fluent/plugin/base.rb +121 -0
  69. data/lib/fluent/plugin/buf_file.rb +101 -182
  70. data/lib/fluent/plugin/buf_memory.rb +9 -92
  71. data/lib/fluent/plugin/buffer.rb +473 -0
  72. data/lib/fluent/plugin/buffer/chunk.rb +135 -0
  73. data/lib/fluent/plugin/buffer/file_chunk.rb +339 -0
  74. data/lib/fluent/plugin/buffer/memory_chunk.rb +100 -0
  75. data/lib/fluent/plugin/exec_util.rb +80 -75
  76. data/lib/fluent/plugin/file_util.rb +33 -28
  77. data/lib/fluent/plugin/file_wrapper.rb +120 -0
  78. data/lib/fluent/plugin/filter.rb +51 -0
  79. data/lib/fluent/plugin/filter_grep.rb +13 -40
  80. data/lib/fluent/plugin/filter_record_transformer.rb +22 -18
  81. data/lib/fluent/plugin/formatter.rb +93 -0
  82. data/lib/fluent/plugin/formatter_csv.rb +48 -0
  83. data/lib/fluent/plugin/formatter_hash.rb +32 -0
  84. data/lib/fluent/plugin/formatter_json.rb +47 -0
  85. data/lib/fluent/plugin/formatter_ltsv.rb +42 -0
  86. data/lib/fluent/plugin/formatter_msgpack.rb +32 -0
  87. data/lib/fluent/plugin/formatter_out_file.rb +45 -0
  88. data/lib/fluent/plugin/formatter_single_value.rb +34 -0
  89. data/lib/fluent/plugin/formatter_stdout.rb +39 -0
  90. data/lib/fluent/plugin/in_debug_agent.rb +4 -0
  91. data/lib/fluent/plugin/in_dummy.rb +22 -18
  92. data/lib/fluent/plugin/in_exec.rb +18 -8
  93. data/lib/fluent/plugin/in_forward.rb +36 -79
  94. data/lib/fluent/plugin/in_gc_stat.rb +4 -0
  95. data/lib/fluent/plugin/in_http.rb +21 -18
  96. data/lib/fluent/plugin/in_monitor_agent.rb +15 -48
  97. data/lib/fluent/plugin/in_object_space.rb +6 -1
  98. data/lib/fluent/plugin/in_stream.rb +7 -3
  99. data/lib/fluent/plugin/in_syslog.rb +46 -95
  100. data/lib/fluent/plugin/in_tail.rb +51 -595
  101. data/lib/fluent/plugin/in_tcp.rb +8 -1
  102. data/lib/fluent/plugin/in_udp.rb +8 -14
  103. data/lib/fluent/plugin/input.rb +33 -0
  104. data/lib/fluent/plugin/multi_output.rb +95 -0
  105. data/lib/fluent/plugin/out_buffered_null.rb +59 -0
  106. data/lib/fluent/plugin/out_copy.rb +11 -7
  107. data/lib/fluent/plugin/out_exec.rb +15 -11
  108. data/lib/fluent/plugin/out_exec_filter.rb +18 -10
  109. data/lib/fluent/plugin/out_file.rb +34 -5
  110. data/lib/fluent/plugin/out_forward.rb +19 -9
  111. data/lib/fluent/plugin/out_null.rb +0 -14
  112. data/lib/fluent/plugin/out_roundrobin.rb +11 -7
  113. data/lib/fluent/plugin/out_stdout.rb +5 -7
  114. data/lib/fluent/plugin/out_stream.rb +3 -1
  115. data/lib/fluent/plugin/output.rb +979 -0
  116. data/lib/fluent/plugin/owned_by_mixin.rb +42 -0
  117. data/lib/fluent/plugin/parser.rb +244 -0
  118. data/lib/fluent/plugin/parser_apache.rb +24 -0
  119. data/lib/fluent/plugin/parser_apache2.rb +84 -0
  120. data/lib/fluent/plugin/parser_apache_error.rb +21 -0
  121. data/lib/fluent/plugin/parser_csv.rb +31 -0
  122. data/lib/fluent/plugin/parser_json.rb +79 -0
  123. data/lib/fluent/plugin/parser_ltsv.rb +50 -0
  124. data/lib/fluent/plugin/parser_multiline.rb +102 -0
  125. data/lib/fluent/plugin/parser_nginx.rb +24 -0
  126. data/lib/fluent/plugin/parser_none.rb +36 -0
  127. data/lib/fluent/plugin/parser_syslog.rb +82 -0
  128. data/lib/fluent/plugin/parser_tsv.rb +37 -0
  129. data/lib/fluent/plugin/socket_util.rb +120 -114
  130. data/lib/fluent/plugin/storage.rb +84 -0
  131. data/lib/fluent/plugin/storage_local.rb +116 -0
  132. data/lib/fluent/plugin/string_util.rb +16 -13
  133. data/lib/fluent/plugin_helper.rb +39 -0
  134. data/lib/fluent/plugin_helper/child_process.rb +298 -0
  135. data/lib/fluent/plugin_helper/compat_parameters.rb +99 -0
  136. data/lib/fluent/plugin_helper/event_emitter.rb +80 -0
  137. data/lib/fluent/plugin_helper/event_loop.rb +118 -0
  138. data/lib/fluent/plugin_helper/retry_state.rb +177 -0
  139. data/lib/fluent/plugin_helper/storage.rb +308 -0
  140. data/lib/fluent/plugin_helper/thread.rb +147 -0
  141. data/lib/fluent/plugin_helper/timer.rb +85 -0
  142. data/lib/fluent/plugin_id.rb +63 -0
  143. data/lib/fluent/process.rb +21 -30
  144. data/lib/fluent/registry.rb +21 -9
  145. data/lib/fluent/root_agent.rb +115 -40
  146. data/lib/fluent/supervisor.rb +330 -320
  147. data/lib/fluent/system_config.rb +42 -18
  148. data/lib/fluent/test.rb +6 -1
  149. data/lib/fluent/test/base.rb +23 -3
  150. data/lib/fluent/test/driver/base.rb +247 -0
  151. data/lib/fluent/test/driver/event_feeder.rb +98 -0
  152. data/lib/fluent/test/driver/filter.rb +35 -0
  153. data/lib/fluent/test/driver/input.rb +31 -0
  154. data/lib/fluent/test/driver/output.rb +78 -0
  155. data/lib/fluent/test/driver/test_event_router.rb +45 -0
  156. data/lib/fluent/test/filter_test.rb +0 -1
  157. data/lib/fluent/test/formatter_test.rb +2 -1
  158. data/lib/fluent/test/input_test.rb +23 -17
  159. data/lib/fluent/test/output_test.rb +28 -39
  160. data/lib/fluent/test/parser_test.rb +1 -1
  161. data/lib/fluent/time.rb +104 -1
  162. data/lib/fluent/{status.rb → unique_id.rb} +15 -24
  163. data/lib/fluent/version.rb +1 -1
  164. data/lib/fluent/winsvc.rb +72 -0
  165. data/test/compat/test_calls_super.rb +164 -0
  166. data/test/config/test_config_parser.rb +83 -0
  167. data/test/config/test_configurable.rb +547 -274
  168. data/test/config/test_configure_proxy.rb +146 -29
  169. data/test/config/test_dsl.rb +3 -181
  170. data/test/config/test_element.rb +274 -0
  171. data/test/config/test_literal_parser.rb +1 -1
  172. data/test/config/test_section.rb +79 -7
  173. data/test/config/test_system_config.rb +21 -0
  174. data/test/config/test_types.rb +3 -26
  175. data/test/helper.rb +78 -8
  176. data/test/plugin/test_bare_output.rb +118 -0
  177. data/test/plugin/test_base.rb +75 -0
  178. data/test/plugin/test_buf_file.rb +420 -521
  179. data/test/plugin/test_buf_memory.rb +32 -194
  180. data/test/plugin/test_buffer.rb +981 -0
  181. data/test/plugin/test_buffer_chunk.rb +110 -0
  182. data/test/plugin/test_buffer_file_chunk.rb +770 -0
  183. data/test/plugin/test_buffer_memory_chunk.rb +265 -0
  184. data/test/plugin/test_filter.rb +255 -0
  185. data/test/plugin/test_filter_grep.rb +2 -73
  186. data/test/plugin/test_filter_record_transformer.rb +24 -68
  187. data/test/plugin/test_filter_stdout.rb +6 -6
  188. data/test/plugin/test_in_debug_agent.rb +2 -0
  189. data/test/plugin/test_in_dummy.rb +11 -17
  190. data/test/plugin/test_in_exec.rb +6 -25
  191. data/test/plugin/test_in_forward.rb +112 -151
  192. data/test/plugin/test_in_gc_stat.rb +2 -0
  193. data/test/plugin/test_in_http.rb +106 -157
  194. data/test/plugin/test_in_object_space.rb +21 -5
  195. data/test/plugin/test_in_stream.rb +14 -13
  196. data/test/plugin/test_in_syslog.rb +30 -275
  197. data/test/plugin/test_in_tail.rb +95 -234
  198. data/test/plugin/test_in_tcp.rb +14 -0
  199. data/test/plugin/test_in_udp.rb +21 -13
  200. data/test/plugin/test_input.rb +122 -0
  201. data/test/plugin/test_multi_output.rb +180 -0
  202. data/test/plugin/test_out_buffered_null.rb +79 -0
  203. data/test/plugin/test_out_copy.rb +15 -2
  204. data/test/plugin/test_out_exec.rb +75 -25
  205. data/test/plugin/test_out_exec_filter.rb +74 -8
  206. data/test/plugin/test_out_file.rb +61 -7
  207. data/test/plugin/test_out_forward.rb +92 -15
  208. data/test/plugin/test_out_roundrobin.rb +1 -0
  209. data/test/plugin/test_out_stdout.rb +22 -13
  210. data/test/plugin/test_out_stream.rb +18 -0
  211. data/test/plugin/test_output.rb +515 -0
  212. data/test/plugin/test_output_as_buffered.rb +1540 -0
  213. data/test/plugin/test_output_as_buffered_overflow.rb +247 -0
  214. data/test/plugin/test_output_as_buffered_retries.rb +808 -0
  215. data/test/plugin/test_output_as_buffered_secondary.rb +776 -0
  216. data/test/plugin/test_output_as_standard.rb +362 -0
  217. data/test/plugin/test_owned_by.rb +35 -0
  218. data/test/plugin/test_storage.rb +167 -0
  219. data/test/plugin/test_storage_local.rb +8 -0
  220. data/test/plugin_helper/test_child_process.rb +599 -0
  221. data/test/plugin_helper/test_compat_parameters.rb +175 -0
  222. data/test/plugin_helper/test_event_emitter.rb +51 -0
  223. data/test/plugin_helper/test_event_loop.rb +52 -0
  224. data/test/plugin_helper/test_retry_state.rb +399 -0
  225. data/test/plugin_helper/test_storage.rb +411 -0
  226. data/test/plugin_helper/test_thread.rb +164 -0
  227. data/test/plugin_helper/test_timer.rb +100 -0
  228. data/test/scripts/exec_script.rb +0 -6
  229. data/test/scripts/fluent/plugin/out_test.rb +3 -0
  230. data/test/test_config.rb +13 -4
  231. data/test/test_event.rb +24 -13
  232. data/test/test_event_router.rb +8 -7
  233. data/test/test_event_time.rb +187 -0
  234. data/test/test_formatter.rb +13 -51
  235. data/test/test_input.rb +1 -1
  236. data/test/test_log.rb +239 -16
  237. data/test/test_mixin.rb +1 -1
  238. data/test/test_output.rb +53 -66
  239. data/test/test_parser.rb +105 -323
  240. data/test/test_plugin_helper.rb +81 -0
  241. data/test/test_root_agent.rb +4 -52
  242. data/test/test_supervisor.rb +272 -0
  243. data/test/test_unique_id.rb +47 -0
  244. metadata +180 -54
  245. data/lib/fluent/buffer.rb +0 -365
  246. data/lib/fluent/plugin/filter_parser.rb +0 -107
  247. data/lib/fluent/plugin/in_status.rb +0 -76
  248. data/lib/fluent/test/helpers.rb +0 -86
  249. data/test/plugin/data/log/foo/bar2 +0 -0
  250. data/test/plugin/test_filter_parser.rb +0 -744
  251. data/test/plugin/test_in_status.rb +0 -38
  252. data/test/test_buffer.rb +0 -624
@@ -0,0 +1,84 @@
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 'fluent/plugin/base'
18
+ require 'fluent/plugin/owned_by_mixin'
19
+
20
+ module Fluent
21
+ module Plugin
22
+ class Storage < Base
23
+ include OwnedByMixin
24
+
25
+ DEFAULT_TYPE = 'local'
26
+
27
+ configured_in :storage
28
+
29
+ config_param :persistent, :bool, default: false # load/save with all operations
30
+ config_param :autosave, :bool, default: true
31
+ config_param :autosave_interval, :time, default: 10
32
+ config_param :save_at_shutdown, :bool, default: true
33
+
34
+ def self.validate_key(key)
35
+ raise ArgumentError, "key must be a string (or symbol for to_s)" unless key.is_a?(String) || key.is_a?(Symbol)
36
+ key.to_s
37
+ end
38
+
39
+ attr_accessor :log
40
+
41
+ def persistent_always?
42
+ false
43
+ end
44
+
45
+ def synchronized?
46
+ false
47
+ end
48
+
49
+ def implementation
50
+ self
51
+ end
52
+
53
+ def load
54
+ # load storage data from any data source, or initialize storage internally
55
+ end
56
+
57
+ def save
58
+ # save internal data store into data source (to be loaded)
59
+ end
60
+
61
+ def get(key)
62
+ raise NotImplementedError, "Implement this method in child class"
63
+ end
64
+
65
+ def fetch(key, defval)
66
+ raise NotImplementedError, "Implement this method in child class"
67
+ end
68
+
69
+ def put(key, value)
70
+ # return value
71
+ raise NotImplementedError, "Implement this method in child class"
72
+ end
73
+
74
+ def delete(key)
75
+ # return deleted value
76
+ raise NotImplementedError, "Implement this method in child class"
77
+ end
78
+
79
+ def update(key, &block) # transactional get-and-update
80
+ raise NotImplementedError, "Implement this method in child class"
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,116 @@
1
+ require 'fluent/plugin'
2
+ require 'fluent/plugin/storage'
3
+
4
+ require 'fileutils'
5
+ require 'yajl'
6
+
7
+ module Fluent
8
+ module Plugin
9
+ class LocalStorage < Storage
10
+ Fluent::Plugin.register_storage('local', self)
11
+
12
+ DEFAULT_DIR_MODE = 0755
13
+ DEFAULT_FILE_MODE = 0644
14
+
15
+ config_param :path, :string, default: nil
16
+ config_param :mode, :integer, default: DEFAULT_FILE_MODE
17
+ config_param :dir_mode, :integer, default: DEFAULT_DIR_MODE
18
+ config_param :pretty_print, :bool, default: false
19
+
20
+ def initialize
21
+ super
22
+ @store = {}
23
+ end
24
+
25
+ def configure(conf)
26
+ super
27
+
28
+ @on_memory = false
29
+ if !@path && !@_plugin_id_configured
30
+ if @persistent
31
+ raise Fluent::ConfigError, "Plugin @id or path for <storage> required to save data"
32
+ else
33
+ if @autosave
34
+ log.warn "both of Plugin @id and path for <storage> are not specified. Using on-memory store."
35
+ else
36
+ log.info "both of Plugin @id and path for <storage> are not specified. Using on-memory store."
37
+ end
38
+ @on_memory = true
39
+ end
40
+ elsif @path
41
+ # ok
42
+ else # @_plugin_id_configured is true
43
+ raise NotImplementedError, "implement this feature later with system_config"
44
+ ## TODO: get process-wide directory for plugin storage, and generate path for this plugin storage instance
45
+ # path =
46
+ end
47
+
48
+ if !@on_memory
49
+ dir = File.dirname(@path)
50
+ FileUtils.mkdir_p(dir, mode: @dir_mode) unless File.exist?(dir)
51
+ if File.exist?(@path)
52
+ raise Fluent::ConfigError, "Plugin storage path '#{@path}' is not readable/writable" unless File.readable?(@path) && File.writable?(@path)
53
+ begin
54
+ data = Yajl::Parser.parse(open(@path, 'r:utf-8'){ |io| io.read })
55
+ raise Fluent::ConfigError, "Invalid contents (not object) in plugin storage file: '#{@path}'" unless data.is_a?(Hash)
56
+ rescue => e
57
+ log.error "failed to read data from plugin storage file", path: @path, error: e
58
+ raise Fluent::ConfigError, "Unexpected error: failed to read data from plugin storage file: '#{@path}'"
59
+ end
60
+ else
61
+ raise Fluent::ConfigError, "Directory is not writable for plugin storage file '#{@path}'" unless File.writable?(@path)
62
+ end
63
+ end
64
+ end
65
+
66
+ def load
67
+ return if @on_memory
68
+ return unless File.exist?(@path)
69
+ begin
70
+ json_string = open(@path, 'r:utf-8'){ |io| io.read }
71
+ json = Yajl::Parser.parse(json_string)
72
+ unless json.is_a?(Hash)
73
+ log.error "broken content for plugin storage (Hash required: ignored)", type: json.class
74
+ log.debug "broken content", content: json_string
75
+ return
76
+ end
77
+ @store = json
78
+ rescue => e
79
+ log.error "failed to load data for plugin storage from file", path: @path, error: e
80
+ end
81
+ end
82
+
83
+ def save
84
+ return if @on_memory
85
+ tmp_path = @path + '.tmp'
86
+ begin
87
+ json_string = Yajl::Encoder.encode(@store, pretty: @pretty_print)
88
+ open(tmp_path, 'w:utf-8', @mode){ |io| io.write json_string }
89
+ File.rename(tmp_path, @path)
90
+ rescue => e
91
+ log.error "failed to save data for plugin storage to file", path: @path, tmp: tmp_path, error: e
92
+ end
93
+ end
94
+
95
+ def get(key)
96
+ @store[key.to_s]
97
+ end
98
+
99
+ def fetch(key, defval)
100
+ @store.fetch(key.to_s, defval)
101
+ end
102
+
103
+ def put(key, value)
104
+ @store[key.to_s] = value
105
+ end
106
+
107
+ def delete(key)
108
+ @store.delete(key.to_s)
109
+ end
110
+
111
+ def update(key, &block)
112
+ @store[key.to_s] = block.call(@store[key.to_s])
113
+ end
114
+ end
115
+ end
116
+ end
@@ -14,21 +14,24 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
- require "string-scrub" unless "".respond_to?(:scrub)
18
-
19
17
  module Fluent
20
- module StringUtil
21
- def match_regexp(regexp, string)
22
- begin
23
- return regexp.match(string)
24
- rescue ArgumentError => e
25
- raise e unless e.message.index("invalid byte sequence in".freeze).zero?
26
- $log.info "invalid byte sequence is replaced in `#{string}`"
27
- string = string.scrub('?')
28
- retry
18
+ module Plugin
19
+ module StringUtil
20
+ def match_regexp(regexp, string)
21
+ begin
22
+ return regexp.match(string)
23
+ rescue ArgumentError => e
24
+ raise e unless e.message.index("invalid byte sequence in".freeze).zero?
25
+ $log.info "invalid byte sequence is replaced in `#{string}`"
26
+ string = string.scrub('?')
27
+ retry
28
+ end
29
+ return true
29
30
  end
30
- return true
31
+ module_function :match_regexp
31
32
  end
32
- module_function :match_regexp
33
33
  end
34
+
35
+ # obsolete
36
+ StringUtil = Fluent::Plugin::StringUtil
34
37
  end
@@ -0,0 +1,39 @@
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 'fluent/plugin_helper/event_emitter'
18
+ require 'fluent/plugin_helper/thread'
19
+ require 'fluent/plugin_helper/event_loop'
20
+ require 'fluent/plugin_helper/timer'
21
+ require 'fluent/plugin_helper/child_process'
22
+ require 'fluent/plugin_helper/storage'
23
+ require 'fluent/plugin_helper/retry_state'
24
+ require 'fluent/plugin_helper/compat_parameters'
25
+
26
+ module Fluent
27
+ module PluginHelper
28
+ module Mixin
29
+ def self.included(mod)
30
+ mod.extend(Fluent::PluginHelper)
31
+ end
32
+ end
33
+
34
+ def helpers(*snake_case_symbols)
35
+ helper_modules = snake_case_symbols.map{|name| Fluent::PluginHelper.const_get(name.to_s.split('_').map(&:capitalize).join) }
36
+ include(*helper_modules)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,298 @@
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 'fluent/plugin_helper/thread'
18
+ require 'fluent/plugin_helper/timer'
19
+
20
+ require 'open3'
21
+ require 'timeout'
22
+
23
+ module Fluent
24
+ module PluginHelper
25
+ module ChildProcess
26
+ include Fluent::PluginHelper::Thread
27
+ include Fluent::PluginHelper::Timer
28
+
29
+ CHILD_PROCESS_LOOP_CHECK_INTERVAL = 0.2 # sec
30
+ CHILD_PROCESS_DEFAULT_EXIT_TIMEOUT = 10 # sec
31
+ CHILD_PROCESS_DEFAULT_KILL_TIMEOUT = 60 # sec
32
+
33
+ MODE_PARAMS = [:read, :write, :stderr, :read_with_stderr]
34
+ STDERR_OPTIONS = [:discard, :connect]
35
+
36
+ # stop : mark callback thread as stopped
37
+ # shutdown : close write IO to child processes (STDIN of child processes), send TERM (KILL for Windows) to all child processes
38
+ # close : send KILL to all child processes
39
+ # terminate: [-]
40
+
41
+ attr_reader :_child_process_processes # for tests
42
+
43
+ def child_process_running?
44
+ # checker for code in callback of child_process_execute
45
+ ::Thread.current[:_fluentd_plugin_helper_child_process_running] || false
46
+ end
47
+
48
+ def child_process_id
49
+ ::Thread.current[:_fluentd_plugin_helper_child_process_pid]
50
+ end
51
+
52
+ def child_process_exit_status
53
+ ::Thread.current[:_fluentd_plugin_helper_child_process_exit_status]
54
+ end
55
+
56
+ def child_process_execute(
57
+ title, command,
58
+ arguments: nil, subprocess_name: nil, interval: nil, immediate: false, parallel: false,
59
+ mode: [:read, :write], stderr: :discard, env: {}, unsetenv: false, chdir: nil,
60
+ internal_encoding: 'utf-8', external_encoding: 'ascii-8bit', scrub: true, replace_string: nil,
61
+ &block
62
+ )
63
+ raise ArgumentError, "BUG: title must be a symbol" unless title.is_a? Symbol
64
+ raise ArgumentError, "BUG: arguments required if subprocess name is replaced" if subprocess_name && !arguments
65
+
66
+ raise ArgumentError, "BUG: invalid mode specification" unless mode.all?{|m| MODE_PARAMS.include?(m) }
67
+ raise ArgumentError, "BUG: read_with_stderr is exclusive with :read and :stderr" if mode.include?(:read_with_stderr) && (mode.include?(:read) || mode.include?(:stderr))
68
+ raise ArgumentError, "BUG: invalid stderr handling specification" unless STDERR_OPTIONS.include?(stderr)
69
+
70
+ raise ArgumentError, "BUG: block not specified which receive i/o object" unless block_given?
71
+ raise ArgumentError, "BUG: number of block arguments are different from size of mode" unless block.arity == mode.size
72
+
73
+ running = false
74
+ callback = ->(*args) {
75
+ running = true
76
+ begin
77
+ block.call(*args)
78
+ ensure
79
+ running = false
80
+ end
81
+ }
82
+
83
+ if immediate || !interval
84
+ child_process_execute_once(title, command, arguments, subprocess_name, mode, stderr, env, unsetenv, chdir, internal_encoding, external_encoding, scrub, replace_string, &callback)
85
+ end
86
+
87
+ if interval
88
+ timer_execute(:child_process_execute, interval, repeat: true) do
89
+ if !parallel && running
90
+ log.warn "previous child process is still running. skipped.", title: title, command: command, arguments: arguments, interval: interval, parallel: parallel
91
+ else
92
+ child_process_execute_once(title, command, arguments, subprocess_name, mode, stderr, env, unsetenv, chdir, internal_encoding, external_encoding, scrub, replace_string, &callback)
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ def initialize
99
+ super
100
+ # plugins MAY configure this parameter
101
+ @_child_process_exit_timeout = CHILD_PROCESS_DEFAULT_EXIT_TIMEOUT
102
+ @_child_process_kill_timeout = CHILD_PROCESS_DEFAULT_KILL_TIMEOUT
103
+ @_child_process_mutex = Mutex.new
104
+ end
105
+
106
+ def start
107
+ super
108
+ @_child_process_processes = {} # pid => ProcessInfo
109
+ end
110
+
111
+ def stop
112
+ @_child_process_mutex.synchronize{ @_child_process_processes.keys }.each do |pid|
113
+ process_info = @_child_process_processes[pid]
114
+ if process_info
115
+ process_info.thread[:_fluentd_plugin_helper_child_process_running] = false
116
+ end
117
+ end
118
+ end
119
+
120
+ def shutdown
121
+ @_child_process_mutex.synchronize{ @_child_process_processes.keys }.each do |pid|
122
+ process_info = @_child_process_processes[pid]
123
+ next if !process_info || !process_info.writeio_in_use
124
+ begin
125
+ Timeout.timeout(@_child_process_exit_timeout) do
126
+ process_info.writeio.close
127
+ end
128
+ rescue Timeout::Error
129
+ log.debug "External process #{process_info.title} doesn't exist after STDIN close in timeout #{@_child_process_exit_timeout}sec"
130
+ end
131
+
132
+ child_process_kill(process_info)
133
+ end
134
+
135
+ super
136
+ end
137
+
138
+ def close
139
+ while (pids = @_child_process_mutex.synchronize{ @_child_process_processes.keys }).size > 0
140
+ pids.each do |pid|
141
+ process_info = @_child_process_processes[pid]
142
+ if !process_info || !process_info.alive
143
+ @_child_process_mutex.synchronize{ @_child_process_processes.delete(pid) }
144
+ next
145
+ end
146
+
147
+ process_info.killed_at ||= Time.now # for illegular case (e.g., created after shutdown)
148
+ next if Time.now < process_info.killed_at + @_child_process_kill_timeout
149
+
150
+ child_process_kill(process_info, force: true)
151
+ @_child_process_mutex.synchronize{ @_child_process_processes.delete(pid) }
152
+ end
153
+
154
+ sleep CHILD_PROCESS_LOOP_CHECK_INTERVAL
155
+ end
156
+
157
+ super
158
+ end
159
+
160
+ def child_process_kill(process_info, force: false)
161
+ if !process_info || !process_info.alive
162
+ return
163
+ end
164
+
165
+ process_info.killed_at = Time.now unless force
166
+
167
+ begin
168
+ pid, status = Process.waitpid2(process_info.pid, Process::WNOHANG)
169
+ if pid && status
170
+ process_info.thread[:_fluentd_plugin_helper_child_process_exit_status] = status
171
+ process_info.alive = false
172
+ end
173
+ rescue Errno::ECHILD, Errno::ESRCH, Errno::EPERM
174
+ process_info.alive = false
175
+ rescue
176
+ # ignore
177
+ end
178
+ if !process_info.alive
179
+ return
180
+ end
181
+
182
+ begin
183
+ signal = (Fluent.windows? || force) ? :KILL : :TERM
184
+ Process.kill(signal, process_info.pid)
185
+ if force
186
+ process_info.alive = false
187
+ end
188
+ rescue Errno::ECHILD, Errno::ESRCH
189
+ process_info.alive = false
190
+ end
191
+ end
192
+
193
+ ProcessInfo = Struct.new(:title, :thread, :pid, :readio, :readio_in_use, :writeio, :writeio_in_use, :stderrio, :stderrio_in_use, :wait_thread, :alive, :killed_at)
194
+
195
+ def child_process_execute_once(
196
+ title, command, arguments, subprocess_name, mode, stderr, env, unsetenv, chdir,
197
+ internal_encoding, external_encoding, scrub, replace_string, &block
198
+ )
199
+ spawn_args = if arguments || subprocess_name
200
+ [ env, (subprocess_name ? [command, subprocess_name] : command), *(arguments || []) ]
201
+ else
202
+ [ env, command ]
203
+ end
204
+ spawn_opts = {
205
+ unsetenv_others: unsetenv,
206
+ }
207
+ if chdir
208
+ spawn_opts[:chdir] = chdir
209
+ end
210
+
211
+ encoding_options = {}
212
+ if scrub
213
+ encoding_options[:invalid] = encoding_options[:undef] = :replace
214
+ if replace_string
215
+ encoding_options[:replace] = replace_string
216
+ end
217
+ end
218
+
219
+ log.debug "Executing command", title: title, spawn: spawn_args, mode: mode, stderr: stderr
220
+
221
+ readio = writeio = stderrio = wait_thread = nil
222
+ readio_in_use = writeio_in_use = stderrio_in_use = false
223
+
224
+ if !mode.include?(:stderr) && !mode.include?(:read_with_stderr) && stderr != :discard # connect
225
+ writeio, readio, wait_thread = *Open3.popen2(*spawn_args, spawn_opts)
226
+ elsif mode.include?(:read_with_stderr)
227
+ writeio, readio, wait_thread = *Open3.popen2e(*spawn_args, spawn_opts)
228
+ else
229
+ writeio, readio, stderrio, wait_thread = *Open3.popen3(*spawn_args, spawn_opts)
230
+ if !mode.include?(:stderr) # stderr == :discard
231
+ stderrio.reopen(IO::NULL)
232
+ end
233
+ end
234
+
235
+ if mode.include?(:write)
236
+ writeio.set_encoding(external_encoding, internal_encoding, encoding_options)
237
+ writeio_in_use = true
238
+ end
239
+ if mode.include?(:read) || mode.include?(:read_with_stderr)
240
+ readio.set_encoding(external_encoding, internal_encoding, encoding_options)
241
+ readio_in_use = true
242
+ end
243
+ if mode.include?(:stderr)
244
+ stderrio.set_encoding(external_encoding, internal_encoding, encoding_options)
245
+ stderrio_in_use = true
246
+ end
247
+
248
+ pid = wait_thread.pid # wait_thread => Process::Waiter
249
+
250
+ io_objects = []
251
+ mode.each do |m|
252
+ io_objects << case m
253
+ when :read then readio
254
+ when :write then writeio
255
+ when :read_with_stderr then readio
256
+ when :stderr then stderrio
257
+ else
258
+ raise "BUG: invalid mode must be checked before here: '#{m}'"
259
+ end
260
+ end
261
+
262
+ m = Mutex.new
263
+ m.lock
264
+ thread = thread_create :child_process_callback do
265
+ m.lock # run after plugin thread get pid, thread instance and i/o
266
+ m.unlock
267
+ begin
268
+ block.call(*io_objects)
269
+ rescue EOFError => e
270
+ log.debug "Process exit and I/O closed", title: title, pid: pid, command: command, arguments: arguments
271
+ rescue IOError => e
272
+ if e.message == 'stream closed'
273
+ log.debug "Process I/O stream closed", title: title, pid: pid, command: command, arguments: arguments
274
+ else
275
+ log.error "Unexpected I/O error for child process", title: title, pid: pid, command: command, arguments: arguments, error: e
276
+ end
277
+ rescue => e
278
+ log.warn "Unexpected error while processing I/O for child process", title: title, pid: pid, command: command, error: e
279
+ end
280
+ process_info = @_child_process_mutex.synchronize do
281
+ process_info = @_child_process_processes[pid]
282
+ @_child_process_processes.delete(pid)
283
+ process_info
284
+ end
285
+ child_process_kill(process_info, force: true) if process_info && process_info.alive && ::Thread.current[:_fluentd_plugin_helper_child_process_running]
286
+ end
287
+ thread[:_fluentd_plugin_helper_child_process_running] = true
288
+ thread[:_fluentd_plugin_helper_child_process_pid] = pid
289
+ pinfo = ProcessInfo.new(title, thread, pid, readio, readio_in_use, writeio, writeio_in_use, stderrio, stderrio_in_use, wait_thread, true, nil)
290
+ @_child_process_mutex.synchronize do
291
+ @_child_process_processes[pid] = pinfo
292
+ end
293
+ m.unlock
294
+ pid
295
+ end
296
+ end
297
+ end
298
+ end