fluentd 0.10.20 → 0.10.21

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.

data/ChangeLog CHANGED
@@ -1,4 +1,14 @@
1
1
 
2
+ Release 0.10.21 - 2012/05/01
3
+
4
+ * in_tail and TextParser support 'format json'
5
+ * in_tail and TextParser support 'time_key' and 'time_format' parameters for 'format json'
6
+ * in_tail: improved the file reader not to consume too much memory if the file is huge
7
+ * out_stdout: uses logger instead of STDOUT to show records even if Fluentd is daemonized
8
+ * Fixed the type of 'retry_wait' parameter of buffered output plugins from 'float' to 'time'
9
+ * Fixed owner of log files for log rotation to work properly with --user and --group options
10
+
11
+
2
12
  Release 0.10.20 - 2012/04/27
3
13
 
4
14
  * Fixed Config#to_s
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.20
1
+ 0.10.21
@@ -227,6 +227,14 @@ class Log
227
227
  nil
228
228
  end
229
229
 
230
+ def write(data)
231
+ @out.write(data)
232
+ end
233
+
234
+ def flush
235
+ @out.flush
236
+ end
237
+
230
238
  private
231
239
  def event(level, args)
232
240
  time = Time.now
@@ -174,7 +174,7 @@ class BufferedOutput < Output
174
174
  config_param :buffer_type, :string, :default => 'memory'
175
175
  config_param :flush_interval, :time, :default => 60
176
176
  config_param :retry_limit, :integer, :default => 17
177
- config_param :retry_wait, :float, :default => 1.0
177
+ config_param :retry_wait, :time, :default => 1.0
178
178
  config_param :num_threads, :integer, :default => 1
179
179
 
180
180
  def configure(conf)
@@ -19,95 +19,136 @@ module Fluent
19
19
 
20
20
 
21
21
  class TextParser
