fluentd 0.12.0.pre.2 → 0.12.0.pre.3

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.

Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/example/v0_12_filter.conf +78 -0
  3. data/fluentd.gemspec +2 -1
  4. data/lib/fluent/agent.rb +2 -1
  5. data/lib/fluent/buffer.rb +9 -5
  6. data/lib/fluent/command/fluentd.rb +4 -0
  7. data/lib/fluent/config/basic_parser.rb +1 -0
  8. data/lib/fluent/config/configure_proxy.rb +7 -7
  9. data/lib/fluent/config/types.rb +1 -0
  10. data/lib/fluent/config/v1_parser.rb +1 -1
  11. data/lib/fluent/engine.rb +0 -25
  12. data/lib/fluent/env.rb +1 -0
  13. data/lib/fluent/event_router.rb +6 -2
  14. data/lib/fluent/filter.rb +12 -1
  15. data/lib/fluent/formatter.rb +85 -16
  16. data/lib/fluent/label.rb +4 -0
  17. data/lib/fluent/output.rb +1 -0
  18. data/lib/fluent/parser.rb +25 -23
  19. data/lib/fluent/plugin.rb +18 -0
  20. data/lib/fluent/plugin/buf_file.rb +1 -1
  21. data/lib/fluent/plugin/in_dummy.rb +103 -0
  22. data/lib/fluent/plugin/in_http.rb +30 -10
  23. data/lib/fluent/plugin/in_syslog.rb +4 -4
  24. data/lib/fluent/plugin/in_tail.rb +6 -6
  25. data/lib/fluent/plugin/out_file.rb +3 -3
  26. data/lib/fluent/plugin/socket_util.rb +2 -2
  27. data/lib/fluent/registry.rb +9 -27
  28. data/lib/fluent/root_agent.rb +26 -7
  29. data/lib/fluent/supervisor.rb +40 -27
  30. data/lib/fluent/test.rb +1 -0
  31. data/lib/fluent/test/base.rb +14 -0
  32. data/lib/fluent/test/filter_test.rb +33 -0
  33. data/lib/fluent/test/output_test.rb +7 -1
  34. data/lib/fluent/version.rb +1 -1
  35. data/test/config/test_config_parser.rb +6 -2
  36. data/test/config/test_configurable.rb +1 -1
  37. data/test/config/test_configure_proxy.rb +1 -1
  38. data/test/config/test_dsl.rb +1 -1
  39. data/test/config/test_literal_parser.rb +2 -2
  40. data/test/config/test_section.rb +1 -1
  41. data/test/config/test_system_config.rb +65 -15
  42. data/test/config/test_types.rb +63 -0
  43. data/test/helper.rb +2 -1
  44. data/test/plugin/test_buf_file.rb +1 -1
  45. data/test/plugin/test_buf_memory.rb +1 -1
  46. data/test/plugin/test_filter_grep.rb +17 -23
  47. data/test/plugin/test_filter_record_transformer.rb +18 -21
  48. data/test/plugin/test_in_dummy.rb +95 -0
  49. data/test/plugin/test_in_exec.rb +1 -1
  50. data/test/plugin/test_in_forward.rb +1 -1
  51. data/test/plugin/test_in_gc_stat.rb +1 -1
  52. data/test/plugin/test_in_http.rb +1 -1
  53. data/test/plugin/test_in_object_space.rb +1 -1
  54. data/test/plugin/test_in_status.rb +1 -1
  55. data/test/plugin/test_in_stream.rb +1 -1
  56. data/test/plugin/test_in_syslog.rb +1 -1
  57. data/test/plugin/test_in_tail.rb +1 -1
  58. data/test/plugin/test_in_tcp.rb +1 -1
  59. data/test/plugin/test_in_udp.rb +1 -1
  60. data/test/plugin/test_out_copy.rb +12 -1
  61. data/test/plugin/test_out_exec.rb +1 -1
  62. data/test/plugin/test_out_exec_filter.rb +1 -1
  63. data/test/plugin/test_out_file.rb +61 -8
  64. data/test/plugin/test_out_forward.rb +1 -1
  65. data/test/plugin/test_out_roundrobin.rb +12 -1
  66. data/test/plugin/test_out_stdout.rb +1 -1
  67. data/test/plugin/test_out_stream.rb +1 -1
  68. data/test/scripts/fluent/plugin/formatter_known.rb +4 -1
  69. data/{lib → test/scripts}/fluent/plugin/out_test.rb +0 -0
  70. data/test/scripts/fluent/plugin/parser_known.rb +2 -1
  71. data/test/test_buffer.rb +1 -1
  72. data/test/test_config.rb +1 -1
  73. data/test/test_configdsl.rb +1 -1
  74. data/test/test_event_router.rb +233 -0
  75. data/test/test_formatter.rb +160 -3
  76. data/test/test_input.rb +30 -0
  77. data/test/test_match.rb +100 -77
  78. data/test/test_mixin.rb +1 -1
  79. data/test/test_output.rb +1 -1
  80. data/test/test_parser.rb +5 -3
  81. data/test/test_plugin_classes.rb +60 -0
  82. metadata +32 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5728345e3e45be5f8327ea9cfe0268b958c78d92
