fluentd 1.11.3 → 1.12.0

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  3. data/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. data/.github/workflows/stale-actions.yml +22 -0
  5. data/.travis.yml +22 -2
  6. data/CHANGELOG.md +66 -0
  7. data/README.md +1 -1
  8. data/appveyor.yml +3 -0
  9. data/bin/fluent-cap-ctl +7 -0
  10. data/bin/fluent-ctl +7 -0
  11. data/fluentd.gemspec +2 -1
  12. data/lib/fluent/capability.rb +87 -0
  13. data/lib/fluent/command/cap_ctl.rb +174 -0
  14. data/lib/fluent/command/ctl.rb +177 -0
  15. data/lib/fluent/command/plugin_config_formatter.rb +2 -1
  16. data/lib/fluent/env.rb +4 -0
  17. data/lib/fluent/plugin.rb +5 -0
  18. data/lib/fluent/plugin/buffer.rb +2 -21
  19. data/lib/fluent/plugin/formatter.rb +24 -0
  20. data/lib/fluent/plugin/formatter_csv.rb +1 -1
  21. data/lib/fluent/plugin/formatter_hash.rb +3 -1
  22. data/lib/fluent/plugin/formatter_json.rb +3 -1
  23. data/lib/fluent/plugin/formatter_ltsv.rb +5 -3
  24. data/lib/fluent/plugin/formatter_out_file.rb +6 -4
  25. data/lib/fluent/plugin/formatter_single_value.rb +4 -2
  26. data/lib/fluent/plugin/formatter_tsv.rb +4 -2
  27. data/lib/fluent/plugin/in_http.rb +23 -2
  28. data/lib/fluent/plugin/in_tail.rb +109 -41
  29. data/lib/fluent/plugin/in_tail/position_file.rb +39 -14
  30. data/lib/fluent/plugin/in_tcp.rb +1 -0
  31. data/lib/fluent/plugin/out_http.rb +20 -2
  32. data/lib/fluent/plugin/output.rb +14 -6
  33. data/lib/fluent/plugin_helper/http_server/compat/server.rb +1 -1
  34. data/lib/fluent/plugin_helper/inject.rb +4 -1
  35. data/lib/fluent/plugin_helper/retry_state.rb +4 -0
  36. data/lib/fluent/supervisor.rb +140 -43
  37. data/lib/fluent/time.rb +1 -0
  38. data/lib/fluent/version.rb +1 -1
  39. data/lib/fluent/winsvc.rb +22 -4
  40. data/test/command/test_binlog_reader.rb +22 -6
  41. data/test/command/test_cap_ctl.rb +100 -0
  42. data/test/command/test_ctl.rb +57 -0
  43. data/test/command/test_plugin_config_formatter.rb +57 -2
  44. data/test/plugin/in_tail/test_position_file.rb +45 -25
  45. data/test/plugin/test_filter_stdout.rb +6 -1
  46. data/test/plugin/test_formatter_hash.rb +6 -3
  47. data/test/plugin/test_formatter_json.rb +14 -4
  48. data/test/plugin/test_formatter_ltsv.rb +13 -5
  49. data/test/plugin/test_formatter_out_file.rb +35 -14
  50. data/test/plugin/test_formatter_single_value.rb +12 -6
  51. data/test/plugin/test_formatter_tsv.rb +12 -4
  52. data/test/plugin/test_in_http.rb +25 -0
  53. data/test/plugin/test_in_tail.rb +430 -30
  54. data/test/plugin/test_out_file.rb +23 -18
  55. data/test/plugin/test_output.rb +12 -0
  56. data/test/plugin/test_parser_syslog.rb +2 -2
  57. data/test/plugin_helper/test_compat_parameters.rb +7 -2
  58. data/test/plugin_helper/test_inject.rb +42 -0
  59. data/test/test_capability.rb +74 -0
  60. data/test/test_formatter.rb +34 -10
  61. data/test/test_output.rb +6 -1
  62. data/test/test_supervisor.rb +119 -1
  63. metadata +33 -4
