fluentd 0.10.49 → 0.10.50

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9d9ea1a003d545a18baf0c61a85fb216d183cb07
4
- data.tar.gz: c3ccc3a4665acdf5726287ce6b29e2e6af5c5194
3
+ metadata.gz: dcc68a7c0e54890fee8388d37e9801edac3fb77f
4
+ data.tar.gz: 992e8adb5d7c043f91f58e095aaf1d6cb5c7b363
5
5
  SHA512:
6
- metadata.gz: cd8c5f25fc9ca7cf31ac5d0fb2de908cee79204deb11e821e31774b8aecc112557ebcc0f0d2ddc033fcb4b189e31b05ff9fbe1a07d04ad4630a2b39980038f8b
7
- data.tar.gz: af93420f7ed26fdf703d11723aa27e76a43528b90b67879687e8bb5fa94bb9bba55fbad61dbeaa10dc2218de776cc39df3e13372204a50359008a4abcebdf6f3
6
+ metadata.gz: 84b5ed1c244b405de5ed3d637fbad708cbb361a42a7efd13793c2992f7aad66c9c9d49256b105545e65e2627d5e3de64838e0df05d9e0d6bd82e6c47d7f56230
7
+ data.tar.gz: ad3c062f06ec7b472f7efb72acc693c9b71e83fc4eaf1df4c6c23ca5820543cb72bfea59778f3f5cb42f1510927f1f52c9f674a8bee3debdb3f86c4cc75e5950
data/ChangeLog CHANGED
@@ -1,3 +1,14 @@
1
+ Release 0.10.50 - 2014/06/17
2
+
3
+ * in_tail: Fix race condition at shutdown
4
+ * in_tail: read_from_head now works without pos_file
5
+ * in_tail: Fix multiline format parsing without format_firstline
6
+ * formatter: Add add_newline option to SingleValueFormatter
7
+ * parser: Add block based API to support multi-events parsing
8
+ * config: Fix encoding mismatch when @include used in v1
9
+ * engine: Serialize shutdown order. Now shutdown input plugins first
10
+ * Update rspec to 3.x
11
+
1
12
  Release 0.10.49 - 2014/06/05
2
13
 
3
14
  * in_http: Add format option to support various input data format
@@ -28,7 +28,7 @@ Gem::Specification.new do |gem|
28
28
  gem.add_development_dependency("rake", [">= 0.9.2"])
29
29
  gem.add_development_dependency("flexmock")
30
30
  gem.add_development_dependency("parallel_tests", [">= 0.15.3"])
31
- gem.add_development_dependency("rspec", ["~> 2.13"])
31
+ gem.add_development_dependency("rspec", ["~> 3.0.0"])
32
32
  gem.add_development_dependency("simplecov", ["~> 0.6.4"])
33
33
  gem.add_development_dependency("rr", [">= 1.0.0"])
34
34
  gem.add_development_dependency("timecop", [">= 0.3.0"])
@@ -142,16 +142,16 @@ module Fluent
142
142
  basepath = File.dirname(path)
143
143
  fname = File.basename(path)
144
144
  data = File.read(path)
145
+ data.force_encoding('UTF-8')
145
146
  ss = StringScanner.new(data)
146
147
  V1Parser.new(ss, basepath, fname, @eval_context).parse_element(true, nil, attrs, elems)
147
148
  }
148
-
149
149
  else
150
+ require 'open-uri'
150
151
  basepath = '/'
151
152
  fname = path
152
- require 'open-uri'
153
- data = nil
154
- open(uri) { |f| data = f.read }
153
+ data = open(uri) { |f| f.read }
154
+ data.force_encoding('UTF-8')
155
155
  ss = StringScanner.new(data)
156
156
  V1Parser.new(ss, basepath, fname, @eval_context).parse_element(true, nil, attrs, elems)
157
157
  end
@@ -22,7 +22,8 @@ module Fluent
22
22
  @sources = []
