fluentd 0.14.7-x86-mingw32 → 0.14.10-x86-mingw32

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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +2 -0
  4. data/CONTRIBUTING.md +6 -1
  5. data/ChangeLog +95 -0
  6. data/Rakefile +21 -0
  7. data/appveyor.yml +1 -0
  8. data/code-of-conduct.md +3 -0
  9. data/example/out_exec_filter.conf +42 -0
  10. data/fluentd.gemspec +1 -1
  11. data/lib/fluent/agent.rb +2 -2
  12. data/lib/fluent/command/binlog_reader.rb +1 -1
  13. data/lib/fluent/command/cat.rb +15 -4
  14. data/lib/fluent/compat/output.rb +14 -9
  15. data/lib/fluent/compat/parser.rb +141 -11
  16. data/lib/fluent/config/configure_proxy.rb +2 -11
  17. data/lib/fluent/config/section.rb +8 -1
  18. data/lib/fluent/configurable.rb +1 -3
  19. data/lib/fluent/env.rb +1 -1
  20. data/lib/fluent/log.rb +1 -1
  21. data/lib/fluent/plugin/base.rb +17 -0
  22. data/lib/fluent/plugin/filter_parser.rb +108 -0
  23. data/lib/fluent/plugin/filter_record_transformer.rb +14 -35
  24. data/lib/fluent/plugin/filter_stdout.rb +1 -1
  25. data/lib/fluent/plugin/formatter.rb +5 -0
  26. data/lib/fluent/plugin/formatter_msgpack.rb +4 -0
  27. data/lib/fluent/plugin/formatter_stdout.rb +3 -2
  28. data/lib/fluent/plugin/formatter_tsv.rb +34 -0
  29. data/lib/fluent/plugin/in_exec.rb +48 -93
  30. data/lib/fluent/plugin/in_forward.rb +66 -265
  31. data/lib/fluent/plugin/in_http.rb +68 -65
  32. data/lib/fluent/plugin/in_monitor_agent.rb +8 -4
  33. data/lib/fluent/plugin/in_syslog.rb +42 -58
  34. data/lib/fluent/plugin/in_tail.rb +29 -14
  35. data/lib/fluent/plugin/in_tcp.rb +54 -14
  36. data/lib/fluent/plugin/in_udp.rb +49 -13
  37. data/lib/fluent/plugin/multi_output.rb +1 -3
  38. data/lib/fluent/plugin/out_exec.rb +58 -71
  39. data/lib/fluent/plugin/out_exec_filter.rb +199 -279
  40. data/lib/fluent/plugin/out_file.rb +172 -81
  41. data/lib/fluent/plugin/out_forward.rb +229 -206
  42. data/lib/fluent/plugin/out_stdout.rb +6 -21
  43. data/lib/fluent/plugin/output.rb +90 -59
  44. data/lib/fluent/plugin/parser.rb +121 -61
  45. data/lib/fluent/plugin/parser_csv.rb +9 -3
  46. data/lib/fluent/plugin/parser_json.rb +37 -35
  47. data/lib/fluent/plugin/parser_ltsv.rb +11 -19
  48. data/lib/fluent/plugin/parser_msgpack.rb +50 -0
  49. data/lib/fluent/plugin/parser_regexp.rb +15 -42
  50. data/lib/fluent/plugin/parser_tsv.rb +8 -3
  51. data/lib/fluent/plugin_helper.rb +10 -1
  52. data/lib/fluent/plugin_helper/child_process.rb +139 -73
  53. data/lib/fluent/plugin_helper/compat_parameters.rb +93 -4
  54. data/lib/fluent/plugin_helper/event_emitter.rb +14 -1
  55. data/lib/fluent/plugin_helper/event_loop.rb +24 -6
  56. data/lib/fluent/plugin_helper/extract.rb +16 -4
  57. data/lib/fluent/plugin_helper/formatter.rb +9 -11
  58. data/lib/fluent/plugin_helper/inject.rb +16 -1
  59. data/lib/fluent/plugin_helper/parser.rb +3 -3
  60. data/lib/fluent/plugin_helper/server.rb +494 -0
  61. data/lib/fluent/plugin_helper/socket.rb +101 -0
  62. data/lib/fluent/plugin_helper/socket_option.rb +84 -0
  63. data/lib/fluent/plugin_helper/timer.rb +1 -0
  64. data/lib/fluent/root_agent.rb +1 -1
  65. data/lib/fluent/test/driver/base.rb +95 -49
  66. data/lib/fluent/test/driver/base_owner.rb +18 -8
  67. data/lib/fluent/test/driver/multi_output.rb +2 -1
  68. data/lib/fluent/test/driver/output.rb +29 -6
  69. data/lib/fluent/test/helpers.rb +3 -1
  70. data/lib/fluent/test/log.rb +4 -0
  71. data/lib/fluent/test/startup_shutdown.rb +13 -0
  72. data/lib/fluent/time.rb +14 -8
  73. data/lib/fluent/version.rb +1 -1
  74. data/lib/fluent/winsvc.rb +1 -1
  75. data/test/command/test_binlog_reader.rb +5 -1
  76. data/test/compat/test_parser.rb +10 -0
  77. data/test/config/test_configurable.rb +193 -0
  78. data/test/config/test_configure_proxy.rb +0 -43
  79. data/test/helper.rb +36 -1
  80. data/test/plugin/test_base.rb +16 -0
  81. data/test/plugin/test_filter_parser.rb +665 -0
  82. data/test/plugin/test_filter_record_transformer.rb +36 -100
  83. data/test/plugin/test_filter_stdout.rb +18 -27
  84. data/test/plugin/test_in_dummy.rb +1 -1
  85. data/test/plugin/test_in_exec.rb +206 -94
  86. data/test/plugin/test_in_forward.rb +268 -347
  87. data/test/plugin/test_in_http.rb +310 -186
  88. data/test/plugin/test_in_monitor_agent.rb +65 -35
  89. data/test/plugin/test_in_syslog.rb +39 -3
  90. data/test/plugin/test_in_tcp.rb +78 -62
  91. data/test/plugin/test_in_udp.rb +101 -80
  92. data/test/plugin/test_out_exec.rb +223 -68
  93. data/test/plugin/test_out_exec_filter.rb +520 -169
  94. data/test/plugin/test_out_file.rb +637 -177
  95. data/test/plugin/test_out_forward.rb +242 -234
  96. data/test/plugin/test_out_null.rb +1 -1
  97. data/test/plugin/test_out_secondary_file.rb +4 -2
  98. data/test/plugin/test_out_stdout.rb +14 -35
  99. data/test/plugin/test_output_as_buffered.rb +60 -2
  100. data/test/plugin/test_parser.rb +359 -0
  101. data/test/plugin/test_parser_csv.rb +1 -2
  102. data/test/plugin/test_parser_json.rb +3 -4
  103. data/test/plugin/test_parser_labeled_tsv.rb +1 -2
  104. data/test/plugin/test_parser_none.rb +1 -2
  105. data/test/plugin/test_parser_regexp.rb +8 -4
  106. data/test/plugin/test_parser_tsv.rb +4 -3
  107. data/test/plugin_helper/test_child_process.rb +184 -0
  108. data/test/plugin_helper/test_compat_parameters.rb +88 -1
  109. data/test/plugin_helper/test_extract.rb +0 -1
  110. data/test/plugin_helper/test_formatter.rb +5 -2
  111. data/test/plugin_helper/test_inject.rb +21 -0
  112. data/test/plugin_helper/test_parser.rb +6 -5
  113. data/test/plugin_helper/test_server.rb +905 -0
  114. data/test/test_event_time.rb +3 -1
  115. data/test/test_output.rb +53 -2
  116. data/test/test_plugin_classes.rb +20 -0
  117. data/test/test_root_agent.rb +139 -0
  118. data/test/test_test_drivers.rb +135 -0
  119. metadata +28 -8
  120. data/test/plugin/test_parser_base.rb +0 -32