@@ -0,0 +1,100 @@
1
+ require_relative '../helper'
2
+
3
+ require 'tempfile'
4
+ require 'fluent/command/cap_ctl'
5
+
6
+ class TestFluentCapCtl < Test::Unit::TestCase
7
+ setup do
8
+ omit "This environment does not handle Linux capability" unless defined?(CapNG)
9
+ end
10
+
11
+ sub_test_case "success" do
12
+ test "clear capability" do
13
+ logs = capture_stdout do
14
+ Fluent::CapCtl.new(["--clear"]).call
15
+ end
16
+ expression = /\AClear capabilities .*\n/m
17
+ assert_match expression, logs
18
+ end
19
+
20
+ test "add capability" do
21
+ logs = capture_stdout do
22
+ Fluent::CapCtl.new(["--add", "dac_override"]).call
23
+ end
24
+ expression = /\AUpdating .* done.\nAdding .*\n/m
25
+ assert_match expression, logs
26
+ end
27
+
28
+ test "drop capability" do
29
+ logs = capture_stdout do
30
+ Fluent::CapCtl.new(["--drop", "chown"]).call
31
+ end
32
+ expression = /\AUpdating .* done.\nDropping .*\n/m
33
+ assert_match expression, logs
34
+ end
35
+
36
+ test "get capability" do
37
+ logs = capture_stdout do
38
+ Fluent::CapCtl.new(["--get"]).call
39
+ end
40
+ expression = /\ACapabilities in .*,\nEffective: .*\nInheritable: .*\nPermitted: .*/m
41
+ assert_match expression, logs
42
+ end
43
+ end
44
+
45
+ sub_test_case "success with file" do
46
+ test "clear capability" do
47
+ logs = capture_stdout do
48
+ Tempfile.create("fluent-cap-") do |tempfile|
49
+ Fluent::CapCtl.new(["--clear-cap", "-f", tempfile.path]).call
50
+ end
51
+ end
52
+ expression = /\AClear capabilities .*\n/m
53
+ assert_match expression, logs
54
+ end
55
+
56
+ test "add capability" do
57
+ logs = capture_stdout do
58
+ Tempfile.create("fluent-cap-") do |tempfile|
59
+ Fluent::CapCtl.new(["--add", "dac_override", "-f", tempfile.path]).call
60
+ end
61
+ end
62
+ expression = /\AUpdating .* done.\nAdding .*\n/m
63
+ assert_match expression, logs
64
+ end
65
+
66
+ test "drop capability" do
67
+ logs = capture_stdout do
68
+ Tempfile.create("fluent-cap-") do |tempfile|
69
+ Fluent::CapCtl.new(["--drop", "chown", "-f", tempfile.path]).call
70
+ end
71
+ end
72
+ expression = /\AUpdating .* done.\nDropping .*\n/m
73
+ assert_match expression, logs
74
+ end
75
+
76
+ test "get capability" do
77
+ logs = capture_stdout do
78
+ Tempfile.create("fluent-cap-") do |tempfile|
79
+ Fluent::CapCtl.new(["--get", "-f", tempfile.path]).call
80
+ end
81
+ end
82
+ expression = /\ACapabilities in .*,\nEffective: .*\nInheritable: .*\nPermitted: .*/m
83
+ assert_match expression, logs
84
+ end
85
+ end
86
+
87
+ sub_test_case "invalid" do
88
+ test "add capability" do
89
+ assert_raise(ArgumentError) do
90
+ Fluent::CapCtl.new(["--add", "nonexitent"]).call
91
+ end
92
+ end
93
+
94
+ test "drop capability" do
95
+ assert_raise(ArgumentError) do
96
+ Fluent::CapCtl.new(["--drop", "invalid"]).call
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,57 @@
1
+ require_relative '../helper'
2
+
3
+ require 'test-unit'
4
+ require 'win32/event' if Fluent.windows?
5
+
6
+ require 'fluent/command/ctl'
7
+
8
+ class TestFluentdCtl < ::Test::Unit::TestCase
9
+ def assert_win32_event(event_name, command, pid_or_svcname)
10
+ command, event_suffix = data
11
+ event = Win32::Event.new(event_name)
12
+ ipc = Win32::Ipc.new(event.handle)
13
+ ret = Win32::Ipc::TIMEOUT
14
+
15
+ wait_thread = Thread.new do
16
+ ret = ipc.wait(1)
17
+ end
18
+ Fluent::Ctl.new([command, pid_or_svcname]).call
19
+ wait_thread.join
20
+ assert_equal(Win32::Ipc::SIGNALED, ret)
21
+ end
22
+
23
+ data("shutdown" => ["shutdown", "TERM", ""],
24
+ "restart" => ["restart", "HUP", "HUP"],
25
+ "flush" => ["flush", "USR1", "USR1"],
26
+ "reload" => ["reload", "USR2", "USR2"])
27
+ def test_commands(data)
28
+ command, signal, event_suffix = data
29
+
30
+ if Fluent.windows?
31
+ event_name = "fluentd_54321"
32
+ event_name << "_#{event_suffix}" unless event_suffix.empty?
33
+ assert_win32_event(event_name, command, "54321")
34
+ else
35
+ got_signal = false
36
+ Signal.trap(signal) do
37
+ got_signal = true
38
+ end
39
+ Fluent::Ctl.new([command, "#{$$}"]).call
40
+ assert_true(got_signal)
41
+ end
42
+ end
43
+
44
+ data("shutdown" => ["shutdown", ""],
45
+ "restart" => ["restart", "HUP"],
46
+ "flush" => ["flush", "USR1"],
47
+ "reload" => ["reload", "USR2"])
48
+ def test_commands_with_winsvcname(data)
49
+ omit "Only for Windows" unless Fluent.windows?
50
+
51
+ command, event_suffix = data
52
+ event_name = "testfluentdwinsvc"
53
+ event_name << "_#{event_suffix}" unless event_suffix.empty?
54
+
55
+ assert_win32_event(event_name, command, "testfluentdwinsvc")
56
+ end
57
+ end
@@ -56,6 +56,32 @@ class TestFluentPluginConfigFormatter < Test::Unit::TestCase
56
56
  end