23
23
  @match_cache = {}
24
24
  @match_cache_keys = []
25
- @started = []
25
+ @started_sources = []
26
+ @started_matches = []
26
27
  @default_loop = nil
27
28
  @engine_stopped = false
28
29
 
@@ -254,25 +255,40 @@ module Fluent
254
255
  def start
255
256
  @matches.each {|m|
256
257
  m.start
257
- @started << m
258
+ @started_matches << m
258
259
  }
259
260
  @sources.each {|s|
260
261
  s.start
261
- @started << s
262
+ @started_sources << s
262
263
  }
263
264
  end
264
265
 
265
266
  def shutdown
266
- @started.map {|s|
267
+ # Shutdown Input plugin first to prevent emitting to terminated Output plugin
268
+ @started_sources.map { |s|
267
269
  Thread.new do
268
270
  begin
269
271
  s.shutdown
270
272
  rescue => e
271
- $log.warn "unexpected error while shutting down", :error_class=>e.class, :error=>e
273
+ $log.warn "unexpected error while shutting down", :plugin => s.class, :plugin_id => s.plugin_id, :error_class => e.class, :error => e
272
274
  $log.warn_backtrace
273
275
  end
274
276
  end
275
- }.each {|t|
277
+ }.each { |t|
278
+ t.join
279
+ }
280
+ # Output plugin as filter emits records at shutdown so emit problem still exist.
281
+ # This problem will be resolved after actual filter mechanizm.
282
+ @started_matches.map { |m|
283
+ Thread.new do
284
+ begin
285
+ m.shutdown
286
+ rescue => e
287
+ $log.warn "unexpected error while shutting down", :plugin => m.output.class, :plugin_id => m.output.plugin_id, :error_class => e.class, :error => e
288
+ $log.warn_backtrace
289
+ end
290
+ end
291
+ }.each { |t|
276
292
  t.join
277
293
  }
278
294
  end
@@ -126,9 +126,12 @@ module Fluent
126
126
  include Configurable
127
127
 
128
128
  config_param :message_key, :string, :default => 'message'
129
+ config_param :add_newline, :bool, :default => true
129
130
 
130
131
  def format(tag, time, record)
131
- record[@message_key]
132
+ text = record[@message_key].to_s
133
+ text << "\n" if @add_newline
134
+ text
132
135
  end
133
136
  end
134
137
 
@@ -67,20 +67,6 @@ module Fluent
67
67
  def format_nocache(time)
68
68
  # will be overridden in initialize
69
69
  end
70
-
71
- def self.configure(conf)
72
- if time_format = conf['time_format']
73
- @time_format = time_format
74
- end
75
-
76
- if localtime = conf['localtime']
77
- @localtime = true
78
- elsif utc = conf['utc']
79
- @localtime = false
80
- end
81
-
82
- @timef = new(@time_format, @localtime)
83
- end
84
70
  end
85
71
 
86
72
 
@@ -147,7 +147,12 @@ module Fluent
147
147
  def call(text)
148
148
  m = @regexp.match(text)
149
149
  unless m
150
- return nil, nil
150
+ if block_given?
151
+ yield nil, nil
152
+ return
153
+ else
154
+ return nil, nil
155
+ end
151
156
  end
152
157
 
153
158
  time = nil
@@ -170,7 +175,11 @@ module Fluent
170
175
 
171
176
  time ||= Engine.now
172
177
 
173
- return time, record
178
+ if block_given?
179
+ yield time, record
180
+ else # keep backward compatibility. will be removed at v1
181
+ return time, record
182
+ end
174
183
  end
175
184
  end
176
185
 
@@ -202,9 +211,17 @@ module Fluent
202
211
  time = Engine.now
203
212
  end
204
213
 
205
- return time, record
214
+ if block_given?
215
+ yield time, record
216
+ else
217
+ return time, record
218
+ end
206
219
  rescue Yajl::ParseError
207
- return nil, nil
220
+ if block_given?
221
+ yield nil, nil
222
+ else
223
+ return nil, nil
224
+ end
208
225
  end
209
226
  end
210
227
 
@@ -267,7 +284,11 @@ module Fluent
267
284
  config_param :delimiter, :string, :default => "\t"
268
285
 
269
286
  def call(text)
270
- return values_map(text.split(@delimiter))
287
+ if block_given?
288
+ yield values_map(text.split(@delimiter))
289
+ else
290
+ return values_map(text.split(@delimiter))
291
+ end
271
292
  end
272
293
  end
273
294
 
@@ -291,7 +312,11 @@ module Fluent
291
312
  values.push(value)
292
313
  end
293
314
 
294
- return values_map(values)
315
+ if block_given?
316
+ yield values_map(values)
317
+ else
318
+ return values_map(values)
319
+ end
295
320
  end
296
321
  end
297
322
 
@@ -302,7 +327,11 @@ module Fluent
302
327
  end
303
328
 
304
329
  def call(text)
305
- return values_map(CSV.parse_line(text))
330
+ if block_given?
331
+ yield values_map(CSV.parse_line(text))
332
+ else
333
+ return values_map(CSV.parse_line(text))
334
+ end
306
335
  end
307
336
  end
308
337
 
@@ -314,7 +343,11 @@ module Fluent
314
343
  def call(text)
315
344
  record = {}
316
345
  record[@message_key] = text
317
- return Engine.now, record
346
+ if block_given?
347
+ yield Engine.now, record
348
+ else
349
+ return Engine.now, record
350
+ end
318
351
  end
319
352
  end
320
353
 
@@ -331,7 +364,12 @@ module Fluent
331
364
  def call(text)
332
365
  m = REGEXP.match(text)
333
366
  unless m
334
- return nil, nil
367
+ if block_given?
368
+ yield nil, nil
369
+ return
370
+ else
371
+ return nil, nil
372
+ end
335
373
  end
336
374
 
337
375
  host = m['host']
@@ -369,7 +407,11 @@ module Fluent
369
407
  "agent" => agent,
370
408
  }
