fluentd 0.14.5-x86-mingw32 → 0.14.7-x86-mingw32
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of fluentd might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/ChangeLog +55 -0
- data/bin/fluent-binlog-reader +7 -0
- data/example/in_dummy_with_compression.conf +23 -0
- data/lib/fluent/agent.rb +8 -12
- data/lib/fluent/command/binlog_reader.rb +234 -0
- data/lib/fluent/command/fluentd.rb +17 -1
- data/lib/fluent/compat/file_util.rb +1 -1
- data/lib/fluent/compat/output.rb +5 -1
- data/lib/fluent/config/configure_proxy.rb +18 -3
- data/lib/fluent/config/element.rb +1 -1
- data/lib/fluent/config/section.rb +1 -1
- data/lib/fluent/config/v1_parser.rb +1 -1
- data/lib/fluent/env.rb +1 -0
- data/lib/fluent/event.rb +49 -2
- data/lib/fluent/event_router.rb +6 -2
- data/lib/fluent/label.rb +8 -0
- data/lib/fluent/log.rb +30 -1
- data/lib/fluent/plugin.rb +1 -1
- data/lib/fluent/plugin/base.rb +3 -0
- data/lib/fluent/plugin/buf_file.rb +2 -2
- data/lib/fluent/plugin/buf_memory.rb +1 -1
- data/lib/fluent/plugin/buffer.rb +12 -2
- data/lib/fluent/plugin/buffer/chunk.rb +68 -7
- data/lib/fluent/plugin/buffer/file_chunk.rb +4 -4
- data/lib/fluent/plugin/buffer/memory_chunk.rb +4 -4
- data/lib/fluent/plugin/compressable.rb +91 -0
- data/lib/fluent/plugin/filter_grep.rb +4 -4
- data/lib/fluent/plugin/formatter.rb +2 -2
- data/lib/fluent/plugin/formatter_json.rb +2 -1
- data/lib/fluent/plugin/formatter_out_file.rb +3 -30
- data/lib/fluent/plugin/in_forward.rb +6 -5
- data/lib/fluent/plugin/in_monitor_agent.rb +7 -21
- data/lib/fluent/plugin/in_syslog.rb +1 -1
- data/lib/fluent/plugin/in_tail.rb +11 -2
- data/lib/fluent/plugin/multi_output.rb +63 -3
- data/lib/fluent/plugin/out_exec.rb +1 -1
- data/lib/fluent/plugin/out_file.rb +5 -1
- data/lib/fluent/plugin/out_forward.rb +17 -5
- data/lib/fluent/plugin/out_stdout.rb +2 -1
- data/lib/fluent/plugin/output.rb +205 -19
- data/lib/fluent/plugin/parser.rb +5 -49
- data/lib/fluent/plugin/parser_apache2.rb +1 -1
- data/lib/fluent/plugin/parser_json.rb +4 -4
- data/lib/fluent/plugin/parser_multiline.rb +5 -5
- data/lib/fluent/plugin/parser_regexp.rb +1 -2
- data/lib/fluent/plugin/parser_syslog.rb +2 -2
- data/lib/fluent/plugin/storage_local.rb +2 -1
- data/lib/fluent/plugin_helper.rb +1 -0
- data/lib/fluent/plugin_helper/compat_parameters.rb +39 -21
- data/lib/fluent/plugin_helper/extract.rb +92 -0
- data/lib/fluent/plugin_helper/inject.rb +10 -12
- data/lib/fluent/plugin_helper/thread.rb +23 -3
- data/lib/fluent/registry.rb +1 -1
- data/lib/fluent/root_agent.rb +2 -1
- data/lib/fluent/supervisor.rb +28 -8
- data/lib/fluent/test/base.rb +0 -7
- data/lib/fluent/test/driver/base.rb +1 -0
- data/lib/fluent/test/driver/output.rb +3 -0
- data/lib/fluent/test/helpers.rb +18 -0
- data/lib/fluent/test/input_test.rb +4 -2
- data/lib/fluent/test/log.rb +3 -1
- data/lib/fluent/time.rb +232 -1
- data/lib/fluent/timezone.rb +1 -1
- data/lib/fluent/version.rb +1 -1
- data/test/command/test_binlog_reader.rb +351 -0
- data/test/config/test_config_parser.rb +6 -0
- data/test/config/test_configurable.rb +47 -1
- data/test/helper.rb +0 -1
- data/test/plugin/test_buffer.rb +22 -2
- data/test/plugin/test_buffer_chunk.rb +34 -4
- data/test/plugin/test_buffer_file_chunk.rb +73 -0
- data/test/plugin/test_buffer_memory_chunk.rb +73 -0
- data/test/plugin/test_compressable.rb +81 -0
- data/test/plugin/test_formatter_json.rb +14 -1
- data/test/plugin/test_in_forward.rb +67 -3
- data/test/plugin/test_in_monitor_agent.rb +17 -1
- data/test/plugin/test_in_tail.rb +8 -8
- data/test/plugin/test_out_file.rb +0 -8
- data/test/plugin/test_out_forward.rb +85 -0
- data/test/plugin/test_out_secondary_file.rb +20 -12
- data/test/plugin/test_out_stdout.rb +11 -10
- data/test/plugin/test_output.rb +234 -0
- data/test/plugin/test_output_as_buffered.rb +223 -0
- data/test/plugin/test_output_as_buffered_compress.rb +165 -0
- data/test/plugin/test_parser_json.rb +8 -0
- data/test/plugin/test_parser_regexp.rb +1 -1
- data/test/plugin_helper/test_child_process.rb +2 -2
- data/test/plugin_helper/test_extract.rb +195 -0
- data/test/plugin_helper/test_inject.rb +0 -7
- data/test/scripts/fluent/plugin/formatter1/formatter_test1.rb +7 -0
- data/test/scripts/fluent/plugin/formatter2/formatter_test2.rb +7 -0
- data/test/test_event.rb +186 -0
- data/test/test_event_router.rb +1 -1
- data/test/test_formatter.rb +0 -7
- data/test/test_log.rb +121 -0
- data/test/test_plugin_classes.rb +62 -0
- data/test/test_root_agent.rb +125 -0
- data/test/test_supervisor.rb +25 -2
- data/test/test_time_formatter.rb +103 -7
- data/test/test_time_parser.rb +211 -0
- metadata +22 -4
- data/test/plugin/test_parser_time.rb +0 -46
@@ -15,8 +15,9 @@
|
|
15
15
|
#
|
16
16
|
|
17
17
|
require 'fluent/event'
|
18
|
-
require 'time'
|
18
|
+
require 'fluent/time'
|
19
19
|
require 'fluent/configurable'
|
20
|
+
require 'socket'
|
20
21
|
|
21
22
|
module Fluent
|
22
23
|
module PluginHelper
|
@@ -66,11 +67,13 @@ module Fluent
|
|
66
67
|
config_param :hostname, :string, default: nil
|
67
68
|
config_param :tag_key, :string, default: nil
|
68
69
|
config_param :time_key, :string, default: nil
|
70
|
+
|
71
|
+
# To avoid defining :time_type twice
|
69
72
|
config_param :time_type, :enum, list: [:float, :unixtime, :string], default: :float
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
73
|
+
|
74
|
+
Fluent::TimeMixin::TIME_PARAMETERS.each do |name, type, opts|
|
75
|
+
config_param name, type, opts
|
76
|
+
end
|
74
77
|
end
|
75
78
|
end
|
76
79
|
|
@@ -89,12 +92,6 @@ module Fluent
|
|
89
92
|
end
|
90
93
|
|
91
94
|
def configure(conf)
|
92
|
-
conf.elements('inject').each do |e|
|
93
|
-
if e.has_key?('utc') && Fluent::Config.bool_value(e['utc'])
|
94
|
-
e['localtime'] = 'false'
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
95
|
super
|
99
96
|
|
100
97
|
if @inject_config
|
@@ -113,7 +110,8 @@ module Fluent
|
|
113
110
|
when :float then ->(time){ time.to_r.truncate(+6).to_f } # microsecond floating point value
|
114
111
|
when :unixtime then ->(time){ time.to_i }
|
115
112
|
else
|
116
|
-
|
113
|
+
localtime = @inject_config.localtime && !@inject_config.utc
|
114
|
+
Fluent::TimeFormatter.new(@inject_config.time_format, localtime, @inject_config.timezone)
|
117
115
|
end
|
118
116
|
end
|
119
117
|
|
@@ -70,7 +70,7 @@ module Fluent
|
|
70
70
|
thread_exit = true
|
71
71
|
raise
|
72
72
|
ensure
|
73
|
-
|
73
|
+
if ::Thread.current.alive? && !thread_exit
|
74
74
|
log.warn "thread doesn't exit correctly (killed or other reason)", plugin: self.class, title: title, thread: ::Thread.current, error: $!
|
75
75
|
end
|
76
76
|
@_threads_mutex.synchronize do
|
@@ -110,11 +110,31 @@ module Fluent
|
|
110
110
|
|
111
111
|
def stop
|
112
112
|
super
|
113
|
+
wakeup_threads = []
|
113
114
|
@_threads_mutex.synchronize do
|
114
|
-
@_threads.
|
115
|
+
@_threads.each_value do |thread|
|
115
116
|
thread[:_fluentd_plugin_helper_thread_running] = false
|
117
|
+
wakeup_threads << thread if thread.alive? && thread.status == "sleep"
|
116
118
|
end
|
117
119
|
end
|
120
|
+
wakeup_threads.each do |thread|
|
121
|
+
if thread.alive?
|
122
|
+
thread.wakeup
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def after_shutdown
|
128
|
+
super
|
129
|
+
wakeup_threads = []
|
130
|
+
@_threads_mutex.synchronize do
|
131
|
+
@_threads.each_value do |thread|
|
132
|
+
wakeup_threads << thread if thread.alive? && thread.status == "sleep"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
wakeup_threads.each do |thread|
|
136
|
+
thread.wakeup if thread.alive?
|
137
|
+
end
|
118
138
|
end
|
119
139
|
|
120
140
|
def close
|
@@ -132,7 +152,7 @@ module Fluent
|
|
132
152
|
super
|
133
153
|
@_threads_mutex.synchronize{ @_threads.keys }.each do |obj_id|
|
134
154
|
thread = @_threads[obj_id]
|
135
|
-
log.warn "killing existing
|
155
|
+
log.warn "killing existing thread", thread: thread
|
136
156
|
thread.kill if thread
|
137
157
|
end
|
138
158
|
@_threads_mutex.synchronize{ @_threads.keys }.each do |obj_id|
|
data/lib/fluent/registry.rb
CHANGED
data/lib/fluent/root_agent.rb
CHANGED
@@ -163,7 +163,7 @@ module Fluent
|
|
163
163
|
end
|
164
164
|
|
165
165
|
def shutdown # Fluentd's shutdown sequence is stop, before_shutdown, shutdown, after_shutdown, close, terminate for plugins
|
166
|
-
#
|
166
|
+
# These method callers does `rescue Exception` to call methods of shutdown sequence as far as possible
|
167
167
|
# if plugin methods does something like infinite recursive call, `exit`, unregistering signal handlers or others.
|
168
168
|
# Plugins should be separated and be in sandbox to protect data in each plugins/buffers.
|
169
169
|
|
@@ -242,6 +242,7 @@ module Fluent
|
|
242
242
|
|
243
243
|
def add_label(name)
|
244
244
|
label = Label.new(name, log: log)
|
245
|
+
raise ConfigError, "Section <label #{name}> appears twice" if @labels[name]
|
245
246
|
label.root_agent = self
|
246
247
|
@labels[name] = label
|
247
248
|
end
|
data/lib/fluent/supervisor.rb
CHANGED
@@ -230,11 +230,17 @@ module Fluent
|
|
230
230
|
log_path = params['log_path']
|
231
231
|
chuser = params['chuser']
|
232
232
|
chgroup = params['chgroup']
|
233
|
+
log_rotate_age = params['log_rotate_age']
|
234
|
+
log_rotate_size = params['log_rotate_size']
|
233
235
|
rpc_endpoint = system_config.rpc_endpoint
|
234
236
|
enable_get_dump = system_config.enable_get_dump
|
235
237
|
|
236
238
|
log_opts = {suppress_repeated_stacktrace: suppress_repeated_stacktrace}
|
237
|
-
logger_initializer = Supervisor::LoggerInitializer.new(
|
239
|
+
logger_initializer = Supervisor::LoggerInitializer.new(
|
240
|
+
log_path, log_level, chuser, chgroup, log_opts,
|
241
|
+
log_rotate_age: log_rotate_age,
|
242
|
+
log_rotate_size: log_rotate_size
|
243
|
+
)
|
238
244
|
# this #init sets initialized logger to $log
|
239
245
|
logger_initializer.init
|
240
246
|
logger = $log
|
@@ -295,42 +301,48 @@ module Fluent
|
|
295
301
|
end
|
296
302
|
|
297
303
|
class LoggerInitializer
|
298
|
-
def initialize(path, level, chuser, chgroup, opts)
|
304
|
+
def initialize(path, level, chuser, chgroup, opts, log_rotate_age: nil, log_rotate_size: nil)
|
299
305
|
@path = path
|
300
306
|
@level = level
|
301
307
|
@chuser = chuser
|
302
308
|
@chgroup = chgroup
|
303
309
|
@opts = opts
|
310
|
+
@log_rotate_age = log_rotate_age
|
311
|
+
@log_rotate_size = log_rotate_size
|
304
312
|
end
|
305
313
|
|
306
314
|
def init
|
307
315
|
if @path && @path != "-"
|
308
|
-
@
|
316
|
+
@logdev = if @log_rotate_age || @log_rotate_size
|
317
|
+
Fluent::LogDeviceIO.new(@path, shift_age: @log_rotate_age, shift_size: @log_rotate_size)
|
318
|
+
else
|
319
|
+
File.open(@path, "a")
|
320
|
+
end
|
309
321
|
if @chuser || @chgroup
|
310
322
|
chuid = @chuser ? ServerEngine::Privilege.get_etc_passwd(@chuser).uid : nil
|
311
323
|
chgid = @chgroup ? ServerEngine::Privilege.get_etc_group(@chgroup).gid : nil
|
312
324
|
File.chown(chuid, chgid, @path)
|
313
325
|
end
|
314
326
|
else
|
315
|
-
@
|
327
|
+
@logdev = STDOUT
|
316
328
|
end
|
317
329
|
|
318
330
|
dl_opts = {}
|
319
331
|
# subtract 1 to match serverengine daemon logger side logging severity.
|
320
332
|
dl_opts[:log_level] = @level - 1
|
321
|
-
logger = ServerEngine::DaemonLogger.new(@
|
333
|
+
logger = ServerEngine::DaemonLogger.new(@logdev, dl_opts)
|
322
334
|
$log = Fluent::Log.new(logger, @opts)
|
323
335
|
$log.enable_color(false) if @path
|
324
336
|
$log.enable_debug if @level <= Fluent::Log::LEVEL_DEBUG
|
325
337
|
end
|
326
338
|
|
327
339
|
def stdout?
|
328
|
-
@
|
340
|
+
@logdev == STDOUT
|
329
341
|
end
|
330
342
|
|
331
343
|
def reopen!
|
332
344
|
if @path && @path != "-"
|
333
|
-
@
|
345
|
+
@logdev.reopen(@path, "a")
|
334
346
|
end
|
335
347
|
self
|
336
348
|
end
|
@@ -381,6 +393,8 @@ module Fluent
|
|
381
393
|
@process_name = nil
|
382
394
|
|
383
395
|
@log_level = opt[:log_level]
|
396
|
+
@log_rotate_age = opt[:log_rotate_age]
|
397
|
+
@log_rotate_size = opt[:log_rotate_size]
|
384
398
|
@suppress_interval = opt[:suppress_interval]
|
385
399
|
@suppress_config_dump = opt[:suppress_config_dump]
|
386
400
|
@without_source = opt[:without_source]
|
@@ -388,7 +402,11 @@ module Fluent
|
|
388
402
|
|
389
403
|
@suppress_repeated_stacktrace = opt[:suppress_repeated_stacktrace]
|
390
404
|
log_opts = {suppress_repeated_stacktrace: @suppress_repeated_stacktrace}
|
391
|
-
@log = LoggerInitializer.new(
|
405
|
+
@log = LoggerInitializer.new(
|
406
|
+
@log_path, @log_level, @chuser, @chgroup, log_opts,
|
407
|
+
log_rotate_age: @log_rotate_age,
|
408
|
+
log_rotate_size: @log_rotate_size
|
409
|
+
)
|
392
410
|
@finished = false
|
393
411
|
end
|
394
412
|
|
@@ -512,6 +530,8 @@ module Fluent
|
|
512
530
|
params['inline_config'] = @inline_config
|
513
531
|
params['log_path'] = @log_path
|
514
532
|
params['log_level'] = @log_level
|
533
|
+
params['log_rotate_age'] = @log_rotate_age
|
534
|
+
params['log_rotate_size'] = @log_rotate_size
|
515
535
|
params['chuser'] = @chuser
|
516
536
|
params['chgroup'] = @chgroup
|
517
537
|
params['use_v1_config'] = @use_v1_config
|
data/lib/fluent/test/base.rb
CHANGED
@@ -18,6 +18,7 @@ require 'fluent/test/driver/base_owner'
|
|
18
18
|
require 'fluent/test/driver/event_feeder'
|
19
19
|
|
20
20
|
require 'fluent/plugin/output'
|
21
|
+
require 'timeout'
|
21
22
|
|
22
23
|
module Fluent
|
23
24
|
module Test
|
@@ -43,6 +44,7 @@ module Fluent
|
|
43
44
|
super(**kwargs, &block)
|
44
45
|
if @flush_buffer_at_cleanup
|
45
46
|
@instance.force_flush
|
47
|
+
Timeout.timeout(10){ sleep 0.1 until !@instance.buffer || @instance.buffer.queue.size == 0 }
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
@@ -52,6 +54,7 @@ module Fluent
|
|
52
54
|
|
53
55
|
def flush
|
54
56
|
@instance.force_flush
|
57
|
+
Timeout.timeout(10){ sleep 0.1 until !@instance.buffer || @instance.buffer.queue.size == 0 }
|
55
58
|
end
|
56
59
|
|
57
60
|
def instance_hook_after_started
|
data/lib/fluent/test/helpers.rb
CHANGED
@@ -21,6 +21,17 @@ require 'fluent/time'
|
|
21
21
|
module Fluent
|
22
22
|
module Test
|
23
23
|
module Helpers
|
24
|
+
# See "Example Custom Assertion: http://test-unit.github.io/test-unit/en/Test/Unit/Assertions.html
|
25
|
+
def assert_equal_event_time(expected, actual, message = nil)
|
26
|
+
message = build_message(message, <<EOT, expected, actual)
|
27
|
+
<?> expected but was
|
28
|
+
<?>.
|
29
|
+
EOT
|
30
|
+
assert_block(message) do
|
31
|
+
expected.is_a?(Fluent::EventTime) && actual.is_a?(Fluent::EventTime) && expected.sec == actual.sec && expected.nsec == actual.nsec
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
24
35
|
def config_element(name = 'test', argument = '', params = {}, elements = [])
|
25
36
|
Fluent::Config::Element.new(name, argument, params, elements)
|
26
37
|
end
|
@@ -37,6 +48,13 @@ module Fluent
|
|
37
48
|
end
|
38
49
|
end
|
39
50
|
|
51
|
+
def with_timezone(tz)
|
52
|
+
oldtz, ENV['TZ'] = ENV['TZ'], tz
|
53
|
+
yield
|
54
|
+
ensure
|
55
|
+
ENV['TZ'] = oldtz
|
56
|
+
end
|
57
|
+
|
40
58
|
def time2str(time, localtime: false, format: nil)
|
41
59
|
if format
|
42
60
|
if localtime
|
@@ -24,6 +24,7 @@ module Fluent
|
|
24
24
|
def initialize(klass, &block)
|
25
25
|
super(klass, &block)
|
26
26
|
@emit_streams = []
|
27
|
+
@event_streams = []
|
27
28
|
@expects = nil
|
28
29
|
# for checking only the number of emitted records during run
|
29
30
|
@expected_emits_length = nil
|
@@ -42,7 +43,7 @@ module Fluent
|
|
42
43
|
|
43
44
|
attr_accessor :expected_emits_length
|
44
45
|
attr_accessor :run_timeout
|
45
|
-
attr_reader :emit_streams
|
46
|
+
attr_reader :emit_streams, :event_streams
|
46
47
|
|
47
48
|
def emits
|
48
49
|
all = []
|
@@ -135,7 +136,7 @@ module Fluent
|
|
135
136
|
end
|
136
137
|
end
|
137
138
|
|
138
|
-
# Set
|
139
|
+
# Set running timeout to avoid infinite loop caused by some errors.
|
139
140
|
started_at = Time.now
|
140
141
|
register_run_breaking_condition do
|
141
142
|
Time.now >= started_at + @run_timeout
|
@@ -165,6 +166,7 @@ module Fluent
|
|
165
166
|
|
166
167
|
private
|
167
168
|
def emit_stream(tag, es)
|
169
|
+
@event_streams << es
|
168
170
|
@emit_streams << [tag, es.to_a]
|
169
171
|
end
|
170
172
|
end
|
data/lib/fluent/test/log.rb
CHANGED
@@ -21,13 +21,15 @@ module Fluent
|
|
21
21
|
module Test
|
22
22
|
class DummyLogDevice
|
23
23
|
attr_reader :logs
|
24
|
+
attr_accessor :flush_logs
|
24
25
|
|
25
26
|
def initialize
|
26
27
|
@logs = []
|
28
|
+
@flush_logs = true
|
27
29
|
end
|
28
30
|
|
29
31
|
def reset
|
30
|
-
@logs = []
|
32
|
+
@logs = [] if @flush_logs
|
31
33
|
end
|
32
34
|
|
33
35
|
def tty?
|
data/lib/fluent/time.rb
CHANGED
@@ -16,7 +16,10 @@
|
|
16
16
|
|
17
17
|
require 'time'
|
18
18
|
require 'msgpack'
|
19
|
+
require 'strptime'
|
19
20
|
require 'fluent/timezone'
|
21
|
+
require 'fluent/configurable'
|
22
|
+
require 'fluent/config/error'
|
20
23
|
|
21
24
|
module Fluent
|
22
25
|
class EventTime
|
@@ -103,8 +106,207 @@ module Fluent
|
|
103
106
|
end
|
104
107
|
end
|
105
108
|
|
109
|
+
module TimeMixin
|
110
|
+
TIME_PARAMETERS = [
|
111
|
+
[:time_format, :string, {default: nil}],
|
112
|
+
[:localtime, :bool, {default: true}], # UTC if :localtime is false and :timezone is nil
|
113
|
+
[:utc, :bool, {default: false}], # to turn :localtime false
|
114
|
+
[:timezone, :string, {default: nil}],
|
115
|
+
]
|
116
|
+
TIME_FULL_PARAMETERS = [
|
117
|
+
# To avoid to define :time_type twice (in plugin_helper/inject)
|
118
|
+
[:time_type, :enum, {default: :string, list: [:string, :unixtime, :float]}],
|
119
|
+
] + TIME_PARAMETERS
|
120
|
+
|
121
|
+
module TimeParameters
|
122
|
+
include Fluent::Configurable
|
123
|
+
TIME_FULL_PARAMETERS.each do |name, type, opts|
|
124
|
+
config_param name, type, opts
|
125
|
+
end
|
126
|
+
|
127
|
+
def configure(conf)
|
128
|
+
if conf.has_key?('localtime') || conf.has_key?('utc')
|
129
|
+
if conf.has_key?('localtime') && conf.has_key?('utc')
|
130
|
+
raise Fluent::ConfigError, "both of utc and localtime are specified, use only one of them"
|
131
|
+
elsif conf.has_key?('localtime')
|
132
|
+
conf['localtime'] = Fluent::Config.bool_value(conf['localtime'])
|
133
|
+
elsif conf.has_key?('utc')
|
134
|
+
conf['localtime'] = !(Fluent::Config.bool_value(conf['utc']))
|
135
|
+
# Specifying "localtime false" means using UTC in TimeFormatter
|
136
|
+
# And specifying "utc" is different from specifying "timezone +0000"(it's not always UTC).
|
137
|
+
# There are difference between "Z" and "+0000" in timezone formatting.
|
138
|
+
# TODO: add kwargs to TimeFormatter to specify "using localtime", "using UTC" or "using specified timezone" in more explicit way
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
super
|
143
|
+
|
144
|
+
Fluent::Timezone.validate!(@timezone) if @timezone
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
module Parser
|
149
|
+
def self.included(mod)
|
150
|
+
mod.include TimeParameters
|
151
|
+
end
|
152
|
+
|
153
|
+
def time_parser_create(type: @time_type, format: @time_format, timezone: @timezone, force_localtime: false)
|
154
|
+
return NumericTimeParser.new(type) if type != :string
|
155
|
+
return TimeParser.new(format, true, nil) if force_localtime
|
156
|
+
|
157
|
+
localtime = @localtime && (timezone.nil? && !@utc)
|
158
|
+
TimeParser.new(format, localtime, timezone)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
module Formatter
|
163
|
+
def self.included(mod)
|
164
|
+
mod.include TimeParameters
|
165
|
+
end
|
166
|
+
|
167
|
+
def time_formatter_create(type: @time_type, format: @time_format, timezone: @timezone, force_localtime: false)
|
168
|
+
return NumericTimeFormatter.new(type) if type != :string
|
169
|
+
return TimeFormatter.new(format, true, nil) if force_localtime
|
170
|
+
|
171
|
+
localtime = @localtime && (timezone.nil? && !@utc)
|
172
|
+
TimeFormatter.new(format, localtime, timezone)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
class TimeParser
|
178
|
+
class TimeParseError < StandardError; end
|
179
|
+
|
180
|
+
def initialize(format = nil, localtime = true, timezone = nil)
|
181
|
+
if format.nil? && (timezone || !localtime)
|
182
|
+
raise Fluent::ConfigError, "specifying timezone requires time format"
|
183
|
+
end
|
184
|
+
|
185
|
+
@cache1_key = nil
|
186
|
+
@cache1_time = nil
|
187
|
+
@cache2_key = nil
|
188
|
+
@cache2_time = nil
|
189
|
+
|
190
|
+
format_with_timezone = format && (format.include?("%z") || format.include?("%Z"))
|
191
|
+
|
192
|
+
# unixtime_in_expected_tz = unixtime_in_localtime + offset_diff
|
193
|
+
offset_diff = case
|
194
|
+
when format_with_timezone then nil
|
195
|
+
when timezone then Time.now.localtime.utc_offset - Time.zone_offset(timezone)
|
196
|
+
when localtime then 0
|
197
|
+
else Time.now.localtime.utc_offset # utc
|
198
|
+
end
|
199
|
+
|
200
|
+
strptime = format && (Strptime.new(time_format) rescue nil)
|
201
|
+
|
202
|
+
@parse = case
|
203
|
+
when format_with_timezone && strptime then ->(v){ Fluent::EventTime.from_time(strptime.exec(v)) }
|
204
|
+
when format_with_timezone then ->(v){ Fluent::EventTime.from_time(Time.strptime(v, format)) }
|
205
|
+
when strptime then ->(v){ t = strptime.exec(v); Fluent::EventTime.new(t.to_i + offset_diff, t.nsec) }
|
206
|
+
when format then ->(v){ t = Time.strptime(v, format); Fluent::EventTime.new(t.to_i + offset_diff, t.nsec) }
|
207
|
+
else ->(v){ Fluent::EventTime.parse(v) }
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# TODO: new cache mechanism using format string
|
212
|
+
def parse(value)
|
213
|
+
unless value.is_a?(String)
|
214
|
+
raise TimeParseError, "value must be string: #{value}"
|
215
|
+
end
|
216
|
+
|
217
|
+
if @cache1_key == value
|
218
|
+
return @cache1_time
|
219
|
+
elsif @cache2_key == value
|
220
|
+
return @cache2_time
|
221
|
+
else
|
222
|
+
begin
|
223
|
+
time = @parse.call(value)
|
224
|
+
rescue => e
|
225
|
+
raise TimeParseError, "invalid time format: value = #{value}, error_class = #{e.class.name}, error = #{e.message}"
|
226
|
+
end
|
227
|
+
@cache1_key = @cache2_key
|
228
|
+
@cache1_time = @cache2_time
|
229
|
+
@cache2_key = value
|
230
|
+
@cache2_time = time
|
231
|
+
return time
|
232
|
+
end
|
233
|
+
end
|
234
|
+
alias :call :parse
|
235
|
+
end
|
236
|
+
|
237
|
+
class NumericTimeParser < TimeParser # to include TimeParseError
|
238
|
+
def initialize(type, localtime = nil, timezone = nil)
|
239
|
+
@cache1_key = @cache1_time = @cache2_key = @cache2_time = nil
|
240
|
+
|
241
|
+
if type == :unixtime
|
242
|
+
define_singleton_method(:parse, method(:parse_unixtime))
|
243
|
+
define_singleton_method(:call, method(:parse_unixtime))
|
244
|
+
else # :float
|
245
|
+
define_singleton_method(:parse, method(:parse_float))
|
246
|
+
define_singleton_method(:call, method(:parse_float))
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def parse_unixtime(value)
|
251
|
+
unless value.is_a?(String)
|
252
|
+
raise TimeParseError, "value must be a string: #{value}"
|
253
|
+
end
|
254
|
+
|
255
|
+
if @cache1_key == value
|
256
|
+
return @cache1_time
|
257
|
+
elsif @cache2_key == value
|
258
|
+
return @cache2_time
|
259
|
+
end
|
260
|
+
|
261
|
+
begin
|
262
|
+
time = Fluent::EventTime.new(value.to_i)
|
263
|
+
rescue => e
|
264
|
+
raise TimeParseError, "invalid time format: value = #{value}, error_class = #{e.class.name}, error = #{e.message}"
|
265
|
+
end
|
266
|
+
@cache1_key = @cache2_key
|
267
|
+
@cache1_time = @cache2_time
|
268
|
+
@cache2_key = value
|
269
|
+
@cache2_time = time
|
270
|
+
time
|
271
|
+
end
|
272
|
+
|
273
|
+
# rough benchmark result to compare handmade parser vs Fluent::EventTime.from_time(Time.at(value.to_r))
|
274
|
+
# full: with 9-digits of nsec after dot
|
275
|
+
# msec: with 3-digits of msec after dot
|
276
|
+
# 10_000_000 times loop on MacBookAir
|
277
|
+
## parse_by_myself(full): 12.162475 sec
|
278
|
+
## parse_by_myself(msec): 15.050435 sec
|
279
|
+
## parse_by_to_r (full): 28.722362 sec
|
280
|
+
## parse_by_to_r (msec): 28.232856 sec
|
281
|
+
def parse_float(value)
|
282
|
+
unless value.is_a?(String)
|
283
|
+
raise TimeParseError, "value must be a string: #{value}"
|
284
|
+
end
|
285
|
+
|
286
|
+
if @cache1_key == value
|
287
|
+
return @cache1_time
|
288
|
+
elsif @cache2_key == value
|
289
|
+
return @cache2_time
|
290
|
+
end
|
291
|
+
|
292
|
+
begin
|
293
|
+
sec_s, nsec_s, _ = value.split('.', 3) # throw away second-dot and later
|
294
|
+
nsec_s = nsec_s[0..9]
|
295
|
+
nsec_s += '0' * (9 - nsec_s.size) if nsec_s.size < 9
|
296
|
+
time = Fluent::EventTime.new(sec_s.to_i, nsec_s.to_i)
|
297
|
+
rescue => e
|
298
|
+
raise TimeParseError, "invalid time format: value = #{value}, error_class = #{e.class.name}, error = #{e.message}"
|
299
|
+
end
|
300
|
+
@cache1_key = @cache2_key
|
301
|
+
@cache1_time = @cache2_time
|
302
|
+
@cache2_key = value
|
303
|
+
@cache2_time = time
|
304
|
+
time
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
106
308
|
class TimeFormatter
|
107
|
-
def initialize(format, localtime, timezone = nil)
|
309
|
+
def initialize(format = nil, localtime = true, timezone = nil)
|
108
310
|
@tc1 = 0
|
109
311
|
@tc1_str = nil
|
110
312
|
@tc2 = 0
|
@@ -172,4 +374,33 @@ module Fluent
|
|
172
374
|
@format_nocache.call(time)
|
173
375
|
end
|
174
376
|
end
|
377
|
+
|
378
|
+
class NumericTimeFormatter < TimeFormatter
|
379
|
+
def initialize(type, localtime = nil, timezone = nil)
|
380
|
+
@cache1_key = @cache1_time = @cache2_key = @cache2_time = nil
|
381
|
+
|
382
|
+
if type == :unixtime
|
383
|
+
define_singleton_method(:format, method(:format_unixtime))
|
384
|
+
define_singleton_method(:call, method(:format_unixtime))
|
385
|
+
else # :float
|
386
|
+
define_singleton_method(:format, method(:format_float))
|
387
|
+
define_singleton_method(:call, method(:format_float))
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
def format_unixtime(time)
|
392
|
+
time.to_i.to_s
|
393
|
+
end
|
394
|
+
|
395
|
+
def format_float(time)
|
396
|
+
if time.is_a?(Fluent::EventTime) || time.is_a?(Time)
|
397
|
+
# 10.015 secs for 10_000_000 times call on MacBookAir
|
398
|
+
nsec_s = time.nsec.to_s
|
399
|
+
nsec_s = '0' * (9 - nsec_s.size) if nsec_s.size < 9
|
400
|
+
"#{time.sec}.#{nsec_s}"
|
401
|
+
else # integer (or float?)
|
402
|
+
time.to_f.to_s
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
175
406
|
end
|