fluentd 1.2.0.pre1 → 1.2.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/CHANGELOG.md +28 -0
- data/MAINTAINERS.md +7 -6
- data/example/counter.conf +18 -0
- data/example/secondary_file.conf +3 -2
- data/lib/fluent/counter.rb +23 -0
- data/lib/fluent/counter/base_socket.rb +46 -0
- data/lib/fluent/counter/client.rb +288 -0
- data/lib/fluent/counter/error.rb +65 -0
- data/lib/fluent/counter/mutex_hash.rb +163 -0
- data/lib/fluent/counter/server.rb +273 -0
- data/lib/fluent/counter/store.rb +205 -0
- data/lib/fluent/counter/validator.rb +145 -0
- data/lib/fluent/env.rb +1 -0
- data/lib/fluent/log.rb +7 -0
- data/lib/fluent/plugin/filter_grep.rb +20 -24
- data/lib/fluent/plugin/output.rb +50 -1
- data/lib/fluent/plugin_helper.rb +1 -0
- data/lib/fluent/plugin_helper/counter.rb +51 -0
- data/lib/fluent/plugin_helper/retry_state.rb +15 -7
- data/lib/fluent/plugin_helper/server.rb +3 -0
- data/lib/fluent/supervisor.rb +30 -5
- data/lib/fluent/system_config.rb +26 -2
- data/lib/fluent/version.rb +1 -1
- data/test/counter/test_client.rb +549 -0
- data/test/counter/test_error.rb +44 -0
- data/test/counter/test_mutex_hash.rb +179 -0
- data/test/counter/test_server.rb +583 -0
- data/test/counter/test_store.rb +252 -0
- data/test/counter/test_validator.rb +137 -0
- data/test/plugin/test_output_as_buffered_backup.rb +271 -0
- data/test/plugin_helper/test_retry_state.rb +20 -0
- data/test/test_supervisor.rb +20 -0
- metadata +29 -5
@@ -0,0 +1,205 @@
|
|
1
|
+
#
|
2
|
+
# Fluentd
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require 'fluent/config'
|
18
|
+
require 'fluent/counter/error'
|
19
|
+
require 'fluent/plugin/storage_local'
|
20
|
+
require 'fluent/time'
|
21
|
+
|
22
|
+
module Fluent
|
23
|
+
module Counter
|
24
|
+
class Store
|
25
|
+
def self.gen_key(scope, key)
|
26
|
+
"#{scope}\t#{key}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(opt = {})
|
30
|
+
@log = opt[:log] || $log
|
31
|
+
|
32
|
+
# Notice: This storage is not be implemented auto save.
|
33
|
+
@storage = Plugin.new_storage('local', parent: DummyParent.new(@log))
|
34
|
+
conf = if opt[:path]
|
35
|
+
{'persistent' => true, 'path' => opt[:path] }
|
36
|
+
else
|
37
|
+
{'persistent' => false }
|
38
|
+
end
|
39
|
+
@storage.configure(Fluent::Config::Element.new('storage', {}, conf, []))
|
40
|
+
end
|
41
|
+
|
42
|
+
# This class behaves as a configurable plugin for using in storage (OwnedByMixin).
|
43
|
+
class DummyParent
|
44
|
+
include Configurable
|
45
|
+
|
46
|
+
attr_reader :log
|
47
|
+
|
48
|
+
def initialize(log)
|
49
|
+
@log = log
|
50
|
+
end
|
51
|
+
|
52
|
+
def plugin_id
|
53
|
+
'dummy_parent_store'
|
54
|
+
end
|
55
|
+
|
56
|
+
def plugin_id_configured?
|
57
|
+
false
|
58
|
+
end
|
59
|
+
|
60
|
+
# storage_local calls PluginId#plugin_root_dir
|
61
|
+
def plugin_root_dir
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def start
|
67
|
+
@storage.load
|
68
|
+
end
|
69
|
+
|
70
|
+
def stop
|
71
|
+
@storage.save
|
72
|
+
end
|
73
|
+
|
74
|
+
def init(key, data, ignore: false)
|
75
|
+
ret = if v = get(key)
|
76
|
+
raise InvalidParams.new("#{key} already exists in counter") unless ignore
|
77
|
+
v
|
78
|
+
else
|
79
|
+
@storage.put(key, build_value(data))
|
80
|
+
end
|
81
|
+
|
82
|
+
build_response(ret)
|
83
|
+
end
|
84
|
+
|
85
|
+
def get(key, raise_error: false, raw: false)
|
86
|
+
ret = if raise_error
|
87
|
+
@storage.get(key) or raise UnknownKey.new("`#{key}` doesn't exist in counter")
|
88
|
+
else
|
89
|
+
@storage.get(key)
|
90
|
+
end
|
91
|
+
if raw
|
92
|
+
ret
|
93
|
+
else
|
94
|
+
ret && build_response(ret)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def key?(key)
|
99
|
+
!!@storage.get(key)
|
100
|
+
end
|
101
|
+
|
102
|
+
def delete(key)
|
103
|
+
ret = @storage.delete(key) or raise UnknownKey.new("`#{key}` doesn't exist in counter")
|
104
|
+
build_response(ret)
|
105
|
+
end
|
106
|
+
|
107
|
+
def inc(key, data, force: false)
|
108
|
+
value = data.delete('value')
|
109
|
+
init(key, data) if !key?(key) && force
|
110
|
+
v = get(key, raise_error: true, raw: true)
|
111
|
+
valid_type!(v, value)
|
112
|
+
|
113
|
+
v['total'] += value
|
114
|
+
v['current'] += value
|
115
|
+
t = EventTime.now
|
116
|
+
v['last_modified_at'] = [t.sec, t.nsec]
|
117
|
+
@storage.put(key, v)
|
118
|
+
|
119
|
+
build_response(v)
|
120
|
+
end
|
121
|
+
|
122
|
+
def reset(key)
|
123
|
+
v = get(key, raise_error: true, raw: true)
|
124
|
+
success = false
|
125
|
+
old_data = v.dup
|
126
|
+
now = EventTime.now
|
127
|
+
last_reset_at = EventTime.new(*v['last_reset_at'])
|
128
|
+
|
129
|
+
# Does it need reset?
|
130
|
+
if (last_reset_at + v['reset_interval']) <= now
|
131
|
+
success = true
|
132
|
+
v['current'] = initial_value(v['type'])
|
133
|
+
t = [now.sec, now.nsec]
|
134
|
+
v['last_reset_at'] = t
|
135
|
+
v['last_modified_at'] = t
|
136
|
+
@storage.put(key, v)
|
137
|
+
end
|
138
|
+
|
139
|
+
{
|
140
|
+
'elapsed_time' => now - last_reset_at,
|
141
|
+
'success' => success,
|
142
|
+
'counter_data' => build_response(old_data)
|
143
|
+
}
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def build_response(d)
|
149
|
+
{
|
150
|
+
'name' => d['name'],
|
151
|
+
'total' => d['total'],
|
152
|
+
'current' => d['current'],
|
153
|
+
'type' => d['type'],
|
154
|
+
'reset_interval' => d['reset_interval'],
|
155
|
+
'last_reset_at' => EventTime.new(*d['last_reset_at']),
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
# value is Hash. value requires these fileds.
|
160
|
+
# :name, :total, :current, :type, :reset_interval, :last_reset_at, :last_modified_at
|
161
|
+
def build_value(data)
|
162
|
+
type = data['type'] || 'numeric'
|
163
|
+
now = EventTime.now
|
164
|
+
t = [now.sec, now.nsec]
|
165
|
+
|
166
|
+
v = initial_value(type)
|
167
|
+
|
168
|
+
data.merge(
|
169
|
+
'type' => type,
|
170
|
+
'last_reset_at' => t,
|
171
|
+
'last_modified_at' => t,
|
172
|
+
'current' => v,
|
173
|
+
'total' => v,
|
174
|
+
)
|
175
|
+
end
|
176
|
+
|
177
|
+
def initial_value(type)
|
178
|
+
case type
|
179
|
+
when 'numeric', 'integer' then 0
|
180
|
+
when 'float' then 0.0
|
181
|
+
else raise InvalidParams.new('`type` should be integer, float, or numeric')
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def valid_type!(v, value)
|
186
|
+
type = v['type']
|
187
|
+
return unless (type != 'numeric') && (type_str(value) != type)
|
188
|
+
raise InvalidParams.new("`type` is #{type}. You should pass #{type} value as a `value`")
|
189
|
+
end
|
190
|
+
|
191
|
+
def type_str(v)
|
192
|
+
case v
|
193
|
+
when Integer
|
194
|
+
'integer'
|
195
|
+
when Float
|
196
|
+
'float'
|
197
|
+
when Numeric
|
198
|
+
'numeric'
|
199
|
+
else
|
200
|
+
raise InvalidParams.new("`type` should be integer, float, or numeric")
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
#
|
2
|
+
# Fluentd
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require 'fluent/counter/error'
|
18
|
+
|
19
|
+
module Fluent
|
20
|
+
module Counter
|
21
|
+
class Validator
|
22
|
+
VALID_NAME = /\A[a-z][a-zA-Z0-9\-_]*\Z/
|
23
|
+
VALID_SCOPE_NAME = /\A[a-z][\ta-zA-Z0-9\-_]*\Z/
|
24
|
+
VALID_METHODS = %w(establish init delete inc get reset)
|
25
|
+
|
26
|
+
def self.request(data)
|
27
|
+
errors = []
|
28
|
+
raise "Received data is not Hash: #{data}" unless data.is_a?(Hash)
|
29
|
+
|
30
|
+
unless data['id']
|
31
|
+
errors << Fluent::Counter::InvalidRequest.new('Request should include `id`')
|
32
|
+
end
|
33
|
+
|
34
|
+
if !data['method']
|
35
|
+
errors << Fluent::Counter::InvalidRequest.new('Request should include `method`')
|
36
|
+
elsif !(VALID_NAME =~ data['method'])
|
37
|
+
errors << Fluent::Counter::InvalidRequest.new('`method` is the invalid format')
|
38
|
+
elsif !VALID_METHODS.include?(data['method'])
|
39
|
+
errors << Fluent::Counter::MethodNotFound.new("Unknown method name passed: #{data['method']}")
|
40
|
+
end
|
41
|
+
|
42
|
+
errors.map(&:to_hash)
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(*types)
|
46
|
+
@types = types.map(&:to_s)
|
47
|
+
@empty = @types.delete('empty')
|
48
|
+
end
|
49
|
+
|
50
|
+
def call(data)
|
51
|
+
success = []
|
52
|
+
errors = []
|
53
|
+
|
54
|
+
if @empty && data.empty?
|
55
|
+
errors << Fluent::Counter::InvalidParams.new('One or more `params` are required')
|
56
|
+
else
|
57
|
+
data.each do |d|
|
58
|
+
begin
|
59
|
+
@types.each { |type| dispatch(type, d) }
|
60
|
+
success << d
|
61
|
+
rescue => e
|
62
|
+
errors << e
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
[success, errors]
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def dispatch(type, data)
|
73
|
+
send("validate_#{type}!", data)
|
74
|
+
rescue NoMethodError => e
|
75
|
+
raise Fluent::Counter::InternalServerError.new(e)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class ArrayValidator < Validator
|
80
|
+
def validate_key!(name)
|
81
|
+
unless name.is_a?(String)
|
82
|
+
raise Fluent::Counter::InvalidParams.new('The type of `key` should be String')
|
83
|
+
end
|
84
|
+
|
85
|
+
unless VALID_NAME =~ name
|
86
|
+
raise Fluent::Counter::InvalidParams.new('`key` is the invalid format')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def validate_scope!(name)
|
91
|
+
unless name.is_a?(String)
|
92
|
+
raise Fluent::Counter::InvalidParams.new('The type of `scope` should be String')
|
93
|
+
end
|
94
|
+
|
95
|
+
unless VALID_SCOPE_NAME =~ name
|
96
|
+
raise Fluent::Counter::InvalidParams.new('`scope` is the invalid format')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class HashValidator < Validator
|
102
|
+
def validate_name!(hash)
|
103
|
+
name = hash['name']
|
104
|
+
unless name
|
105
|
+
raise Fluent::Counter::InvalidParams.new('`name` is required')
|
106
|
+
end
|
107
|
+
|
108
|
+
unless name.is_a?(String)
|
109
|
+
raise Fluent::Counter::InvalidParams.new('The type of `name` should be String')
|
110
|
+
end
|
111
|
+
|
112
|
+
unless VALID_NAME =~ name
|
113
|
+
raise Fluent::Counter::InvalidParams.new("`name` is the invalid format")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def validate_value!(hash)
|
118
|
+
value = hash['value']
|
119
|
+
unless value
|
120
|
+
raise Fluent::Counter::InvalidParams.new('`value` is required')
|
121
|
+
end
|
122
|
+
|
123
|
+
unless value.is_a?(Numeric)
|
124
|
+
raise Fluent::Counter::InvalidParams.new("The type of `value` type should be Numeric")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def validate_reset_interval!(hash)
|
129
|
+
interval = hash['reset_interval']
|
130
|
+
|
131
|
+
unless interval
|
132
|
+
raise Fluent::Counter::InvalidParams.new('`reset_interval` is required')
|
133
|
+
end
|
134
|
+
|
135
|
+
unless interval.is_a?(Numeric)
|
136
|
+
raise Fluent::Counter::InvalidParams.new('The type of `reset_interval` should be Numeric')
|
137
|
+
end
|
138
|
+
|
139
|
+
if interval < 0
|
140
|
+
raise Fluent::Counter::InvalidParams.new('`reset_interval` should be a positive number')
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
data/lib/fluent/env.rb
CHANGED
@@ -20,6 +20,7 @@ module Fluent
|
|
20
20
|
DEFAULT_CONFIG_PATH = ENV['FLUENT_CONF'] || '/etc/fluent/fluent.conf'
|
21
21
|
DEFAULT_PLUGIN_DIR = ENV['FLUENT_PLUGIN'] || '/etc/fluent/plugin'
|
22
22
|
DEFAULT_SOCKET_PATH = ENV['FLUENT_SOCKET'] || '/var/run/fluent/fluent.sock'
|
23
|
+
DEFAULT_BACKUP_DIR = ENV['FLUENT_BACKUP_DIR'] || '/tmp/fluent'
|
23
24
|
DEFAULT_OJ_OPTIONS = {bigdecimal_load: :float, mode: :compat, use_to_json: true}
|
24
25
|
|
25
26
|
def self.windows?
|
data/lib/fluent/log.rb
CHANGED
@@ -25,10 +25,10 @@ module Fluent::Plugin
|
|
25
25
|
def initialize
|
26
26
|
super
|
27
27
|
|
28
|
-
@_regexp_and_conditions =
|
29
|
-
@_exclude_and_conditions =
|
30
|
-
@_regexp_or_conditions =
|
31
|
-
@_exclude_or_conditions =
|
28
|
+
@_regexp_and_conditions = nil
|
29
|
+
@_exclude_and_conditions = nil
|
30
|
+
@_regexp_or_conditions = nil
|
31
|
+
@_exclude_or_conditions = nil
|
32
32
|
end
|
33
33
|
|
34
34
|
# for test
|
@@ -153,35 +153,31 @@ module Fluent::Plugin
|
|
153
153
|
end
|
154
154
|
end
|
155
155
|
|
156
|
-
@_regexp_and_conditions = regexp_and_conditions.values
|
157
|
-
@_exclude_and_conditions = exclude_and_conditions.values
|
158
|
-
@_regexp_or_conditions = regexp_or_conditions.values
|
159
|
-
@_exclude_or_conditions = exclude_or_conditions.values
|
156
|
+
@_regexp_and_conditions = regexp_and_conditions.values unless regexp_and_conditions.empty?
|
157
|
+
@_exclude_and_conditions = exclude_and_conditions.values unless exclude_and_conditions.empty?
|
158
|
+
@_regexp_or_conditions = regexp_or_conditions.values unless regexp_or_conditions.empty?
|
159
|
+
@_exclude_or_conditions = exclude_or_conditions.values unless exclude_or_conditions.empty?
|
160
160
|
end
|
161
161
|
|
162
162
|
def filter(tag, time, record)
|
163
|
-
result = nil
|
164
163
|
begin
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
throw :break_loop if expression.match?(record)
|
177
|
-
end
|
178
|
-
result = record
|
164
|
+
if @_regexp_and_conditions && @_regexp_and_conditions.any? { |expression| !expression.match?(record) }
|
165
|
+
return nil
|
166
|
+
end
|
167
|
+
if @_regexp_or_conditions && @_regexp_or_conditions.none? { |expression| expression.match?(record) }
|
168
|
+
return nil
|
169
|
+
end
|
170
|
+
if @_exclude_and_conditions && @_exclude_and_conditions.all? { |expression| expression.match?(record) }
|
171
|
+
return nil
|
172
|
+
end
|
173
|
+
if @_exclude_or_conditions && @_exclude_or_conditions.any? { |expression| expression.match?(record) }
|
174
|
+
return nil
|
179
175
|
end
|
180
176
|
rescue => e
|
181
177
|
log.warn "failed to grep events", error: e
|
182
178
|
log.warn_backtrace
|
183
179
|
end
|
184
|
-
|
180
|
+
record
|
185
181
|
end
|
186
182
|
|
187
183
|
Expression = Struct.new(:key, :pattern) do
|
data/lib/fluent/plugin/output.rb
CHANGED
@@ -14,6 +14,7 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
16
|
|
17
|
+
require 'fluent/error'
|
17
18
|
require 'fluent/plugin/base'
|
18
19
|
require 'fluent/plugin_helper/record_accessor'
|
19
20
|
require 'fluent/log'
|
@@ -873,7 +874,7 @@ module Fluent
|
|
873
874
|
begin
|
874
875
|
oldest = @buffer.dequeue_chunk
|
875
876
|
if oldest
|
876
|
-
log.warn "dropping oldest chunk to make space after buffer overflow", chunk_id: oldest.unique_id
|
877
|
+
log.warn "dropping oldest chunk to make space after buffer overflow", chunk_id: dump_unique_id_hex(oldest.unique_id)
|
877
878
|
@buffer.purge_chunk(oldest.unique_id)
|
878
879
|
else
|
879
880
|
log.error "no queued chunks to be dropped for drop_oldest_chunk"
|
@@ -1056,6 +1057,8 @@ module Fluent
|
|
1056
1057
|
end
|
1057
1058
|
end
|
1058
1059
|
|
1060
|
+
UNRECOVERABLE_ERRORS = [Fluent::UnrecoverableError, TypeError, ArgumentError, NoMethodError]
|
1061
|
+
|
1059
1062
|
def try_flush
|
1060
1063
|
chunk = @buffer.dequeue_chunk
|
1061
1064
|
return unless chunk
|
@@ -1100,6 +1103,37 @@ module Fluent
|
|
1100
1103
|
commit_write(chunk_id, delayed: false, secondary: using_secondary)
|
1101
1104
|
log.trace "done to commit a chunk", chunk: dump_chunk_id
|
1102
1105
|
end
|
1106
|
+
rescue *UNRECOVERABLE_ERRORS => e
|
1107
|
+
if @secondary
|
1108
|
+
if using_secondary
|
1109
|
+
log.warn "got unrecoverable error in secondary.", error: e
|
1110
|
+
backup_chunk(chunk, using_secondary, output.delayed_commit)
|
1111
|
+
else
|
1112
|
+
if (self.class == @secondary.class)
|
1113
|
+
log.warn "got unrecoverable error in primary and secondary type is same as primary. Skip secondary", error: e
|
1114
|
+
backup_chunk(chunk, using_secondary, output.delayed_commit)
|
1115
|
+
else
|
1116
|
+
# Call secondary output directly without retry update.
|
1117
|
+
# In this case, delayed commit causes inconsistent state in dequeued chunks so async output in secondary is not allowed for now.
|
1118
|
+
if @secondary.delayed_commit
|
1119
|
+
log.warn "got unrecoverable error in primary and secondary is async output. Skip secondary for backup", error: e
|
1120
|
+
backup_chunk(chunk, using_secondary, output.delayed_commit)
|
1121
|
+
else
|
1122
|
+
log.warn "got unrecoverable error in primary. Skip retry and flush chunk to secondary", error: e
|
1123
|
+
begin
|
1124
|
+
@secondary.write(chunk)
|
1125
|
+
commit_write(chunk_id, delayed: output.delayed_commit, secondary: true)
|
1126
|
+
rescue => e
|
1127
|
+
log.warn "got an error in secondary for unrecoverable error", error: e
|
1128
|
+
backup_chunk(chunk, using_secondary, output.delayed_commit)
|
1129
|
+
end
|
1130
|
+
end
|
1131
|
+
end
|
1132
|
+
end
|
1133
|
+
else
|
1134
|
+
log.warn "got unrecoverable error in primary and no secondary", error: e
|
1135
|
+
backup_chunk(chunk, using_secondary, output.delayed_commit)
|
1136
|
+
end
|
1103
1137
|
rescue => e
|
1104
1138
|
log.debug "taking back chunk for errors.", chunk: dump_unique_id_hex(chunk.unique_id)
|
1105
1139
|
if output.delayed_commit
|
@@ -1115,6 +1149,21 @@ module Fluent
|
|
1115
1149
|
end
|
1116
1150
|
end
|
1117
1151
|
|
1152
|
+
def backup_chunk(chunk, using_secondary, delayed_commit)
|
1153
|
+
unique_id = dump_unique_id_hex(chunk.unique_id)
|
1154
|
+
safe_plugin_id = plugin_id.gsub(/[ "\/\\:;|*<>?]/, '_')
|
1155
|
+
backup_base_dir = system_config.root_dir || DEFAULT_BACKUP_DIR
|
1156
|
+
backup_file = File.join(backup_base_dir, 'backup', "worker#{fluentd_worker_id}", safe_plugin_id, "#{unique_id}.log")
|
1157
|
+
backup_dir = File.dirname(backup_file)
|
1158
|
+
|
1159
|
+
log.warn "bad chunk is moved to #{backup_file}"
|
1160
|
+
FileUtils.mkdir_p(backup_dir) unless Dir.exist?(backup_dir)
|
1161
|
+
File.open(backup_file, 'ab', system_config.file_permission || 0644) { |f|
|
1162
|
+
chunk.write_to(f)
|
1163
|
+
}
|
1164
|
+
commit_write(chunk.unique_id, secondary: using_secondary, delayed: delayed_commit)
|
1165
|
+
end
|
1166
|
+
|
1118
1167
|
def check_slow_flush(start)
|
1119
1168
|
elapsed_time = Fluent::Clock.now - start
|
1120
1169
|
if elapsed_time > @slow_flush_log_threshold
|