fluentd 0.10.31 → 0.10.32

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,13 @@
1
1
 
2
+ Release 0.10.32 - 2013/03/04
3
+
4
+ * in_tail: fixed the nil error problem which occurs with pos_file option
5
+ * TextParser/in_tail: supports 'ltsv' (Labeled Tab-Separated Values) format: http://ltsv.org
6
+ * #105: TextParser/in_tail: fixed 'nginx' format
7
+ * #102: show log level in log messages
8
+ * Gemfile: uses https source of rubygems instead of http
9
+
10
+
2
11
  Release 0.10.31 - 2013/01/31
3
12
 
4
13
  * out_forward: supports TCP heartbeat
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.31
1
+ 0.10.32
@@ -40,13 +40,15 @@ class Log
40
40
  LEVEL_ERROR = 4
41
41
  LEVEL_FATAL = 5
42
42
 
43
+ LEVEL_TEXT = %w(trace debug info warn error fatal)
44
+
43
45
  def initialize(out=STDERR, level=LEVEL_TRACE)
44
46
  @out = out
45
47
  @level = level
46
48
  @debug_mode = false
47
49
  @self_event = false
48
50
  @tag = 'fluent'
49
- @time_format = '%Y-%m-%d %H:%M:%S %z: '
51
+ @time_format = '%Y-%m-%d %H:%M:%S %z '
50
52
  enable_color out.tty?
51
53
  end
52
54
 
@@ -95,7 +97,7 @@ class Log
95
97
  return if @level > LEVEL_TRACE
96
98
  args << block.call if block
97
99
  time, msg = event(:trace, args)
98
- puts [@color_trace, caller_line(time, 1), msg, @color_reset].join
100
+ puts [@color_trace, caller_line(time, 1, LEVEL_TRACE), msg, @color_reset].join
99
101
  end
100
102
  alias TRACE trace
101
103
 
@@ -103,7 +105,7 @@ class Log
103
105
  return if @level > LEVEL_TRACE
104
106
  time = Time.now
105
107
  backtrace.each {|msg|
106
- puts [" ", caller_line(time,4), msg].join
108
+ puts [" ", caller_line(time, 4, LEVEL_TRACE), msg].join
107
109
  }
108
110
  nil
109
111
  end
@@ -117,7 +119,7 @@ class Log
117
119
  return if @level > LEVEL_DEBUG
118
120
  args << block.call if block
119
121
  time, msg = event(:debug, args)
120
- puts [@color_debug, caller_line(time, 1), msg, @color_reset].join
122
+ puts [@color_debug, caller_line(time, 1, LEVEL_DEBUG), msg, @color_reset].join
121
123
  end
122
124
  alias DEBUG debug
123
125
 
@@ -125,7 +127,7 @@ class Log
125
127
  return if @level > LEVEL_DEBUG
126
128
  time = Time.now
127
129
  backtrace.each {|msg|
128
- puts [" ", caller_line(time,4), msg].join
130
+ puts [" ", caller_line(time, 4, LEVEL_DEBUG), msg].join
129
131
  }
130
132
  nil
131
133
  end
@@ -139,7 +141,7 @@ class Log
139
141
  return if @level > LEVEL_INFO
140
142
  args << block.call if block
141
143
  time, msg = event(:info, args)
142
- puts [@color_info, caller_line(time, 1), msg, @color_reset].join
144
+ puts [@color_info, caller_line(time, 1, LEVEL_INFO), msg, @color_reset].join
143
145
  end
144
146
  alias INFO info
145
147
 
@@ -147,7 +149,7 @@ class Log
147
149
  return if @level > LEVEL_INFO
148
150
  time = Time.now
149
151
  backtrace.each {|msg|
150
- puts [" ", caller_line(time,4), msg].join
152
+ puts [" ", caller_line(time, 4, LEVEL_INFO), msg].join
151
153
  }
152
154
  nil
153
155
  end
@@ -161,7 +163,7 @@ class Log
161
163
  return if @level > LEVEL_WARN
162
164
  args << block.call if block
163
165
  time, msg = event(:warn, args)
164
- puts [@color_warn, caller_line(time, 1), msg, @color_reset].join
166
+ puts [@color_warn, caller_line(time, 1, LEVEL_WARN), msg, @color_reset].join
165
167
  end
166
168
  alias WARN warn
167
169
 
@@ -169,7 +171,7 @@ class Log
169
171
  return if @level > LEVEL_WARN
170
172
  time = Time.now
171
173
  backtrace.each {|msg|
172
- puts [" ", caller_line(time,4), msg].join
174
+ puts [" ", caller_line(time, 4, LEVEL_WARN), msg].join
173
175
  }