57
57
  end
58
58
 
59
+ class FakeStorage < ::Fluent::Plugin::Storage
60
+ ::Fluent::Plugin.register_storage('fake', self)
61
+
62
+ def get(key)
63
+ end
64
+
65
+ def fetch(key, defval)
66
+ end
67
+
68
+ def put(key, value)
69
+ end
70
+
71
+ def delete(key)
72
+ end
73
+
74
+ def update(key, &block)
75
+ end
76
+ end
77
+
78
+ class FakeServiceDiscovery < ::Fluent::Plugin::ServiceDiscovery
79
+ ::Fluent::Plugin.register_sd('fake', self)
80
+
81
+ desc "hostname"
82
+ config_param :hostname, :string
83
+ end
84
+
59
85
  class SimpleInput < ::Fluent::Plugin::Input
60
86
  ::Fluent::Plugin.register_input("simple", self)
61
87
  helpers :inject, :compat_parameters
@@ -88,6 +114,13 @@ class TestFluentPluginConfigFormatter < Test::Unit::TestCase
88
114
  end
89
115
  end
90
116
 
117
+ class SimpleServiceDiscovery < ::Fluent::Plugin::ServiceDiscovery
118
+ ::Fluent::Plugin.register_sd('simple', self)
119
+
120
+ desc "servers"
121
+ config_param :servers, :array
122
+ end
123
+
91
124
  sub_test_case "json" do