@@ -184,49 +184,6 @@ module Fluent::Config
184
184
  end
185
185
  end
186
186
 
187
- sub_test_case '#config_param without default values cause error if section is configured as init:true' do
188
- setup do
189
- @proxy = Fluent::Config::ConfigureProxy.new(:section, type_lookup: @type_lookup)
190
- end
191
-
192
- test 'with simple config_param with default value' do
193
- assert_nothing_raised do
194
- @proxy.config_section :subsection, init: true do
195
- config_param :param1, :integer, default: 1
196
- end
197
- end
198
- end
199
- test 'with simple config_param without default value' do
200
- assert_raise ArgumentError do
201
- @proxy.config_section :subsection, init: true do
202
- config_param :param1, :integer
203
- end
204
- end
205
- end
206
- test 'with config_param with config_set_default' do
207
- assert_nothing_raised do
208
- @proxy.config_section :subsection, init: true do
209
- config_param :param1, :integer
210
- config_set_default :param1, 1
211
- end
212
- end
213
- end
214
- test 'with config_argument' do
215
- assert_raise ArgumentError do
216
- @proxy.config_section :subsection, init: true do
217
- config_argument :param0, :string
218
- end
219
- end
220
- end
221
- test 'with config_argument with default value' do
222
- assert_nothing_raised do
223
- @proxy.config_section :subsection, init: true do
224
- config_argument :param0, :string, default: ''
225
- end
226
- end
227
- end
228
- end
229
-
230
187
  sub_test_case '#config_set_desc' do
231
188
  setup do
232
189
  @proxy = Fluent::Config::ConfigureProxy.new(:section, type_lookup: @type_lookup)
@@ -69,7 +69,18 @@ end
69
69
 
70
70
  include Fluent::Test::Helpers
71
71
 
