fluent-mixin-plaintextformatter 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *~
19
+ \#*
20
+ .\#*
21
+ *.swp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fluent-mixin-plaintextformatter.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 TAGOMORI Satoshi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # Fluent::Mixin::PlainTextFormatter
2
+
3
+ Fluent::Mixin::PlainTextFormatter is a mix-in module, that provides '#format' instance method and its configurations to Fluentd BufferedOutput Plugin and TimeSlicedOutput Plugin, to output plain text data (to file, REST storages, KVSs ...).
4
+
5
+ This module provides features to:
6
+
7
+ * format whole data as serialized JSON, single attribute or separated multi attributes
8
+ * include time as line header (formatted by time_format in UTC(default) or localtime), or not
9
+ * include tag as line header (remove_prefix available), or not
10
+ * change field separator (TAB(default), SPACE or COMMA)
11
+ * add new line as termination, or not
12
+
13
+ ## Usage
14
+
15
+ To use this module in your fluentd plugin, include this module like below:
16
+
17
+ class FooOutput < BufferedOutput
18
+ Fluent::Plugin.register_output('foo', self)
19
+
20
+ config_set_default :buffer_type, 'memory'
21
+
22
+ include Fluent::Mixin::PlainTextFormatter
23
+
24
+ config_param :foo_config_x, :string, :default => nil
25
+
26
+ # and other your plugin's configuration parameters...
27
+
28
+ def configure(conf)
29
+ super
30
+
31
+ # ....
32
+ end
33
+
34
+ def start
35
+ # ...
36
+ end
37
+
38
+ def shutdown
39
+ # ....
40
+ end
41
+
42
+ # def format(tag, time, record)
43
+ # # do not define 'format'
44
+ # end
45
+
46
+ def write(chunk)
47
+ # ....
48
+ end
49
+ end
50
+
51
+ And you can overwrite default formatting configurations on your plugin (values below are default of mix-in):
52
+
53
+ class FooOutput < BufferedOutput
54
+ # ...
55
+
56
+ config_set_default :output_include_time, true
57
+ config_set_default :output_include_tag, true
58
+ config_set_default :output_data_type, 'json'
59
+ config_set_default :field_separator, 'TAB'
60
+ config_set_default :add_newline, true
61
+ config_set_default :time_format, nil # nil means ISO8601 '2012-07-13T19:29:49+09:00'
62
+ config_set_default :remove_prefix, nil
63
+ config_set_default :default_tag, nil
64
+
65
+ # ...
66
+ end
67
+
68
+ Provided configurations are below:
69
+
70
+ * output\_include\_time [yes/no]
71
+ * output\_include\_tag [yes/no]
72
+ * output\_data\_type
73
+ * 'json': output by JSON
74
+ * 'attr:key1,key2,key3': values of 'key1' and 'key2' and ..., with separator specified by 'field_separator'
75
+ * field\_separator [TAB/SPACE/COMMA]
76
+ * add_newline [yes/no]
77
+ * time_format
78
+ * format string like '%Y-%m-%d %H:%M:%S' or you want
79
+ * remove_prefix
80
+ * input tag 'test.foo' with 'remove_prefix test', output tag is 'foo'.
81
+ * 'default\_tag' configuration is used when input tag is completely equal to 'remove\_prefix'
82
+
83
+ ## AUTHORS
84
+
85
+ * TAGOMORI Satoshi <tagomoris@gmail.com>
86
+
87
+ ## LICENSE
88
+
89
+ * Copyright: Copyright (c) 2012- tagomoris
90
+ * License: Apache License, Version 2.0
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new(:test) do |test|
6
+ test.libs << 'lib' << 'test'
7
+ test.pattern = 'test/**/test_*.rb'
8
+ test.verbose = true
9
+ end
10
+
11
+ task :default => :test
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ Gem::Specification.new do |gem|
3
+ gem.name = "fluent-mixin-plaintextformatter"
4
+ gem.version = "0.1.0"
5
+ gem.authors = ["TAGOMORI Satoshi"]
6
+ gem.email = ["tagomoris@gmail.com"]
7
+ gem.description = %q{included to format values into json, tsv or csv}
8
+ gem.summary = %q{Text formatter mixin module to create fluentd plugin}
9
+ gem.homepage = "https://github.com/tagomoris/fluent-mixin-plaintextformatter"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.require_paths = ["lib"]
15
+
16
+ gem.add_development_dependency "fluentd"
17
+ gem.add_runtime_dependency "fluentd"
18
+ end
@@ -0,0 +1,98 @@
1
+ require 'fluent/config'
2
+
3
+ module Fluent
4
+ module Mixin
5
+ module PlainTextFormatter
6
+ attr_accessor :output_include_time, :output_include_tag, :output_data_type
7
+ attr_accessor :add_newline, :field_separator
8
+ attr_accessor :remove_prefix, :default_tag
9
+
10
+ attr_accessor :f_separator
11
+
12
+ def first_value(*args)
13
+ args.reduce{|a,b| (not a.nil?) ? a : b}
14
+ end
15
+
16
+ def configure(conf)
17
+ super
18
+
19
+ @output_include_time = first_value( Fluent::Config.bool_value(conf['output_include_time']), @output_include_time, true )
20
+ @output_include_tag = first_value( Fluent::Config.bool_value(conf['output_include_tag']), @output_include_tag, true )
21
+
22
+ @output_data_type = first_value( conf['output_data_type'], @output_data_type, 'json' )
23
+
24
+ @field_separator = first_value( conf['field_separator'], @field_separator, 'TAB' )
25
+ @f_separator = case @field_separator
26
+ when /SPACE/i then ' '
27
+ when /COMMA/i then ','
28
+ else "\t"
29
+ end
30
+
31
+ @add_newline = first_value( Fluent::Config.bool_value(conf['add_newline']), @add_newline, true )
32
+
33
+ @remove_prefix = conf['remove_prefix']
34
+ if @remove_prefix
35
+ @removed_prefix_string = @remove_prefix + '.'
36
+ @removed_length = @removed_prefix_string.length
37
+ end
38
+ if @output_include_tag and @remove_prefix and @remove_prefix.length > 0
39
+ @default_tag = first_value( conf['default_tag'], @default_tag, nil )
40
+ if @default_tag.nil? or @default_tag.length < 1
41
+ raise Fluent::ConfigError, "Missing 'default_tag' with output_include_tag and remove_prefix."
42
+ end
43
+ end
44
+
45
+ # default timezone: utc
46
+ if not conf.has_key?('localtime') and not conf.has_key?('utc')
47
+ @localtime = false
48
+ elsif conf.has_key?('localtime')
49
+ @localtime = true
50
+ elsif conf.has_key?('utc')
51
+ @localtime = false
52
+ end
53
+ # mix-in default time formatter (or you can overwrite @timef on your own configure)
54
+ @time_format = first_value( conf['time_format'], @time_format, nil )
55
+ @timef = @output_include_time ? Fluent::TimeFormatter.new(@time_format, @localtime) : nil
56
+
57
+ @custom_attributes = if @output_data_type == 'json'
58
+ nil
59
+ elsif @output_data_type =~ /^attr:(.+)$/
60
+ $1.split(',')
61
+ else
62
+ raise Fluent::ConfigError, "invalid output_data_type:'#{@output_data_type}'"
63
+ end
64
+ end
65
+
66
+ def stringify_record(record)
67
+ if @custom_attributes.nil?
68
+ record.to_json
69
+ else
70
+ @custom_attributes.map{|attr|
71
+ (record[attr] || 'NULL').to_s
72
+ }.join(@f_separator)
73
+ end
74
+ end
75
+
76
+ def format(tag, time, record)
77
+ if @remove_prefix
78
+ if tag.start_with?(@removed_prefix_string) and tag.length > @removed_length
79
+ tag = tag[@removed_length..-1]
80
+ elsif tag == @remove_prefix
81
+ tag = @default_tag
82
+ end
83
+ end
84
+ time_str = if @output_include_time
85
+ @timef.format(time) + @f_separator
86
+ else
87
+ ''
88
+ end
89
+ tag_str = if @output_include_tag
90
+ tag + @f_separator
91
+ else
92
+ ''
93
+ end
94
+ time_str + tag_str + stringify_record(record) + (@add_newline ? "\n" : '')
95
+ end
96
+ end
97
+ end
98
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+
12
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+
15
+ require 'fluent/test'
16
+ unless ENV.has_key?('VERBOSE')
17
+ nulllogger = Object.new
18
+ nulllogger.instance_eval {|obj|
19
+ def method_missing(method, *args)
20
+ # pass
21
+ end
22
+ }
23
+ $log = nulllogger
24
+ end
25
+
26
+ require 'fluent/mixin/plaintextformatter'
27
+ require_relative 'output'
28
+
29
+ class Test::Unit::TestCase
30
+ end
@@ -0,0 +1,156 @@
1
+ require 'helper'
2
+
3
+ class PlainTextFormatterTest < Test::Unit::TestCase
4
+ def create_plugin_instance(type, conf = CONFIG, tag='test')
5
+ Fluent::Test::BufferedOutputTestDriver.new(type, tag).configure(conf).instance
6
+ end
7
+
8
+ def test_first_value
9
+ p = create_plugin_instance(Fluent::TestAOutput, "type testa\n")
10
+ assert_equal nil, p.first_value()
11
+ assert_equal nil, p.first_value(nil, nil)
12
+ assert_equal 'hoge', p.first_value(nil, nil, 'hoge')
13
+ assert_equal 'bar', p.first_value(nil, 'bar', 'hoge')
14
+ assert_equal 'foo', p.first_value('foo', 'bar', 'hoge')
15
+ assert_equal 'foo', p.first_value('foo', nil, 'hoge')
16
+ assert_equal 'foo', p.first_value('foo', 'bar', nil)
17
+ assert_equal 'foo', p.first_value('foo', nil, nil)
18
+ end
19
+
20
+ def test_default_with_time_tag
21
+ p = create_plugin_instance(Fluent::TestAOutput, "type testa\n")
22
+ r = {'foo' => 'foo foo baz', 'bar' => 10000}
23
+ # stringify
24
+ assert_equal r, JSON.parse(p.stringify_record(r))
25
+
26
+ line = p.format('test.a', 1342163105, r)
27
+ # add_newline true
28
+ assert_equal line[0..-2], line.chomp
29
+ # output_include_time true, output_include_tag true
30
+ assert_equal ['2012-07-13T07:05:05Z', 'test.a'], line.chomp.split(/\t/)[0..1]
31
+ # output_data_type json
32
+ assert_equal r, JSON.parse(line.chomp.split(/\t/)[2])
33
+ end
34
+
35
+ def test_field_separator_newline_json
36
+ p = create_plugin_instance(Fluent::TestBOutput, "type testb\nlocaltime\n")
37
+ r = {'foo' => 'foo foo baz', 'bar' => 10000}
38
+ # stringify
39
+ assert_equal r, JSON.parse(p.stringify_record(r))
40
+
41
+ line = p.format('test.b', 1342163105, r)
42
+ # add_newline false
43
+ assert_equal line[0..-1], line.chomp
44
+ # output_include_time true, output_include_tag true, localtime, separator COMMA
45
+ assert_equal ['2012-07-13T16:05:05+09:00', 'test.b'], line.chomp.split(/,/, 3)[0..1]
46
+ # output_data_type json
47
+ assert_equal r, JSON.parse(line.chomp.split(/,/, 3)[2])
48
+ end
49
+
50
+ def test_time_format
51
+ p = create_plugin_instance(Fluent::TestBOutput, %[
52
+ type testb
53
+ localtime
54
+ time_format %Y/%m/%d %H:%M:%S
55
+ ])
56
+ r = {'foo' => 'foo foo baz', 'bar' => 10000}
57
+ # stringify
58
+ assert_equal r, JSON.parse(p.stringify_record(r))
59
+
60
+ line = p.format('test.b', 1342163105, r)
61
+ # add_newline false
62
+ assert_equal line[0..-1], line.chomp
63
+ # output_include_time true, output_include_tag true, localtime, separator COMMA
64
+ assert_equal ['2012/07/13 16:05:05', 'test.b'], line.chomp.split(/,/, 3)[0..1]
65
+ # output_data_type json
66
+ assert_equal r, JSON.parse(line.chomp.split(/,/, 3)[2])
67
+ end
68
+ def test_separator_space_remove_prefix
69
+ p = create_plugin_instance(Fluent::TestBOutput, %[
70
+ type testb
71
+ localtime
72
+ time_format %Y/%m/%d:%H:%M:%S
73
+ field_separator space
74
+ remove_prefix test
75
+ ])
76
+ r = {'foo' => 'foo foo baz', 'bar' => 10000}
77
+ # stringify
78
+ assert_equal r, JSON.parse(p.stringify_record(r))
79
+
80
+ line = p.format('test.b', 1342163105, r)
81
+ # add_newline false
82
+ assert_equal line[0..-1], line.chomp
83
+ # output_include_time true, output_include_tag true, localtime, separator COMMA
84
+ assert_equal ['2012/07/13:16:05:05', 'b'], line.chomp.split(/ /, 3)[0..1]
85
+ # output_data_type json
86
+ assert_equal r, JSON.parse(line.chomp.split(/ /, 3)[2])
87
+ end
88
+
89
+ def test_default_without_time_tag
90
+ p = create_plugin_instance(Fluent::TestCOutput, "type testc\n")
91
+ r = {'foo' => 'foo foo baz', 'bar' => 10000}
92
+ # stringify
93
+ assert_equal r, JSON.parse(p.stringify_record(r))
94
+
95
+ line = p.format('test.c', 1342163105, r)
96
+ # add_newline
97
+ assert_equal line[0..-2], line.chomp
98
+ # output_include_time false, output_include_tag false
99
+ assert_equal 1, line.chomp.split(/\t/).size
100
+ # output_data_type json
101
+ assert_equal r, JSON.parse(line.chomp.split(/\t/).first)
102
+ end
103
+
104
+ def test_format_single_attribute
105
+ p = create_plugin_instance(Fluent::TestAOutput, %[
106
+ type testa
107
+ output_include_time true
108
+ output_include_tag true
109
+ output_data_type attr:foo
110
+ ])
111
+ r = {'foo' => 'foo foo baz', 'bar' => 10000}
112
+ # stringify
113
+ assert_equal 'foo foo baz', p.stringify_record(r)
114
+ # format
115
+ assert_equal "2012-07-13T07:05:05Z\ttest.a\tfoo foo baz\n", p.format('test.a', 1342163105, r)
116
+
117
+ p = create_plugin_instance(Fluent::TestAOutput, %[
118
+ type testa
119
+ output_include_time false
120
+ output_include_tag false
121
+ output_data_type attr:bar
122
+ ])
123
+ r = {'foo' => 'foo foo baz', 'bar' => 10000}
124
+ # stringify
125
+ assert_equal '10000', p.stringify_record(r)
126
+ # format
127
+ assert_equal "10000\n", p.format('test.a', 1342163105, r)
128
+ end
129
+
130
+ def test_format_multi_attribute
131
+ p = create_plugin_instance(Fluent::TestAOutput, %[
132
+ type testa
133
+ output_include_time true
134
+ output_include_tag true
135
+ output_data_type attr:foo,bar
136
+ ])
137
+ r = {'foo' => 'foo foo baz', 'bar' => 10000}
138
+ # stringify
139
+ assert_equal "foo foo baz\t10000", p.stringify_record(r)
140
+ # format
141
+ assert_equal "2012-07-13T07:05:05Z\ttest.a\tfoo foo baz\t10000\n", p.format('test.a', 1342163105, r)
142
+
143
+ p = create_plugin_instance(Fluent::TestAOutput, %[
144
+ type testa
145
+ output_include_time false
146
+ output_include_tag false
147
+ output_data_type attr:bar,foo
148
+ field_separator comma
149
+ ])
150
+ r = {'foo' => 'foo foo baz', 'bar' => 10000}
151
+ # stringify
152
+ assert_equal "10000,foo foo baz", p.stringify_record(r)
153
+ # format
154
+ assert_equal "10000,foo foo baz\n", p.format('test.a', 1342163105, r)
155
+ end
156
+ end
data/test/output.rb ADDED
@@ -0,0 +1,64 @@
1
+ require 'fluent/mixin/plaintextformatter'
2
+
3
+ module Fluent
4
+ class TestAOutput < Fluent::BufferedOutput
5
+ Fluent::Plugin.register_output('testa', self)
6
+
7
+ config_set_default :buffer_type, 'memory'
8
+
9
+ include Fluent::Mixin::PlainTextFormatter
10
+
11
+ config_set_default :output_include_time, true
12
+ config_set_default :output_include_tag, true
13
+ config_set_default :output_data_type, 'json'
14
+ config_set_default :field_separator, "\t"
15
+ config_set_default :add_newline, true
16
+ config_set_default :remove_prefix, nil
17
+ config_set_default :default_tag, 'tag.blank'
18
+
19
+ def configure(conf)
20
+ super
21
+ end
22
+ end
23
+
24
+ class TestBOutput < Fluent::BufferedOutput
25
+ Fluent::Plugin.register_output('testa', self)
26
+
27
+ config_set_default :buffer_type, 'memory'
28
+
29
+ include Fluent::Mixin::PlainTextFormatter
30
+
31
+ config_set_default :output_include_time, true
32
+ config_set_default :output_include_tag, true
33
+ config_set_default :output_data_type, 'json'
34
+ config_set_default :field_separator, "COMMA"
35
+ config_set_default :add_newline, false
36
+ config_set_default :remove_prefix, nil
37
+ config_set_default :default_tag, 'tag.blank'
38
+
39
+ def configure(conf)
40
+ super
41
+ end
42
+ end
43
+
44
+ class TestCOutput < Fluent::BufferedOutput
45
+ Fluent::Plugin.register_output('testc', self)
46
+
47
+ config_set_default :buffer_type, 'memory'
48
+
49
+ include Fluent::Mixin::PlainTextFormatter
50
+
51
+ config_set_default :output_include_time, false
52
+ config_set_default :output_include_tag, false
53
+ config_set_default :output_data_type, 'json'
54
+ config_set_default :field_separator, "\t"
55
+ config_set_default :add_newline, true
56
+ config_set_default :remove_prefix, nil
57
+ config_set_default :default_tag, 'tag.blank'
58
+
59
+ def configure(conf)
60
+ super
61
+ end
62
+ end
63
+
64
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-mixin-plaintextformatter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - TAGOMORI Satoshi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: fluentd
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: fluentd
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: included to format values into json, tsv or csv
47
+ email:
48
+ - tagomoris@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - Gemfile
55
+ - LICENSE
56
+ - README.md
57
+ - Rakefile
58
+ - fluent-mixin-plaintextformatter.gemspec
59
+ - lib/fluent/mixin/plaintextformatter.rb
60
+ - test/helper.rb
61
+ - test/mixin/test_plaintextformatter.rb
62
+ - test/output.rb
63
+ homepage: https://github.com/tagomoris/fluent-mixin-plaintextformatter
64
+ licenses: []
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 1.8.21
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Text formatter mixin module to create fluentd plugin
87
+ test_files:
88
+ - test/helper.rb
89
+ - test/mixin/test_plaintextformatter.rb
90
+ - test/output.rb