92
125
  data(input: [FakeInput, "input"],
93
126
  output: [FakeOutput, "output"],
@@ -196,6 +229,28 @@ TEXT
196
229
  assert_equal(expected, dumped_config)
197
230
  end
198
231
 
232
+ data("abbrev" => "sd",
233
+ "normal" => "service_discovery")
234
+ test "service_discovery simple" do |data|
235
+ plugin_type = data
236
+ dumped_config = capture_stdout do
237
+ FluentPluginConfigFormatter.new(["--format=markdown", plugin_type, "simple"]).call
238
+ end
239
+ expected = <<TEXT
240
+ * See also: [ServiceDiscovery Plugin Overview](https://docs.fluentd.org/v/1.0/servicediscovery#overview)
241
+
242
+ ## TestFluentPluginConfigFormatter::SimpleServiceDiscovery
243
+
244
+ ### servers (array) (required)
245
+
246
+ servers
247
+
248
+
249
+ TEXT
250
+ assert_equal(expected, dumped_config)
251
+ end
252
+
253
+
199
254
  test "output complex" do
200
255
  dumped_config = capture_stdout do
201
256
  FluentPluginConfigFormatter.new(["--format=markdown", "output", "complex"]).call
@@ -251,7 +306,7 @@ TEXT
251
306
  sub_test_case "arguments" do
252
307
  data do
253
308
  hash = {}
254
- ["input", "output", "filter", "parser", "formatter"].each do |type|
309
+ ["input", "output", "filter", "parser", "formatter", "storage", "service_discovery"].each do |type|
255
310
  ["txt", "json", "markdown"].each do |format|
256
311
  argv = ["--format=#{format}"]
257
312
  [
@@ -259,7 +314,7 @@ TEXT
259
314
  ["--verbose"],
260
315
  ["--compact"]
261
316
  ].each do |options|
262
- hash[(argv + options).join(" ")] = argv + options + [type, "fake"]
317
+ hash["[#{type}] " + (argv + options).join(" ")] = argv + options + [type, "fake"]
263
318
  end
264
319
  end
265
320
  end
@@ -1,5 +1,6 @@
1
1
  require_relative '../../helper'
2
2
  require 'fluent/plugin/in_tail/position_file'
3
+ require 'fluent/plugin/in_tail'
3
4
 
4
5
  require 'fileutils'
5
6
  require 'tempfile'
@@ -27,9 +28,15 @@ class IntailPositionFileTest < Test::Unit::TestCase
27
28
  f.seek(0)
28
29
  end
29
30
 
31
+ def follow_inodes_block
32
+ [true, false].each do |follow_inodes|
33
+ yield follow_inodes
34
+ end
35
+ end
36
+
30
37
  test '.load' do
31
38
  write_data(@file, TEST_CONTENT)
32
- Fluent::Plugin::TailInput::PositionFile.load(@file, logger: $log)
39
+ Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, **{logger: $log})
33
40
 
34
41
  @file.seek(0)
35
42
  lines = @file.readlines
@@ -41,7 +48,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
41
48
  sub_test_case '#try_compact' do
42
49
  test 'compact invalid and convert 32 bit inode value' do
43
50
  write_data(@file, TEST_CONTENT)
44
- Fluent::Plugin::TailInput::PositionFile.new(@file, logger: $log).try_compact
51
+ Fluent::Plugin::TailInput::PositionFile.new(@file, false, {}, **{logger: $log}).try_compact
45
52
 
46
53
  @file.seek(0)
47
54
  lines = @file.readlines
@@ -55,7 +62,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
55
62
  valid_path\t0000000000000002\t0000000000000001
56
63
  valid_path\t0000000000000003\t0000000000000004
57
64
  EOF
58
- Fluent::Plugin::TailInput::PositionFile.new(@file, logger: $log).try_compact
65
+ Fluent::Plugin::TailInput::PositionFile.new(@file, false, {}, **{logger: $log}).try_compact
59
66
 
60
67
  @file.seek(0)
61
68
  lines = @file.readlines
@@ -64,7 +71,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
64
71
 
65
72
  test 'does not change when the file is changed' do
66
73
  write_data(@file, TEST_CONTENT)
67
- pf = Fluent::Plugin::TailInput::PositionFile.new(@file, logger: $log)
74
+ pf = Fluent::Plugin::TailInput::PositionFile.new(@file, false, {}, **{logger: $log})
68
75
 
69
76
  mock.proxy(pf).fetch_compacted_entries do |r|
70
77
  @file.write("unwatched\t#{UNWATCHED_STR}\t0000000000000000\n")
@@ -79,11 +86,16 @@ class IntailPositionFileTest < Test::Unit::TestCase
79
86
  end
80
87
 
81
88
  test 'update seek postion of remained position entry' do
82
- pf = Fluent::Plugin::TailInput::PositionFile.new(@file, logger: $log)
83
- pf['path1']
84
- pf['path2']
85
- pf['path3']
86
- pf.unwatch('path1')
89
+ pf = Fluent::Plugin::TailInput::PositionFile.new(@file, false, {}, **{logger: $log})
90
+ target_info1 = Fluent::Plugin::TailInput::TargetInfo.new('path1', -1)
91
+ target_info2 = Fluent::Plugin::TailInput::TargetInfo.new('path2', -1)
92
+ target_info3 = Fluent::Plugin::TailInput::TargetInfo.new('path3', -1)
93
+ pf[target_info1]
94
+ pf[target_info2]
95
+ pf[target_info3]
96
+
97
+ target_info1_2 = Fluent::Plugin::TailInput::TargetInfo.new('path1', 1234)
98
+ pf.unwatch(target_info1_2)
87
99
 
88
100
  pf.try_compact
89
101
 
@@ -93,8 +105,10 @@ class IntailPositionFileTest < Test::Unit::TestCase
93
105
  assert_equal "path3\t0000000000000000\t0000000000000000\n", lines[1]
94
106
  assert_equal 2, lines.size
95
107
 
96
- pf.unwatch('path2')
97
- pf.unwatch('path3')
108
+ target_info2_2 = Fluent::Plugin::TailInput::TargetInfo.new('path2', 1235)
109
+ target_info3_2 = Fluent::Plugin::TailInput::TargetInfo.new('path3', 1236)
110
+ pf.unwatch(target_info2_2)
111
+ pf.unwatch(target_info3_2)
98
112
  @file.seek(0)
99
113
  lines = @file.readlines
100
114
  assert_equal "path2\t#{UNWATCHED_STR}\t0000000000000000\n", lines[0]
@@ -106,7 +120,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
106
120
  sub_test_case '#load' do
107
121
  test 'compact invalid and convert 32 bit inode value' do
108
122
  write_data(@file, TEST_CONTENT)
109
- Fluent::Plugin::TailInput::PositionFile.load(@file, logger: $log)
123
+ Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, **{logger: $log})
110
124
 
