fluentd 0.10.46 → 0.10.47

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: f5c5c7236589cb3aa5fadf040dbee965523f6ef0
4
- data.tar.gz: 392f590233abe73edab6c425556de56ab7ddd6f3
3
+ metadata.gz: 2fe2bff2ce1a5aecb6cee52438d601388c2aa896
4
+ data.tar.gz: f8aed6a81a228824681acde0c7f38656c4ea17b8
5
5
  SHA512:
6
- metadata.gz: ea036af28812b5a21fafe9731fe93959f85c2529e88feff2347d1ebfb0724c54a93a65bd738371cc924247e43ef9c6f997dd88792893fbb94c2fbdfc52fa362d
7
- data.tar.gz: 6087c6298709c8f0e32f79830c8950ab5378225c8a68ecf8ae236accdf2e08b670e51dbd7c08ac2e4397a36d704e4219a4585ceebb67ab974428cdce578e806d
6
+ metadata.gz: 59337e23670288e3d11cbbf954d2601cd79d6f32b9cb008ffd94c3b59f5e1a5272774be6640d0a7c2af2fef26cca690e88422752d35f9b64beae2383627eb297
7
+ data.tar.gz: 1ed344c08218d378a029dc575083fd1217b1c227a38030e9907eca5b8d445e534bc04e7e068c0a79c8cb4324cc015568174e761ef3dcc3b347db1e91a239cf1b
@@ -3,11 +3,14 @@ language: ruby
3
3
  rvm:
4
4
  - 1.9.3
5
5
  - 2.0.0
6
- - 2.1.0
7
- - 2.1.1
6
+ - 2.1
8
7
  - ruby-head
9
8
  - rbx
10
9
 
10
+ os:
11
+ - linux
12
+ - osx
13
+
11
14
  branches:
12
15
  only:
13
16
  - master
data/ChangeLog CHANGED
@@ -1,3 +1,12 @@
1
+ Release 0.10.47 - 2014/05/15
2
+
3
+ * in_tail: Fix typo in flush_buffer
4
+ * in_forward: Add chunk_size_warn_limit and chunk_size_limit for large chunks
5
+ * in_monitor_agent: Add /api/config APIs to expose Fluentd configurations
6
+ * supervisor: Add Supervisor.default_options to return default Fluentd options
7
+ * test: Add DummyLogDevise and improve log handling in tests
8
+ * config: Allow empty string value like `tag_mapped` in v1 configuration
9
+
1
10
  Release 0.10.46 - 2014/04/21
2
11
 
3
12
  * in_tail: Fix typo in rescue for dumping backtrace
@@ -19,26 +19,13 @@
19
19
  require 'optparse'
20
20
  require 'fluent/log'
21
21
  require 'fluent/env'
22
+ require 'fluent/supervisor'
22
23
  require 'fluent/version'
23
24
 
24
25
  op = OptionParser.new
25
26
  op.version = Fluent::VERSION
26
27
 
27
- # default values
28
- opts = {
29
- :config_path => Fluent::DEFAULT_CONFIG_PATH,
30
- :plugin_dirs => [Fluent::DEFAULT_PLUGIN_DIR],
31
- :log_level => Fluent::Log::LEVEL_INFO,
32
- :log_path => nil,
33
- :daemonize => false,
34
- :libs => [],
35
- :setup_path => nil,
36
- :chuser => nil,
37
- :chgroup => nil,
38
- :suppress_interval => 0,
39
- :suppress_repeated_stacktrace => false,
40
- :use_v1_config => false,
41
- }
28
+ opts = Fluent::Supervisor.default_options
42
29
 