371
409
 
372
- return time, record
410
+ if block_given?
411
+ yield time, record
412
+ else
413
+ return time, record
414
+ end
373
415
  end
374
416
  end
375
417
 
@@ -396,16 +438,24 @@ module Fluent
396
438
 
397
439
  if @format_firstline
398
440
  check_format_regexp(@format_firstline, 'format_firstline')
399
- @regex = Regexp.new(@format_firstline[1..-2])
441
+ @firstline_regex = Regexp.new(@format_firstline[1..-2])
400
442
  end
401
443
  end
402
444
 
403
- def call(text)
404
- @parser.call(text)
445
+ def call(text, &block)
446
+ if block
447
+ @parser.call(text, &block)
448
+ else
449
+ @parser.call(text)
450
+ end
451
+ end
452
+
453
+ def has_firstline?
454
+ !!@format_firstline
405
455
  end
406
456
 
407
457
  def firstline?(text)
408
- @regex.match(text)
458
+ @firstline_regex.match(text)
409
459
  end
410
460
 
411
461
  private
@@ -506,7 +556,6 @@ module Fluent
506
556
  end
507
557
 
508
558
  @parser = RegexpParser.new(regexp, conf)
509
-
510
559
  else
511
560
  # built-in template
512
561
  begin
@@ -524,8 +573,12 @@ module Fluent
524
573
  return true
525
574
  end
526
575
 
527
- def parse(text)
528
- return @parser.call(text)
576
+ def parse(text, &block)
577
+ if block
578
+ @parser.call(text, &block)
579
+ else # keep backward compatibility. Will be removed at v1
580
+ return @parser.call(text)
581
+ end
529
582
  end
530
583
  end
531
584
  end
@@ -22,7 +22,7 @@ module Fluent
22
22
  @data.force_encoding('ASCII-8BIT')