111
125
  @file.seek(0)
112
126
  lines = @file.readlines
@@ -120,7 +134,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
120
134
  valid_path\t0000000000000002\t0000000000000001
121
135
  valid_path\t0000000000000003\t0000000000000004
122
136
  EOF
123
- Fluent::Plugin::TailInput::PositionFile.new(@file, logger: $log).load
137
+ Fluent::Plugin::TailInput::PositionFile.new(@file, false, {}, **{logger: $log}).load
124
138
 
125
139
  @file.seek(0)
126
140
  lines = @file.readlines
@@ -131,9 +145,10 @@ class IntailPositionFileTest < Test::Unit::TestCase
131
145
  sub_test_case '#[]' do
132
146
  test 'return entry' do
133
147
  write_data(@file, TEST_CONTENT)
134
- pf = Fluent::Plugin::TailInput::PositionFile.load(@file, logger: $log)
148
+ pf = Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, **{logger: $log})
135
149
 
136
- f = pf['valid_path']
150
+ valid_target_info = Fluent::Plugin::TailInput::TargetInfo.new('valid_path', Fluent::FileWrapper.stat(@file).ino)
151
+ f = pf[valid_target_info]
137
152
  assert_equal Fluent::Plugin::TailInput::FilePositionEntry, f.class
138
153
  assert_equal 2, f.read_pos
139
154
  assert_equal 1, f.read_inode
@@ -142,7 +157,8 @@ class IntailPositionFileTest < Test::Unit::TestCase
142
157
  lines = @file.readlines
143
158
  assert_equal 2, lines.size
144
159
 
145
- f = pf['nonexist_path']
160
+ nonexistent_target_info = Fluent::Plugin::TailInput::TargetInfo.new('nonexist_path', -1)
161
+ f = pf[nonexistent_target_info]
146
162
  assert_equal Fluent::Plugin::TailInput::FilePositionEntry, f.class
147
163
  assert_equal 0, f.read_pos
148
164
  assert_equal 0, f.read_inode
@@ -155,19 +171,19 @@ class IntailPositionFileTest < Test::Unit::TestCase
155
171
 
156
172
  test 'does not change other value position if other entry try to write' do
157
173
  write_data(@file, TEST_CONTENT)
158
- pf = Fluent::Plugin::TailInput::PositionFile.load(@file, logger: $log)
174
+ pf = Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, logger: $log)
159
175
 
160
- f = pf['nonexist_path']
176
+ f = pf[Fluent::Plugin::TailInput::TargetInfo.new('nonexist_path', -1)]
161
177
  assert_equal 0, f.read_inode
