fluentd 0.12.43 → 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 (253) 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 +1239 -0
  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 +58 -640
  101. data/lib/fluent/plugin/in_tcp.rb +8 -1
  102. data/lib/fluent/plugin/in_udp.rb +8 -18
  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 +25 -19
  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 +119 -117
  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 -282
  198. data/test/plugin/test_in_tcp.rb +14 -0
  199. data/test/plugin/test_in_udp.rb +21 -67
  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 +181 -55
  245. data/CHANGELOG.md +0 -710
  246. data/lib/fluent/buffer.rb +0 -365
  247. data/lib/fluent/plugin/filter_parser.rb +0 -107
  248. data/lib/fluent/plugin/in_status.rb +0 -76
  249. data/lib/fluent/test/helpers.rb +0 -86
  250. data/test/plugin/data/log/foo/bar2 +0 -0
  251. data/test/plugin/test_filter_parser.rb +0 -744
  252. data/test/plugin/test_in_status.rb +0 -38
  253. data/test/test_buffer.rb +0 -624
@@ -19,26 +19,29 @@ require 'cool.io'
19
19
  require 'fluent/input'
20
20
  require 'fluent/config/error'
21
21
  require 'fluent/event'
22
+ require 'fluent/system_config'
23
+ require 'fluent/plugin/buffer'
24
+
25
+ if Fluent.windows?
26
+ require_relative 'file_wrapper'
27
+ else
28
+ Fluent::FileWrapper = File
29
+ end
22
30
 
23
31
  module Fluent
24
- class NewTailInput < Input
25
- Plugin.register_input('tail', self)
32
+ class TailInput < Input
33
+ include SystemConfig::Mixin
26
34
 
27
- class WatcherSetupError < StandardError
28
- def initialize(msg)
29
- @message = msg
30
- end
35
+ Plugin.register_input('tail', self)
31
36
 
32
- def to_s
33
- @message
34
- end
35
- end
37
+ FILE_PERMISSION = 0644
36
38
 
37
39
  def initialize
38
40
  super
39
41
  @paths = []
40
42
  @tails = {}
41
- @ignore_list = []
43
+ @pf_file = nil
44
+ @pf = nil
42
45
  end
43
46
 
44
47
  desc 'The paths to read. Multiple paths can be specified, separated by comma.'
@@ -59,24 +62,18 @@ module Fluent
59
62
  config_param :read_lines_limit, :integer, default: 1000
60
63
  desc 'The interval of flushing the buffer for multiline format'
61
64
  config_param :multiline_flush_interval, :time, default: nil
62
- desc 'Enable the option to emit unmatched lines.'
63
- config_param :emit_unmatched_lines, :bool, default: false
64
65
  desc 'Enable the additional watch timer.'
65
66
  config_param :enable_watch_timer, :bool, default: true
66
- desc 'The encoding after conversion of the input.'
67
- config_param :encoding, :string, default: nil
68
67
  desc 'The encoding of the input.'
69
- config_param :from_encoding, :string, default: nil
68
+ config_param :encoding, default: nil do |encoding_name|
69
+ begin
70
+ Encoding.find(encoding_name)
71
+ rescue ArgumentError => e
72
+ raise ConfigError, e.message
73
+ end
74
+ end
70
75
  desc 'Add the log path being tailed to records. Specify the field name to be used.'
71
76
  config_param :path_key, :string, default: nil
72
- desc 'Limit the watching files that the modification time is within the specified time range (when use \'*\' in path).'
73
- config_param :limit_recently_modified, :time, default: nil
74
- desc 'Enable the option to skip the refresh of watching list on startup.'
75
- config_param :skip_refresh_on_startup, :bool, default: false
76
- desc 'Ignore repeated permission error logs'
77
- config_param :ignore_repeated_permission_error, :bool, default: false
78
- # This option is for Cool.io's loop wait timeout to avoid loop stuck at shutdown. Almost users don't need to change this value.
79
- config_param :blocking_timeout, :time, default: 0.5
80
77
 
81
78
  attr_reader :paths
82
79
 
@@ -95,7 +92,6 @@ module Fluent
95
92
 
96
93
  configure_parser(conf)
97
94
  configure_tag
98
- configure_encoding
99
95
 