43
30
  op.on('-s', "--setup [DIR=#{File.dirname(Fluent::DEFAULT_CONFIG_PATH)}]", "install sample configuration file to the directory") {|s|
44
31
  opts[:setup_path] = s || File.dirname(Fluent::DEFAULT_CONFIG_PATH)
@@ -65,6 +65,10 @@ module Fluent
65
65
  @ss.eos?
66
66
  end
67
67
 
68
+ def prev_match
69
+ @ss[0]
70
+ end
71
+
68
72
  def line_end
69
73
  skip(LINE_END)
70
74
  end
@@ -38,6 +38,8 @@ module Fluent
38
38
  true
39
39
  when 'false', 'no'
40
40
  false
41
+ when ''
42
+ true
41
43
  else
42
44
  nil
43
45
  end
@@ -102,11 +102,15 @@ module Fluent
102
102
  else
103
103
  k = scan_string(SPACING)
104
104
  spacing
105
- v = parse_literal
106
- unless line_end
107
- parse_error! "expected end of line"
105
+ if prev_match.include?("\n") # support 'tag_mapped' like "without value" configuration
106
+ attrs[k] = ""
107
+ else
108
+ v = parse_literal
109
+ unless line_end
110
+ parse_error! "expected end of line"
111
+ end
112
+ attrs[k] = v
108
113
  end
109
- attrs[k] = v
110
114
  end
111
115
  end
112
116
 
@@ -357,7 +357,9 @@ module Fluent
357
357
  super
358
358
 
359
359
  if @log_level
360
- @log = PluginLogger.new($log)
360
+ unless @log.is_a?(PluginLogger)
361
+ @log = PluginLogger.new($log)
362
+ end
361
363
  @log.level = @log_level
362
364
  end
363
365
  end
@@ -34,6 +34,9 @@ module Fluent
34
34
  # This option is for Cool.io's loop wait timeout to avoid loop stuck at shutdown. Almost users don't need to change this value.
35
35
  config_param :blocking_timeout, :time, :default => 0.5
36
36
 
37
+ config_param :chunk_size_warn_limit, :size, :default => nil
38
+ config_param :chunk_size_limit, :size, :default => nil
39
+
37
40
  def configure(conf)
38
41
  super
39
42
  end
@@ -122,7 +125,7 @@ module Fluent
122
125
  # 2: long? time
123
126
  # 3: object record
124
127
  # }
125
- def on_message(msg)
128
+ def on_message(msg, chunk_size, source)
126
129
  if msg.nil?
127
130
  # for future TCP heartbeat_request
128
131
  return
@@ -132,6 +135,13 @@ module Fluent
132
135
  tag = msg[0].to_s
133
136
  entries = msg[1]
134
137
 
138
+ if @chunk_size_limit && (chunk_size > @chunk_size_limit)
139
+ log.warn "Input chunk size is larger than 'chunk_size_limit', dropped:", tag: tag, source: source, limit: @chunk_size_limit, size: chunk_size
140
+ return
141
+ elsif @chunk_size_warn_limit && (chunk_size > @chunk_size_warn_limit)
142
+ log.warn "Input chunk size is larger than 'chunk_size_warn_limit':", tag: tag, source: source, limit: @chunk_size_warn_limit, size: chunk_size
143
+ end
144
+
135
145
  if entries.class == String
136
146
  # PackedForward
137
147
  es = MessagePackEventStream.new(entries, @cached_unpacker)
@@ -160,16 +170,29 @@ module Fluent
160
170
  end
161
171
 
162
172
  class Handler < Coolio::Socket
173
+ PEERADDR_FAILED = ["?", "?", "name resolusion failed", "?"]
174
+
163
175
  def initialize(io, linger_timeout, log, on_message)
164
176
  super(io)
165
- if io.is_a?(TCPSocket)
177
+
178
+ if io.is_a?(TCPSocket) # for unix domain socket support in the future
179
+ proto, port, host, addr = ( io.peeraddr rescue PEERADDR_FAILED )
180
+ @source = "host: #{host}, addr: #{addr}, port: #{port}"
181
+
166
182
  opt = [1, linger_timeout].pack('I!I!') # { int l_onoff; int l_linger; }
167
183
  io.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
168
184
  end
185
+
186
+ @chunk_counter = 0
169
187
  @on_message = on_message
170
188
  @log = log
171
189
  @log.trace {
172
- remote_port, remote_addr = *Socket.unpack_sockaddr_in(@_io.getpeername) rescue nil
190
+ begin
191
+ remote_port, remote_addr = *Socket.unpack_sockaddr_in(@_io.getpeername)
192
+ rescue => e
193
+ remote_port = nil
194
+ remote_addr = nil
195
+ end
173
196
  "accepted fluent socket from '#{remote_addr}:#{remote_port}': object_id=#{self.object_id}"
174
197
  }
175
198
  end
@@ -182,7 +205,10 @@ module Fluent
182
205
  if first == '{' || first == '['
183
206
  m = method(:on_read_json)
184
207
  @y = Yajl::Parser.new
185
- @y.on_parse_complete = @on_message
208
+ @y.on_parse_complete = lambda { |obj|
209
+ @on_message.call(obj, @chunk_counter, @source)
210
+ @chunk_counter = 0
211
+ }
186
212
  else
187
213
  m = method(:on_read_msgpack)
188
214
  @u = MessagePack::Unpacker.new
@@ -195,6 +221,7 @@ module Fluent
195
221
  end
196
222
 
197
223
  def on_read_json(data)
224
+ @chunk_counter += data.bytesize
198
225
  @y << data
199
226
  rescue => e
200
227
  @log.error "forward error", :error => e, :error_class => e.class
@@ -203,7 +230,11 @@ module Fluent
203
230
  end
204
231
 
205
232
  def on_read_msgpack(data)
206
- @u.feed_each(data, &@on_message)
233
+ @chunk_counter += data.bytesize
234
+ @u.feed_each(data) do |obj|
235
+ @on_message.call(obj, @chunk_counter, @source)
236
+ @chunk_counter = 0
237
+ end
207
238
  rescue => e
208
239
  @log.error "forward error", :error => e, :error_class => e.class
209
240
  @log.error_backtrace
@@ -153,6 +153,36 @@ module Fluent
153
153
  end
154
154
  end
155
155
 
156
+ class ConfigMonitorServlet < MonitorServlet
157
+ def build_object(req, res)
158
+ {
159
+ 'pid' => Process.pid,
160
+ 'ppid' => Process.ppid
161
+ }.merge(@agent.fluentd_opts)
162
+ end
163
+ end
164
+
165
+ class LTSVConfigMonitorServlet < ConfigMonitorServlet
166
+ def process(req, res)
167
+ result = build_object(req, res)
168
+
169
+ row = []
170
+ JSON.parse(result.to_json).each_pair { |k, v|
171
+ row << "#{k}:#{v}"
172
+ }
173
+ text = row.join("\t")
174
+
175
+ [200, {'Content-Type'=>'text/plain'}, text]
176
+ end
177
+ end
178
+
179
+ class JSONConfigMonitorServlet < ConfigMonitorServlet
180
+ def process(req, res)
181
+ result = build_object(req, res)
182
+ render_json(result)
183
+ end
184
+ end
185
+
156
186
  def start
157
187
  log.debug "listening monitoring http server on http://#{@bind}:#{@port}/api/plugins"
158
188
  @srv = WEBrick::HTTPServer.new({
@@ -163,6 +193,8 @@ module Fluent
163
193
  })
164
194
  @srv.mount('/api/plugins', LTSVMonitorServlet, self)
165
195
  @srv.mount('/api/plugins.json', JSONMonitorServlet, self)
196
+ @srv.mount('/api/config', LTSVConfigMonitorServlet, self)
197
+ @srv.mount('/api/config.json', JSONConfigMonitorServlet, self)
166
198
  @thread = Thread.new {
167
199
  @srv.start
168
200
  }
@@ -282,5 +314,18 @@ module Fluent
282
314
 
283
315
  obj
284
316
  end
317
+
318
+ def fluentd_opts
319
+ @fluentd_opts ||= get_fluentd_opts
320
+ end
321
+
322
+ def get_fluentd_opts
323
+ opts = {}
324
+ ObjectSpace.each_object(Fluent::Supervisor) { |obj|
325
+ opts.merge!(obj.options)
326
+ break
327
+ }
328
+ opts
329
+ end
285
330
  end
286
331
  end
@@ -188,7 +188,7 @@ module Fluent
188
188
  time, record = parse_line(lb)
189
189
  if time && record
190
190
  tag = if @tag_prefix || @tag_suffix
191
- @tag_prefix + tail_watcher.tag + @tag_suffix
191
+ @tag_prefix + tw.tag + @tag_suffix
192
192
  else
193
193
  @tag
194
194
  end
@@ -15,6 +15,10 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
  #
18
+
19
+ require 'fluent/env'
20
+ require 'fluent/log'
21
+
18
22
  module Fluent
19
23
  class Supervisor
20
24
  class LoggerInitializer
@@ -56,6 +60,23 @@ module Fluent
56
60
  end
57
61
  end
58
62
 
63
+ def self.default_options
64
+ {
65
+ :config_path => Fluent::DEFAULT_CONFIG_PATH,
66
+ :plugin_dirs => [Fluent::DEFAULT_PLUGIN_DIR],
67
+ :log_level => Fluent::Log::LEVEL_INFO,
68
+ :log_path => nil,
69
+ :daemonize => nil,
70
+ :libs => [],
71
+ :setup_path => nil,
72
+ :chuser => nil,
73
+ :chgroup => nil,
74
+ :suppress_interval => 0,
75
+ :suppress_repeated_stacktrace => false,
76
+ :use_v1_config => false,
77
+ }
78
+ end
79
+
59
80
  def initialize(opt)
60
81
  @config_path = opt[:config_path]
61
82
  @log_path = opt[:log_path]
@@ -99,6 +120,15 @@ module Fluent
99
120
  end
100
121
  end
101
122
 
123
+ def options
124
+ {
125
+ 'config_path' => @config_path,
126
+ 'pid_file' => @daemonize,
127
+ 'plugin_dirs' => @plugin_dirs,
128
+ 'log_path' => @log_path
129
+ }
130
+ end
131
+
102
132
  private
103
133
 
104
134
  def dry_run
@@ -388,4 +418,3 @@ module Fluent
388
418
  end
389
419
  end
390
420
  end
391
-
@@ -4,5 +4,3 @@ require 'fluent/test/base'
4
4
  require 'fluent/test/input_test'
5
5
  require 'fluent/test/output_test'
6
6
 
7
- $log ||= Fluent::Log.new
8
-
@@ -32,6 +32,8 @@ module Fluent
32
32
  else
33
33
  @instance = klass
34
34
  end
35
+ @instance.log = TestLogger.new
36
+
35
37
  @config = Config.new
36
38
  end
37
39
 
@@ -58,5 +60,44 @@ module Fluent
58
60
  end
59
61
  end
60
62
  end
63
+
64
+ class DummyLogDevice
65
+ attr_reader :logs
66
+
67
+ def initialize
68
+ @logs = []
69
+ end
70
+
71
+ def tty?
72
+ false
73
+ end
74
+
75
+ def puts(*args)
76
+ args.each{ |arg| write(arg + "\n") }
77
+ end
78
+
79
+ def write(message)
80
+ @logs.push message
81
+ end
82
+
83
+ def flush
84
+ true
85
+ end
86
+
87
+ def close
88
+ true
89
+ end
90
+ end
91
+
92
+ class TestLogger < Fluent::PluginLogger
93
+ def initialize
94
+ @logdev = DummyLogDevice.new
95
+ super(Fluent::Log.new(@logdev))
96
+ end
97
+
98
+ def logs
99
+ @logdev.logs
100
+ end
101
+ end
61
102
  end
62
103
  end
@@ -1,5 +1,5 @@
1
1
  module Fluent
2
2
 
3
- VERSION = '0.10.46'
3
+ VERSION = '0.10.47'
4
4
 
5
5
  end
@@ -34,6 +34,13 @@ describe Fluent::Config::V1Parser do
34
34
  ].should be_parsed_as("k1"=>"v1", "k2"=>"v2")