4
- data.tar.gz: 453abbcdf2c21a2ff97fb6da4b9e4468c0519de1
3
+ metadata.gz: c5a6a2cf3f13289f87a554bfafb531593977ffcd
4
+ data.tar.gz: bbb612c523e612b177980433bcc86cb7a6571d90
5
5
  SHA512:
6
- metadata.gz: 0c2878ba5c1d0d45e12d75eea0ed5700ec513b2dde96ecbf8efce4d6e2a0d81eeb9b4a20b7d43b3a053c9c84c24caf3a794fdc1af750b37d7d8e4e3feaf4bdcd
7
- data.tar.gz: 68f2edd339b18575244d1643df2144f71b4dc2d48dac9355071fbc4a0ceb141bddcdee67189d84d1bf451b8445b83fe50bc545ecc6c866593012702c57b1bb24
6
+ metadata.gz: ea0f0eeb1718e00caef76ae713101292daa2019cab3af19a2e26c4f2d2c74920026757e729b5e57b8f60f98f366035211e206a8f9d97849c3f0bfc0fad8ccdc5
7
+ data.tar.gz: b44314b0bcd226f4666b1079b297ad6f67a646b6be0f22a4e855e5176caac17cdf7b792a2b760bd0ba05184146cacdacc121f643661f41c41e25ee7d9632b5e3
@@ -0,0 +1,78 @@
1
+ # An example config to use filter plugins.
2
+ # THIS FEATURE IS SUPPORTED FOR v0.12 AND ABOVE.
3
+
4
+ # in_forward to generate events to be tested.
5
+ # You can send an arbitrary event with an arbitrary tag.
6
+ # For example, the following command creates the event
7
+ # {"message":"hello world"} with tag = foo
8
+ #
9
+ # $ echo '{"message":"hello world"}' | fluent-cat foo
10
+
11
+ <source>
12
+ type forward
13
+ port 24224
14
+ </source>
15
+
16
+ # For all events with the tag "foo", filter it out
17
+ # UNLESS the value of the "message" field matches /keep this/
18
+ #
19
+ # - {"message":"keep this please"} is kept.
20
+ # - {"message":"Do not keep"} is filtered out.
21
+ # - {"messag22":"keep this please"} is filtered out.
22
+
23
+ <filter foo>
24
+ type grep
25
+ regexp1 message keep this
26
+ </filter>
27
+
28
+ # Matches the events that was kept by the above filter
29
+ <match foo>
30
+ type stdout
31
+ </match>
32
+
33
+ # For all events with the tag "bar", add the machine's hostname with
34
+ # the key "hostname" BEFORE forwarding to another instance of Fluentd
35
+ # at 123.4.2.4:24224.
36
+
37
+ <filter bar>
38
+ type record_transformer
39
+ <record>
40
+ hostname ${hostname}
41
+ </record>
42
+ </filter>
43
+
44
+ # By the time it is getting matched here, the event has
45
+ # the "hostname" field.
46
+ <match bar>
47
+ type forward
48
+ <server>
49
+ host 123.4.2.4
50
+ port 24225
51
+ </server>
52
+ </match>
53
+
54
+ # Composing two filters. For all events with the tag foo.bar,
55
+ #
56
+ # 1. The first filter filters out all events that has the field "hello"
57
+ # 2. Then, for those events, we downcase the value of the "name" field.
58
+ #
59
+ # - {"name":"SADA", "hello":100} gets filtered out
60
+ # - {"name":"SADA"} becomes {"name":"sada"}
61
+ # - {"last_name":"FURUHASHI"} throws an error because it has no field called "name"
62
+
63
+ <filter foo.bar>
64
+ type grep
65
+ exclude1 hello .
66
+ </filter>
67
+
68
+ <filter foo.bar>
69
+ type record_transformer
70
+ enable_ruby true
71
+ <record>
72
+ name ${name.downcase}
73
+ </record>
74
+ </filter>
75
+
76
+ <match foo.bar>
77
+ type stdout
78
+ </match>
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
16
16
  gem.require_paths = ["lib"]