22
- TEMPLATES = {
23
- 'apache' => [/^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/, "%d/%b/%Y:%H:%M:%S %z"],
24
- 'syslog' => [/^(?<time>[^ ]*\s*[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?[^\:]*\: *(?<message>.*)$/, "%b %d %H:%M:%S"],
25
- }
22
+ class RegexpParser
23
+ include Configurable
26
24
 
27
- def self.register_template(name, regexp, time_format=nil)
28
- TEMPLATES[name] = [regexp, time_format]
29
- end
25
+ config_param :time_format, :string
30
26
 
31
- def self.get_template(name)
32
- return *TEMPLATES[name]
33
- end
27
+ def initialize(regexp, conf={})
28
+ super()
29
+ @regexp = regexp
30
+ unless conf.empty?
31
+ configure(conf)
32
+ end
33
+ end
34
34
 
35
- def initialize
36
- require 'time' # Time.strptime, Time.parse
37
- @regexp = nil
38
- @time_format = nil
39
- end
35
+ def call(text)
36
+ m = @regexp.match(text)
37
+ unless m
38
+ $log.debug "pattern not match: #{text}"
39
+ # TODO?
40
+ return nil, nil
41
+ end
42
+
43
+ time = nil
44
+ record = {}
45
+
46
+ m.names.each {|name|
47
+ if value = m[name]
48
+ case name
49
+ when "time"
50
+ if @time_format
51
+ time = Time.strptime(value, @time_format).to_i
52
+ else
53
+ time = Time.parse(value).to_i
54
+ end
55
+ else
56
+ record[name] = value
57
+ end
58
+ end
59
+ }
40
60
 
41
- attr_accessor :regexp, :time_format
61
+ time ||= Engine.now
42
62
 
43
- def use_template(name)
44
- @regexp, @time_format = TextParser.get_template(name)
45
- unless @regexp
46
- raise ConfigError, "Unknown format template '#{name}'"
63
+ return time, record
47
64
  end
48
65
  end
49
66
 
50
- def configure(conf, required=true)
51
- if format = conf['format']
52
- if format[0] == ?/ && format[format.length-1] == ?/
53
- # regexp
54
- begin
55
- @regexp = Regexp.new(format[1..-2])
56
- if @regexp.named_captures.empty?
57
- raise "No named captures"
58
- end
59
- rescue
60
- raise ConfigError, "Invalid regexp '#{format[1..-2]}': #{$!}"
61
- end
67
+ class JSONParser
68
+ include Configurable
69
+
70
+ config_param :time_key, :string, :default => 'time'
71
+ config_param :time_format, :string, :default => nil
62
72
 
73
+ def call(text)
74
+ record = Yajl.load(text)
75
+
76
+ if value = record.delete(@time_key)
77
+ if @time_format
78
+ time = Time.strptime(value, @time_format).to_i
79
+ else
80
+ time = value.to_i
81
+ end
63
82
  else
64
- # template
65
- use_template(format)
83
+ time = Engine.now
66
84
  end
67
- else
68
- return nil if !required
69
- raise ConfigError, "'format' parameter is required"
85
+
86
+ return time, record
70
87
  end
88
+ end
71
89
 
72
- if time_format = conf['time_format']
73
- unless @regexp.names.include?('time')
74
- raise ConfigError, "'time_format' parameter is invalid when format doesn't have 'time' capture"
75
- end
76
- @time_format = time_format
90
+ TEMPLATES = {
91
+ 'apache' => RegexpParser.new(/^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/, {'time_format'=>"%d/%b/%Y:%H:%M:%S %z"}),
92
+ 'syslog' => RegexpParser.new(/^(?<time>[^ ]*\s*[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?[^\:]*\: *(?<message>.*)$/, {'time_format'=>"%b %d %H:%M:%S"}),
93
+ 'json' => JSONParser.new,
94
+ }
95
+
96
+ def self.register_template(name, regexp_or_proc, time_format=nil)
97
+ if regexp_or_proc.is_a?(Regexp)
98
+ pr = regexp_or_proc
99
+ else
100
+ regexp = regexp_or_proc
101
+ pr = RegexpParser.new(regexp, {'time_format'=>time_format})
77
102
  end
78
103
 
79
- return true
104
+ TEMPLATES[name] = pr
80
105
  end
81
106
 
82
- def parse(text)
83
- m = @regexp.match(text)
84
- unless m
85
- $log.debug "pattern not match: #{text}"
86
- # TODO?
87
- return nil, nil
88
- end
107
+ def initialize
108
+ @parser = nil
109
+ end
89
110
 
90
- time = nil
91
- record = {}
111
+ def configure(conf, required=true)
112
+ format = conf['format']
92
113
 
93
- m.names.each {|name|
94
- if value = m[name]
95
- case name
96
- when "time"
97
- if @time_format
98
- time = Time.strptime(value, @time_format).to_i
99
- else
100
- time = Time.parse(value).to_i
101
- end
102
- else
103
- record[name] = value
114
+ if format == nil
115
+ if required
116
+ raise ConfigError, "'format' parameter is required"
117
+ else
118
+ return nil
119
+ end
120
+ end
121
+
122
+ if format[0] == ?/ && format[format.length-1] == ?/
123
+ # regexp
124
+ begin
125
+ regexp = Regexp.new(format[1..-2])
126
+ if regexp.named_captures.empty?
127
+ raise "No named captures"
104
128
  end
129
+ rescue
130
+ raise ConfigError, "Invalid regexp '#{format[1..-2]}': #{$!}"
131
+ end
132
+
133
+ @parser = RegexpParser.new(regexp)
134
+
135
+ else
136
+ # built-in template
137
+ @parser = TEMPLATES[format]
138
+ unless @parser
139
+ raise ConfigError, "Unknown format template '#{format}'"
105
140
  end
106
- }
141
+ end
142
+
143
+ if @parser.respond_to?(:configure)
144
+ @parser.configure(conf)
145
+ end
107
146
 
108
- time ||= Engine.now
147
+ return true
148
+ end
109
149
 
110
- return time, record
150
+ def parse(text)
151
+ return @parser.call(text)
111
152
  end
112
153
  end
113
154
 
@@ -255,6 +255,8 @@ class TailInput < Input
255
255
  end
256
256
  end
257
257
 
258
+ MAX_LINES_AT_ONCE = 1000
259
+
258
260
  class IOHandler
259
261
  def initialize(io, pe, &receive_lines)
260
262
  $log.info "following tail of #{io.path}"
@@ -268,27 +270,36 @@ class TailInput < Input
268
270
  attr_reader :io
269
271
 
270
272
  def on_notify
271
- lines = []
273
+ begin
274
+ lines = []
275
+ read_more = false
272
276
 
273
- while true
274
277
  begin
275
- if @buffer.empty?
276
- @io.read_nonblock(2048, @buffer)
277
- else
278
- @buffer << @io.read_nonblock(2048, @iobuf)
279
- end
280
- while line = @buffer.slice!(/.*?\n/m)
281
- lines << line
278
+ while true
279
+ if @buffer.empty?
280
+ @io.read_nonblock(2048, @buffer)
281
+ else
282
+ @buffer << @io.read_nonblock(2048, @iobuf)
283
+ end
284
+ while line = @buffer.slice!(/.*?\n/m)
285
+ lines << line
286
+ end
287
+ if lines.size >= MAX_LINES_AT_ONCE
288
+ # not to use too much memory in case the file is very large
289
+ read_more = true
290
+ break
291
+ end
282
292
  end
283
293
  rescue EOFError
284
- break
285
294
  end
286
- end
287
295
 
288
- unless lines.empty?
289
- @receive_lines.call(lines)
290
- @pe.update_pos(@io.pos - @buffer.bytesize)
291
- end
296
+ unless lines.empty?
297
+ @receive_lines.call(lines)
298
+ @pe.update_pos(@io.pos - @buffer.bytesize)
299
+ end
300
+
301
+ end while read_more
302
+
292
303
  rescue
293
304
  $log.error $!.to_s
294
305
  $log.error_backtrace
@@ -21,13 +21,11 @@ module Fluent
21
21
  class StdoutOutput < Output
22
22
  Plugin.register_output('stdout', self)
23
23
 
24
- config_param :autoflush, :bool, :default => false
25
-
26
24
  def emit(tag, es, chain)
27
25
  es.each {|time,record|
28
- puts "#{Time.at(time).localtime} #{tag}: #{Yajl.dump(record)}"
26
+ $log.write "#{Time.at(time).localtime} #{tag}: #{Yajl.dump(record)}\n"
29
27
  }
30
- STDOUT.flush if @autoflush
28
+ $log.flush
31
29
 
32
30
  chain.next
33
31
  end
@@ -20,14 +20,21 @@ module Fluent
20
20
 
21
21
  class Supervisor
22
22
  class LoggerInitializer
23
- def initialize(path, level)
23
+ def initialize(path, level, chuser, chgroup)
24
24
  @path = path
25
25
  @level = level
26
+ @chuser = chuser
27
+ @chgroup = chgroup
26
28
  end
27
29
 
28
30
  def init
29
31
  if @path && @path != "-"
30
32
  @io = File.open(@path, "a")
33
+ if @chuser || @chgroup
34
+ chuid = @chuser ? `id -u #{@chuser}`.to_i : nil
35
+ chgid = @chgroup ? `id -g #{@chgroup}`.to_i : nil
36
+ File.chown(chuid, chgid, @path)
37
+ end
31
38
  else
32
39
  @io = STDOUT
33
40
  end
@@ -61,7 +68,7 @@ class Supervisor
61
68
  @plugin_dirs = opt[:plugin_dirs]
62
69
  @inline_config = opt[:inline_config]
63
70
 
64
- @log = LoggerInitializer.new(@log_path, @log_level)
71
+ @log = LoggerInitializer.new(@log_path, @log_level, @chuser, @chgroup)
65
72
  @finished = false
66
73
  @main_pid = nil
67
74
  end
@@ -1,5 +1,5 @@
1
1
  module Fluent
2
2
 
3
- VERSION = '0.10.20'
3
+ VERSION = '0.10.21'
4
4
 
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluentd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.20
4
+ version: 0.10.21
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-27 00:00:00.000000000Z
12
+ date: 2012-05-01 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: msgpack
16
- requirement: &70174884555560 !ruby/object:Gem::Requirement
16
+ requirement: &70301065884780 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.4.4
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70174884555560
24
+ version_requirements: *70301065884780
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: json
27
- requirement: &70174884536260 !ruby/object:Gem::Requirement
27
+ requirement: &70301065884240 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 1.4.3
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70174884536260
35
+ version_requirements: *70301065884240
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: yajl-ruby
38
- requirement: &70174884535660 !ruby/object:Gem::Requirement
38
+ requirement: &70301065883720 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '1.0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70174884535660
46
+ version_requirements: *70301065883720
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: cool.io
49
- requirement: &70174884535020 !ruby/object:Gem::Requirement
49
+ requirement: &70301065883120 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 1.1.0
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *70174884535020
57
+ version_requirements: *70301065883120
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: http_parser.rb
60
- requirement: &70174884534420 !ruby/object:Gem::Requirement
60
+ requirement: &70301065880180 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: 0.5.1
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *70174884534420
68
+ version_requirements: *70301065880180
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
- requirement: &70174884533820 !ruby/object:Gem::Requirement
71
+ requirement: &70301065879580 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: 0.9.2
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70174884533820
79
+ version_requirements: *70301065879580
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: rr
82
- requirement: &70174884533220 !ruby/object:Gem::Requirement
82
+ requirement: &70301065878920 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: 1.0.0
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70174884533220
90
+ version_requirements: *70301065878920
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: timecop
93
- requirement: &70174884532660 !ruby/object:Gem::Requirement
93
+ requirement: &70301065878300 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: 0.3.0
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *70174884532660
101
+ version_requirements: *70301065878300
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: jeweler
104
- requirement: &70174884532120 !ruby/object:Gem::Requirement
104
+ requirement: &70301065877720 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ! '>='
@@ -109,7 +109,7 @@ dependencies:
109
109
  version: 1.0.0
110
110
  type: :development
111
111
  prerelease: false
112
- version_requirements: *70174884532120
112
+ version_requirements: *70301065877720
113
113
  description:
114
114
  email: frsyuki@gmail.com
115
115
  executables: