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.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE.md +6 -0
- data/.gitignore +2 -0
- data/.travis.yml +33 -21
- data/CONTRIBUTING.md +1 -0
- data/ChangeLog +1239 -0
- data/README.md +0 -25
- data/Rakefile +2 -1
- data/Vagrantfile +17 -0
- data/appveyor.yml +35 -0
- data/example/filter_stdout.conf +5 -5
- data/example/in_forward.conf +2 -2
- data/example/in_http.conf +2 -2
- data/example/in_out_forward.conf +17 -0
- data/example/in_syslog.conf +2 -2
- data/example/in_tail.conf +2 -2
- data/example/in_tcp.conf +2 -2
- data/example/in_udp.conf +2 -2
- data/example/out_copy.conf +4 -4
- data/example/out_file.conf +2 -2
- data/example/out_forward.conf +2 -2
- data/example/out_forward_buf_file.conf +23 -0
- data/example/v0_12_filter.conf +8 -8
- data/fluent.conf +29 -0
- data/fluentd.gemspec +18 -11
- data/lib/fluent/agent.rb +60 -58
- data/lib/fluent/command/cat.rb +1 -1
- data/lib/fluent/command/debug.rb +7 -5
- data/lib/fluent/command/fluentd.rb +97 -2
- data/lib/fluent/compat/call_super_mixin.rb +67 -0
- data/lib/fluent/compat/filter.rb +50 -0
- data/lib/fluent/compat/formatter.rb +109 -0
- data/lib/fluent/compat/input.rb +50 -0
- data/lib/fluent/compat/output.rb +617 -0
- data/lib/fluent/compat/output_chain.rb +60 -0
- data/lib/fluent/compat/parser.rb +163 -0
- data/lib/fluent/compat/propagate_default.rb +62 -0
- data/lib/fluent/config.rb +23 -20
- data/lib/fluent/config/configure_proxy.rb +119 -70
- data/lib/fluent/config/dsl.rb +5 -18
- data/lib/fluent/config/element.rb +72 -8
- data/lib/fluent/config/error.rb +0 -3
- data/lib/fluent/config/literal_parser.rb +0 -2
- data/lib/fluent/config/parser.rb +4 -4
- data/lib/fluent/config/section.rb +39 -28
- data/lib/fluent/config/types.rb +2 -13
- data/lib/fluent/config/v1_parser.rb +1 -3
- data/lib/fluent/configurable.rb +48 -16
- data/lib/fluent/daemon.rb +15 -0
- data/lib/fluent/engine.rb +26 -52
- data/lib/fluent/env.rb +6 -4
- data/lib/fluent/event.rb +58 -11
- data/lib/fluent/event_router.rb +5 -5
- data/lib/fluent/filter.rb +2 -50
- data/lib/fluent/formatter.rb +4 -293
- data/lib/fluent/input.rb +2 -32
- data/lib/fluent/label.rb +2 -2
- data/lib/fluent/load.rb +3 -2
- data/lib/fluent/log.rb +107 -38
- data/lib/fluent/match.rb +0 -36
- data/lib/fluent/mixin.rb +117 -7
- data/lib/fluent/msgpack_factory.rb +62 -0
- data/lib/fluent/output.rb +7 -612
- data/lib/fluent/output_chain.rb +23 -0
- data/lib/fluent/parser.rb +4 -800
- data/lib/fluent/plugin.rb +100 -121
- data/lib/fluent/plugin/bare_output.rb +63 -0
- data/lib/fluent/plugin/base.rb +121 -0
- data/lib/fluent/plugin/buf_file.rb +101 -182
- data/lib/fluent/plugin/buf_memory.rb +9 -92
- data/lib/fluent/plugin/buffer.rb +473 -0
- data/lib/fluent/plugin/buffer/chunk.rb +135 -0
- data/lib/fluent/plugin/buffer/file_chunk.rb +339 -0
- data/lib/fluent/plugin/buffer/memory_chunk.rb +100 -0
- data/lib/fluent/plugin/exec_util.rb +80 -75
- data/lib/fluent/plugin/file_util.rb +33 -28
- data/lib/fluent/plugin/file_wrapper.rb +120 -0
- data/lib/fluent/plugin/filter.rb +51 -0
- data/lib/fluent/plugin/filter_grep.rb +13 -40
- data/lib/fluent/plugin/filter_record_transformer.rb +22 -18
- data/lib/fluent/plugin/formatter.rb +93 -0
- data/lib/fluent/plugin/formatter_csv.rb +48 -0
- data/lib/fluent/plugin/formatter_hash.rb +32 -0
- data/lib/fluent/plugin/formatter_json.rb +47 -0
- data/lib/fluent/plugin/formatter_ltsv.rb +42 -0
- data/lib/fluent/plugin/formatter_msgpack.rb +32 -0
- data/lib/fluent/plugin/formatter_out_file.rb +45 -0
- data/lib/fluent/plugin/formatter_single_value.rb +34 -0
- data/lib/fluent/plugin/formatter_stdout.rb +39 -0
- data/lib/fluent/plugin/in_debug_agent.rb +4 -0
- data/lib/fluent/plugin/in_dummy.rb +22 -18
- data/lib/fluent/plugin/in_exec.rb +18 -8
- data/lib/fluent/plugin/in_forward.rb +36 -79
- data/lib/fluent/plugin/in_gc_stat.rb +4 -0
- data/lib/fluent/plugin/in_http.rb +21 -18
- data/lib/fluent/plugin/in_monitor_agent.rb +15 -48
- data/lib/fluent/plugin/in_object_space.rb +6 -1
- data/lib/fluent/plugin/in_stream.rb +7 -3
- data/lib/fluent/plugin/in_syslog.rb +46 -95
- data/lib/fluent/plugin/in_tail.rb +58 -640
- data/lib/fluent/plugin/in_tcp.rb +8 -1
- data/lib/fluent/plugin/in_udp.rb +8 -18
- data/lib/fluent/plugin/input.rb +33 -0
- data/lib/fluent/plugin/multi_output.rb +95 -0
- data/lib/fluent/plugin/out_buffered_null.rb +59 -0
- data/lib/fluent/plugin/out_copy.rb +11 -7
- data/lib/fluent/plugin/out_exec.rb +15 -11
- data/lib/fluent/plugin/out_exec_filter.rb +18 -10
- data/lib/fluent/plugin/out_file.rb +34 -5
- data/lib/fluent/plugin/out_forward.rb +25 -19
- data/lib/fluent/plugin/out_null.rb +0 -14
- data/lib/fluent/plugin/out_roundrobin.rb +11 -7
- data/lib/fluent/plugin/out_stdout.rb +5 -7
- data/lib/fluent/plugin/out_stream.rb +3 -1
- data/lib/fluent/plugin/output.rb +979 -0
- data/lib/fluent/plugin/owned_by_mixin.rb +42 -0
- data/lib/fluent/plugin/parser.rb +244 -0
- data/lib/fluent/plugin/parser_apache.rb +24 -0
- data/lib/fluent/plugin/parser_apache2.rb +84 -0
- data/lib/fluent/plugin/parser_apache_error.rb +21 -0
- data/lib/fluent/plugin/parser_csv.rb +31 -0
- data/lib/fluent/plugin/parser_json.rb +79 -0
- data/lib/fluent/plugin/parser_ltsv.rb +50 -0
- data/lib/fluent/plugin/parser_multiline.rb +102 -0
- data/lib/fluent/plugin/parser_nginx.rb +24 -0
- data/lib/fluent/plugin/parser_none.rb +36 -0
- data/lib/fluent/plugin/parser_syslog.rb +82 -0
- data/lib/fluent/plugin/parser_tsv.rb +37 -0
- data/lib/fluent/plugin/socket_util.rb +119 -117
- data/lib/fluent/plugin/storage.rb +84 -0
- data/lib/fluent/plugin/storage_local.rb +116 -0
- data/lib/fluent/plugin/string_util.rb +16 -13
- data/lib/fluent/plugin_helper.rb +39 -0
- data/lib/fluent/plugin_helper/child_process.rb +298 -0
- data/lib/fluent/plugin_helper/compat_parameters.rb +99 -0
- data/lib/fluent/plugin_helper/event_emitter.rb +80 -0
- data/lib/fluent/plugin_helper/event_loop.rb +118 -0
- data/lib/fluent/plugin_helper/retry_state.rb +177 -0
- data/lib/fluent/plugin_helper/storage.rb +308 -0
- data/lib/fluent/plugin_helper/thread.rb +147 -0
- data/lib/fluent/plugin_helper/timer.rb +85 -0
- data/lib/fluent/plugin_id.rb +63 -0
- data/lib/fluent/process.rb +21 -30
- data/lib/fluent/registry.rb +21 -9
- data/lib/fluent/root_agent.rb +115 -40
- data/lib/fluent/supervisor.rb +330 -320
- data/lib/fluent/system_config.rb +42 -18
- data/lib/fluent/test.rb +6 -1
- data/lib/fluent/test/base.rb +23 -3
- data/lib/fluent/test/driver/base.rb +247 -0
- data/lib/fluent/test/driver/event_feeder.rb +98 -0
- data/lib/fluent/test/driver/filter.rb +35 -0
- data/lib/fluent/test/driver/input.rb +31 -0
- data/lib/fluent/test/driver/output.rb +78 -0
- data/lib/fluent/test/driver/test_event_router.rb +45 -0
- data/lib/fluent/test/filter_test.rb +0 -1
- data/lib/fluent/test/formatter_test.rb +2 -1
- data/lib/fluent/test/input_test.rb +23 -17
- data/lib/fluent/test/output_test.rb +28 -39
- data/lib/fluent/test/parser_test.rb +1 -1
- data/lib/fluent/time.rb +104 -1
- data/lib/fluent/{status.rb → unique_id.rb} +15 -24
- data/lib/fluent/version.rb +1 -1
- data/lib/fluent/winsvc.rb +72 -0
- data/test/compat/test_calls_super.rb +164 -0
- data/test/config/test_config_parser.rb +83 -0
- data/test/config/test_configurable.rb +547 -274
- data/test/config/test_configure_proxy.rb +146 -29
- data/test/config/test_dsl.rb +3 -181
- data/test/config/test_element.rb +274 -0
- data/test/config/test_literal_parser.rb +1 -1
- data/test/config/test_section.rb +79 -7
- data/test/config/test_system_config.rb +21 -0
- data/test/config/test_types.rb +3 -26
- data/test/helper.rb +78 -8
- data/test/plugin/test_bare_output.rb +118 -0
- data/test/plugin/test_base.rb +75 -0
- data/test/plugin/test_buf_file.rb +420 -521
- data/test/plugin/test_buf_memory.rb +32 -194
- data/test/plugin/test_buffer.rb +981 -0
- data/test/plugin/test_buffer_chunk.rb +110 -0
- data/test/plugin/test_buffer_file_chunk.rb +770 -0
- data/test/plugin/test_buffer_memory_chunk.rb +265 -0
- data/test/plugin/test_filter.rb +255 -0
- data/test/plugin/test_filter_grep.rb +2 -73
- data/test/plugin/test_filter_record_transformer.rb +24 -68
- data/test/plugin/test_filter_stdout.rb +6 -6
- data/test/plugin/test_in_debug_agent.rb +2 -0
- data/test/plugin/test_in_dummy.rb +11 -17
- data/test/plugin/test_in_exec.rb +6 -25
- data/test/plugin/test_in_forward.rb +112 -151
- data/test/plugin/test_in_gc_stat.rb +2 -0
- data/test/plugin/test_in_http.rb +106 -157
- data/test/plugin/test_in_object_space.rb +21 -5
- data/test/plugin/test_in_stream.rb +14 -13
- data/test/plugin/test_in_syslog.rb +30 -275
- data/test/plugin/test_in_tail.rb +95 -282
- data/test/plugin/test_in_tcp.rb +14 -0
- data/test/plugin/test_in_udp.rb +21 -67
- data/test/plugin/test_input.rb +122 -0
- data/test/plugin/test_multi_output.rb +180 -0
- data/test/plugin/test_out_buffered_null.rb +79 -0
- data/test/plugin/test_out_copy.rb +15 -2
- data/test/plugin/test_out_exec.rb +75 -25
- data/test/plugin/test_out_exec_filter.rb +74 -8
- data/test/plugin/test_out_file.rb +61 -7
- data/test/plugin/test_out_forward.rb +92 -15
- data/test/plugin/test_out_roundrobin.rb +1 -0
- data/test/plugin/test_out_stdout.rb +22 -13
- data/test/plugin/test_out_stream.rb +18 -0
- data/test/plugin/test_output.rb +515 -0
- data/test/plugin/test_output_as_buffered.rb +1540 -0
- data/test/plugin/test_output_as_buffered_overflow.rb +247 -0
- data/test/plugin/test_output_as_buffered_retries.rb +808 -0
- data/test/plugin/test_output_as_buffered_secondary.rb +776 -0
- data/test/plugin/test_output_as_standard.rb +362 -0
- data/test/plugin/test_owned_by.rb +35 -0
- data/test/plugin/test_storage.rb +167 -0
- data/test/plugin/test_storage_local.rb +8 -0
- data/test/plugin_helper/test_child_process.rb +599 -0
- data/test/plugin_helper/test_compat_parameters.rb +175 -0
- data/test/plugin_helper/test_event_emitter.rb +51 -0
- data/test/plugin_helper/test_event_loop.rb +52 -0
- data/test/plugin_helper/test_retry_state.rb +399 -0
- data/test/plugin_helper/test_storage.rb +411 -0
- data/test/plugin_helper/test_thread.rb +164 -0
- data/test/plugin_helper/test_timer.rb +100 -0
- data/test/scripts/exec_script.rb +0 -6
- data/test/scripts/fluent/plugin/out_test.rb +3 -0
- data/test/test_config.rb +13 -4
- data/test/test_event.rb +24 -13
- data/test/test_event_router.rb +8 -7
- data/test/test_event_time.rb +187 -0
- data/test/test_formatter.rb +13 -51
- data/test/test_input.rb +1 -1
- data/test/test_log.rb +239 -16
- data/test/test_mixin.rb +1 -1
- data/test/test_output.rb +53 -66
- data/test/test_parser.rb +105 -323
- data/test/test_plugin_helper.rb +81 -0
- data/test/test_root_agent.rb +4 -52
- data/test/test_supervisor.rb +272 -0
- data/test/test_unique_id.rb +47 -0
- metadata +181 -55
- data/CHANGELOG.md +0 -710
- data/lib/fluent/buffer.rb +0 -365
- data/lib/fluent/plugin/filter_parser.rb +0 -107
- data/lib/fluent/plugin/in_status.rb +0 -76
- data/lib/fluent/test/helpers.rb +0 -86
- data/test/plugin/data/log/foo/bar2 +0 -0
- data/test/plugin/test_filter_parser.rb +0 -744
- data/test/plugin/test_in_status.rb +0 -38
- 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
|
25
|
-
|
32
|
+
class TailInput < Input
|
33
|
+
include SystemConfig::Mixin
|
26
34
|
|
27
|
-
|
28
|
-
def initialize(msg)
|
29
|
-
@message = msg
|
30
|
-
end
|
35
|
+
Plugin.register_input('tail', self)
|
31
36
|
|
32
|
-
|
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
|
-
@
|
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 :
|
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,
|
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
|
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
|
-
|
176
|
-
|
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
|
-
|
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(
|
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
|
-
|
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
|
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
|
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
|
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
|
461
|
-
@stat_trigger.detach if @stat_trigger
|
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
|
494
|
-
|
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(
|
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
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
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
|
-
|
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
|
648
|
-
@lines <<
|
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 =
|
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 =
|
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
|
-
|
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
|