72
- def unused_port(num = 1)
72
+ def unused_port(num = 1, protocol: :tcp, bind: "0.0.0.0")
73
+ case protocol
74
+ when :tcp
75
+ unused_port_tcp(num)
76
+ when :udp
77
+ unused_port_udp(num, bind: bind)
78
+ else
79
+ raise ArgumentError, "unknown protocol: #{protocol}"
80
+ end
81
+ end
82
+
83
+ def unused_port_tcp(num = 1)
73
84
  ports = []
74
85
  sockets = []
75
86
  num.times do
@@ -85,6 +96,30 @@ def unused_port(num = 1)
85
96
  end
86
97
  end
87
98
 
99
+ PORT_RANGE_AVAILABLE = (1024...65535)
100
+
101
+ def unused_port_udp(num = 1, bind: "0.0.0.0")
102
+ family = IPAddr.new(IPSocket.getaddress(bind)).ipv4? ? ::Socket::AF_INET : ::Socket::AF_INET6
103
+ ports = []
104
+ sockets = []
105
+ while ports.size < num
106
+ port = rand(PORT_RANGE_AVAILABLE)
107
+ u = UDPSocket.new(family)
108
+ if (u.bind(bind, port) rescue nil)
109
+ ports << port
110
+ sockets << u
111
+ else
112
+ u.close
113
+ end
114
+ end
115
+ sockets.each{|s| s.close }
116
+ if num == 1
117
+ return ports.first
118
+ else
119
+ return *ports
120
+ end
121
+ end
122
+
88
123
  def waiting(seconds, logs: nil, plugin: nil)
89
124
  begin
90
125
  Timeout.timeout(seconds) do
@@ -72,4 +72,20 @@ class BaseTest < Test::Unit::TestCase
72
72
  assert_equal 'myvalue1', p2.myparam1
73
73
  assert_equal 99, p2.mysection.myparam2
74
74
  end
75
+
76
+ test 'provides #string_safe_encoding to scrub invalid sequence string with info logging' do
77
+ logger = Fluent::Test::TestLogger.new
78
+ m = Module.new do
79
+ define_method(:log) do
80
+ logger
81
+ end
82
+ end
83
+ @p.extend m
84
+ assert_equal [], logger.logs
85
+
86
+ ret = @p.string_safe_encoding("abc\xff.\x01f"){|s| s.split(/\./) }
87
+ assert_equal ['abc?', "\u0001f"], ret
88
+ assert_equal 1, logger.logs.size
89
+ assert{ logger.logs.first.include?("invalid byte sequence is replaced in ") }
90
+ end
75
91
  end