23
23
  now = Time.now.utc
24
24
  u1 = ((now.to_i*1000*1000+now.usec) << 12 | rand(0xfff))
25
- @unique_id = [u1 >> 32, u1 & u1 & 0xffffffff, rand(0xffffffff), rand(0xffffffff)].pack('NNNN')
25
+ @unique_id = [u1 >> 32, u1 & 0xffffffff, rand(0xffffffff), rand(0xffffffff)].pack('NNNN')
26
26
  super(key)
27
27
  end
28
28
 
@@ -54,13 +54,6 @@ module Fluent
54
54
  end
55
55
 
56
56
  class KeepaliveManager < Coolio::TimerWatcher
57
- class TimerValue
58
- def initialize
59
- @value = 0
60
- end
61
- attr_accessor :value
62
- end
63
-
64
57
  def initialize(timeout)
65
58
  super(1, true)
66
59
  @cons = {}
@@ -131,14 +131,14 @@ module Fluent
131
131
  pri = m[1].to_i
132
132
  text = m[2]
133
133
 
134
- time, record = @parser.parse(text)
135
- unless time && record
136
- log.warn "pattern not match: #{text.inspect}"
137
- return
138
- end
139
-
140
- emit(pri, time, record)
134
+ @parser.parse(text) { |time, record|
135
+ unless time && record
136
+ log.warn "pattern not match: #{text.inspect}"
137
+ return
138
+ end
141
139
 
140
+ emit(pri, time, record)
141
+ }
142
142
  rescue
143
143
  log.warn data.dump, :error=>$!.to_s
144
144
  log.debug_backtrace
@@ -129,7 +129,7 @@ module Fluent
129
129
  end
130
130
 
131
131
  def setup_watcher(path, pe)
132
- tw = TailWatcher.new(path, @rotate_wait, pe, log, method(:update_watcher), &method(:receive_lines))
132
+ tw = TailWatcher.new(path, @rotate_wait, pe, log, @read_from_head, method(:update_watcher), &method(:receive_lines))
133
133
  tw.attach(@loop)
134
134
  tw
135
135
  end
@@ -158,7 +158,7 @@ module Fluent
158
158
  if tw
159
159
  tw.unwatched = unwatched
160
160
  if immediate
161
- close_watcher(tw)
161
+ close_watcher(tw, false)
162
162
  else
163
163
  close_watcher_after_rotate_wait(tw)
164
164
  end
@@ -173,8 +173,12 @@ module Fluent
173
173
  close_watcher_after_rotate_wait(rotated_tw) if rotated_tw
174
174
  end
175
175
 
176
- def close_watcher(tw)
177
- tw.close
176
+ # TailWatcher#close is called by another thread at shutdown phase.
177
+ # It causes 'can't modify string; temporarily locked' error in IOHandler
178
+ # so adding close_io argument to avoid this problem.
179
+ # At shutdown, IOHandler's io will be released automatically after detached the event loop
180
+ def close_watcher(tw, close_io = true)
181
+ tw.close(close_io)
178
182
  flush_buffer(tw)
179
183
  if tw.unwatched && @pf
180
184
  @pf[tw.path].update_pos(PositionFile::UNWATCHED_POSITION)
@@ -189,17 +193,18 @@ module Fluent
189
193
  def flush_buffer(tw)
190
194
  if lb = tw.line_buffer
191
195
  lb.chomp!
192
- time, record = parse_line(lb)
193
- if time && record
194
- tag = if @tag_prefix || @tag_suffix
195
- @tag_prefix + tw.tag + @tag_suffix
196
- else
197
- @tag
198
- end
199
- Engine.emit(tag, time, record)
200
- else
201
- log.warn "got incomplete line at shutdown from #{tw.path}: #{lb.inspect}"
202
- end
196
+ @parser.parse(lb) { |time, record|
197
+ if time && record
198
+ tag = if @tag_prefix || @tag_suffix
199
+ @tag_prefix + tw.tag + @tag_suffix
200
+ else
201
+ @tag
202
+ end
203
+ Engine.emit(tag, time, record)
204
+ else
205
+ log.warn "got incomplete line at shutdown from #{tw.path}: #{lb.inspect}"
206
+ end
207
+ }
203
208
  end