174
176
  nil
175
177
  end
@@ -183,7 +185,7 @@ class Log
183
185
  return if @level > LEVEL_ERROR
184
186
  args << block.call if block
185
187
  time, msg = event(:error, args)
186
- puts [@color_error, caller_line(time, 1), msg, @color_reset].join
188
+ puts [@color_error, caller_line(time, 1, LEVEL_ERROR), msg, @color_reset].join
187
189
  end
188
190
  alias ERROR error
189
191
 
@@ -191,7 +193,7 @@ class Log
191
193
  return if @level > LEVEL_ERROR
192
194
  time = Time.now
193
195
  backtrace.each {|msg|
194
- puts [" ", caller_line(time,4), msg].join
196
+ puts [" ", caller_line(time, 4, LEVEL_ERROR), msg].join
195
197
  }
196
198
  nil
197
199
  end
@@ -205,7 +207,7 @@ class Log
205
207
  return if @level > LEVEL_FATAL
206
208
  args << block.call if block
207
209
  time, msg = event(:fatal, args)
208
- puts [@color_fatal, caller_line(time, 1), msg, @color_reset].join
210
+ puts [@color_fatal, caller_line(time, 1, LEVEL_FATAL), msg, @color_reset].join
209
211
  end
210
212
  alias FATAL fatal
211
213
 
@@ -213,7 +215,7 @@ class Log
213
215
  return if @level > LEVEL_FATAL
214
216
  time = Time.now
215
217
  backtrace.each {|msg|
216
- puts [" ", caller_line(time,4), msg].join
218
+ puts [" ", caller_line(time, 4, LEVEL_FATAL), msg].join
217
219
  }
218
220
  nil
219
221
  end
@@ -266,17 +268,18 @@ class Log
266
268
  return time, message
267
269
  end
268
270
 
269
- def caller_line(time, level)
270
- line = caller(level+1)[0]
271
+ def caller_line(time, depth, level)
272
+ line = caller(depth+1)[0]
273
+ log_msg = "#{time.strftime(@time_format)}[#{LEVEL_TEXT[level]}]: "
271
274
  if @debug_mode