17
17
  gem.has_rdoc = false
18
18
 
19
- gem.required_ruby_version = '>= 1.9.2'
19
+ gem.required_ruby_version = '>= 1.9.3'
20
20
 
21
21
  gem.add_runtime_dependency("msgpack", [">= 0.5.4", "< 0.6.0"])
22
22
  gem.add_runtime_dependency("json", [">= 1.4.3"])
@@ -35,4 +35,5 @@ Gem::Specification.new do |gem|
35
35
  gem.add_development_dependency("rr", [">= 1.0.0"])
36
36
  gem.add_development_dependency("timecop", [">= 0.3.0"])
37
37
  gem.add_development_dependency("test-unit", ["~> 3.0.2"])
38
+ gem.add_development_dependency("test-unit-rr", ["~> 1.0.3"])
38
39
  end
@@ -37,7 +37,7 @@ module Fluent
37
37
  @started_filters = []
38
38
 
39
39
  @log = Engine.log
40
- @event_router = EventRouter.new(self, NoMatchMatch.new(log))
40
+ @event_router = EventRouter.new(NoMatchMatch.new(log), self)
41
41
  @error_collector = nil
42
42
  end
43
43
 
@@ -137,6 +137,7 @@ module Fluent
137
137
  log.info "adding filter#{@context.nil? ? '' : " in #{@context}"}", pattern: pattern, type: type
138
138
 
139
139
  filter = Plugin.new_filter(type)
140
+ filter.router = @event_router
140
141
  filter.configure(conf)
141
142
  @filters << filter
142
143
  @event_router.add_rule(pattern, filter)
@@ -237,11 +237,15 @@ module Fluent
237
237
 
238
238
  def total_queued_chunk_size
239
239
  total = 0