100
96
  @multiline_mode = conf['format'] =~ /multiline/
101
97
  @receive_handler = if @multiline_mode
@@ -103,6 +99,7 @@ module Fluent
103
99
  else
104
100
  method(:parse_singleline)
105
101
  end
102
+ @file_perm = system_config.file_permission || FILE_PERMISSION
106
103
  end
107
104
 
108
105
  def configure_parser(conf)
@@ -113,7 +110,6 @@ module Fluent
113
110
  def configure_tag
114
111
  if @tag.index('*')
115
112
  @tag_prefix, @tag_suffix = @tag.split('*')
116
- @tag_prefix ||= ''
117
113
  @tag_suffix ||= ''
118
114
  else
119
115
  @tag_prefix = nil
@@ -121,34 +117,17 @@ module Fluent
121
117
  end
122
118
  end
123
119
 
124
- def configure_encoding
125
- unless @encoding
126
- if @from_encoding
127
- raise ConfigError, "tail: 'from_encoding' parameter must be specified with 'encoding' parameter."
128
- end
129
- end
130
-
131
- @encoding = parse_encoding_param(@encoding) if @encoding
132
- @from_encoding = parse_encoding_param(@from_encoding) if @from_encoding
133
- end
134
-
135
- def parse_encoding_param(encoding_name)
136
- begin
137
- Encoding.find(encoding_name) if encoding_name
138
- rescue ArgumentError => e
139
- raise ConfigError, e.message
140
- end
141
- end
142
-
143
120
  def start
121
+ super
122
+
144
123
  if @pos_file
145
- @pf_file = File.open(@pos_file, File::RDWR|File::CREAT, DEFAULT_FILE_PERMISSION)
124
+ @pf_file = File.open(@pos_file, File::RDWR|File::CREAT|File::BINARY, @file_perm)
146
125
  @pf_file.sync = true
147
126
  @pf = PositionFile.parse(@pf_file)
148
127
  end
149
128
 
150
129
  @loop = Coolio::Loop.new
151
- refresh_watchers unless @skip_refresh_on_startup
130
+ refresh_watchers
152
131
 
153
132
  @refresh_trigger = TailWatcher::TimerWatcher.new(@refresh_interval, true, log, &method(:refresh_watchers))
154
133
  @refresh_trigger.attach(@loop)
@@ -162,30 +141,23 @@ module Fluent
162
141
  @loop.stop rescue nil # when all watchers are detached, `stop` raises RuntimeError. We can ignore this exception.
163
142
  @thread.join
164
143
  @pf_file.close if @pf_file
144
+
145
+ super
165
146
  end
166
147
 
167
148
  def expand_paths
168
149
  date = Time.now
169
150
  paths = []
170
151
 
152
+ excluded = @exclude_path.map { |path| path = date.strftime(path); path.include?('*') ? Dir.glob(path) : path }.flatten.uniq
171
153
  @paths.each { |path|
172
154
  path = date.strftime(path)
173
155
  if path.include?('*')
174
156
  paths += Dir.glob(path).select { |p|
175
- is_file = !File.directory?(p)
176
- if File.readable?(p) && is_file
177
- if @limit_recently_modified && File.mtime(p) < (date - @limit_recently_modified)
178
- false
179
- else
180
- true
181
- end
157
+ if File.readable?(p)
158
+ true
182
159
  else
183
- if is_file
184
- unless @ignore_list.include?(path)
185
- log.warn "#{p} unreadable. It is excluded and would be examined next time."
186
- @ignore_list << path if @ignore_repeated_permission_error
187
- end
188
- end
160
+ log.warn "#{p} unreadable. It is excluded and would be examined next time."
189
161
  false
190
162
  end
191
163
  }
@@ -194,7 +166,6 @@ module Fluent
194
166
  paths << path
195
167
  end
196
168
  }
197
- excluded = @exclude_path.map { |path| path = date.strftime(path); path.include?('*') ? Dir.glob(path) : path }.flatten.uniq
198
169
  paths - excluded
199
170
  end
200
171
 
@@ -219,9 +190,6 @@ module Fluent
219
190
  tw = TailWatcher.new(path, @rotate_wait, pe, log, @read_from_head, @enable_watch_timer, @read_lines_limit, method(:update_watcher), line_buffer_timer_flusher, &method(:receive_lines))