35
35
  end
36
36
 
37
+ it "allows attribute without value" do
38
+ %[
39
+ k1
40
+ k2 v2
41
+ ].should be_parsed_as("k1"=>"", "k2"=>"v2")
42
+ end
43
+
37
44
  it "parses attribute key always string" do
38
45
  "1 1".should be_parsed_as("1" => "1")
39
46
  end
@@ -1,6 +1,7 @@
1
1
  require 'test/unit'
2
2
  require 'fileutils'
3
3
  require 'fluent/log'
4
+ require 'fluent/test'
4
5
  require 'rr'
5
6
 
6
7
  unless defined?(Test::Unit::AssertionFailedError)
@@ -26,4 +27,4 @@ def ipv6_enabled?
26
27
  end
27
28
  end
28
29
 
29
- $log = Fluent::Log.new(STDOUT, Fluent::Log::LEVEL_WARN)
30
+ $log = Fluent::Log.new(Fluent::Test::DummyLogDevice.new, Fluent::Log::LEVEL_WARN)
@@ -116,6 +116,93 @@ class ForwardInputTest < Test::Unit::TestCase
116
116
  end
117
117
  end
118
118
 
119
+ def test_send_large_chunk_warning
120
+ d = create_driver(CONFIG + %[
121
+ chunk_size_warn_limit 16M
122
+ chunk_size_limit 32M
123
+ ])
124
+
125
+ time = Time.parse("2014-04-25 13:14:15 UTC").to_i
126
+
127
+ # generate over 16M chunk
128
+ str = "X" * 1024 * 1024
129
+ chunk = [ "test.tag", (0...16).map{|i| [time + i, {"data" => str}] } ].to_msgpack
130
+ assert chunk.size > (16 * 1024 * 1024)
131
+ assert chunk.size < (32 * 1024 * 1024)
132
+
133
+ d.run do
134
+ MessagePack::Unpacker.new.feed_each(chunk) do |obj|
135
+ d.instance.send(:on_message, obj, chunk.size, "host: 127.0.0.1, addr: 127.0.0.1, port: 0000")
136
+ end
137
+ end
138
+
139
+ # check emitted data
140
+ emits = d.emits
141
+ assert_equal 16, emits.size
142
+ assert emits.map(&:first).all?{|t| t == "test.tag" }
143
+ assert_equal (0...16).to_a, emits.map{|tag, t, record| t - time }
144
+
145
+ # check log
146
+ assert d.instance.log.logs.select{|line|
147
+ line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_warn_limit':/ &&
148
+ line =~ / tag="test.tag" source="host: 127.0.0.1, addr: 127.0.0.1, port: \d+" limit=16777216 size=16777501/
149
+ }.size == 1, "large chunk warning is not logged"
150
+ end
151
+
152
+ def test_send_large_chunk_only_warning
153
+ d = create_driver(CONFIG + %[
154
+ chunk_size_warn_limit 16M
155
+ ])
156
+ time = Time.parse("2014-04-25 13:14:15 UTC").to_i
157
+
158
+ # generate over 16M chunk
159
+ str = "X" * 1024 * 1024
160
+ chunk = [ "test.tag", (0...16).map{|i| [time + i, {"data" => str}] } ].to_msgpack
161
+
162
+ d.run do
163
+ MessagePack::Unpacker.new.feed_each(chunk) do |obj|
164
+ d.instance.send(:on_message, obj, chunk.size, "host: 127.0.0.1, addr: 127.0.0.1, port: 0000")
165
+ end
166
+ end
167
+
168
+ # check log
169
+ assert d.instance.log.logs.select{ |line|
170
+ line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_warn_limit':/ &&
171
+ line =~ / tag="test.tag" source="host: 127.0.0.1, addr: 127.0.0.1, port: \d+" limit=16777216 size=16777501/
172
+ }.size == 1, "large chunk warning is not logged"
173
+ end
174
+
175
+ def test_send_large_chunk_limit
176
+ d = create_driver(CONFIG + %[
177
+ chunk_size_warn_limit 16M
178
+ chunk_size_limit 32M
179
+ ])
180
+
181
+ time = Time.parse("2014-04-25 13:14:15 UTC").to_i
182
+
183
+ # generate over 32M chunk
184
+ str = "X" * 1024 * 1024
185
+ chunk = [ "test.tag", (0...32).map{|i| [time + i, {"data" => str}] } ].to_msgpack
186
+ assert chunk.size > (32 * 1024 * 1024)
187
+
188
+ # d.run => send_data
189
+ d.run do
190
+ MessagePack::Unpacker.new.feed_each(chunk) do |obj|
191
+ d.instance.send(:on_message, obj, chunk.size, "host: 127.0.0.1, addr: 127.0.0.1, port: 0000")
192
+ end
193
+ end
194
+
195
+ # check emitted data
196
+ emits = d.emits
197
+ assert_equal 0, emits.size
198
+
199
+ # check log
200
+ assert d.instance.log.logs.select{|line|
201
+ line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_limit', dropped:/ &&
202
+ line =~ / tag="test.tag" source="host: 127.0.0.1, addr: 127.0.0.1, port: \d+" limit=33554432 size=33554989/
203
+ }.size == 1, "large chunk warning is not logged"
204
+ end
205
+
119
206
  def send_data(data)
120
207
  io = connect
121
208
  begin
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluentd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.46
4
+ version: 0.10.47
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sadayuki Furuhashi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-21 00:00:00.000000000 Z
11
+ date: 2014-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -253,7 +253,6 @@ extensions: []
253
253
  extra_rdoc_files: []
254
254
  files:
255
255
  - ".gitignore"
256
- - ".rvmrc"
257
256
  - ".travis.yml"
258
257
  - AUTHORS
259
258
  - COPYING
data/.rvmrc DELETED
@@ -1 +0,0 @@
1
- rvm 1.9.3