162
178
  assert_equal 0, f.read_pos
163
179
 
164
- pf['valid_path'].update(1, 2)
180
+ pf[Fluent::Plugin::TailInput::TargetInfo.new('valid_path', Fluent::FileWrapper.stat(@file).ino)].update(1, 2)
165
181
 
166
- f = pf['nonexist_path']
182
+ f = pf[Fluent::Plugin::TailInput::TargetInfo.new('nonexist_path', -1)]
167
183
  assert_equal 0, f.read_inode
168
184
  assert_equal 0, f.read_pos
169
185
 
170
- pf['nonexist_path'].update(1, 2)
186
+ pf[Fluent::Plugin::TailInput::TargetInfo.new('nonexist_path', -1)].update(1, 2)
171
187
  assert_equal 1, f.read_inode
172
188
  assert_equal 2, f.read_pos
173
189
  end
@@ -176,14 +192,18 @@ class IntailPositionFileTest < Test::Unit::TestCase
176
192
  sub_test_case '#unwatch' do
177
193
  test 'deletes entry by path' do
178
194
  write_data(@file, TEST_CONTENT)
179
- pf = Fluent::Plugin::TailInput::PositionFile.load(@file, logger: $log)
180
- p1 = pf['valid_path']
195
+ pf = Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, logger: $log)
196
+ inode1 = Fluent::FileWrapper.stat(@file).ino
197
+ target_info1 = Fluent::Plugin::TailInput::TargetInfo.new('valid_path', inode1)
198
+ p1 = pf[target_info1]
181
199
  assert_equal Fluent::Plugin::TailInput::FilePositionEntry, p1.class
182
200
 
183
- pf.unwatch('valid_path')
201
+ pf.unwatch(target_info1)
184
202
  assert_equal p1.read_pos, Fluent::Plugin::TailInput::PositionFile::UNWATCHED_POSITION
185
203
 
186
- p2 = pf['valid_path']
204
+ inode2 = Fluent::FileWrapper.stat(@file).ino
205
+ target_info2 = Fluent::Plugin::TailInput::TargetInfo.new('valid_path', inode2)
206
+ p2 = pf[target_info2]
187
207
  assert_equal Fluent::Plugin::TailInput::FilePositionEntry, p2.class
188
208
 
189
209
  assert_not_equal p1, p2
@@ -12,6 +12,11 @@ class StdoutFilterTest < Test::Unit::TestCase
12
12
  @old_tz = ENV["TZ"]
13
13
  ENV["TZ"] = "UTC"
14
14
  Timecop.freeze
15
+ @default_newline = if Fluent.windows?
16
+ "\r\n"
17
+ else
18
+ "\n"
19
+ end
15
20
  end
16
21
 
17
22
  def teardown
@@ -106,7 +111,7 @@ class StdoutFilterTest < Test::Unit::TestCase
106
111
  def test_format_json
107
112
  d = create_driver(CONFIG + config_element("", "", { "format" => "json" }))
108
113
  out = capture_log(d) { filter(d, event_time, {'test' => 'test'}) }
109
- assert_equal "{\"test\":\"test\"}\n", out
114
+ assert_equal "{\"test\":\"test\"}#{@default_newline}", out
110
115
  end
111
116
  end
112
117
 
@@ -19,11 +19,14 @@ class HashFormatterTest < ::Test::Unit::TestCase
19
19
  {'message' => 'awesome', 'greeting' => 'hello'}
20
20
  end
21
21
 
22
- def test_format
23
- d = create_driver({})
22
+ data("newline (LF)" => ["lf", "\n"],
23
+ "newline (CRLF)" => ["crlf", "\r\n"])
24
+ def test_format(data)
25
+ newline_conf, newline = data
26
+ d = create_driver({"newline" => newline_conf})
24
27
  formatted = d.instance.format(tag, @time, record)
25
28
 
26
- assert_equal(%Q!{"message"=>"awesome", "greeting"=>"hello"}\n!, formatted.encode(Encoding::UTF_8))
29
+ assert_equal(%Q!{"message"=>"awesome", "greeting"=>"hello"}#{newline}!, formatted.encode(Encoding::UTF_8))
27
30
  end
28
31
 
29
32
  def test_format_without_newline