220
191
  tw.attach(@loop)
221
192
  tw
222
- rescue => e
223
- tw.close if tw
224
- raise e
225
193
  end
226
194
 
227
195
  def start_watchers(paths)
@@ -231,20 +199,14 @@ module Fluent
231
199
  pe = @pf[path]
232
200
  if @read_from_head && pe.read_inode.zero?
233
201
  begin
234
- pe.update(File::Stat.new(path).ino, 0)
202
+ pe.update(FileWrapper.stat(path).ino, 0)
235
203
  rescue Errno::ENOENT
236
204
  $log.warn "#{path} not found. Continuing without tailing it."
237
205
  end
238
206
  end
239
207
  end
240
208
 
241
- begin
242
- tw = setup_watcher(path, pe)
243
- rescue WatcherSetupError => e
244
- log.warn "Skip #{path} because unexpected setup error happens: #{e}"
245
- next
246
- end
247
- @tails[path] = tw
209
+ @tails[path] = setup_watcher(path, pe)
248
210
  }
249
211
  end
250
212
 
@@ -289,13 +251,7 @@ module Fluent
289
251
  def flush_buffer(tw)
290
252
  if lb = tw.line_buffer
291
253
  lb.chomp!
292
- if @encoding
293
- if @from_encoding
294
- lb.encode!(@encoding, @from_encoding)
295
- else
296
- lb.force_encoding(@encoding)
297
- end
298
- end
254
+ lb.force_encoding(@encoding) if @encoding
299
255
  @parser.parse(lb) { |time, record|
300
256
  if time && record
301
257
  tag = if @tag_prefix || @tag_suffix
@@ -313,13 +269,13 @@ module Fluent
313
269
  end
314
270
 
315
271
  def run
316
- @loop.run(@blocking_timeout)
272
+ @loop.run
317
273
  rescue
318
274
  log.error "unexpected error", error: $!.to_s
319
275
  log.error_backtrace
320
276
  end
321
277
 
322
- # @return true if no error or unrecoverable error happens in emit action. false if got BufferQueueLimitError
278
+ # @return true if no error or unrecoverable error happens in emit action. false if got BufferOverflowError
323
279
  def receive_lines(lines, tail_watcher)
324
280
  es = @receive_handler.call(lines, tail_watcher)
325
281
  unless es.empty?
@@ -330,7 +286,7 @@ module Fluent
330
286
  end
331
287
  begin
332
288
  router.emit_stream(tag, es)
333
- rescue BufferQueueLimitError
289
+ rescue Fluent::Plugin::Buffer::BufferOverflowError
334
290
  return false
335
291
  rescue
336
292
  # ignore non BufferQueueLimitError errors because in_tail can't recover. Engine shows logs and backtraces.
@@ -344,23 +300,12 @@ module Fluent
344
300
  def convert_line_to_event(line, es, tail_watcher)
345
301
  begin
346
302
  line.chomp! # remove \n
347
- if @encoding
348
- if @from_encoding
349
- line.encode!(@encoding, @from_encoding)
350
- else
351
- line.force_encoding(@encoding)
352
- end
353
- end
303
+ line.force_encoding(@encoding) if @encoding
354
304
  @parser.parse(line) { |time, record|
355
305
  if time && record
356
306
  record[@path_key] ||= tail_watcher.path unless @path_key.nil?
357
307
  es.add(time, record)
358
308
  else
359
- if @emit_unmatched_lines
360
- record = {'unmatched_line' => line}
361
- record[@path_key] ||= tail_watcher.path unless @path_key.nil?
362
- es.add(::Fluent::Engine.now, record)
363
- end
364
309
  log.warn "pattern not match: #{line.inspect}"
365
310
  end
366
311
  }
@@ -391,9 +336,6 @@ module Fluent
391
336
  lb = line
392
337
  else
393
338
  if lb.nil?
394
- if @emit_unmatched_lines
395
- convert_line_to_event(line, es, tail_watcher)
396
- end
397
339
  log.warn "got incomplete line before first line from #{tail_watcher.path}: #{line.inspect}"
398
340
  else
399
341
  lb << line
@@ -457,8 +399,8 @@ module Fluent
457
399
  end
458
400
 
459
401
  def detach
460
- @timer_trigger.detach if @enable_watch_timer && @timer_trigger && @timer_trigger.attached?
461
- @stat_trigger.detach if @stat_trigger && @stat_trigger.attached?
402
+ @timer_trigger.detach if @enable_watch_timer && @timer_trigger.attached?
403
+ @stat_trigger.detach if @stat_trigger.attached?
462
404
  end
463
405
 
464
406
  def close(close_io = true)
@@ -490,16 +432,8 @@ module Fluent
490
432
  # assuming following situation:
491
433
  # a) file was once renamed and backed, or
492
434
  # b) symlink or hardlink to the same file is recreated
493
- # in either case of a and b, seek to the saved position
494
- # c) file was once renamed, truncated and then backed
495
- # in this case, consider it truncated
496
- last_pos = @pe.read_pos
497
- if stat.size < last_pos
498
- pos = 0
499
- @pe.update(inode, pos)
500
- else
501
- pos = last_pos
502
- end
435
+ # in either case, seek to the saved position
436
+ pos = @pe.read_pos
503
437
  elsif last_inode != 0
504
438
  # this is FilePositionEntry and fluentd once started.
505
439
  # read data from the head of the rotated file.
@@ -514,12 +448,7 @@ module Fluent
514
448
  pos = @read_from_head ? 0 : fsize
515
449
  @pe.update(inode, pos)
516
450
  end
517
-
518
- begin
519
- io.seek(pos)
520
- rescue RangeError
521
- raise WatcherSetupError, "seek error with #{@path}: file_position = #{pos.to_s(16)}"
522
- end
451
+ io.seek(pos)
523
452
 
524
453
  @io_handler = IOHandler.new(io, @pe, @log, @read_lines_limit, &method(:wrap_receive_lines))
525
454
  else
@@ -534,7 +463,7 @@ module Fluent
534
463
  stat = io.stat
535
464
  inode = stat.ino
536
465
  if inode == @pe.read_inode # truncated
537
- @pe.update_pos(0)
466
+ @pe.update_pos(stat.size)
538
467
  io_handler = IOHandler.new(io, @pe, @log, @read_lines_limit, &method(:wrap_receive_lines))
539
468
  @io_handler.close
540
469
  @io_handler = io_handler
@@ -543,27 +472,24 @@ module Fluent
543
472
  io_handler = IOHandler.new(io, @pe, @log, @read_lines_limit, &method(:wrap_receive_lines))
544
473
  @io_handler = io_handler
545
474
  else # file is rotated and new file found
546
- io.close unless io.closed?
547
- detach
548
475
  @update_watcher.call(@path, swap_state(@pe))
549
476
  end
550
477
  else # file is rotated and new file not found
551
478
  # Clear RotateHandler to avoid duplicated file watch in same path.
552
479
  @rotate_handler = nil
553
- detach
554
480
  @update_watcher.call(@path, swap_state(@pe))
555
481
  end
556
482
  end
483
+ end
557
484
 
558
- def swap_state(pe)
559
- # Use MemoryPositionEntry for rotated file temporary
560
- mpe = MemoryPositionEntry.new
561
- mpe.update(pe.read_inode, pe.read_pos)
562
- @pe = mpe
563
- @io_handler.pe = mpe # Don't re-create IOHandler because IOHandler has an internal buffer.
485
+ def swap_state(pe)
486
+ # Use MemoryPositionEntry for rotated file temporary
487
+ mpe = MemoryPositionEntry.new
488
+ mpe.update(pe.read_inode, pe.read_pos)
489
+ @pe = mpe
490
+ @io_handler.pe = mpe # Don't re-create IOHandler because IOHandler has an internal buffer.
564
491
 
565
- pe # This pe will be updated in on_rotate after TailWatcher is initialized
566
- end
492
+ pe # This pe will be updated in on_rotate after TailWatcher is initialized
567
493
  end
568
494
 
569
495
  class TimerWatcher < Coolio::TimerWatcher
@@ -644,8 +570,8 @@ module Fluent
644
570
  else
645
571
  @buffer << @io.readpartial(2048, @iobuf)
646
572
  end
647
- while idx = @buffer.index("\n".freeze)
648
- @lines << @buffer.slice!(0, idx + 1)
573
+ while line = @buffer.slice!(/.*?\n/m)
574
+ @lines << line
649
575
  end
650
576
  if @lines.size >= @read_lines_limit
651
577
  # not to use too much memory in case the file is very large
@@ -703,7 +629,7 @@ module Fluent
703
629
 
704
630
  def on_notify
705
631
  begin
706
- stat = File.stat(@path)
632
+ stat = FileWrapper.stat(@path)
707
633
  inode = stat.ino
708
634
  fsize = stat.size
709
635
  rescue Errno::ENOENT
@@ -716,7 +642,7 @@ module Fluent
716
642
  if @inode != inode || fsize < @fsize
717
643
  # rotated or truncated
718
644
  begin
719
- io = File.open(@path)
645
+ io = FileWrapper.open(@path)
720
646
  rescue Errno::ENOENT
721
647
  end
722
648
  @on_rotate.call(io)
@@ -725,9 +651,6 @@ module Fluent
725
651
  @fsize = fsize
726
652
  end
727
653
 
728
- rescue WatcherSetupError => e
729
- io.close if io
730
- raise e
731
654
  rescue
732
655
  @log.error $!.to_s
733
656
  @log.error_backtrace
@@ -793,8 +716,6 @@ module Fluent
793
716
  m = /^([^\t]+)\t([0-9a-fA-F]+)\t([0-9a-fA-F]+)/.match(line)
794
717
  next unless m
795
718
  path = m[1]
796
- pos = m[2].to_i(16)
797
- ino = m[3].to_i(16)
798
719
  seek = file.pos - line.bytesize + path.bytesize + 1
799
720
  map[path] = FilePositionEntry.new(file, seek)
800
721
  }
