fluentd 0.12.0.pre.1 → 0.12.0.pre.2

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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.travis.yml +1 -0
  4. data/ChangeLog +21 -0
  5. data/README.md +10 -2
  6. data/Rakefile +4 -13
  7. data/example/v1_literal_example.conf +36 -0
  8. data/fluentd.gemspec +4 -1
  9. data/lib/fluent/buffer.rb +73 -46
  10. data/lib/fluent/command/fluentd.rb +7 -2
  11. data/lib/fluent/config/basic_parser.rb +5 -0
  12. data/lib/fluent/config/element.rb +2 -5
  13. data/lib/fluent/config/literal_parser.rb +26 -7
  14. data/lib/fluent/config/section.rb +2 -0
  15. data/lib/fluent/config/v1_parser.rb +9 -2
  16. data/lib/fluent/formatter.rb +2 -1
  17. data/lib/fluent/mixin.rb +22 -7
  18. data/lib/fluent/output.rb +17 -8
  19. data/lib/fluent/parser.rb +14 -3
  20. data/lib/fluent/plugin/buf_file.rb +30 -15
  21. data/lib/fluent/plugin/filter_grep.rb +69 -0
  22. data/lib/fluent/plugin/filter_record_transformer.rb +183 -0
  23. data/lib/fluent/plugin/in_exec.rb +6 -0
  24. data/lib/fluent/plugin/in_forward.rb +34 -4
  25. data/lib/fluent/plugin/in_http.rb +1 -1
  26. data/lib/fluent/plugin/out_exec.rb +1 -1
  27. data/lib/fluent/plugin/out_exec_filter.rb +8 -1
  28. data/lib/fluent/plugin/out_forward.rb +82 -4
  29. data/lib/fluent/supervisor.rb +1 -1
  30. data/lib/fluent/timezone.rb +131 -0
  31. data/lib/fluent/version.rb +1 -1
  32. data/test/config/assertions.rb +42 -0
  33. data/test/config/test_config_parser.rb +385 -0
  34. data/test/config/test_configurable.rb +530 -0
  35. data/test/config/test_configure_proxy.rb +99 -0
  36. data/test/config/test_dsl.rb +237 -0
  37. data/test/config/test_literal_parser.rb +293 -0
  38. data/test/config/test_section.rb +112 -0
  39. data/test/config/test_system_config.rb +49 -0
  40. data/test/helper.rb +25 -0
  41. data/test/plugin/test_buf_file.rb +604 -0
  42. data/test/plugin/test_buf_memory.rb +204 -0
  43. data/test/plugin/test_filter_grep.rb +124 -0
  44. data/test/plugin/test_filter_record_transformer.rb +251 -0
  45. data/test/plugin/test_in_exec.rb +1 -0
  46. data/test/plugin/test_in_forward.rb +205 -2
  47. data/test/plugin/test_in_gc_stat.rb +1 -0
  48. data/test/plugin/test_in_http.rb +58 -2
  49. data/test/plugin/test_in_object_space.rb +1 -0
  50. data/test/plugin/test_in_status.rb +1 -0
  51. data/test/plugin/test_in_stream.rb +1 -1
  52. data/test/plugin/test_in_syslog.rb +1 -1
  53. data/test/plugin/test_in_tail.rb +1 -0
  54. data/test/plugin/test_in_tcp.rb +1 -1
  55. data/test/plugin/test_in_udp.rb +1 -1
  56. data/test/plugin/test_out_copy.rb +1 -0
  57. data/test/plugin/test_out_exec.rb +1 -0
  58. data/test/plugin/test_out_exec_filter.rb +1 -0
  59. data/test/plugin/test_out_file.rb +36 -0
  60. data/test/plugin/test_out_forward.rb +279 -8
  61. data/test/plugin/test_out_roundrobin.rb +1 -0
  62. data/test/plugin/test_out_stdout.rb +1 -0
  63. data/test/plugin/test_out_stream.rb +1 -1
  64. data/test/test_buffer.rb +530 -0
  65. data/test/test_config.rb +1 -1
  66. data/test/test_configdsl.rb +1 -1
  67. data/test/test_formatter.rb +223 -0
  68. data/test/test_match.rb +1 -2
  69. data/test/test_mixin.rb +74 -2
  70. data/test/test_parser.rb +7 -1
  71. metadata +88 -35
  72. data/lib/fluent/plugin/buf_zfile.rb +0 -75
  73. data/spec/config/config_parser_spec.rb +0 -314
  74. data/spec/config/configurable_spec.rb +0 -524
  75. data/spec/config/configure_proxy_spec.rb +0 -96
  76. data/spec/config/dsl_spec.rb +0 -239
  77. data/spec/config/helper.rb +0 -49
  78. data/spec/config/literal_parser_spec.rb +0 -222
  79. data/spec/config/section_spec.rb +0 -97
  80. data/spec/config/system_config_spec.rb +0 -49
  81. data/spec/spec_helper.rb +0 -60