272
275
  if match = /^(.+?):(\d+)(?::in `(.*)')?/.match(line)
273
276
  file = match[1].split('/')[-2,2].join('/')
274
277
  line = match[2]
275
278
  method = match[3]
276
- return "#{time.strftime(@time_format)}#{file}:#{line}:#{method}: "
279
+ return "#{log_msg}#{file}:#{line}:#{method}: "
277
280
  end
278
281
  end
279
- return time.strftime(@time_format)
282
+ return log_msg
280
283
  end
281
284
  end
282
285
 
@@ -136,6 +136,29 @@ class TextParser
136
136
  end
137
137
  end
138
138
 
139
+ class LabeledTSVParser < ValuesParser
140
+ config_param :delimiter, :string, :default => "\t"
141
+ config_param :label_delimiter, :string, :default => ":"
142
+
143
+ def configure(conf)
144
+ conf['keys'] = conf['time_key'] || ''
145
+ super(conf)
146
+ end
147
+
148
+ def call(text)
149
+ @keys = []
150
+ values = []
151
+
152
+ text.split(delimiter).each do |pair|
153
+ key, value = pair.split(label_delimiter, 2)
154
+ @keys.push(key)
155
+ values.push(value)
156
+ end
157
+
158
+ return values_map(values)
159
+ end
160
+ end
161
+
139
162
  class CSVParser < ValuesParser
140
163
  def initialize
141
164
  super
@@ -204,8 +227,9 @@ class TextParser
204
227
  'syslog' => Proc.new { RegexpParser.new(/^(?<time>[^ ]*\s*[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?[^\:]*\: *(?<message>.*)$/, {'time_format'=>"%b %d %H:%M:%S"}) },
205
228
  'json' => Proc.new { JSONParser.new },
206
229
  'tsv' => Proc.new { TSVParser.new },
230
+ 'ltsv' => Proc.new { LabeledTSVParser.new },
207
231
  'csv' => Proc.new { CSVParser.new },
208
- 'nginx' => Proc.new { RegexpParser.new(/^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/, {'time_format'=>"%d/%b/%Y:%H:%M:%S %z"}) },
232
+ 'nginx' => Proc.new { RegexpParser.new(/^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/, {'time_format'=>"%d/%b/%Y:%H:%M:%S %z"}) },
209
233
  }
210
234
 
211
235
  def self.register_template(name, regexp_or_proc, time_format=nil)
@@ -459,12 +459,14 @@ class TailInput < Input
459
459
 
460
460
  def read_inode
461
461
  @file.pos = @seek + INO_OFFSET
462
- @file.read(8).to_i(16)
462
+ raw = @file.read(8)
463
+ raw ? raw.to_i(16) : 0
463
464
  end
464
465
 
465
466
  def read_pos
466
467
  @file.pos = @seek
467
- @file.read(16).to_i(16)
468
+ raw = @file.read(16)
469
+ raw ? raw.to_i(16) : 0
468
470
  end
469
471
  end
470
472
 
@@ -128,14 +128,6 @@ class ForwardOutput < ObjectBufferedOutput
128
128
  $log.error_backtrace
129
129
  end
130
130
 
131
- # override BufferedOutput#emit
132
- def emit(tag, es, chain)
133
- data = es.to_msgpack_stream
134
- if @buffer.emit(tag, data, chain) # use key = tag
135
- submit_flush
136
- end
137
- end
138
-
139
131
  def write_objects(tag, es)
140
132
  error = nil
141
133
 
@@ -22,20 +22,25 @@ module Fluent
22
22
  class StatusClass
23
23
  def initialize
24
24
  @entries = {}
25
+ @mutex = Mutex.new
25
26
  end
26
27
 
27
28
  def register(instance, name, &block)
28
- (@entries[instance.object_id] ||= {})[name] = block
29
+ @mutex.synchronize {
30
+ (@entries[instance.object_id] ||= {})[name] = block
31
+ }
29
32
  nil
30
33
  end
31
34
 
32
35
  def each(&block)
33
- @entries.each {|obj_id,hash|
34
- record = {}
35
- hash.each_pair {|name,block|
36
- record[name] = block.call
36
+ @mutex.synchronize {
37
+ @entries.each {|obj_id,hash|
38
+ record = {}
39
+ hash.each_pair {|name,block|
40
+ record[name] = block.call
41
+ }
42
+ block.call(record)
37
43
  }
38
- block.call(record)
39
44
  }
40
45
  end
41
46
  end
@@ -1,5 +1,5 @@
1
1
  module Fluent
2
2
 
3
- VERSION = '0.10.31'
3
+ VERSION = '0.10.32'
4
4
 
5
5
  end
@@ -0,0 +1,193 @@
1
+ require 'helper'
2
+ require 'fluent/test'
3
+ require 'fluent/parser'
4
+
5
+ module ParserTest
6
+ include Fluent
7
+
8
+ def str2time(str_time, format = nil)
9
+ if format
10
+ Time.strptime(str_time, format).to_i
11
+ else
12
+ Time.parse(str_time)
13
+ end
14
+ end
15
+
16
+ class ApacheParserTest < ::Test::Unit::TestCase
17
+ include ParserTest
18
+
19
+ def setup
20
+ @parser = TextParser::TEMPLATE_FACTORIES['apache'].call
21
+ end
22
+
23
+ def test_call
24
+ time, record = @parser.call('192.168.0.1 - - [28/Feb/2013:12:00:00 +0900] "GET / HTTP/1.1" 200 777')
25
+
26
+ assert_equal(str2time('28/Feb/2013:12:00:00 +0900', '%d/%b/%Y:%H:%M:%S %z'), time)
27
+ assert_equal({
28
+ 'user' => '-',
29
+ 'method' => 'GET',
30
+ 'code' => '200',
31
+ 'size' => '777',
32
+ 'host' => '192.168.0.1',
33
+ 'path' => '/'
34
+ }, record)
35
+ end
36
+ end
37
+
38
+ class Apache2ParserTest < ::Test::Unit::TestCase
39
+ include ParserTest
40
+
41
+ def setup
42
+ @parser = TextParser::ApacheParser.new
43
+ end
44
+
45
+ def test_call
46
+ time, record = @parser.call('192.168.0.1 - - [28/Feb/2013:12:00:00 +0900] "GET / HTTP/1.1" 200 777 "-" "Opera/12.0"')
47
+
48
+ assert_equal(str2time('28/Feb/2013:12:00:00 +0900', '%d/%b/%Y:%H:%M:%S %z'), time)
49
+ assert_equal({
50
+ 'user' => nil,
51
+ 'method' => 'GET',
52
+ 'code' => 200,
53
+ 'size' => 777,
54
+ 'host' => '192.168.0.1',
55
+ 'path' => '/',
56
+ 'referer' => nil,
57
+ 'agent' => 'Opera/12.0'
58
+ }, record)
59
+ end
60
+ end
61
+
62
+ class SyslogParserTest < ::Test::Unit::TestCase
63
+ include ParserTest
64
+
65
+ def setup
66
+ @parser = TextParser::TEMPLATE_FACTORIES['syslog'].call
67
+ end
68
+
69
+ def test_call
70
+ time, record = @parser.call('Feb 28 12:00:00 192.168.0.1 fluentd[11111]: [error] Syslog test')
71
+
72
+ assert_equal(str2time('Feb 28 12:00:00', '%b %d %H:%M:%S'), time)
73
+ assert_equal({
74
+ 'host' => '192.168.0.1',
75
+ 'ident' => 'fluentd',
76
+ 'pid' => '11111',
77
+ 'message' => '[error] Syslog test'
78
+ }, record)
79
+ end
80
+ end
81
+
82
+ class JsonParserTest < ::Test::Unit::TestCase
83
+ include ParserTest
84
+
85
+ def setup
86
+ @parser = TextParser::JSONParser.new
87
+ end
88
+
89
+ def test_call
90
+ time, record = @parser.call('{"time":1362020400,"host":"192.168.0.1","size":777,"method":"PUT"}')
91
+
92
+ assert_equal(str2time('2013-02-28 12:00:00 +0900').to_i, time)
93
+ assert_equal({
94
+ 'host' => '192.168.0.1',
95
+ 'size' => 777,
96
+ 'method' => 'PUT',
97
+ }, record)
98
+ end
99
+ end
100
+
101
+ class NginxParserTest < ::Test::Unit::TestCase
102
+ include ParserTest
103
+
104
+ def setup
105
+ @parser = TextParser::TEMPLATE_FACTORIES['nginx'].call
106
+ @expected = {
107
+ 'remote' => '127.0.0.1',
108
+ 'host' => '192.168.0.1',
109
+ 'user' => '-',
110
+ 'method' => 'GET',
111
+ 'path' => '/',
112
+ 'code' => '200',
113
+ 'size' => '777',
114
+ 'referer' => '-',
115
+ 'agent' => 'Opera/12.0'
116
+ }
117
+ end
118
+
119
+ def test_call
120
+ time, record = @parser.call('127.0.0.1 192.168.0.1 - [28/Feb/2013:12:00:00 +0900] "GET / HTTP/1.1" 200 777 "-" "Opera/12.0"')
121
+
122
+ assert_equal(str2time('28/Feb/2013:12:00:00 +0900', '%d/%b/%Y:%H:%M:%S %z'), time)
123
+ assert_equal(@expected, record)
124
+ end
125
+
126
+ def test_call_with_empty_included_path
127
+ time, record = @parser.call('127.0.0.1 192.168.0.1 - [28/Feb/2013:12:00:00 +0900] "GET /a[ ]b HTTP/1.1" 200 777 "-" "Opera/12.0"')
128
+
129
+ assert_equal(str2time('28/Feb/2013:12:00:00 +0900', '%d/%b/%Y:%H:%M:%S %z'), time)
130
+ assert_equal(@expected.merge('path' => '/a[ ]b'), record)
131
+ end
132
+ end
133
+
134
+ class LabeledTSVParserTest < ::Test::Unit::TestCase
135
+ include ParserTest
136
+
137
+ def test_config_params
138
+ parser = TextParser::LabeledTSVParser.new
139
+
140
+ assert_equal "\t", parser.delimiter
141
+ assert_equal ":", parser.label_delimiter
142
+
143
+ parser.configure(
144
+ 'delimiter' => ',',
145
+ 'label_delimiter' => '=',
146
+ )
147
+
148
+ assert_equal ",", parser.delimiter
149
+ assert_equal "=", parser.label_delimiter
150
+ end
151
+
152
+ def test_call
153
+ parser = TextParser::LabeledTSVParser.new
154
+ time, record = parser.call("time:[28/Feb/2013:12:00:00 +0900]\thost:192.168.0.1\treq:GET /list HTTP/1.1")
155
+
156
+ assert_equal({
157
+ 'time' => '[28/Feb/2013:12:00:00 +0900]',
158
+ 'host' => '192.168.0.1',
159
+ 'req' => 'GET /list HTTP/1.1',
160
+ }, record)
161
+ end
162
+
163
+ def test_call_with_customized_delimiter
164
+ parser = TextParser::LabeledTSVParser.new
165
+ parser.configure(
166
+ 'delimiter' => ',',
167
+ 'label_delimiter' => '=',
168
+ )
169
+ time, record = parser.call('time=[28/Feb/2013:12:00:00 +0900],host=192.168.0.1,req=GET /list HTTP/1.1')
170
+
171
+ assert_equal({
172
+ 'time' => '[28/Feb/2013:12:00:00 +0900]',
173
+ 'host' => '192.168.0.1',
174
+ 'req' => 'GET /list HTTP/1.1',
175
+ }, record)
176
+ end
177
+
178
+ def test_call_with_customized_time_format
179
+ parser = TextParser::LabeledTSVParser.new
180
+ parser.configure(
181
+ 'time_key' => 'time',
182
+ 'time_format' => '[%d/%b/%Y:%H:%M:%S %z]',
183
+ )
184
+ time, record = parser.call("time:[28/Feb/2013:12:00:00 +0900]\thost:192.168.0.1\treq:GET /list HTTP/1.1")
185
+
186
+ assert_equal({
187
+ 'time' => '[28/Feb/2013:12:00:00 +0900]',
188
+ 'host' => '192.168.0.1',
189
+ 'req' => 'GET /list HTTP/1.1',
190
+ }, record)
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,36 @@
1
+ require 'fluent/test'
2
+
3
+ class ObjectSpaceInputTest < Test::Unit::TestCase
4
+ def setup
5
+ Fluent::Test.setup
6
+ end
7
+
8
+ CONFIG = %[
9
+ emit_interval 1
10
+ tag t1
11
+ ]
12
+
13
+ def create_driver(conf=CONFIG)
14
+ Fluent::Test::InputTestDriver.new(Fluent::GCStatInput).configure(conf)
15
+ end
16
+
17
+ def test_configure
18
+ d = create_driver
19
+ assert_equal(1, d.instance.emit_interval)
20
+ assert_equal("t1", d.instance.tag)
21
+ end
22
+
23
+ def test_emit
24
+ stat = GC.stat
25
+ stub(GC).stat { stat }
26
+
27
+ d = create_driver
28
+ d.run do
29
+ sleep 2
30
+ end
31
+
32
+ emits = d.emits
33
+ assert(emits.length > 0)
34
+ assert_equal(stat, emits[0][2])
35
+ end
36
+ end
@@ -0,0 +1,37 @@
1
+ require 'fluent/test'
2
+
3
+ class StatusInputTest < Test::Unit::TestCase
4
+ def setup
5
+ Fluent::Test.setup
6
+ end
7
+
8
+ CONFIG = %[
9
+ emit_interval 1
10
+ tag t1
11
+ ]
12
+
13
+ def create_driver(conf=CONFIG)
14
+ Fluent::Test::InputTestDriver.new(Fluent::StatusInput).configure(conf)
15
+ end
16
+
17
+ def test_configure
18
+ d = create_driver
19
+ assert_equal(1, d.instance.emit_interval)
20
+ assert_equal("t1", d.instance.tag)
21
+ end
22
+
23
+ def test_emit
24
+ stub(Fluent::Status).each { |b|
25
+ b.call("answer" => "42")
26
+ }
27
+
28
+ d = create_driver
29
+ d.run do
30
+ sleep 2
31
+ end
32
+
33
+ emits = d.emits
34
+ assert(emits.length > 0)
35
+ assert_equal({"answer" => "42"}, emits[0][2])
36
+ end
37
+ 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.31
4
+ version: 0.10.32
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-31 00:00:00.000000000 Z
12
+ date: 2013-03-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: msgpack
@@ -229,10 +229,13 @@ files:
229
229
  - test/helper.rb
230
230
  - test/match.rb
231
231
  - test/mixin.rb
232
+ - test/parser.rb
232
233
  - test/plugin/in_exec.rb
233
234
  - test/plugin/in_forward.rb
235
+ - test/plugin/in_gc_stat.rb
234
236
  - test/plugin/in_http.rb
235
237
  - test/plugin/in_object_space.rb
238
+ - test/plugin/in_status.rb
236
239
  - test/plugin/in_stream.rb
237
240
  - test/plugin/in_syslog.rb
238
241
  - test/plugin/in_tail.rb
@@ -275,10 +278,13 @@ test_files:
275
278
  - test/helper.rb
276
279
  - test/match.rb
277
280
  - test/mixin.rb
281
+ - test/parser.rb
278
282
  - test/plugin/in_exec.rb
279
283
  - test/plugin/in_forward.rb
284
+ - test/plugin/in_gc_stat.rb
280
285
  - test/plugin/in_http.rb
281
286
  - test/plugin/in_object_space.rb
287
+ - test/plugin/in_status.rb
282
288
  - test/plugin/in_stream.rb
283
289
  - test/plugin/in_syslog.rb
284
290
  - test/plugin/in_tail.rb