204
209
  end
205
210
 
@@ -226,19 +231,16 @@ module Fluent
226
231
  end
227
232
  end
228
233
 
229
- def parse_line(line)
230
- return @parser.parse(line)
231
- end
232
-
233
234
  def convert_line_to_event(line, es)
234
235
  begin
235
236
  line.chomp! # remove \n
236
- time, record = parse_line(line)
237
- if time && record
238
- es.add(time, record)
239
- else
240
- log.warn "pattern not match: #{line.inspect}"
241
- end
237
+ @parser.parse(line) { |time, record|
238
+ if time && record
239
+ es.add(time, record)
240
+ else
241
+ log.warn "pattern not match: #{line.inspect}"
242
+ end
243
+ }
242
244
  rescue => e
243
245
  log.warn line.dump, :error => e.to_s
244
246
  log.debug_backtrace(e.backtrace)
@@ -256,29 +258,43 @@ module Fluent
256
258
  def parse_multilines(lines, tail_watcher)
257
259
  lb = tail_watcher.line_buffer
258
260
  es = MultiEventStream.new
259
- lines.each { |line|
260
- if @parser.parser.firstline?(line)
261
- if lb
262
- convert_line_to_event(lb, es)
263
- end
264
- lb = line
265
- else
266
- if lb.nil?
267
- log.warn "got incomplete line before first line from #{tail_watcher.path}: #{lb.inspect}"
261
+ if @parser.parser.has_firstline?
262
+ lines.each { |line|
263
+ if @parser.parser.firstline?(line)
264
+ if lb
265
+ convert_line_to_event(lb, es)
266
+ end
267
+ lb = line
268
268
  else
269
- lb << line
269
+ if lb.nil?
270
+ log.warn "got incomplete line before first line from #{tail_watcher.path}: #{lb.inspect}"
271
+ else
272
+ lb << line
273
+ end
270
274
  end
275
+ }
276
+ else
277
+ lb ||= ''
278
+ lines.each do |line|
279
+ lb << line
280
+ @parser.parse(lb) { |time, record|
281
+ if time && record
282
+ convert_line_to_event(lb, es)
283
+ lb = ''
284
+ end
285
+ }
271
286
  end
272
- }
287
+ end
273
288
  tail_watcher.line_buffer = lb
274
289
  es
275
290
  end
276
291
 
277
292
  class TailWatcher
278
- def initialize(path, rotate_wait, pe, log, update_watcher, &receive_lines)
293
+ def initialize(path, rotate_wait, pe, log, read_from_head, update_watcher, &receive_lines)
279
294
  @path = path
280
295
  @rotate_wait = rotate_wait
281
296
  @pe = pe || MemoryPositionEntry.new
297
+ @read_from_head = read_from_head
282
298
  @receive_lines = receive_lines
283
299
  @update_watcher = update_watcher
284
300
 
@@ -313,8 +329,8 @@ module Fluent
313
329
  @stat_trigger.detach if @stat_trigger.attached?
314
330
  end
315
331
 
316
- def close
317
- if @io_handler
332
+ def close(close_io = true)
333
+ if close_io && @io_handler
318
334
  @io_handler.on_notify
319
335
  @io_handler.close
320
336
  end
@@ -354,7 +370,7 @@ module Fluent
354
370
  # seek to the end of the any files.
355
371
  # logs may duplicate without this seek because it's not sure the file is
356
372
  # existent file or rotated new file.
357
- pos = fsize
373
+ pos = @read_from_head ? 0 : fsize
358
374
  @pe.update(inode, pos)
359
375
  end
360
376
  io.seek(pos)