@@ -0,0 +1,665 @@
1
+ require_relative '../helper'
2
+ require 'timecop'
3
+ require 'fluent/test/driver/filter'
4
+ require 'fluent/plugin/filter_parser'
5
+ require 'flexmock/test_unit'
6
+
7
+ class ParserFilterTest < Test::Unit::TestCase
8
+ include FlexMock::TestCase
9
+
10
+ def setup
11
+ Fluent::Test.setup
12
+ @tag = 'test'
13
+ @default_time = Time.parse('2010-05-04 03:02:01 UTC')
14
+ Timecop.freeze(@default_time)
15
+ end
16
+
17
+ def teardown
18
+ super
19
+ Timecop.return
20
+ end
21
+
22
+ def assert_equal_parsed_time(expected, actual)
23
+ if expected.is_a?(Integer)
24
+ assert_equal(expected, actual.to_i)
25
+ else
26
+ assert_equal_event_time(expected, actual)
27
+ end
28
+ end
29
+
30
+ ParserError = Fluent::Plugin::Parser::ParserError
31
+ CONFIG = %[
32
+ key_name message
33
+ reserve_data true
34
+ <parse>
35
+ @type regexp
36
+ expression /^(?<x>.)(?<y>.) (?<time>.+)$/
37
+ time_format %Y%m%d%H%M%S
38
+ </parse>
39
+ ]
40
+
41
+ def create_driver(conf=CONFIG)
42
+ Fluent::Test::Driver::Filter.new(Fluent::Plugin::ParserFilter).configure(conf)
43
+ end
44
+
45
+ def test_configure
46
+ assert_raise(Fluent::ConfigError) {
47
+ create_driver('')
48
+ }
49
+ assert_raise(Fluent::ConfigError) {
50
+ create_driver %[
51
+ key_name foo
52
+ <parse>
53
+ @type unknown_format_that_will_never_be_implemented
54
+ </parse>
55
+ ]
56
+ }
57
+ assert_nothing_raised {
58
+ create_driver %[
59
+ key_name foo
60
+ <parse>
61
+ @type regexp
62
+ expression /(?<x>.)/
63
+ </parse>
64
+ ]
65
+ }
66
+ assert_nothing_raised {
67
+ create_driver %[
68
+ key_name foo
69
+ <parse>
70
+ @type json
71
+ </parse>
72
+ ]
73
+ }
74
+ assert_nothing_raised {
75
+ create_driver %[
76
+ key_name foo
77
+ format json
78
+ ]
79
+ }
80
+ assert_nothing_raised {
81
+ create_driver %[
82
+ key_name foo
83
+ <parse>
84
+ @type ltsv
85
+ </parse>
86
+ ]
87
+ }
88
+ assert_nothing_raised {
89
+ create_driver %[
90
+ key_name message
91
+ <parse>
92
+ @type regexp
93
+ expression /^col1=(?<col1>.+) col2=(?<col2>.+)$/
94
+ </parse>
95
+ ]
96
+ }
97
+ d = create_driver %[
98
+ key_name foo
99
+ <parse>
100
+ @type regexp
101
+ expression /(?<x>.)/
102
+ </parse>
103
+ ]
104
+ assert_false d.instance.reserve_data
105
+ end
106
+
107
+ # CONFIG = %[
108
+ # remove_prefix test
109
+ # add_prefix parsed
110
+ # key_name message
111
+ # format /^(?<x>.)(?<y>.) (?<time>.+)$/
112
+ # time_format %Y%m%d%H%M%S
113
+ # reserve_data true
114
+ # ]
115
+ def test_filter
116
+ d1 = create_driver(CONFIG)
117
+ time = event_time("2012-01-02 13:14:15")
118
+ d1.run(default_tag: @tag) do
119
+ d1.feed(time, {'message' => '12 20120402182059'})
120
+ d1.feed(time, {'message' => '34 20120402182100'})
121
+ d1.feed(time, {'message' => '56 20120402182100'})
122
+ d1.feed(time, {'message' => '78 20120402182101'})
123
+ d1.feed(time, {'message' => '90 20120402182100'})
124
+ end
125
+ filtered = d1.filtered
126
+ assert_equal 5, filtered.length
127
+
128
+ first = filtered[0]
129
+ assert_equal_event_time event_time("2012-04-02 18:20:59"), first[0]
130
+ assert_equal '1', first[1]['x']
131
+ assert_equal '2', first[1]['y']
132
+ assert_equal '12 20120402182059', first[1]['message']
133
+
134
+ second = filtered[1]
135
+ assert_equal_event_time event_time("2012-04-02 18:21:00"), second[0]
136
+ assert_equal '3', second[1]['x']
137
+ assert_equal '4', second[1]['y']
138
+
139
+ third = filtered[2]
140
+ assert_equal_event_time event_time("2012-04-02 18:21:00"), third[0]
141
+ assert_equal '5', third[1]['x']
142
+ assert_equal '6', third[1]['y']
143
+
144
+ fourth = filtered[3]
145
+ assert_equal_event_time event_time("2012-04-02 18:21:01"), fourth[0]
146
+ assert_equal '7', fourth[1]['x']
147
+ assert_equal '8', fourth[1]['y']
148
+
149
+ fifth = filtered[4]
150
+ assert_equal_event_time event_time("2012-04-02 18:21:00"), fifth[0]
151
+ assert_equal '9', fifth[1]['x']
152
+ assert_equal '0', fifth[1]['y']
153
+
154
+ d2 = create_driver(%[
155
+ key_name data
156
+ format /^(?<x>.)(?<y>.) (?<t>.+)$/
157
+ ])
158
+ time = Fluent::EventTime.from_time(@default_time) # EventTime emit test
159
+ d2.run(default_tag: @tag) do
160
+ d2.feed(time, {'data' => '12 20120402182059'})
161
+ d2.feed(time, {'data' => '34 20120402182100'})
162
+ end
163
+ filtered = d2.filtered
164
+ assert_equal 2, filtered.length
165
+
166
+ first = filtered[0]
167
+ assert_equal_event_time time, first[0]
168
+ assert_nil first[1]['data']
169
+ assert_equal '1', first[1]['x']
170
+ assert_equal '2', first[1]['y']
171
+ assert_equal '20120402182059', first[1]['t']
172
+
173
+ second = filtered[1]
174
+ assert_equal_event_time time, second[0]
175
+ assert_nil second[1]['data']
176
+ assert_equal '3', second[1]['x']
177
+ assert_equal '4', second[1]['y']
178
+ assert_equal '20120402182100', second[1]['t']
179
+
180
+ d3 = create_driver(%[
181
+ key_name data
182
+ <parse>
183
+ @type regexp
184
+ expression /^(?<x>[0-9])(?<y>[0-9]) (?<t>.+)$/
185
+ </parse>
186
+ ])
187
+ time = Time.parse("2012-04-02 18:20:59").to_i
188
+ d3.run(default_tag: @tag) do
189
+ d3.feed(time, {'data' => '12 20120402182059'})
190
+ d3.feed(time, {'data' => '34 20120402182100'})
191
+ d3.feed(time, {'data' => 'xy 20120402182101'})
192
+ end
193
+ filtered = d3.filtered
194
+ assert_equal 2, filtered.length
195
+
196
+ d4 = create_driver(%[
197
+ key_name data
198
+ <parse>
199
+ @type json
200
+ </parse>
201
+ ])
202
+ time = Time.parse("2012-04-02 18:20:59").to_i
203
+ d4.run(default_tag: @tag) do
204
+ d4.feed(time, {'data' => '{"xxx":"first","yyy":"second"}', 'xxx' => 'x', 'yyy' => 'y'})
205
+ d4.feed(time, {'data' => 'foobar', 'xxx' => 'x', 'yyy' => 'y'})
206
+ end
207
+ filtered = d4.filtered
208
+ assert_equal 1, filtered.length
209
+
210
+ end
211
+
212
+ def test_filter_with_reserved_data
213
+ d1 = create_driver(%[
214
+ key_name data
215
+ reserve_data yes
216
+ <parse>
217
+ @type regexp
218
+ expression /^(?<x>\\d)(?<y>\\d) (?<t>.+)$/
219
+ </parse>
220
+ ])
221
+ time = event_time("2012-04-02 18:20:59")
222
+ d1.run(default_tag: @tag) do
223
+ d1.feed(time, {'data' => '12 20120402182059'})
224
+ d1.feed(time, {'data' => '34 20120402182100'})
225
+ d1.feed(time, {'data' => 'xy 20120402182101'})
226
+ end
227
+ filtered = d1.filtered
228
+ assert_equal 3, filtered.length
229
+
230
+ d2 = create_driver(%[
231
+ key_name data
232
+ reserve_data yes
233
+ <parse>
234
+ @type json
235
+ </parse>
236
+ ])
237
+ time = Fluent::EventTime.from_time(@default_time)
238
+ d2.run(default_tag: @tag) do
239
+ d2.feed(time, {'data' => '{"xxx":"first","yyy":"second"}', 'xxx' => 'x', 'yyy' => 'y'})
240
+ d2.feed(time, {'data' => 'foobar', 'xxx' => 'x', 'yyy' => 'y'})
241
+ end
242
+ filtered = d2.filtered
243
+ assert_equal 2, filtered.length
244
+
245
+ first = filtered[0]
246
+ assert_equal_event_time time, first[0]
247
+ assert_equal '{"xxx":"first","yyy":"second"}', first[1]['data']
248
+ assert_equal 'first', first[1]['xxx']
249
+ assert_equal 'second', first[1]['yyy']
250
+
251
+ second = filtered[1]
252
+ assert_equal_event_time time, second[0]
253
+ assert_equal 'foobar', second[1]['data']
254
+ assert_equal 'x', second[1]['xxx']
255
+ assert_equal 'y', second[1]['yyy']
256
+ end
257
+
258
+
259
+ CONFIG_LTSV = %[
260
+ key_name data
261
+ <parse>
262
+ @type ltsv
263
+ </parse>
264
+ ]
265
+ CONFIG_LTSV_WITH_TYPES = %[
266
+ key_name data
267
+ <parse>
268
+ @type ltsv
269
+ types i:integer,s:string,f:float,b:bool
270
+ </parse>
271
+ ]
272
+ data(:event_time => lambda { |time| Fluent::EventTime.from_time(time) },
273
+ :int_time => lambda { |time| time.to_i })
274
+ def test_filter_ltsv(time_parse)
275
+ d = create_driver(CONFIG_LTSV)
276
+ time = time_parse.call(@default_time)
277
+ d.run(default_tag: @tag) do
278
+ d.feed(time, {'data' => "xxx:first\tyyy:second", 'xxx' => 'x', 'yyy' => 'y'})
279
+ d.feed(time, {'data' => "xxx:first\tyyy:second2", 'xxx' => 'x', 'yyy' => 'y'})
280
+ end
281
+ filtered = d.filtered
282
+ assert_equal 2, filtered.length
283
+
284
+ first = filtered[0]
285
+ assert_equal_parsed_time time, first[0]
286
+ assert_nil first[1]['data']
287
+ assert_equal 'first', first[1]['xxx']
288
+ assert_equal 'second', first[1]['yyy']
289
+
290
+ second = filtered[1]
291
+ assert_equal_parsed_time time, second[0]
292
+ assert_nil first[1]['data']
293
+ assert_equal 'first', second[1]['xxx']
294
+ assert_equal 'second2', second[1]['yyy']
295
+
296
+ d = create_driver(CONFIG_LTSV + %[reserve_data yes])
297
+ time = @default_time.to_i
298
+ d.run(default_tag: @tag) do
299
+ d.feed(time, {'data' => "xxx:first\tyyy:second", 'xxx' => 'x', 'yyy' => 'y'})
300
+ d.feed(time, {'data' => "xxx:first\tyyy:second2", 'xxx' => 'x', 'yyy' => 'y'})
301
+ end
302
+ filtered = d.filtered
303
+ assert_equal 2, filtered.length
304
+
305
+ first = filtered[0]
306
+ assert_equal_parsed_time time, first[0]
307
+ assert_equal "xxx:first\tyyy:second", first[1]['data']
308
+ assert_equal 'first', first[1]['xxx']
309
+ assert_equal 'second', first[1]['yyy']
310
+
311
+ second = filtered[1]
312
+ assert_equal_parsed_time time, second[0]
313
+ assert_equal "xxx:first\tyyy:second", first[1]['data']
314
+ assert_equal 'first', second[1]['xxx']
315
+ assert_equal 'second2', second[1]['yyy']
316
+
317
+ # convert types
318
+ #d = create_driver(CONFIG_LTSV + %[
319
+ d = create_driver(CONFIG_LTSV_WITH_TYPES)
320
+ time = @default_time.to_i
321
+ d.run do
322
+ d.feed(@tag, time, {'data' => "i:1\ts:2\tf:3\tb:true\tx:123"})
323
+ end
324
+ filtered = d.filtered
325
+ assert_equal 1, filtered.length
326
+
327
+ first = filtered[0]
328
+ assert_equal_parsed_time time, first[0]
329
+ assert_equal 1, first[1]['i']
330
+ assert_equal '2', first[1]['s']
331
+ assert_equal 3.0, first[1]['f']
332
+ assert_equal true, first[1]['b']
333
+ assert_equal '123', first[1]['x']
334
+ end
335
+
336
+ CONFIG_TSV = %[
337
+ key_name data
338
+ <parse>
339
+ @type tsv
340
+ keys key1,key2,key3
341
+ </parse>
342
+ ]
343
+ data(:event_time => lambda { |time| Fluent::EventTime.from_time(time) },
344
+ :int_time => lambda { |time| time.to_i })
345
+ def test_filter_tsv(time_parse)
346
+ d = create_driver(CONFIG_TSV)
347
+ time = time_parse.call(@default_time)
348
+ d.run do
349
+ d.feed(@tag, time, {'data' => "value1\tvalue2\tvalueThree", 'xxx' => 'x', 'yyy' => 'y'})
350
+ end
351
+ filtered = d.filtered
352
+ assert_equal 1, filtered.length
353
+
354
+ first = filtered[0]
355
+ assert_equal_parsed_time time, first[0]
356
+ assert_nil first[1]['data']
357
+ assert_equal 'value1', first[1]['key1']
358
+ assert_equal 'value2', first[1]['key2']
359
+ assert_equal 'valueThree', first[1]['key3']
360
+ end
361
+
362
+ CONFIG_CSV = %[
363
+ key_name data
364
+ <parse>
365
+ @type csv
366
+ keys key1,key2,key3
367
+ </parse>
368
+ ]
369
+ CONFIG_CSV_COMPAT = %[
370
+ key_name data
371
+ format csv
372
+ keys key1,key2,key3
373
+ ]
374
+ data(new_conf: CONFIG_CSV,
375
+ compat_conf: CONFIG_CSV_COMPAT)
376
+ def test_filter_csv(conf)
377
+ d = create_driver(conf)
378
+ time = @default_time.to_i
379
+ d.run do
380
+ d.feed(@tag, time, {'data' => 'value1,"value2","value""ThreeYes!"', 'xxx' => 'x', 'yyy' => 'y'})
381
+ end
382
+ filtered = d.filtered
383
+ assert_equal 1, filtered.length
384
+
385
+ first = filtered[0]
386
+ assert_equal time, first[0]
387
+ assert_nil first[1]['data']
388
+ assert_equal 'value1', first[1]['key1']
389
+ assert_equal 'value2', first[1]['key2']
390
+ assert_equal 'value"ThreeYes!', first[1]['key3']
391
+ end
392
+
393
+ CONFIG_HASH_VALUE_FIELD = %[
394
+ key_name data
395
+ hash_value_field parsed
396
+ <parse>
397
+ @type json
398
+ </parse>
399
+ ]
400
+ CONFIG_HASH_VALUE_FIELD_RESERVE_DATA = %[
401
+ key_name data
402
+ reserve_data yes
403
+ hash_value_field parsed
404
+ <parse>
405
+ @type json
406
+ </parse>
407
+ ]
408
+ CONFIG_HASH_VALUE_FIELD_WITH_INJECT_KEY_PREFIX = %[
409
+ key_name data
410
+ hash_value_field parsed
411
+ inject_key_prefix data.
412
+ <parse>
413
+ @type json
414
+ </parse>
415
+ ]
416
+ data(:event_time => lambda { |time| Fluent::EventTime.from_time(time) },
417
+ :int_time => lambda { |time| time.to_i })
418
+ def test_filter_inject_hash_value_field(time_parse)
419
+ original = {'data' => '{"xxx":"first","yyy":"second"}', 'xxx' => 'x', 'yyy' => 'y'}
420
+
421
+ d = create_driver(CONFIG_HASH_VALUE_FIELD)
422
+ time = time_parse.call(@default_time)
423
+ d.run do
424
+ d.feed(@tag, time, original)
425
+ end
426
+ filtered = d.filtered
427
+ assert_equal 1, filtered.length
428
+
429
+ first = filtered[0]
430
+ assert_equal_parsed_time time, first[0]
431
+
432
+ record = first[1]
433
+ assert_equal 1, record.keys.size
434
+ assert_equal({"xxx"=>"first","yyy"=>"second"}, record['parsed'])
435
+
436
+ d = create_driver(CONFIG_HASH_VALUE_FIELD_RESERVE_DATA)
437
+ time = @default_time.to_i
438
+ d.run do
439
+ d.feed(@tag, time, original)
440
+ end
441
+ filtered = d.filtered
442
+ assert_equal 1, filtered.length
443
+
444
+ first = filtered[0]
445
+ assert_equal_parsed_time time, first[0]
446
+
447
+ record = first[1]
448
+ assert_equal 4, record.keys.size
449
+ assert_equal original['data'], record['data']
450
+ assert_equal original['xxx'], record['xxx']
451
+ assert_equal original['yyy'], record['yyy']
452
+ assert_equal({"xxx"=>"first","yyy"=>"second"}, record['parsed'])
453
+
454
+ d = create_driver(CONFIG_HASH_VALUE_FIELD_WITH_INJECT_KEY_PREFIX)
455
+ time = @default_time.to_i
456
+ d.run do
457
+ d.feed(@tag, time, original)
458
+ end
459
+ filtered = d.filtered
460
+ assert_equal 1, filtered.length
461
+
462
+ first = filtered[0]
463
+ assert_equal_parsed_time time, first[0]
464
+
465
+ record = first[1]
466
+ assert_equal 1, record.keys.size
467
+ assert_equal({"data.xxx"=>"first","data.yyy"=>"second"}, record['parsed'])
468
+ end
469
+
470
+ CONFIG_DONT_PARSE_TIME = %[
471
+ key_name data
472
+ reserve_time true
473
+ <parse>
474
+ @type json
475
+ keep_time_key true
476
+ </parse>
477
+ ]
478
+ CONFIG_DONT_PARSE_TIME_COMPAT = %[
479
+ key_name data
480
+ reserve_time true
481
+ format json
482
+ keep_time_key true
483
+ ]
484
+ data(new_conf: CONFIG_DONT_PARSE_TIME,
485
+ compat_conf: CONFIG_DONT_PARSE_TIME_COMPAT)
486
+ def test_time_should_be_reserved(conf)
487
+ t = Time.now.to_i
488
+ d = create_driver(conf)
489
+ d.run(default_tag: @tag) do
490
+ d.feed(t, {'data' => '{"time":1383190430, "f1":"v1"}'})
491
+ d.feed(t, {'data' => '{"time":"1383190430", "f1":"v1"}'})
492
+ d.feed(t, {'data' => '{"time":"2013-10-31 12:34:03 +0900", "f1":"v1"}'})
493
+ end
494
+ filtered = d.filtered
495
+ assert_equal 3, filtered.length
496
+
497
+ assert_equal 'v1', filtered[0][1]['f1']
498
+ assert_equal 1383190430, filtered[0][1]['time']
499
+ assert_equal t, filtered[0][0]
500
+
501
+ assert_equal 'v1', filtered[1][1]['f1']
502
+ assert_equal "1383190430", filtered[1][1]['time']
503
+ assert_equal t, filtered[1][0]
504
+
505
+ assert_equal 'v1', filtered[2][1]['f1']
506
+ assert_equal '2013-10-31 12:34:03 +0900', filtered[2][1]['time']
507
+ assert_equal t, filtered[2][0]
508
+ end
509
+
510
+ CONFIG_INVALID_TIME_VALUE = %[
511
+ key_name data
512
+ <parse>
513
+ @type json
514
+ </parse>
515
+ ] # 'time' is implicit @time_key
516
+ def test_filter_invalid_time_data
517
+ # should not raise errors
518
+ time = Time.now.to_i
519
+ d = create_driver(CONFIG_INVALID_TIME_VALUE)
520
+ assert_nothing_raised {
521
+ d.run(default_tag: @tag) do
522
+ d.feed(time, {'data' => '{"time":[], "f1":"v1"}'})
523
+ d.feed(time, {'data' => '{"time":"thisisnottime", "f1":"v1"}'})
524
+ end
525
+ }
526
+ filtered = d.filtered
527
+ assert_equal 1, filtered.length
528
+
529
+ assert_equal 0, filtered[0][0].to_i
530
+ assert_equal 'v1', filtered[0][1]['f1']
531
+ assert_equal nil, filtered[0][1]['time']
532
+ end
533
+
534
+ # REGEXP = /^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/
535
+
536
+ CONFIG_NOT_REPLACE = %[
537
+ key_name data
538
+ <parse>
539
+ @type regexp
540
+ expression /^(?<message>.*)$/
541
+ </parse>
542
+ ]
543
+ CONFIG_INVALID_BYTE = CONFIG_NOT_REPLACE + %[
544
+ replace_invalid_sequence true
545
+ ]
546
+ def test_filter_invalid_byte
547
+ invalid_utf8 = "\xff".force_encoding('UTF-8')
548
+
549
+ d = create_driver(CONFIG_NOT_REPLACE)
550
+ d.run do
551
+ d.feed(@tag, Fluent::EventTime.now.to_i, {'data' => invalid_utf8})
552
+ end
553
+ error_event = d.error_events.first
554
+ assert_equal "test", error_event[0]
555
+ assert_instance_of ArgumentError, error_event[3]
556
+
557
+ d = create_driver(CONFIG_INVALID_BYTE)
558
+ assert_nothing_raised {
559
+ d.run do
560
+ d.feed(@tag, Fluent::EventTime.now.to_i, {'data' => invalid_utf8})
561
+ end
562
+ }
563
+ filtered = d.filtered
564
+ assert_equal 1, filtered.length
565
+ assert_nil filtered[0][1]['data']
566
+ assert_equal '?'.force_encoding('UTF-8'), filtered[0][1]['message']
567
+
568
+ d = create_driver(CONFIG_INVALID_BYTE + %[reserve_data yes])
569
+ assert_nothing_raised {
570
+ d.run do
571
+ d.feed(@tag, Fluent::EventTime.now.to_i, {'data' => invalid_utf8})
572
+ end
573
+ }
574
+ filtered = d.filtered
575
+ assert_equal 1, filtered.length
576
+ assert_equal invalid_utf8, filtered[0][1]['data']
577
+ assert_equal '?'.force_encoding('UTF-8'), filtered[0][1]['message']
578
+
579
+ invalid_ascii = "\xff".force_encoding('US-ASCII')
580
+ d = create_driver(CONFIG_INVALID_BYTE)
581
+ assert_nothing_raised {
582
+ d.run do
583
+ d.feed(@tag, Fluent::EventTime.now.to_i, {'data' => invalid_ascii})
584
+ end
585
+ }
586
+ filtered = d.filtered
587
+ assert_equal 1, filtered.length
588
+ assert_nil filtered[0][1]['data']
589
+ assert_equal '?'.force_encoding('US-ASCII'), filtered[0][1]['message']
590
+ end
591
+
592
+ CONFIG_NOT_IGNORE = %[
593
+ key_name data
594
+ hash_value_field parsed
595
+ <parse>
596
+ @type json
597
+ </parse>
598
+ ]
599
+ CONFIG_PASS_SAME_RECORD = CONFIG_NOT_IGNORE + %[
600
+ reserve_data true
601
+ ]
602
+ def test_filter_key_not_exist
603
+ d = create_driver(CONFIG_NOT_IGNORE)
604
+ flexmock(d.instance.router).should_receive(:emit_error_event).
605
+ with(String, Integer, Hash, ArgumentError.new("data does not exist")).once
606
+ assert_nothing_raised {
607
+ d.run do
608
+ d.feed(@tag, Fluent::EventTime.now.to_i, {'foo' => 'bar'})
609
+ end
610
+ }
611
+
612
+ d = create_driver(CONFIG_PASS_SAME_RECORD)
613
+ assert_nothing_raised {
614
+ d.run do
615
+ d.feed(@tag, Fluent::EventTime.now.to_i, {'foo' => 'bar'})
616
+ end
617
+ }
618
+ filtered = d.filtered
619
+ assert_equal 1, filtered.length
620
+ assert_nil filtered[0][1]['data']
621
+ assert_equal 'bar', filtered[0][1]['foo']
622
+ end
623
+
624
+ # emit_error_event test
625
+ INVALID_MESSAGE = 'foo bar'
626
+ VALID_MESSAGE = 'col1=foo col2=bar'
627
+
628
+ def test_call_emit_error_event_when_parser_error
629
+ d = create_driver(CONFIG_INVALID_TIME_VALUE)
630
+ flexmock(d.instance.router).should_receive(:emit_error_event).
631
+ with(String, Integer, Hash, ParserError).once
632
+ d.run do
633
+ d.feed(@tag, Fluent::EventTime.now.to_i, {'data' => '{"time":[], "f1":"v1"}'})
634
+ end
635
+ end
636
+
637
+ CONFIG_UNMATCHED_PATTERN_LOG = %[
638
+ key_name message
639
+ <parse>
640
+ @type regexp
641
+ expression /^col1=(?<col1>.+) col2=(?<col2>.+)$/
642
+ </parse>
643
+ ]
644
+
645
+ class UnmatchedPatternLogTest < self
646
+ setup do
647
+ @d = create_driver(CONFIG_UNMATCHED_PATTERN_LOG)
648
+ end
649
+
650
+ def test_call_emit_error_event_when_pattern_is_mismached
651
+ flexmock(@d.instance.router).should_receive(:emit_error_event).
652
+ with(String, Integer, Hash, ParserError.new("pattern not match with data '#{INVALID_MESSAGE}'")).once
653
+ @d.run do
654
+ @d.feed(@tag, Fluent::EventTime.now.to_i, {'message' => INVALID_MESSAGE})
655
+ end
656
+ end
657
+
658
+ def test_not_call_emit_error_event_when_pattern_is_mached
659
+ flexmock(@d.instance.router).should_receive(:emit_error_event).never
660
+ @d.run do
661
+ @d.feed(@tag, Fluent::EventTime.now.to_i, {'message' => VALID_MESSAGE})
662
+ end
663
+ end
664
+ end
665
+ end