240
- @map.each_value {|c|
241
- total += c.size
242
- }
243
- @queue.each {|c|
244
- total += c.size
240
+ synchronize {
241
+ @map.each_value {|c|
242
+ total += c.size
243
+ }
244
+ @queue.synchronize {
245
+ @queue.each {|c|
246
+ total += c.size
247
+ }
248
+ }
245
249
  }
246
250
  total
247
251
  end
@@ -50,6 +50,10 @@ op.on('-d', '--daemon PIDFILE', "daemonize fluent process") {|s|
50
50
  opts[:daemonize] = s
51
51
  }
52
52
 
53
+ op.on('--no-supervisor', "run without fluent supervisor") {
54
+ opts[:supervise] = false
55
+ }
56
+
53
57
  op.on('--user USER', "change user") {|s|
54
58
  opts[:chuser] = s
55
59
  }
@@ -27,6 +27,7 @@ module Fluent
27
27
 
28
28
  LINE_END = /(?:[ \t]*(?:\#.*)?(?:\z|[\r\n]))+/
29
29
  SPACING = /(?:[ \t\r\n]|\z|\#.*?(?:\z|[\r\n]))+/
30
+ ZERO_OR_MORE_SPACING = /(?:[ \t\r\n]|\z|\#.*?(?:\z|[\r\n]))*/
30
31
  SPACING_WITHOUT_COMMENT = /(?:[ \t\r\n]|\z)+/
31
32
 
32
33
  module ClassMethods
@@ -84,13 +84,13 @@ module Fluent
84
84
  elsif a.is_a?(Hash)
85
85
  opts.merge!(a)
86
86
  else
87
- raise ArgumentError, "wrong number of arguments (#{1 + args.length} for #{block ? 2 : 3})"
87
+ raise ArgumentError, "#{self.name}: wrong number of arguments (#{1 + args.length} for #{block ? 2 : 3})"
88
88
  end
89
89
  }
90
90
 
91
91
  type = opts[:type]
92
92
  if block && type
93
- raise ArgumentError, "both of block and type cannot be specified"
93
+ raise ArgumentError, "#{self.name}: both of block and type cannot be specified"
94
94
  end
95
95
 
96
96
  begin
@@ -98,7 +98,7 @@ module Fluent
98
98
  block ||= Configurable.lookup_type(type)
99
99
  rescue ConfigError
100
100
  # override error message
101
- raise ArgumentError, "unknown config_argument type `#{type}'"
101
+ raise ArgumentError, "#{self.name}: unknown config_argument type `#{type}'"
102
102
  end
103
103
 
104
104
  if opts.has_key?(:default)
@@ -110,7 +110,7 @@ module Fluent
110
110
 
111
111
  def config_argument(name, *args, &block)
112
112
  if @argument
113
- raise ArgumentError, "config_argument called twice"
113
+ raise ArgumentError, "#{self.name}: config_argument called twice"
114
114
  end
115
115
  name, block, opts = parameter_configuration(name, *args, &block)
116
116
 
@@ -130,7 +130,7 @@ module Fluent
130
130
  name = name.to_sym
131
131
 
132
132
  if @defaults.has_key?(name)
133
- raise ArgumentError, "default value specified twice for #{name}"
133
+ raise ArgumentError, "#{self.name}: default value specified twice for #{name}"
134
134
  end
135
135
 
136
136
  @defaults[name] = defval
@@ -139,13 +139,13 @@ module Fluent
139
139
 
140
140
  def config_section(name, *args, &block)
141
141
  unless block_given?
142
- raise ArgumentError, "config_section requires block parameter"
142
+ raise ArgumentError, "#{self.name}: config_section requires block parameter"
143
143
  end
144
144
  name = name.to_sym
145
145
 
146
146
  opts = {}
147
147
  unless args.empty? || args.size == 1 && args.first.is_a?(Hash)
148
- raise ArgumentError, "unknown config_section arguments: #{args.inspect}"
148
+ raise ArgumentError, "#{self.name}: unknown config_section arguments: #{args.inspect}"
149
149
  end
150
150
 
151
151
  sub_proxy = ConfigureProxy.new(name, *args)
@@ -49,6 +49,7 @@ module Fluent
49
49
  end
50
50
 
51
51
  def self.bool_value(str)
52
+ return nil if str.nil?
52
53
  case str.to_s
53
54
  when 'true', 'yes'
54
55
  true
@@ -79,7 +79,7 @@ module Fluent
79
79
  elsif skip(/\</)
80
80
  e_name = scan(ELEMENT_NAME)
81
81
  spacing
82
- e_arg = scan_nonquoted_string(/(?:#{SPACING}|\>)/)
82
+ e_arg = scan_nonquoted_string(/(?:#{ZERO_OR_MORE_SPACING}\>)/)
83
83
  spacing
84
84
  unless skip(/\>/)
85
85
  parse_error! "expected '>'"
@@ -207,29 +207,4 @@ module Fluent
207
207
  end
208
208
 
209
209
  Engine = EngineClass.new
210
-
211
- module Test
212
- @@test = false
213
-
214
- def test?
215
- @@test
216
- end
217
-
218
- def self.setup
219
- @@test = true
220
-
221
- Fluent.__send__(:remove_const, :Engine)
222
- engine = Fluent.const_set(:Engine, EngineClass.new).init
223
-
224
- engine.define_singleton_method(:now=) {|n|
225
- @now = n.to_i
226
- }
227
- engine.define_singleton_method(:now) {
228
- @now || super()
229
- }
230
-
231
- nil
232
- end
233
- end
234
210
  end
235
-
@@ -20,4 +20,5 @@ module Fluent
20
20
  DEFAULT_SOCKET_PATH = ENV['FLUENT_SOCKET'] || '/var/run/fluent/fluent.sock'
21
21
  DEFAULT_LISTEN_PORT = 24224
22
22
  DEFAULT_FILE_PERMISSION = 0644
23
+ DEFAULT_DIR_PERMISSION = 0755
23
24
  end
@@ -38,7 +38,7 @@ module Fluent
38
38
  # Collector is either of Output, Filter or other EventRouter.
39
39
  #
40
40
  class EventRouter
41
- def initialize(emit_error_handler, default_collector)
41
+ def initialize(default_collector, emit_error_handler)
42
42
  @match_rules = []
43
43
  @match_cache = MatchCache.new
44
44
  @default_collector = default_collector
@@ -73,7 +73,7 @@ module Fluent
73
73
  end
74
74
 
75
75
  attr_reader :collector
76
- attr_reader :patatern_str
76
+ attr_reader :pattern_str
77
77
  end
78
78
 
79
79
  # called by Agent to add new match pattern and collector
@@ -97,6 +97,10 @@ module Fluent
97
97
  @emit_error_handler.handle_emits_error(tag, es, e)
98
98
  end
99
99
 
100
+ def emit_error_event(tag, time, record, error)
101
+ @emit_error_handler.emit_error_event(tag, time, record, error)
102
+ end
103
+
100
104
  def match?(tag)
101
105
  !!find(tag)
102
106
  end
@@ -4,12 +4,19 @@ module Fluent
4
4
  include PluginId
5
5
  include PluginLoggerMixin
6
6
 
7
+ attr_accessor :router
8
+
7
9
  def initialize
8
10
  super
9
11
  end
10
12
 
11
13
  def configure(conf)
12
14
  super
15
+
16
+ if label_name = conf['@label']
17
+ label = Engine.root_agent.find_label(label_name)
18
+ @router = label.event_router
19
+ end
13
20
  end
14
21
 
15
22
  def start
@@ -24,7 +31,11 @@ module Fluent
24
31
  def filter_stream(tag, es)
25
32
  new_es = MultiEventStream.new
26
33
  es.each { |time, record|
27
- new_es.add(time, filter(tag, time, record))
34
+ begin
35
+ new_es.add(time, filter(tag, time, record))
36
+ rescue => e
37
+ router.emit_error_event(tag, time, record, e)
38
+ end
28
39
  }
29
40
  new_es
30
41
  end
@@ -77,11 +77,12 @@ module Fluent
77
77
  end
78
78
  end
79
79
 
80
- class JSONFormatter
81
- include Configurable
82
- include HandleTagAndTimeMixin
83
-
84
- config_param :time_as_epoch, :bool, :default => false
80
+ module StructuredFormatMixin
81
+ def self.included(klass)
82
+ klass.instance_eval {
83
+ config_param :time_as_epoch, :bool, :default => false
84
+ }
85
+ end
85
86
 
86
87
  def configure(conf)
87
88
  super
@@ -99,10 +100,30 @@ module Fluent
99
100
  def format(tag, time, record)
100
101
  filter_record(tag, time, record)
101
102
  record[@time_key] = time if @time_as_epoch
103
+ format_record(record)
104
+ end
105
+ end
106
+
107
+ class JSONFormatter
108
+ include Configurable
109
+ include HandleTagAndTimeMixin
110
+ include StructuredFormatMixin
111
+
112
+ def format_record(record)
102
113
  "#{Yajl.dump(record)}\n"
103
114
  end
104
115
  end
105
116
 
117
+ class MessagePackFormatter
118
+ include Configurable
119
+ include HandleTagAndTimeMixin
120
+ include StructuredFormatMixin
121
+
122
+ def format_record(record)
123
+ record.to_msgpack
124
+ end
125
+ end
126
+
106
127
  class LabeledTSVFormatter
107
128
  include Configurable
108
129
  include HandleTagAndTimeMixin
@@ -121,6 +142,37 @@ module Fluent
121
142
  end
122
143
  end
123
144
 
145
+ class CsvFormatter
146
+ include Configurable
147
+ include HandleTagAndTimeMixin
148
+
149
+ config_param :delimiter, :default => ',' do |val|
150
+ ['\t', 'TAB'].include?(val) ? "\t" : val
151
+ end
152
+ config_param :force_quotes, :bool, :default => true
153
+ config_param :fields, :default => [] do |val|
154
+ val.split(',').map do |f|
155
+ f.strip!
156
+ f.size > 0 ? f : nil
157
+ end.compact
158
+ end
159
+
160
+ def initialize
161
+ super
162
+ require 'csv'
163
+ end
164
+
165
+ def format(tag, time, record)
166
+ filter_record(tag, time, record)
167
+ row = @fields.inject([]) do |memo, key|
168
+ memo << record[key]
169
+ memo
170
+ end
171
+ CSV.generate_line(row, :col_sep => @delimiter,
172
+ :force_quotes => @force_quotes)
173
+ end
174
+ end
175
+
124
176
  class SingleValueFormatter
125
177
  include Configurable
126
178
 
@@ -134,41 +186,58 @@ module Fluent
134
186
  end
135
187
  end
136
188
 
189
+ class ProcWrappedFormatter
190
+ def initialize(proc)
191
+ @proc = proc
192
+ end
193
+
194
+ def configure(conf)
195
+ end
196
+
197
+ def format(tag, time, record)
198
+ @proc.call(tag, time, record)
199
+ end
200
+ end
201
+
137
202
  TEMPLATE_REGISTRY = Registry.new(:formatter_type, 'fluent/plugin/formatter_')
138
203
  {
139
204
  'out_file' => Proc.new { OutFileFormatter.new },
140
205
  'json' => Proc.new { JSONFormatter.new },
206
+ 'msgpack' => Proc.new { MessagePackFormatter.new },
141
207
  'ltsv' => Proc.new { LabeledTSVFormatter.new },
208
+ 'csv' => Proc.new { CsvFormatter.new },
142
209
  'single_value' => Proc.new { SingleValueFormatter.new },
143
210
  }.each { |name, factory|
144
211
  TEMPLATE_REGISTRY.register(name, factory)
145
212
  }
146
213
 
147
214
  def self.register_template(name, factory_or_proc)
148
- factory = if factory_or_proc.arity == 3
149
- Proc.new { factory_or_proc }
150
- else
215
+ factory = if factory_or_proc.is_a?(Class) # XXXFormatter
216
+ Proc.new { factory_or_proc.new }
217
+ elsif factory_or_proc.arity == 3 # Proc.new { |tag, time, record| }
218
+ Proc.new { ProcWrappedFormatter.new(factory_or_proc) }
219
+ else # Proc.new { XXXFormatter.new }
151
220
  factory_or_proc
152
221
  end
153
222
 
154
223
  TEMPLATE_REGISTRY.register(name, factory)
155
224
  end
156
225
 
226
+ def self.lookup(format)
227
+ TEMPLATE_REGISTRY.lookup(format).call
228
+ end
229
+
230
+ # Keep backward-compatibility
157
231
  def self.create(conf)
158
232
  format = conf['format']
159
233
  if format.nil?
160
234
  raise ConfigError, "'format' parameter is required"
161
235
  end
162
236
 
163
- # built-in template
164
- begin
165
- factory = TEMPLATE_REGISTRY.lookup(format)
166
- rescue ConfigError => e
167
- raise ConfigError, "unknown format: '#{format}'"
237
+ formatter = lookup(format)
238
+ if formatter.respond_to?(:configure)
239
+ formatter.configure(conf)
168
240
  end
169
-
170
- formatter = factory.call
171
- formatter.configure(conf)
172
241
  formatter
173
242
  end
174
243
  end