@@ -0,0 +1,204 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'helper'
3
+ require 'fluent/test'
4
+ require 'fluent/plugin/buf_memory'
5
+
6
+ require 'stringio'
7
+ require 'msgpack'
8
+
9
+ module FluentMemoryBufferTest
10
+ class MemoryBufferChunkTest < Test::Unit::TestCase
11
+ def test_init
12
+ chunk = Fluent::MemoryBufferChunk.new('key')
13
+ assert_equal 'key', chunk.key
14
+ assert_equal '', chunk.instance_eval{ @data }
15
+ assert_equal 'ASCII-8BIT', chunk.instance_eval{ @data }.encoding.to_s
16
+ assert chunk.unique_id # non nil
17
+
18
+ chunk2 = Fluent::MemoryBufferChunk.new('initdata', 'data')
19
+ assert_equal 'initdata', chunk2.key
20
+ assert_equal 'data', chunk2.instance_eval{ @data }
21
+ end
22
+
23
+ def test_buffer_chunk_interface
24
+ chunk = Fluent::BufferChunk.new('key')
25
+
26
+ assert chunk.respond_to?(:empty?)
27
+ assert chunk.respond_to?(:<<)
28
+ assert chunk.respond_to?(:size)
29
+ assert chunk.respond_to?(:close)
30
+ assert chunk.respond_to?(:purge)
31
+ assert chunk.respond_to?(:read)
32
+ assert chunk.respond_to?(:open)
33
+ assert chunk.respond_to?(:write_to)
34
+ assert chunk.respond_to?(:msgpack_each)
35
+ end
36
+
37
+ def test_empty?
38
+ chunk = Fluent::MemoryBufferChunk.new('key')
39
+ assert chunk.empty?
40
+
41
+ chunk.instance_eval{ @data = "non empty" }
42
+ assert !(chunk.empty?)
43
+ end
44
+
45
+ def test_append_data_and_size
46
+ chunk = Fluent::MemoryBufferChunk.new('key')
47
+ assert_equal '', chunk.instance_eval{ @data }
48
+
49
+ chunk << "foo bar baz\n".force_encoding('UTF-8')
50
+ assert_equal "foo bar baz\n", chunk.instance_eval{ @data }
51
+ assert_equal 'ASCII-8BIT', chunk.instance_eval{ @data }.encoding.to_s
52
+
53
+ assert_equal 12, chunk.size # bytesize
54
+
55
+ chunk << "日本語Japanese\n".force_encoding('UTF-8')
56
+ assert_equal "foo bar baz\n日本語Japanese\n".force_encoding('ASCII-8BIT'), chunk.instance_eval{ @data }
57
+ assert_equal 'ASCII-8BIT', chunk.instance_eval{ @data }.encoding.to_s
58
+
59
+ assert_equal 30, chunk.size # bytesize
60
+ end
61
+
62
+ def test_close_and_purge_does_nothing
63
+ chunk = Fluent::MemoryBufferChunk.new('key', 'data')
64
+ chunk.close
65
+ chunk.close
66
+ chunk.close
67
+ chunk.close
68
+ chunk.purge
69
+ chunk.purge
70
+ chunk.purge
71
+ chunk.purge
72
+ assert_equal 'data', chunk.instance_eval{ @data }
73
+ end
74
+
75
+ def test_read_just_returns_data
76
+ data = "data1\ndata2\n"
77
+ chunk = Fluent::MemoryBufferChunk.new('key', data)
78
+ assert_equal data, chunk.read
79
+ assert_equal data.object_id, chunk.read.object_id
80
+ end
81
+
82
+ def test_open
83
+ # StringIO.open(@data, &block)
84
+ chunk = Fluent::MemoryBufferChunk.new('key', 'foo bar baz')
85
+ chunk.open do |io|
86
+ assert 'foo bar baz', io.read
87
+ end
88
+ end
89
+
90
+ def test_write_to
91
+ chunk = Fluent::MemoryBufferChunk.new('key', 'foo bar baz')
92
+ dummy_dst = StringIO.new
93
+ chunk.write_to(dummy_dst)
94
+ assert_equal 'foo bar baz', dummy_dst.string
95
+ end
96
+
97
+ def test_msgpack_each
98
+ d0 = MessagePack.pack([[1, "foo"], [2, "bar"], [3, "baz"]])
99
+ d1 = MessagePack.pack({"key1" => "value1", "key2" => "value2"})
100
+ d2 = MessagePack.pack("string1")
101
+ d3 = MessagePack.pack(1)
102
+ d4 = MessagePack.pack(nil)
103
+ chunk = Fluent::MemoryBufferChunk.new('key', d0 + d1 + d2 + d3 + d4)
104
+
105
+ store = []
106
+ chunk.msgpack_each do |data|
107
+ store << data
108
+ end
109
+
110
+ assert_equal 5, store.size
111
+ assert_equal [[1, "foo"], [2, "bar"], [3, "baz"]], store[0]
112
+ assert_equal({"key1" => "value1", "key2" => "value2"}, store[1])
113
+ assert_equal "string1", store[2]
114
+ assert_equal 1, store[3]
115
+ assert_equal nil, store[4]
116
+ end
117
+ end
118
+
119
+ class MemoryBufferTest < Test::Unit::TestCase
120
+ def test_init_configure
121
+ buf = Fluent::MemoryBuffer.new
122
+
123
+ buf.configure({})
124
+ assert buf.flush_at_shutdown
125
+ assert_equal 64, buf.buffer_queue_limit
126
+ end
127
+
128
+ class DummyOutput
129
+ attr_accessor :written
130
+
131
+ def write(chunk)
132
+ @written ||= []
133
+ @written.push chunk
134
+ "return value"
135
+ end
136
+ end
137
+
138
+ def test_before_shutdown
139
+ buf = Fluent::MemoryBuffer.new
140
+ buf.start
141
+
142
+ # before_shutdown flushes all chunks in @map and @queue
143
+
144
+ c1 = [ buf.new_chunk('k0'), buf.new_chunk('k1'), buf.new_chunk('k2'), buf.new_chunk('k3') ]
145
+ c2 = [ buf.new_chunk('q0'), buf.new_chunk('q1') ]
146
+
147
+ buf.instance_eval do
148
+ @map = {
149
+ 'k0' => c1[0], 'k1' => c1[1], 'k2' => c1[2], 'k3' => c1[3],
150
+ 'q0' => c2[0], 'q1' => c2[1]
151
+ }
152
+ end
153
+ c1[0] << "data1\ndata2\n"
154
+ c1[1] << "data1\ndata2\n"
155
+ c1[2] << "data1\ndata2\n"
156
+ # k3 chunk is empty!
157
+
158
+ c2[0] << "data1\ndata2\n"
159
+ c2[1] << "data1\ndata2\n"
160
+ buf.push('q0')
161
+ buf.push('q1')
162
+
163
+ buf.instance_eval do
164
+ @enqueue_hook_times = 0
165
+ def enqueue(chunk)
166
+ @enqueue_hook_times += 1
167
+ end
168
+ end
169
+ assert_equal 0, buf.instance_eval{ @enqueue_hook_times }
170
+
171
+ out = DummyOutput.new
172
+ assert_equal nil, out.written
173
+
174
+ buf.before_shutdown(out)
175
+
176
+ assert_equal 3, buf.instance_eval{ @enqueue_hook_times } # k0, k1, k2
177
+ assert_equal 5, out.written.size
178
+ assert_equal [c2[0], c2[1], c1[0], c1[1], c1[2]], out.written
179
+ end
180
+
181
+ def test_new_chunk
182
+ buf = Fluent::MemoryBuffer.new
183
+ chunk = buf.new_chunk('key')
184
+ assert_equal Fluent::MemoryBufferChunk, chunk.class
185
+ assert_equal 'key', chunk.key
186
+ assert chunk.empty?
187
+ end
188
+
189
+ def test_resume
190
+ buf = Fluent::MemoryBuffer.new
191
+ resumed_queue, resumed_store = buf.resume
192
+ assert resumed_queue.empty?
193
+ assert resumed_store.empty?
194
+ end
195
+
196
+ def test_enqueue_does_nothing # enqueue hook
197
+ buf = Fluent::MemoryBuffer.new
198
+ chunk = Fluent::MemoryBufferChunk.new('k', "data1\ndata2\n")
199
+ assert_equal "data1\ndata2\n", chunk.read
200
+ buf.enqueue(chunk)
201
+ assert_equal "data1\ndata2\n", chunk.read
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,124 @@
1
+ require 'helper'
2
+ require 'fluent/test'
3
+ require 'fluent/plugin/filter_grep'
4
+
5
+ # TODO: Replace with FilterTestDriver
6
+ class GrepFilterTest < Test::Unit::TestCase
7
+ setup do
8
+ @filter = Fluent::GrepFilter.new
9
+ @time = Fluent::Engine.now
10
+ end
11
+
12
+ def configure(instance, conf, use_v1 = false)
13
+ config = if conf.is_a?(Fluent::Config::Element)
14
+ str
15
+ else
16
+ Fluent::Config.parse(conf, "(test)", "(test_dir)", use_v1)
17
+ end
18
+ instance.configure(config)
19
+ instance
20
+ end
21
+
22
+ sub_test_case 'configure' do
23
+ test 'check default' do
24
+ configure(@filter, '')
25
+ assert_empty(@filter.regexps)
26
+ assert_empty(@filter.excludes)
27
+ end
28
+
29
+ test "regexpN can contain a space" do
30
+ configure(@filter, %[regexp1 message foo])
31
+ assert_equal(Regexp.compile(/ foo/), @filter.regexps['message'])
32
+ end
33
+
34
+ test "excludeN can contain a space" do
35
+ configure(@filter, %[exclude1 message foo])
36
+ assert_equal(Regexp.compile(/ foo/), @filter.excludes['message'])
37
+ end
38
+ end
39
+
40
+ sub_test_case 'filter_stream' do
41
+ def messages
42
+ [
43
+ "2013/01/13T07:02:11.124202 INFO GET /ping",
44
+ "2013/01/13T07:02:13.232645 WARN POST /auth",
45
+ "2013/01/13T07:02:21.542145 WARN GET /favicon.ico",
46
+ "2013/01/13T07:02:43.632145 WARN POST /login",
47
+ ]
48
+ end
49
+
50
+ def emit(config, msgs)
51
+ es = Fluent::MultiEventStream.new
52
+ msgs.each { |msg|
53
+ es.add(@time, {'foo' => 'bar', 'message' => msg})
54
+ }
55
+
56
+ configure(@filter, config)
57
+ @filter.filter_stream('filter.test', es);
58
+ end
59
+
60
+ test 'empty config' do
61
+ es = emit('', messages)
62
+ assert_equal(4, es.instance_variable_get(:@record_array).size)
63
+ end
64
+
65
+ test 'regexpN' do
66
+ es = emit('regexp1 message WARN', messages)
67
+ assert_equal(3, es.instance_variable_get(:@record_array).size)
68
+ assert_block('only WARN logs') do
69
+ es.all? { |t, r|
70
+ !r['message'].include?('INFO')
71
+ }
72
+ end
73
+ end
74
+
75
+ test 'excludeN' do
76
+ es = emit('exclude1 message favicon', messages)
77
+ assert_equal(3, es.instance_variable_get(:@record_array).size)
78
+ assert_block('remove favicon logs') do
79
+ es.all? { |t, r|
80
+ !r['message'].include?('favicon')
81
+ }
82
+ end
83
+ end
84
+
85
+ sub_test_case 'with invalid sequence' do
86
+ def messages
87
+ [
88
+ "\xff".force_encoding('UTF-8'),
89
+ ]
90
+ end
91
+
92
+ test "don't raise an exception" do
93
+ assert_nothing_raised {
94
+ emit(%[regexp1 message WARN], ["\xff".force_encoding('UTF-8')])
95
+ }
96
+ end
97
+ end
98
+ end
99
+
100
+ sub_test_case 'grep non-string jsonable values' do
101
+ def emit(msg, config = 'regexp1 message 0')
102
+ es = Fluent::MultiEventStream.new
103
+ es.add(@time, {'foo' => 'bar', 'message' => msg})
104
+
105
+ configure(@filter, config)
106
+ @filter.filter_stream('filter.test', es);
107
+ end
108
+
109
+ data(
110
+ 'array' => ["0"],
111
+ 'hash' => ["0" => "0"],
112
+ 'integer' => 0,
113
+ 'float' => 0.1)
114
+ test "value" do |data|
115
+ es = emit(data)
116
+ assert_equal(1, es.instance_variable_get(:@record_array).size)
117
+ end
118
+
119
+ test "value boolean" do
120
+ es = emit(true, %[regexp1 message true])
121
+ assert_equal(1, es.instance_variable_get(:@record_array).size)
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,251 @@
1
+ require_relative '../helper'
2
+ require 'timecop'
3
+ require 'fluent/test'
4
+ require 'fluent/plugin/filter_record_transformer'
5
+
6
+ # TODO: Replace with FilterTestDriver
7
+ class RecordTransformerFilterTest < Test::Unit::TestCase
8
+ setup do
9
+ @instance = Fluent::RecordTransformerFilter.new
10
+ @hostname = Socket.gethostname.chomp
11
+ @tag = 'test.tag'
12
+ @tag_parts = @tag.split('.')
13
+ @time = Time.utc(1,2,3,4,5,2010,nil,nil,nil,nil)
14
+ Timecop.freeze(@time)
15
+ end
16
+
17
+ teardown do
18
+ Timecop.return
19
+ end
20
+
21
+ def configure(instance, conf, use_v1 = false)
22
+ config = if conf.is_a?(Fluent::Config::Element)
23
+ str
24
+ else
25
+ Fluent::Config.parse(conf, "(test)", "(test_dir)", use_v1)
26
+ end
27
+ instance.configure(config)
28
+ instance
29
+ end
30
+
31
+ sub_test_case 'configure' do
32
+ test 'check default' do
33
+ assert_nothing_raised do
34
+ configure(@instance, '')
35
+ end
36
+ end
37
+
38
+ test "keep_keys must be specified together with renew_record true" do
39
+ assert_raise(Fluent::ConfigError) do
40
+ configure(@instance, %[keep_keys a])
41
+ end
42
+ end
43
+ end
44
+
45
+ sub_test_case "test options" do
46
+ def emit(config, msgs = [''])
47
+ es = Fluent::MultiEventStream.new
48
+ msgs.each do |msg|
49
+ es.add(@time, {'foo' => 'bar', 'message' => msg})
50
+ end
51
+
52
+ configure(@instance, config)
53
+ @instance.filter_stream(@tag, es)
54
+ end
55
+
56
+ CONFIG = %[
57
+ <record>
58
+ hostname ${hostname}
59
+ tag ${tag}
60
+ time ${time}
61
+ message ${hostname} ${tag_parts[-1]} ${message}
62
+ </record>
63
+ ]
64
+
65
+ test 'typical usage' do
66
+ msgs = ['1', '2']
67
+ es = emit(CONFIG, msgs)
68
+ es.each_with_index do |(t, r), i|
69
+ assert_equal('bar', r['foo'])
70
+ assert_equal(@hostname, r['hostname'])
71
+ assert_equal(@tag, r['tag'])
72
+ assert_equal(@time.to_s, r['time'])
73
+ assert_equal("#{@hostname} #{@tag_parts[-1]} #{msgs[i]}", r['message'])
74
+ end
75
+ end
76
+
77
+ test 'remove_keys' do
78
+ config = CONFIG + %[remove_keys foo,message]
79
+ es = emit(config)
80
+ es.each_with_index do |(t, r), i|
81
+ assert_not_include(r, 'foo')
82
+ assert_equal(@hostname, r['hostname'])
83
+ assert_equal(@tag, r['tag'])
84
+ assert_equal(@time.to_s, r['time'])
85
+ assert_not_include(r, 'message')
86
+ end
87
+ end
88
+
89
+ test 'renew_record' do
90
+ config = CONFIG + %[renew_record true]
91
+ msgs = ['1', '2']
92
+ es = emit(config, msgs)
93
+ es.each_with_index do |(t, r), i|
94
+ assert_not_include(r, 'foo')
95
+ assert_equal(@hostname, r['hostname'])
96
+ assert_equal(@tag, r['tag'])
97
+ assert_equal(@time.to_s, r['time'])
98
+ assert_equal("#{@hostname} #{@tag_parts[-1]} #{msgs[i]}", r['message'])
99
+ end
100
+ end
101
+
102
+ test 'keep_keys' do
103
+ config = %[renew_record true\nkeep_keys foo,message]
104
+ msgs = ['1', '2']
105
+ es = emit(config, msgs)
106
+ es.each_with_index do |(t, r), i|
107
+ assert_equal('bar', r['foo'])
108
+ assert_equal(msgs[i], r['message'])
109
+ end
110
+ end
111
+
112
+ test 'enable_ruby' do
113
+ config = %[
114
+ enable_ruby yes
115
+ <record>
116
+ message ${hostname} ${tag_parts.last} ${URI.encode(message)}
117
+ </record>
118
+ ]
119
+ msgs = ['1', '2']
120
+ es = emit(config, msgs)
121
+ es.each_with_index do |(t, r), i|
122
+ assert_equal("#{@hostname} #{@tag_parts[-1]} #{msgs[i]}", r['message'])
123
+ end
124
+ end
125
+ end
126
+
127
+ sub_test_case 'test placeholders' do
128
+ def emit(config, msgs = [''])
129
+ es = Fluent::MultiEventStream.new
130
+ msgs.each do |msg|
131
+ es.add(@time, {'eventType0' => 'bar', 'message' => msg})
132
+ end
133
+
134
+ configure(@instance, config)
135
+ @instance.filter_stream(@tag, es)
136
+ end
137
+
138
+ %w[yes no].each do |enable_ruby|
139
+ test "hostname with enble_ruby #{enable_ruby}" do
140
+ config = %[
141
+ enable_ruby #{enable_ruby}
142
+ <record>
143
+ message ${hostname}
144
+ </record>
145
+ ]
146
+ es = emit(config)
147
+ es.each do |t, r|
148
+ assert_equal(@hostname, r['message'])
149
+ end
150
+ end
151
+
152
+ test "tag with enable_ruby #{enable_ruby}" do
153
+ config = %[
154
+ enable_ruby #{enable_ruby}
155
+ <record>
156
+ message ${tag}
157
+ </record>
158
+ ]
159
+ es = emit(config)
160
+ es.each do |t, r|
161
+ assert_equal(@tag, r['message'])
162
+ end
163
+ end
164
+
165
+ test "tag_parts with enable_ruby #{enable_ruby}" do
166
+ config = %[
167
+ enable_ruby #{enable_ruby}
168
+ <record>
169
+ message ${tag_parts[0]} ${tag_parts[-1]}
170
+ </record>
171
+ ]
172
+ expected = "#{@tag.split('.').first} #{@tag.split('.').last}"
173
+ es = emit(config)
174
+ es.each do |t, r|
175
+ assert_equal(expected, r['message'])
176
+ end
177
+ end
178
+
179
+ test "${tag_prefix[N]} and ${tag_suffix[N]} with enable_ruby #{enable_ruby}" do
180
+ config = %[
181
+ enable_ruby #{enable_ruby}
182
+ <record>
183
+ message ${tag_prefix[1]} ${tag_prefix[-2]} ${tag_suffix[2]} ${tag_suffix[-3]}
184
+ </record>
185
+ ]
186
+ @tag = 'prefix.test.tag.suffix'
187
+ expected = "prefix.test prefix.test.tag tag.suffix test.tag.suffix"
188
+ es = emit(config)
189
+ es.each do |t, r|
190
+ assert_equal(expected, r['message'])
191
+ end
192
+ end
193
+
194
+ test "time with enable_ruby #{enable_ruby}" do
195
+ config = %[
196
+ enable_ruby #{enable_ruby}
197
+ <record>
198
+ message ${time}
199
+ </record>
200
+ ]
201
+ es = emit(config)
202
+ es.each do |t, r|
203
+ assert_equal(@time.to_s, r['message'])
204
+ end
205
+ end
206
+
207
+ test "record keys with enable_ruby #{enable_ruby}" do
208
+ config = %[
209
+ enable_ruby #{enable_ruby}
210
+ remove_keys eventType0
211
+ <record>
212
+ message bar ${message}
213
+ eventtype ${eventType0}
214
+ </record>
215
+ ]
216
+ msgs = ['1', '2']
217
+ es = emit(config, msgs)
218
+ es.each_with_index do |(t, r), i|
219
+ assert_not_include(r, 'eventType0')
220
+ assert_equal("bar", r['eventtype'])
221
+ assert_equal("bar #{msgs[i]}", r['message'])
222
+ end
223
+ end
224
+ end
225
+
226
+ test 'unknown placeholder (enable_ruby no)' do
227
+ config = %[
228
+ enable_ruby no
229
+ <record>
230
+ message ${unknown}
231
+ </record>
232
+ ]
233
+ mock(@instance.log).warn("unknown placeholder `${unknown}` found")
234
+ emit(config)
235
+ end
236
+
237
+ test 'failed to expand (enable_ruby yes)' do
238
+ config = %[
239
+ enable_ruby yes
240
+ <record>
241
+ message ${unknown['bar']}
242
+ </record>
243
+ ]
244
+ mock(@instance.log).warn("failed to expand `${unknown['bar']}`", anything)
245
+ es = emit(config)
246
+ es.each do |t, r|
247
+ assert_nil(r['message'])
248
+ end
249
+ end
250
+ end
251
+ end