@@ -882,508 +803,5 @@ module Fluent
882
803
  end
883
804
  end
884
805
 
885
- # This TailInput is for existence plugins which extends old in_tail
886
- # This class will be removed after release v1.
887
- class TailInput < Input
888
- def initialize
889
- super
890
- @paths = []
891
- end
892
-
893
- config_param :path, :string
894
- config_param :tag, :string
895
- config_param :rotate_wait, :time, :default => 5
896
- config_param :pos_file, :string, :default => nil
897
-
898
- attr_reader :paths
899
-
900
- def configure(conf)
901
- super
902
-
903
- @paths = @path.split(',').map {|path| path.strip }
904
- if @paths.empty?
905
- raise ConfigError, "tail: 'path' parameter is required on tail input"
906
- end
907
-
908
- unless @pos_file
909
- $log.warn "'pos_file PATH' parameter is not set to a 'tail' source."
910
- $log.warn "this parameter is highly recommended to save the position to resume tailing."
911
- end
912
-
913
- configure_parser(conf)
914
- end
915
-
916
- def configure_parser(conf)
917
- @parser = TextParser.new
918
- @parser.configure(conf)
919
- end
920
-
921
- def start
922
- if @pos_file
923
- @pf_file = File.open(@pos_file, File::RDWR|File::CREAT, DEFAULT_FILE_PERMISSION)
924
- @pf_file.sync = true
925
- @pf = PositionFile.parse(@pf_file)
926
- end
927
-
928
- @loop = Coolio::Loop.new
929
- @tails = @paths.map {|path|
930
- pe = @pf ? @pf[path] : MemoryPositionEntry.new
931
- tw = TailWatcher.new(path, @rotate_wait, pe, &method(:receive_lines))
932
- tw.log = log
933
- tw
934
- }
935
- @tails.each {|tail|
936
- tail.attach(@loop)
937
- }
938
- @thread = Thread.new(&method(:run))
939
- end
940
-
941
- def shutdown
942
- @tails.each {|tail|
943
- tail.close
944
- }
945
- @loop.stop
946
- @thread.join
947
- @pf_file.close if @pf_file
948
- end
949
-
950
- def run
951
- @loop.run
952
- rescue
953
- log.error "unexpected error", :error=>$!.to_s
954
- log.error_backtrace
955
- end
956
-
957
- def receive_lines(lines)
958
- es = MultiEventStream.new
959
- lines.each {|line|
960
- begin
961
- line.chomp! # remove \n
962
- time, record = parse_line(line)
963
- if time && record
964
- es.add(time, record)
965
- else
966
- log.warn "pattern not match: #{line.inspect}"
967
- end
968
- rescue
969
- log.warn line.dump, :error=>$!.to_s
970
- log.debug_backtrace
971
- end
972
- }
973
-
974
- unless es.empty?
975
- begin
976
- router.emit_stream(@tag, es)
977
- rescue
978
- # ignore errors. Engine shows logs and backtraces.
979
- end
980
- end
981
- end
982
-
983
- def parse_line(line)
984
- return @parser.parse(line)
985
- end
986
-
987
- class TailWatcher
988
- def initialize(path, rotate_wait, pe, &receive_lines)
989
- @path = path
990
- @rotate_wait = rotate_wait
991
- @pe = pe || MemoryPositionEntry.new
992
- @receive_lines = receive_lines
993
-
994
- @rotate_queue = []
995
-
996
- @timer_trigger = TimerWatcher.new(1, true, &method(:on_notify))
997
- @stat_trigger = StatWatcher.new(path, &method(:on_notify))
998
-
999
- @rotate_handler = RotateHandler.new(path, &method(:on_rotate))
1000
- @io_handler = nil
1001
- @log = $log
1002
- end
1003
-
1004
- # We use accessor approach to assign each logger, not passing log object at initialization,
1005
- # because several plugins depend on these internal classes.
1006
- # This approach avoids breaking plugins with new log_level option.
1007
- attr_accessor :log
1008
-
1009
- def log=(logger)
1010
- @log = logger
1011
- @timer_trigger.log = logger
1012
- @stat_trigger.log = logger
1013
- @rotate_handler.log = logger
1014
- end
1015
-
1016
- def attach(loop)
1017
- @timer_trigger.attach(loop)
1018
- @stat_trigger.attach(loop)
1019
- on_notify
1020
- end
1021
-
1022
- def detach
1023
- @timer_trigger.detach if @timer_trigger.attached?
1024
- @stat_trigger.detach if @stat_trigger.attached?
1025
- end
1026
-
1027
- def close
1028
- @rotate_queue.reject! {|req|
1029
- req.io.close
1030
- true
1031
- }
1032
- detach
1033
- end
1034
-
1035
- def on_notify
1036
- @rotate_handler.on_notify
1037
- return unless @io_handler
1038
- @io_handler.on_notify
1039
-
1040
- # proceeds rotate queue
1041
- return if @rotate_queue.empty?
1042
- @rotate_queue.first.tick
1043
-
1044
- while @rotate_queue.first.ready?
1045
- if io = @rotate_queue.first.io
1046
- stat = io.stat
1047
- inode = stat.ino
1048
- if inode == @pe.read_inode
1049
- # rotated file has the same inode number with the last file.
1050
- # assuming following situation:
1051
- # a) file was once renamed and backed, or
1052
- # b) symlink or hardlink to the same file is recreated
1053
- # in either case, seek to the saved position
1054
- pos = @pe.read_pos
1055
- else
1056
- pos = io.pos
1057
- end
1058
- @pe.update(inode, pos)
1059
- io_handler = IOHandler.new(io, @pe, log, &@receive_lines)
1060
- else
1061
- io_handler = NullIOHandler.new
1062
- end
1063
- @io_handler.close
1064
- @io_handler = io_handler
1065
- @rotate_queue.shift
1066
- break if @rotate_queue.empty?
1067
- end
1068
- end
1069
-
1070
- def on_rotate(io)
1071
- if @io_handler == nil
1072
- if io
1073
- # first time
1074
- stat = io.stat
1075
- fsize = stat.size
1076
- inode = stat.ino
1077
-
1078
- last_inode = @pe.read_inode
1079
- if inode == last_inode
1080
- # seek to the saved position
1081
- pos = @pe.read_pos
1082
- elsif last_inode != 0
1083
- # this is FilePositionEntry and fluentd once started.
1084
- # read data from the head of the rotated file.
1085
- # logs never duplicate because this file is a rotated new file.
1086
- pos = 0
1087
- @pe.update(inode, pos)
1088
- else
1089
- # this is MemoryPositionEntry or this is the first time fluentd started.
1090
- # seek to the end of the any files.
1091
- # logs may duplicate without this seek because it's not sure the file is
1092
- # existent file or rotated new file.
1093
- pos = fsize
1094
- @pe.update(inode, pos)
1095
- end
1096
- io.seek(pos)
1097
-
1098
- @io_handler = IOHandler.new(io, @pe, log, &@receive_lines)
1099
- else
1100
- @io_handler = NullIOHandler.new
1101
- end
1102
-
1103
- else
1104
- if io && @rotate_queue.find {|req| req.io == io }
1105
- return
1106
- end
1107
- last_io = @rotate_queue.empty? ? @io_handler.io : @rotate_queue.last.io
1108
- if last_io == nil
1109
- log.info "detected rotation of #{@path}"
1110
- # rotate imeediately if previous file is nil
1111
- wait = 0
1112
- else
1113
- log.info "detected rotation of #{@path}; waiting #{@rotate_wait} seconds"
1114
- wait = @rotate_wait
1115
- wait -= @rotate_queue.first.wait unless @rotate_queue.empty?
1116
- end
1117
- @rotate_queue << RotationRequest.new(io, wait)
1118
- end
1119
- end
1120
-
1121
- class TimerWatcher < Coolio::TimerWatcher
1122
- def initialize(interval, repeat, &callback)
1123
- @callback = callback
1124
- @log = $log
1125
- super(interval, repeat)
1126
- end
1127
-
1128
- attr_accessor :log
1129
-
1130
- def on_timer
1131
- @callback.call
1132
- rescue
1133
- # TODO log?
1134
- @log.error $!.to_s
1135
- @log.error_backtrace
1136
- end
1137
- end
1138
-
1139
- class StatWatcher < Coolio::StatWatcher
1140
- def initialize(path, &callback)
1141
- @callback = callback
1142
- @log = $log
1143
- super(path)
1144
- end
1145
-
1146
- attr_accessor :log
1147
-
1148
- def on_change(prev, cur)
1149
- @callback.call
1150
- rescue
1151
- # TODO log?
1152
- @log.error $!.to_s
1153
- @log.error_backtrace
1154
- end
1155
- end
1156
-
1157
- class RotationRequest
1158
- def initialize(io, wait)
1159
- @io = io
1160
- @wait = wait
1161
- end
1162
-
1163
- attr_reader :io, :wait
1164
-
1165
- def tick
1166
- @wait -= 1
1167
- end
1168
-
1169
- def ready?
1170
- @wait <= 0
1171
- end
1172
- end
1173
-
1174
- MAX_LINES_AT_ONCE = 1000
1175
-
1176
- class IOHandler
1177
- def initialize(io, pe, log, &receive_lines)
1178
- @log = log
1179
- @log.info "following tail of #{io.path}"
1180
- @io = io
1181
- @pe = pe
1182
- @receive_lines = receive_lines
1183
- @buffer = ''.force_encoding('ASCII-8BIT')
1184
- @iobuf = ''.force_encoding('ASCII-8BIT')
1185
- end
1186
-
1187
- attr_reader :io
1188
-
1189
- def on_notify
1190
- begin
1191
- lines = []
1192
- read_more = false
1193
-
1194
- begin
1195
- while true
1196
- if @buffer.empty?
1197
- @io.read_nonblock(2048, @buffer)
1198
- else
1199
- @buffer << @io.read_nonblock(2048, @iobuf)
1200
- end
1201
- while line = @buffer.slice!(/.*?\n/m)
1202
- lines << line
1203
- end
1204
- if lines.size >= MAX_LINES_AT_ONCE
1205
- # not to use too much memory in case the file is very large
1206
- read_more = true
1207
- break
1208
- end
1209
- end
1210
- rescue EOFError
1211
- end
1212
-
1213
- unless lines.empty?
1214
- @receive_lines.call(lines)
1215
- @pe.update_pos(@io.pos - @buffer.bytesize)
1216
- end
1217
-
1218
- end while read_more
1219
-
1220
- rescue
1221
- @log.error $!.to_s
1222
- @log.error_backtrace
1223
- close
1224
- end
1225
-
1226
- def close
1227
- @io.close unless @io.closed?
1228
- end
1229
- end
1230
-
1231
- class NullIOHandler
1232
- def initialize
1233
- end
1234
-
1235
- def io
1236
- end
1237
-
1238
- def on_notify
1239
- end
1240
-
1241
- def close
1242
- end
1243
- end
1244
-
1245
- class RotateHandler
1246
- def initialize(path, &on_rotate)
1247
- @path = path
1248
- @inode = nil
1249
- @fsize = -1 # first
1250
- @on_rotate = on_rotate
1251
- @log = $log
1252
- end
1253
-
1254
- attr_accessor :log
1255
-
1256
- def on_notify
1257
- begin
1258
- io = File.open(@path)
1259
- stat = io.stat
1260
- inode = stat.ino
1261
- fsize = stat.size
1262
- rescue Errno::ENOENT
1263
- # moved or deleted
1264
- inode = nil
1265
- fsize = 0
1266
- end
1267
-
1268
- begin
1269
- if @inode != inode || fsize < @fsize
1270
- # rotated or truncated
1271
- @on_rotate.call(io)
1272
- io = nil
1273
- end
1274
-
1275
- @inode = inode
1276
- @fsize = fsize
1277
- ensure
1278
- io.close if io
1279
- end
1280
-
1281
- rescue
1282
- @log.error $!.to_s
1283
- @log.error_backtrace
1284
- end
1285
- end
1286
- end
1287
-
1288
-
1289
- class PositionFile
1290
- def initialize(file, map, last_pos)
1291
- @file = file
1292
- @map = map
1293
- @last_pos = last_pos
1294
- end
1295
-
1296
- def [](path)
1297
- if m = @map[path]
1298
- return m
1299
- end
1300
-
1301
- @file.pos = @last_pos
1302
- @file.write path
1303
- @file.write "\t"
1304
- seek = @file.pos
1305
- @file.write "0000000000000000\t00000000\n"
1306
- @last_pos = @file.pos
1307
-
1308
- @map[path] = FilePositionEntry.new(@file, seek)
1309
- end
1310
-
1311
- def self.parse(file)
1312
- map = {}
1313
- file.pos = 0
1314
- file.each_line {|line|
1315
- m = /^([^\t]+)\t([0-9a-fA-F]+)\t([0-9a-fA-F]+)/.match(line)
1316
- next unless m
1317
- path = m[1]
1318
- pos = m[2].to_i(16)
1319
- ino = m[3].to_i(16)
1320
- seek = file.pos - line.bytesize + path.bytesize + 1
1321
- map[path] = FilePositionEntry.new(file, seek)
1322
- }
1323
- new(file, map, file.pos)
1324
- end
1325
- end
1326
-
1327
- # pos inode
1328
- # ffffffffffffffff\tffffffff\n
1329
- class FilePositionEntry
1330
- POS_SIZE = 16
1331
- INO_OFFSET = 17
1332
- INO_SIZE = 8
1333
- LN_OFFSET = 25
1334
- SIZE = 26
1335
-
1336
- def initialize(file, seek)
1337
- @file = file
1338
- @seek = seek
1339
- end
1340
-
1341
- def update(ino, pos)
1342
- @file.pos = @seek
1343
- @file.write "%016x\t%08x" % [pos, ino]
1344
- @inode = ino
1345
- end
1346
-
1347
- def update_pos(pos)
1348
- @file.pos = @seek
1349
- @file.write "%016x" % pos
1350
- end
1351
-
1352
- def read_inode
1353
- @file.pos = @seek + INO_OFFSET
1354
- raw = @file.read(8)
1355
- raw ? raw.to_i(16) : 0
1356
- end
1357
-
1358
- def read_pos
1359
- @file.pos = @seek
1360
- raw = @file.read(16)
1361
- raw ? raw.to_i(16) : 0
1362
- end
1363
- end
1364
-
1365
- class MemoryPositionEntry
1366
- def initialize
1367
- @pos = 0
1368
- @inode = 0
1369
- end
1370
-
1371
- def update(ino, pos)
1372
- @inode = ino
1373
- @pos = pos
1374
- end
1375
-
1376
- def update_pos(pos)
1377
- @pos = pos
1378
- end
1379
-
1380
- def read_pos
1381
- @pos
1382
- end
1383
-
1384
- def read_inode
1385
- @inode
1386
- end
1387
- end
1388
- end
806
+ NewTailInput = TailInput # for backward compatibility
1389
807
  end