fluent-plugin-forest 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 +25 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -0
- data/README.md +103 -0
- data/Rakefile +11 -0
- data/fluent-plugin-forest.gemspec +18 -0
- data/lib/fluent/plugin/out_forest.rb +120 -0
- data/test/helper.rb +28 -0
- data/test/output/out_forest_test.rb +43 -0
- data/test/plugin/test_out_forest.rb +181 -0
- data/test/plugin/test_out_forest_test.rb +103 -0
- metadata +93 -0
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
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
|
+
# For TextMate, emacs, vim
|
19
|
+
*.tmproj
|
20
|
+
tmtags
|
21
|
+
*~
|
22
|
+
\#*
|
23
|
+
.\#*
|
24
|
+
*.swp
|
25
|
+
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (c) 2012- TAGOMORI Satoshi
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# fluent-plugin-forest
|
2
|
+
|
3
|
+
## Component
|
4
|
+
|
5
|
+
### ForestOutput
|
6
|
+
|
7
|
+
ForestOutput creates sub plugin instance of a output plugin dynamically per tag, from template configurations.
|
8
|
+
In template configurations, you can write configuration lines for overall tags by <template>, and for specified tags by <case TAG_PATTERN>, and you can use \_\_TAG\_\_ placeholder at anywhere in <template> and <case>.
|
9
|
+
|
10
|
+
This plugin helps you if you are writing very long configurations by copy&paste with a little little diff for many tags.
|
11
|
+
|
12
|
+
You SHOULD NOT use ForestOutput for tags increasing infinitly.
|
13
|
+
|
14
|
+
## Configuration
|
15
|
+
|
16
|
+
### ForestOutput
|
17
|
+
|
18
|
+
If you are writing long long configurations like below:
|
19
|
+
|
20
|
+
<match service.blog>
|
21
|
+
type file
|
22
|
+
time_slice_format %Y%m%d%H
|
23
|
+
compress yes
|
24
|
+
path /var/log/blog.*.log
|
25
|
+
</match>
|
26
|
+
<match service.portal>
|
27
|
+
type file
|
28
|
+
time_slice_format %Y%m%d%H
|
29
|
+
compress yes
|
30
|
+
path /var/log/portal.*.log
|
31
|
+
</match>
|
32
|
+
<match service.news>
|
33
|
+
type file
|
34
|
+
time_slice_format %Y%m%d%H
|
35
|
+
compress yes
|
36
|
+
path /var/log/news.*.log
|
37
|
+
</match>
|
38
|
+
<match service.sns>
|
39
|
+
type file
|
40
|
+
time_slice_format %Y%m%d%H
|
41
|
+
compress yes
|
42
|
+
path /var/log/sns.*.log
|
43
|
+
</match>
|
44
|
+
# ...
|
45
|
+
|
46
|
+
You can write configuration with ForestOutput like below:
|
47
|
+
|
48
|
+
<match service.*>
|
49
|
+
type forest
|
50
|
+
subtype file
|
51
|
+
remove_prefix service
|
52
|
+
<template>
|
53
|
+
time_slice_format %Y%m%d%H
|
54
|
+
compress yes
|
55
|
+
path /var/log/__TAG__.*.log
|
56
|
+
</template>
|
57
|
+
</match>
|
58
|
+
|
59
|
+
If you want to place logs /var/archive for `service.search.**` without compression, `case` directive is useful:
|
60
|
+
|
61
|
+
<match service.*>
|
62
|
+
type forest
|
63
|
+
subtype file
|
64
|
+
remove_prefix service
|
65
|
+
<template>
|
66
|
+
time_slice_format %Y%m%d%H
|
67
|
+
</template>
|
68
|
+
<case search.**>
|
69
|
+
compress no
|
70
|
+
path /var/archive/__TAG__.*.log
|
71
|
+
</case>
|
72
|
+
<case *>
|
73
|
+
compress yes
|
74
|
+
path /var/log/__TAG__.*.log
|
75
|
+
</case>
|
76
|
+
</match>
|
77
|
+
|
78
|
+
`case` configuration overwrites `template` configuration, so you can also write like this:
|
79
|
+
|
80
|
+
<match service.*>
|
81
|
+
type forest
|
82
|
+
subtype file
|
83
|
+
remove_prefix service
|
84
|
+
<template>
|
85
|
+
time_slice_format %Y%m%d%H
|
86
|
+
compress yes
|
87
|
+
path /var/log/__TAG__.*.log
|
88
|
+
</template>
|
89
|
+
<case search.**>
|
90
|
+
compress no
|
91
|
+
path /var/archive/__TAG__.*.log
|
92
|
+
</case>
|
93
|
+
</match>
|
94
|
+
|
95
|
+
## TODO
|
96
|
+
|
97
|
+
* consider what to do next
|
98
|
+
* patches welcome!
|
99
|
+
|
100
|
+
## Copyright
|
101
|
+
|
102
|
+
Copyright:: Copyright (c) 2012- TAGOMORI Satoshi (tagomoris)
|
103
|
+
License:: Apache License, Version 2.0
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
Gem::Specification.new do |gem|
|
3
|
+
gem.name = "fluent-plugin-forest"
|
4
|
+
gem.version = "0.1.0"
|
5
|
+
gem.authors = ["TAGOMORI Satoshi"]
|
6
|
+
gem.email = ["tagomoris@gmail.com"]
|
7
|
+
gem.description = %q{create sub-plugin dynamically per tags, with template configuration and parameters}
|
8
|
+
gem.summary = %q{plugin to create output plugin instances per tags dynamically}
|
9
|
+
gem.homepage = "https://github.com/tagomoris/fluent-plugin-forest"
|
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,120 @@
|
|
1
|
+
class Fluent::ForestOutput < Fluent::Output
|
2
|
+
Fluent::Plugin.register_output('forest', self)
|
3
|
+
|
4
|
+
config_param :subtype, :string
|
5
|
+
config_param :remove_prefix, :string, :default => nil
|
6
|
+
config_param :add_prefix, :string, :default => nil
|
7
|
+
|
8
|
+
def configure(conf)
|
9
|
+
super
|
10
|
+
|
11
|
+
if @remove_prefix
|
12
|
+
@removed_prefix_string = @remove_prefix + '.'
|
13
|
+
@removed_length = @removed_prefix_string.length
|
14
|
+
end
|
15
|
+
if @add_prefix
|
16
|
+
@added_prefix_string = @add_prefix + '.'
|
17
|
+
end
|
18
|
+
|
19
|
+
@mapping = {} # tag => output
|
20
|
+
@mutex = Mutex.new
|
21
|
+
|
22
|
+
@template = nil
|
23
|
+
@parameter = nil
|
24
|
+
@cases = []
|
25
|
+
|
26
|
+
conf.elements.each do |element|
|
27
|
+
element.keys.each do |k|
|
28
|
+
# read and throw away to supress unread configuration warning
|
29
|
+
element[k]
|
30
|
+
end
|
31
|
+
case element.name
|
32
|
+
when 'template'
|
33
|
+
@template = element
|
34
|
+
when 'case'
|
35
|
+
matcher = Fluent::GlobMatchPattern.new(element.arg)
|
36
|
+
@cases.push([matcher, element])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def shutdown
|
44
|
+
super
|
45
|
+
@mapping.values.each do |output|
|
46
|
+
output.shutdown
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def parameter(tag, e)
|
51
|
+
pairs = {}
|
52
|
+
e.each do |k,v|
|
53
|
+
pairs[k] = v.gsub('__TAG__', tag)
|
54
|
+
end
|
55
|
+
Fluent::Config::Element.new('instance', '', pairs, [])
|
56
|
+
end
|
57
|
+
|
58
|
+
def spec(tag)
|
59
|
+
conf = Fluent::Config::Element.new('instance', '', {}, [])
|
60
|
+
conf = parameter(tag, @template) + conf if @template # a + b -> b.merge(a) (see: fluentd/lib/fluent/config.rb)
|
61
|
+
@cases.each do |m,e|
|
62
|
+
if m.match(tag)
|
63
|
+
conf = parameter(tag, e) + conf
|
64
|
+
break
|
65
|
+
end
|
66
|
+
end
|
67
|
+
conf
|
68
|
+
end
|
69
|
+
|
70
|
+
def plant(tag)
|
71
|
+
output = nil
|
72
|
+
begin
|
73
|
+
@mutex.synchronize {
|
74
|
+
output = @mapping[tag]
|
75
|
+
unless output
|
76
|
+
output = Fluent::Plugin.new_output(@subtype)
|
77
|
+
output.configure(spec(tag))
|
78
|
+
output.start
|
79
|
+
@mapping[tag] = output
|
80
|
+
end
|
81
|
+
}
|
82
|
+
$log.info "out_forest plants new output: #{@subtype} for tag '#{tag}'"
|
83
|
+
rescue Fluent::ConfigError => e
|
84
|
+
$log.error "failed to configure sub output #{@subtype}: #{e.message}"
|
85
|
+
$log.error e.backtrace.join("\n")
|
86
|
+
$log.error "Cannot output messages with tag '#{tag}'"
|
87
|
+
output = nil
|
88
|
+
rescue StandardError => e
|
89
|
+
$log.error "failed to configure/start sub output #{@subtype}: #{e.message}"
|
90
|
+
$log.error e.backtrace.join("\n")
|
91
|
+
$log.error "Cannot output messages with tag '#{tag}'"
|
92
|
+
output = nil
|
93
|
+
end
|
94
|
+
output
|
95
|
+
end
|
96
|
+
|
97
|
+
def emit(tag, es, chain)
|
98
|
+
if @remove_prefix and
|
99
|
+
( (tag.start_with?(@removed_prefix_string) and tag.length > @removed_length) or tag == @remove_prefix)
|
100
|
+
tag = tag[@removed_length..-1]
|
101
|
+
end
|
102
|
+
if @add_prefix
|
103
|
+
tag = if tag.length > 0
|
104
|
+
@added_prefix_string + tag
|
105
|
+
else
|
106
|
+
@add_prefix
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
output = @mapping[tag]
|
111
|
+
unless output
|
112
|
+
output = plant(tag)
|
113
|
+
end
|
114
|
+
if output
|
115
|
+
output.emit(tag, es, chain)
|
116
|
+
else
|
117
|
+
chain.next
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
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
|
+
require 'fluent/test'
|
15
|
+
unless ENV.has_key?('VERBOSE')
|
16
|
+
nulllogger = Object.new
|
17
|
+
nulllogger.instance_eval {|obj|
|
18
|
+
def method_missing(method, *args)
|
19
|
+
# pass
|
20
|
+
end
|
21
|
+
}
|
22
|
+
$log = nulllogger
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'fluent/plugin/out_forest'
|
26
|
+
|
27
|
+
class Test::Unit::TestCase
|
28
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Fluent::ForestTestOutput < Fluent::Output
|
2
|
+
Fluent::Plugin.register_output('forest_test', self)
|
3
|
+
|
4
|
+
config_param :key_name, :string, :default => 'msg'
|
5
|
+
config_param :tag
|
6
|
+
config_param :prefix, :string, :default => ''
|
7
|
+
config_param :suffix, :string, :default => ''
|
8
|
+
config_param :tagfield, :string, :default => nil
|
9
|
+
|
10
|
+
attr_accessor :started, :stopped
|
11
|
+
|
12
|
+
def configure(conf)
|
13
|
+
super
|
14
|
+
|
15
|
+
if @tag == 'raise.error'
|
16
|
+
raise Fluent::ConfigError, "specified to raise.error"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def start
|
21
|
+
super
|
22
|
+
@started = true
|
23
|
+
end
|
24
|
+
|
25
|
+
def shutdown
|
26
|
+
super
|
27
|
+
@stopped = true
|
28
|
+
end
|
29
|
+
|
30
|
+
def emit(tag, es, chain)
|
31
|
+
es.each {|time, record|
|
32
|
+
r = record.merge({@key_name => @prefix + record[@key_name] + @suffix})
|
33
|
+
unless @started
|
34
|
+
r = r.merge({'not_started' => true})
|
35
|
+
end
|
36
|
+
if @tagfield
|
37
|
+
r[@tagfield] = tag
|
38
|
+
end
|
39
|
+
Fluent::Engine.emit(@tag, time, r)
|
40
|
+
}
|
41
|
+
chain.next
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require_relative '../output/out_forest_test'
|
3
|
+
|
4
|
+
class ForestOutputTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
Fluent::Test.setup
|
7
|
+
end
|
8
|
+
|
9
|
+
CONFIG = %[
|
10
|
+
subtype forest_test
|
11
|
+
remove_prefix test
|
12
|
+
<template>
|
13
|
+
key_name f
|
14
|
+
suffix !
|
15
|
+
tag out.__TAG__
|
16
|
+
</template>
|
17
|
+
<case foo.bar>
|
18
|
+
prefix p1:
|
19
|
+
</case>
|
20
|
+
<case foo.*>
|
21
|
+
prefix p2:
|
22
|
+
</case>
|
23
|
+
<case bar.**>
|
24
|
+
prefix p3:
|
25
|
+
</case>
|
26
|
+
<case *>
|
27
|
+
prefix p4:
|
28
|
+
</case>
|
29
|
+
]
|
30
|
+
|
31
|
+
def create_driver(conf = CONFIG, tag='test.default')
|
32
|
+
Fluent::Test::OutputTestDriver.new(Fluent::ForestOutput, tag).configure(conf)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_configure
|
36
|
+
assert_nothing_raised { d = create_driver }
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_spec
|
40
|
+
d = create_driver %[
|
41
|
+
subtype hoge
|
42
|
+
<template>
|
43
|
+
keyx xxxxxx
|
44
|
+
keyy yyyyyy.__TAG__
|
45
|
+
alt_key a
|
46
|
+
</template>
|
47
|
+
<case xx>
|
48
|
+
keyz z1
|
49
|
+
alt_key b
|
50
|
+
</case>
|
51
|
+
<case yy.**>
|
52
|
+
keyz z2
|
53
|
+
alt_key c
|
54
|
+
</case>
|
55
|
+
<case *>
|
56
|
+
keyz z3
|
57
|
+
alt_key d.__TAG__
|
58
|
+
</case>
|
59
|
+
]
|
60
|
+
conf = d.instance.spec('xx')
|
61
|
+
assert_equal 'xxxxxx', conf['keyx']
|
62
|
+
assert_equal 'yyyyyy.xx', conf['keyy']
|
63
|
+
assert_equal 'z1', conf['keyz']
|
64
|
+
assert_equal 'b', conf['alt_key']
|
65
|
+
|
66
|
+
conf = d.instance.spec('yy')
|
67
|
+
assert_equal 'xxxxxx', conf['keyx']
|
68
|
+
assert_equal 'yyyyyy.yy', conf['keyy']
|
69
|
+
assert_equal 'z2', conf['keyz']
|
70
|
+
assert_equal 'c', conf['alt_key']
|
71
|
+
|
72
|
+
conf = d.instance.spec('yy.3')
|
73
|
+
assert_equal 'xxxxxx', conf['keyx']
|
74
|
+
assert_equal 'yyyyyy.yy.3', conf['keyy']
|
75
|
+
assert_equal 'z2', conf['keyz']
|
76
|
+
assert_equal 'c', conf['alt_key']
|
77
|
+
|
78
|
+
conf = d.instance.spec('zz')
|
79
|
+
assert_equal 'xxxxxx', conf['keyx']
|
80
|
+
assert_equal 'yyyyyy.zz', conf['keyy']
|
81
|
+
assert_equal 'z3', conf['keyz']
|
82
|
+
assert_equal 'd.zz', conf['alt_key']
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_faild_plant
|
86
|
+
d = create_driver
|
87
|
+
time = Time.parse("2012-01-02 13:14:15").to_i
|
88
|
+
d.tag = 'test.xxxxxx'; d.run { d.emit({'f' => "message 1"}, time) }
|
89
|
+
emits = d.emits
|
90
|
+
assert_equal 1, emits.length
|
91
|
+
|
92
|
+
d = create_driver %[
|
93
|
+
subtype forest_test
|
94
|
+
remove_prefix test
|
95
|
+
<template>
|
96
|
+
key_name f
|
97
|
+
suffix !
|
98
|
+
tag __TAG__
|
99
|
+
</template>
|
100
|
+
<case foo.bar>
|
101
|
+
prefix p1:
|
102
|
+
</case>
|
103
|
+
<case foo.*>
|
104
|
+
prefix p2:
|
105
|
+
</case>
|
106
|
+
<case bar.**>
|
107
|
+
prefix p3:
|
108
|
+
</case>
|
109
|
+
<case *>
|
110
|
+
prefix p4:
|
111
|
+
</case>
|
112
|
+
]
|
113
|
+
time = Time.parse("2012-01-02 13:14:15").to_i
|
114
|
+
d.tag = 'test.raise.error'; d.run { d.emit({'f' => "message 1"}, time) }
|
115
|
+
emits = d.emits
|
116
|
+
assert_equal 0, emits.length
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_emit
|
120
|
+
d = create_driver
|
121
|
+
time = Time.parse("2012-01-02 13:14:15").to_i
|
122
|
+
d.tag = 'test.first'; d.run { d.emit({'f' => "message 1"}, time) }
|
123
|
+
d.tag = 'test.second'; d.run { d.emit({'f' => "message 2"}, time) }
|
124
|
+
d.tag = 'test.foo.bar'; d.run { d.emit({'f' => "message 3"}, time) }
|
125
|
+
d.tag = 'test.foo.baz'; d.run { d.emit({'f' => "message 4"}, time) }
|
126
|
+
d.tag = 'test.bar'; d.run { d.emit({'f' => "message 5"}, time) }
|
127
|
+
d.tag = 'test.baz'; d.run { d.emit({'f' => "message 6"}, time) }
|
128
|
+
d.tag = 'test.foo.bar'; d.run { d.emit({'f' => "message 7"}, time) }
|
129
|
+
d.tag = 'test.bar'; d.run { d.emit({'f' => "message 8"}, time) }
|
130
|
+
|
131
|
+
emits = d.emits
|
132
|
+
|
133
|
+
e = emits[0]
|
134
|
+
assert_equal 'out.first', e[0]
|
135
|
+
assert_equal time, e[1]
|
136
|
+
assert_equal "p4:message 1!", e[2]['f']
|
137
|
+
assert_nil e[2]['not_started']
|
138
|
+
|
139
|
+
e = emits[1]
|
140
|
+
assert_equal 'out.second', e[0]
|
141
|
+
assert_equal time, e[1]
|
142
|
+
assert_equal "p4:message 2!", e[2]['f']
|
143
|
+
assert_nil e[2]['not_started']
|
144
|
+
|
145
|
+
e = emits[2]
|
146
|
+
assert_equal 'out.foo.bar', e[0]
|
147
|
+
assert_equal time, e[1]
|
148
|
+
assert_equal "p1:message 3!", e[2]['f']
|
149
|
+
assert_nil e[2]['not_started']
|
150
|
+
|
151
|
+
e = emits[3]
|
152
|
+
assert_equal 'out.foo.baz', e[0]
|
153
|
+
assert_equal time, e[1]
|
154
|
+
assert_equal "p2:message 4!", e[2]['f']
|
155
|
+
assert_nil e[2]['not_started']
|
156
|
+
|
157
|
+
e = emits[4]
|
158
|
+
assert_equal 'out.bar', e[0]
|
159
|
+
assert_equal time, e[1]
|
160
|
+
assert_equal "p3:message 5!", e[2]['f']
|
161
|
+
assert_nil e[2]['not_started']
|
162
|
+
|
163
|
+
e = emits[5]
|
164
|
+
assert_equal 'out.baz', e[0]
|
165
|
+
assert_equal time, e[1]
|
166
|
+
assert_equal "p4:message 6!", e[2]['f']
|
167
|
+
assert_nil e[2]['not_started']
|
168
|
+
|
169
|
+
e = emits[6]
|
170
|
+
assert_equal 'out.foo.bar', e[0]
|
171
|
+
assert_equal time, e[1]
|
172
|
+
assert_equal "p1:message 7!", e[2]['f']
|
173
|
+
assert_nil e[2]['not_started']
|
174
|
+
|
175
|
+
e = emits[7]
|
176
|
+
assert_equal 'out.bar', e[0]
|
177
|
+
assert_equal time, e[1]
|
178
|
+
assert_equal "p3:message 8!", e[2]['f']
|
179
|
+
assert_nil e[2]['not_started']
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require_relative '../output/out_forest_test'
|
3
|
+
|
4
|
+
class ForestTestOutputTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
Fluent::Test.setup
|
7
|
+
end
|
8
|
+
|
9
|
+
CONFIG = %[
|
10
|
+
tag foo.bar
|
11
|
+
tagfield ttt
|
12
|
+
prefix fixed:
|
13
|
+
suffix :end
|
14
|
+
]
|
15
|
+
|
16
|
+
def create_driver(conf = CONFIG, tag='test')
|
17
|
+
Fluent::Test::OutputTestDriver.new(Fluent::ForestTestOutput, tag).configure(conf)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_configure
|
21
|
+
d = create_driver
|
22
|
+
assert_equal 'foo.bar', d.instance.tag
|
23
|
+
assert_equal 'fixed:', d.instance.prefix
|
24
|
+
assert_equal ':end', d.instance.suffix
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_emit
|
28
|
+
d = create_driver
|
29
|
+
time = Time.parse("2012-01-02 13:14:15").to_i
|
30
|
+
d.run do
|
31
|
+
d.emit({'msg' => 'xyz 123', 'alt' => 'aaa bbb ccc'}, time)
|
32
|
+
d.emit({'msg' => 'xyz 456', 'alt' => 'aaa bbb ccc ddd'}, time)
|
33
|
+
end
|
34
|
+
emits = d.emits
|
35
|
+
assert_equal 2, emits.length
|
36
|
+
|
37
|
+
assert_equal 'foo.bar', emits[0][0]
|
38
|
+
assert_equal time, emits[0][1]
|
39
|
+
assert_equal 'fixed:xyz 123:end', emits[0][2]['msg']
|
40
|
+
assert_equal 'aaa bbb ccc', emits[0][2]['alt']
|
41
|
+
assert_equal 'test', emits[0][2]['ttt']
|
42
|
+
|
43
|
+
assert_equal 'foo.bar', emits[1][0]
|
44
|
+
assert_equal time, emits[1][1]
|
45
|
+
assert_equal 'fixed:xyz 456:end', emits[1][2]['msg']
|
46
|
+
assert_equal 'aaa bbb ccc ddd', emits[1][2]['alt']
|
47
|
+
assert_equal 'test', emits[1][2]['ttt']
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_emit
|
51
|
+
d = create_driver
|
52
|
+
time = Time.parse("2012-01-02 13:14:15").to_i
|
53
|
+
d.run do
|
54
|
+
d.emit({'msg' => 'xyz 123', 'alt' => 'aaa bbb ccc'}, time)
|
55
|
+
d.tag = 'test2'
|
56
|
+
d.emit({'msg' => 'xyz 456', 'alt' => 'aaa bbb ccc ddd'}, time)
|
57
|
+
end
|
58
|
+
emits = d.emits
|
59
|
+
assert_equal 2, emits.length
|
60
|
+
|
61
|
+
assert_equal 'foo.bar', emits[0][0]
|
62
|
+
assert_equal time, emits[0][1]
|
63
|
+
assert_equal 'fixed:xyz 123:end', emits[0][2]['msg']
|
64
|
+
assert_equal 'aaa bbb ccc', emits[0][2]['alt']
|
65
|
+
assert_equal 'test', emits[0][2]['ttt']
|
66
|
+
assert_nil emits[0][2]['not_started']
|
67
|
+
|
68
|
+
assert_equal 'foo.bar', emits[1][0]
|
69
|
+
assert_equal time, emits[1][1]
|
70
|
+
assert_equal 'fixed:xyz 456:end', emits[1][2]['msg']
|
71
|
+
assert_equal 'aaa bbb ccc ddd', emits[1][2]['alt']
|
72
|
+
assert_equal 'test2', emits[1][2]['ttt']
|
73
|
+
assert_nil emits[1][2]['not_started']
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_emit2
|
77
|
+
d = create_driver
|
78
|
+
time = Time.parse("2012-01-02 13:14:15").to_i
|
79
|
+
d.run do
|
80
|
+
d.emit({'msg' => 'xyz 123', 'alt' => 'aaa bbb ccc'}, time)
|
81
|
+
end
|
82
|
+
d.tag = 'test2'
|
83
|
+
d.run do
|
84
|
+
d.emit({'msg' => 'xyz 456', 'alt' => 'aaa bbb ccc ddd'}, time)
|
85
|
+
end
|
86
|
+
emits = d.emits
|
87
|
+
assert_equal 2, emits.length
|
88
|
+
|
89
|
+
assert_equal 'foo.bar', emits[0][0]
|
90
|
+
assert_equal time, emits[0][1]
|
91
|
+
assert_equal 'fixed:xyz 123:end', emits[0][2]['msg']
|
92
|
+
assert_equal 'aaa bbb ccc', emits[0][2]['alt']
|
93
|
+
assert_equal 'test', emits[0][2]['ttt']
|
94
|
+
assert_nil emits[0][2]['not_started']
|
95
|
+
|
96
|
+
assert_equal 'foo.bar', emits[1][0]
|
97
|
+
assert_equal time, emits[1][1]
|
98
|
+
assert_equal 'fixed:xyz 456:end', emits[1][2]['msg']
|
99
|
+
assert_equal 'aaa bbb ccc ddd', emits[1][2]['alt']
|
100
|
+
assert_equal 'test2', emits[1][2]['ttt']
|
101
|
+
assert_nil emits[1][2]['not_started']
|
102
|
+
end
|
103
|
+
end
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluent-plugin-forest
|
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-04-10 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: create sub-plugin dynamically per tags, with template configuration and
|
47
|
+
parameters
|
48
|
+
email:
|
49
|
+
- tagomoris@gmail.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- .gitignore
|
55
|
+
- Gemfile
|
56
|
+
- LICENSE.txt
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- fluent-plugin-forest.gemspec
|
60
|
+
- lib/fluent/plugin/out_forest.rb
|
61
|
+
- test/helper.rb
|
62
|
+
- test/output/out_forest_test.rb
|
63
|
+
- test/plugin/test_out_forest.rb
|
64
|
+
- test/plugin/test_out_forest_test.rb
|
65
|
+
homepage: https://github.com/tagomoris/fluent-plugin-forest
|
66
|
+
licenses: []
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options: []
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 1.8.21
|
86
|
+
signing_key:
|
87
|
+
specification_version: 3
|
88
|
+
summary: plugin to create output plugin instances per tags dynamically
|
89
|
+
test_files:
|
90
|
+
- test/helper.rb
|
91
|
+
- test/output/out_forest_test.rb
|
92
|
+
- test/plugin/test_out_forest.rb
|
93
|
+
- test/plugin/test_out_forest_test.rb
|