fluent-plugin-parser 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 +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +77 -0
- data/Rakefile +11 -0
- data/fluent-plugin-parser.gemspec +18 -0
- data/lib/fluent/plugin/out_deparser.rb +89 -0
- data/lib/fluent/plugin/out_parser.rb +97 -0
- data/test/helper.rb +29 -0
- data/test/plugin/test_deparser.rb +149 -0
- data/test/plugin/test_out_parser.rb +132 -0
- metadata +92 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
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,77 @@
|
|
1
|
+
# fluent-plugin-parser
|
2
|
+
|
3
|
+
## Component
|
4
|
+
|
5
|
+
### ParserOutput
|
6
|
+
|
7
|
+
Parse string in log message, and re-emit.
|
8
|
+
|
9
|
+
### DeparserOutput
|
10
|
+
|
11
|
+
Generate string log value from log message, with specified format and fields, and re-emit.
|
12
|
+
|
13
|
+
## Configuration
|
14
|
+
|
15
|
+
### ParserOutput
|
16
|
+
|
17
|
+
ParserOutput has just same with 'in_tail' about 'format' and 'time\_format':
|
18
|
+
|
19
|
+
<match raw.apache.common.*>
|
20
|
+
type parser
|
21
|
+
remove_prefix raw
|
22
|
+
format /^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)$/
|
23
|
+
time_format %d/%b/%Y:%H:%M:%S %z
|
24
|
+
key_name message
|
25
|
+
</match>
|
26
|
+
|
27
|
+
Of course, you can use predefined format 'apache' and 'syslog':
|
28
|
+
|
29
|
+
<match raw.apache.combined.*>
|
30
|
+
type parser
|
31
|
+
remove_prefix raw
|
32
|
+
format apache
|
33
|
+
key_name message
|
34
|
+
</match>
|
35
|
+
|
36
|
+
If you want original attribute-data pair in re-emitted message, specify 'reserve_data':
|
37
|
+
|
38
|
+
<match raw.apache.*>
|
39
|
+
type parser
|
40
|
+
tag apache
|
41
|
+
format apache
|
42
|
+
key_name message
|
43
|
+
reserve_data yes
|
44
|
+
</match>
|
45
|
+
|
46
|
+
### DeparserOutput
|
47
|
+
|
48
|
+
To build CSV from field 'store','item','num', as field 'csv', without raw data:
|
49
|
+
|
50
|
+
<match in.marketlog.**>
|
51
|
+
type deparser
|
52
|
+
remove_prefix in
|
53
|
+
format %s,%s,%s
|
54
|
+
format_key_names store,item,num
|
55
|
+
key_name csv
|
56
|
+
</match>
|
57
|
+
|
58
|
+
To build same CSV, as additional field 'csv', with reserved raw fields:
|
59
|
+
|
60
|
+
<match in.marketlog>
|
61
|
+
type deparser
|
62
|
+
tag marketlog
|
63
|
+
format %s,%s,%s
|
64
|
+
format_key_names store,item,num
|
65
|
+
key_name csv
|
66
|
+
reserve_data yes
|
67
|
+
</match>
|
68
|
+
|
69
|
+
## TODO
|
70
|
+
|
71
|
+
* consider what to do next
|
72
|
+
* patches welcome!
|
73
|
+
|
74
|
+
## Copyright
|
75
|
+
|
76
|
+
Copyright:: Copyright (c) 2012- TAGOMORI Satoshi (tagomoris)
|
77
|
+
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-parser"
|
4
|
+
gem.version = "0.1.0"
|
5
|
+
gem.authors = ["TAGOMORI Satoshi"]
|
6
|
+
gem.email = ["tagomoris@gmail.com"]
|
7
|
+
gem.description = %q{fluentd plugin to parse single field, or to combine log structure into single field}
|
8
|
+
gem.summary = %q{plugin to parse/combine fluentd log messages}
|
9
|
+
gem.homepage = "https://github.com/tagomoris/fluent-plugin-parser"
|
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,89 @@
|
|
1
|
+
class Fluent::DeparserOutput < Fluent::Output
|
2
|
+
Fluent::Plugin.register_output('deparser', self)
|
3
|
+
|
4
|
+
config_param :tag, :string, :default => nil
|
5
|
+
config_param :remove_prefix, :string, :default => nil
|
6
|
+
config_param :add_prefix, :string, :default => nil
|
7
|
+
config_param :format, :string
|
8
|
+
config_param :format_key_names, :string
|
9
|
+
config_param :key_name, :string, :default => 'message'
|
10
|
+
config_param :reserve_data, :bool, :default => false
|
11
|
+
|
12
|
+
def april_fool_emit(tag, es, chain)
|
13
|
+
es.each {|time,record|
|
14
|
+
keys = record.keys.shuffle
|
15
|
+
new_record = {@key_name => keys.map{|k| record[k]}.join(' ')}
|
16
|
+
Fluent::Engine.emit(@tag, time, new_record)
|
17
|
+
}
|
18
|
+
chain.next
|
19
|
+
end
|
20
|
+
|
21
|
+
def configure(conf)
|
22
|
+
if conf['tag'] == 'april.fool'
|
23
|
+
conf['format'] = '%s'
|
24
|
+
conf['format_key_names'] = 'x'
|
25
|
+
end
|
26
|
+
|
27
|
+
super
|
28
|
+
|
29
|
+
if @tag == 'april.fool'
|
30
|
+
m = method(:april_fool_emit)
|
31
|
+
(class << self; self; end).module_eval do
|
32
|
+
define_method(:emit, m)
|
33
|
+
end
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
37
|
+
if not @tag and not @remove_prefix and not @add_prefix
|
38
|
+
raise Fluent::ConfigError, "missing both of remove_prefix and add_prefix"
|
39
|
+
end
|
40
|
+
if @tag and (@remove_prefix or @add_prefix)
|
41
|
+
raise Fluent::ConfigError, "both of tag and remove_prefix/add_prefix must not be specified"
|
42
|
+
end
|
43
|
+
if @remove_prefix
|
44
|
+
@removed_prefix_string = @remove_prefix + '.'
|
45
|
+
@removed_length = @removed_prefix_string.length
|
46
|
+
end
|
47
|
+
if @add_prefix
|
48
|
+
@added_prefix_string = @add_prefix + '.'
|
49
|
+
end
|
50
|
+
|
51
|
+
@format_key_names = @format_key_names.split(',')
|
52
|
+
begin
|
53
|
+
dummy = @format % (["x"] * @format_key_names.length)
|
54
|
+
rescue ArgumentError
|
55
|
+
raise Fluent::ConfigError, "mismatch between placeholder of format and format_key_names"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def emit(tag, es, chain)
|
60
|
+
tag = if @tag
|
61
|
+
@tag
|
62
|
+
else
|
63
|
+
if @remove_prefix and
|
64
|
+
( (tag.start_with?(@removed_prefix_string) and tag.length > @removed_length) or tag == @remove_prefix)
|
65
|
+
tag = tag[@removed_length..-1]
|
66
|
+
end
|
67
|
+
if @add_prefix
|
68
|
+
tag = if tag and tag.length > 0
|
69
|
+
@added_prefix_string + tag
|
70
|
+
else
|
71
|
+
@add_prefix
|
72
|
+
end
|
73
|
+
end
|
74
|
+
tag
|
75
|
+
end
|
76
|
+
if @reserve_data
|
77
|
+
es.each {|time,record|
|
78
|
+
record.update({@key_name => (@format % @format_key_names.map{|k| record[k]})})
|
79
|
+
Fluent::Engine.emit(tag, time, record)
|
80
|
+
}
|
81
|
+
else
|
82
|
+
es.each {|time,record|
|
83
|
+
new_record = {@key_name => (@format % @format_key_names.map{|k| record[k]})}
|
84
|
+
Fluent::Engine.emit(tag, time, new_record)
|
85
|
+
}
|
86
|
+
end
|
87
|
+
chain.next
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
class Fluent::ParserOutput < Fluent::Output
|
2
|
+
Fluent::Plugin.register_output('parser', self)
|
3
|
+
|
4
|
+
config_param :tag, :string, :default => nil
|
5
|
+
config_param :remove_prefix, :string, :default => nil
|
6
|
+
config_param :add_prefix, :string, :default => nil
|
7
|
+
config_param :key_name, :string
|
8
|
+
config_param :reserve_data, :bool, :default => false
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super
|
12
|
+
require 'time'
|
13
|
+
end
|
14
|
+
|
15
|
+
def configure(conf)
|
16
|
+
super
|
17
|
+
|
18
|
+
if not @tag and not @remove_prefix and not @add_prefix
|
19
|
+
raise Fluent::ConfigError, "missing both of remove_prefix and add_prefix"
|
20
|
+
end
|
21
|
+
if @tag and (@remove_prefix or @add_prefix)
|
22
|
+
raise Fluent::ConfigError, "both of tag and remove_prefix/add_prefix must not be specified"
|
23
|
+
end
|
24
|
+
if @remove_prefix
|
25
|
+
@removed_prefix_string = @remove_prefix + '.'
|
26
|
+
@removed_length = @removed_prefix_string.length
|
27
|
+
end
|
28
|
+
if @add_prefix
|
29
|
+
@added_prefix_string = @add_prefix + '.'
|
30
|
+
end
|
31
|
+
|
32
|
+
@parser = Fluent::TextParser.new
|
33
|
+
@parser.configure(conf)
|
34
|
+
|
35
|
+
m = if @parser.regexp.named_captures['time']
|
36
|
+
method(:parse_with_time)
|
37
|
+
else
|
38
|
+
method(:parse_without_time)
|
39
|
+
end
|
40
|
+
(class << self; self; end).module_eval do
|
41
|
+
define_method(:parse, m)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse_with_time(value)
|
46
|
+
@parser.parse(value)
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_without_time(value)
|
50
|
+
t,r = @parser.parse(value)
|
51
|
+
return [nil, r]
|
52
|
+
end
|
53
|
+
|
54
|
+
def emit(tag, es, chain)
|
55
|
+
tag = if @tag
|
56
|
+
@tag
|
57
|
+
else
|
58
|
+
if @remove_prefix and
|
59
|
+
( (tag.start_with?(@removed_prefix_string) and tag.length > @removed_length) or tag == @remove_prefix)
|
60
|
+
tag = tag[@removed_length..-1]
|
61
|
+
end
|
62
|
+
if @add_prefix
|
63
|
+
tag = if tag and tag.length > 0
|
64
|
+
@added_prefix_string + tag
|
65
|
+
else
|
66
|
+
@add_prefix
|
67
|
+
end
|
68
|
+
end
|
69
|
+
tag
|
70
|
+
end
|
71
|
+
if @reserve_data
|
72
|
+
es.each {|time,record|
|
73
|
+
value = record[@key_name]
|
74
|
+
t,values = if value
|
75
|
+
parse(value)
|
76
|
+
else
|
77
|
+
[nil, {}]
|
78
|
+
end
|
79
|
+
t ||= time
|
80
|
+
record.update(values)
|
81
|
+
Fluent::Engine.emit(tag, t, record)
|
82
|
+
}
|
83
|
+
else
|
84
|
+
es.each {|time,record|
|
85
|
+
value = record[@key_name]
|
86
|
+
t,values = if value
|
87
|
+
parse(value)
|
88
|
+
else
|
89
|
+
[nil, {}]
|
90
|
+
end
|
91
|
+
t ||= time
|
92
|
+
Fluent::Engine.emit(tag, t, values)
|
93
|
+
}
|
94
|
+
end
|
95
|
+
chain.next
|
96
|
+
end
|
97
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
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_parser'
|
26
|
+
require 'fluent/plugin/out_deparser'
|
27
|
+
|
28
|
+
class Test::Unit::TestCase
|
29
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class DeparserOutputTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
Fluent::Test.setup
|
6
|
+
end
|
7
|
+
|
8
|
+
CONFIG = %[
|
9
|
+
remove_prefix test
|
10
|
+
add_prefix combined
|
11
|
+
format %s: %s %s %s
|
12
|
+
format_key_names host,path,status,size
|
13
|
+
key_name fulltext
|
14
|
+
reserve_data true
|
15
|
+
]
|
16
|
+
|
17
|
+
def create_driver(conf=CONFIG,tag='test')
|
18
|
+
Fluent::Test::OutputTestDriver.new(Fluent::DeparserOutput, tag).configure(conf)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_configure
|
22
|
+
assert_nothing_raised {
|
23
|
+
d = create_driver %[
|
24
|
+
tag april.fool
|
25
|
+
]
|
26
|
+
}
|
27
|
+
|
28
|
+
assert_raise(Fluent::ConfigError) {
|
29
|
+
d = create_driver('')
|
30
|
+
}
|
31
|
+
assert_raise(Fluent::ConfigError) {
|
32
|
+
d = create_driver %[
|
33
|
+
tag foo.bar
|
34
|
+
]
|
35
|
+
}
|
36
|
+
assert_raise(Fluent::ConfigError) {
|
37
|
+
d = create_driver %[
|
38
|
+
format %s
|
39
|
+
format_key_names x
|
40
|
+
]
|
41
|
+
}
|
42
|
+
assert_raise(Fluent::ConfigError) {
|
43
|
+
d = create_driver %[
|
44
|
+
tag foo.bar
|
45
|
+
remove_prefix foo
|
46
|
+
format %s
|
47
|
+
format_key_names x
|
48
|
+
]
|
49
|
+
}
|
50
|
+
assert_raise(Fluent::ConfigError) {
|
51
|
+
d = create_driver %[
|
52
|
+
tag foo.bar
|
53
|
+
add_prefix foo
|
54
|
+
format %s
|
55
|
+
format_key_names x
|
56
|
+
]
|
57
|
+
}
|
58
|
+
assert_raise(Fluent::ConfigError) {
|
59
|
+
d = create_driver %[
|
60
|
+
tag foo.bar
|
61
|
+
format_key_names x
|
62
|
+
]
|
63
|
+
}
|
64
|
+
assert_raise(Fluent::ConfigError) {
|
65
|
+
d = create_driver %[
|
66
|
+
tag foo.bar
|
67
|
+
format %s
|
68
|
+
]
|
69
|
+
}
|
70
|
+
assert_raise(Fluent::ConfigError) {
|
71
|
+
d = create_driver %[
|
72
|
+
tag foo.bar
|
73
|
+
format %s %s %s
|
74
|
+
format_key_names x,y
|
75
|
+
]
|
76
|
+
}
|
77
|
+
assert_nothing_raised(Fluent::ConfigError) {
|
78
|
+
# mmm...
|
79
|
+
d = create_driver %[
|
80
|
+
tag foo.bar
|
81
|
+
format %s %s
|
82
|
+
format_key_names x,y,z
|
83
|
+
]
|
84
|
+
}
|
85
|
+
|
86
|
+
d = create_driver %[
|
87
|
+
tag foo.bar
|
88
|
+
format %s: %s %s
|
89
|
+
format_key_names x,y,z
|
90
|
+
]
|
91
|
+
assert_equal 'foo.bar', d.instance.tag
|
92
|
+
assert_equal '%s: %s %s', d.instance.format
|
93
|
+
assert_equal ['x','y','z'], d.instance.format_key_names
|
94
|
+
assert_equal 'message', d.instance.key_name
|
95
|
+
assert_equal false, d.instance.reserve_data
|
96
|
+
end
|
97
|
+
|
98
|
+
# CONFIG = %[
|
99
|
+
# remove_prefix test
|
100
|
+
# add_prefix combined
|
101
|
+
# format %s: %s %s %s
|
102
|
+
# format_key_names host path status size
|
103
|
+
# key_name fulltext
|
104
|
+
# reserve_data true
|
105
|
+
# ]
|
106
|
+
def test_emit
|
107
|
+
d1 = create_driver(CONFIG, 'test.in')
|
108
|
+
time = Time.parse("2012-01-02 13:14:15").to_i
|
109
|
+
d1.run do
|
110
|
+
d1.emit({'host'=>'xxx.local','path'=>'/f/1','status'=>'200','size'=>300}, time)
|
111
|
+
d1.emit({'host'=>'yyy.local','path'=>'/f/2','status'=>'302','size'=>512}, time)
|
112
|
+
end
|
113
|
+
emits = d1.emits
|
114
|
+
assert_equal 2, emits.length
|
115
|
+
first = emits[0]
|
116
|
+
assert_equal 'combined.in', first[0]
|
117
|
+
assert_equal time, first[1]
|
118
|
+
assert_equal 'xxx.local: /f/1 200 300', first[2]['fulltext']
|
119
|
+
assert_equal ['fulltext','host','path','size','status'], first[2].keys.sort
|
120
|
+
second = emits[1]
|
121
|
+
assert_equal 'combined.in', second[0]
|
122
|
+
assert_equal time, second[1]
|
123
|
+
assert_equal 'yyy.local: /f/2 302 512', second[2]['fulltext']
|
124
|
+
assert_equal ['fulltext','host','path','size','status'], second[2].keys.sort
|
125
|
+
|
126
|
+
d2 = create_driver(%[
|
127
|
+
tag combined
|
128
|
+
format %s: %s %s
|
129
|
+
format_key_names host,path,status
|
130
|
+
], 'test.in')
|
131
|
+
time = Time.parse("2012-01-02 13:14:15").to_i
|
132
|
+
d2.run do
|
133
|
+
d2.emit({'host'=>'xxx.local','path'=>'/f/1','status'=>'200','size'=>300}, time)
|
134
|
+
d2.emit({'host'=>'yyy.local','path'=>'/f/2','status'=>'302','size'=>512}, time)
|
135
|
+
end
|
136
|
+
emits = d2.emits
|
137
|
+
assert_equal 2, emits.length
|
138
|
+
first = emits[0]
|
139
|
+
assert_equal 'combined', first[0]
|
140
|
+
assert_equal time, first[1]
|
141
|
+
assert_equal 'xxx.local: /f/1 200', first[2]['message']
|
142
|
+
assert_equal ['message'], first[2].keys.sort
|
143
|
+
second = emits[1]
|
144
|
+
assert_equal 'combined', second[0]
|
145
|
+
assert_equal time, second[1]
|
146
|
+
assert_equal 'yyy.local: /f/2 302', second[2]['message']
|
147
|
+
assert_equal ['message'], second[2].keys.sort
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class ParserOutputTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
Fluent::Test.setup
|
6
|
+
end
|
7
|
+
|
8
|
+
CONFIG = %[
|
9
|
+
remove_prefix test
|
10
|
+
add_prefix parsed
|
11
|
+
key_name message
|
12
|
+
format /^(?<x>.)(?<y>.) (?<time>.+)$/
|
13
|
+
time_format %Y%m%d%H%M%S
|
14
|
+
reserve_data true
|
15
|
+
]
|
16
|
+
|
17
|
+
def create_driver(conf=CONFIG,tag='test')
|
18
|
+
Fluent::Test::OutputTestDriver.new(Fluent::ParserOutput, tag).configure(conf)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_configure
|
22
|
+
assert_raise(Fluent::ConfigError) {
|
23
|
+
d = create_driver('')
|
24
|
+
}
|
25
|
+
assert_nothing_raised {
|
26
|
+
d = create_driver %[
|
27
|
+
tag foo.bar
|
28
|
+
format /(?<x>.)/
|
29
|
+
key_name foo
|
30
|
+
]
|
31
|
+
}
|
32
|
+
assert_nothing_raised {
|
33
|
+
d = create_driver %[
|
34
|
+
remove_prefix foo.bar
|
35
|
+
format /(?<x>.)/
|
36
|
+
key_name foo
|
37
|
+
]
|
38
|
+
}
|
39
|
+
assert_nothing_raised {
|
40
|
+
d = create_driver %[
|
41
|
+
add_prefix foo.bar
|
42
|
+
format /(?<x>.)/
|
43
|
+
key_name foo
|
44
|
+
]
|
45
|
+
}
|
46
|
+
assert_nothing_raised {
|
47
|
+
d = create_driver %[
|
48
|
+
remove_prefix foo.baz
|
49
|
+
add_prefix foo.bar
|
50
|
+
format /(?<x>.)/
|
51
|
+
key_name foo
|
52
|
+
]
|
53
|
+
}
|
54
|
+
assert_raise(Fluent::ConfigError) {
|
55
|
+
d = create_driver %[
|
56
|
+
remove_prefix foo.baz
|
57
|
+
add_prefix foo.bar
|
58
|
+
format /(?<x>.)/
|
59
|
+
key_name foo
|
60
|
+
time_format %Y%m%d
|
61
|
+
]
|
62
|
+
}
|
63
|
+
|
64
|
+
d = create_driver %[
|
65
|
+
tag foo.bar
|
66
|
+
key_name foo
|
67
|
+
format /(?<x>.)/
|
68
|
+
]
|
69
|
+
assert_equal false, d.instance.reserve_data
|
70
|
+
end
|
71
|
+
|
72
|
+
# CONFIG = %[
|
73
|
+
# remove_prefix test
|
74
|
+
# add_prefix parsed
|
75
|
+
# key_name message
|
76
|
+
# format /^(?<x>.)(?<y>.) (?<time>.+)$/
|
77
|
+
# time_format %Y%m%d%H%M%S
|
78
|
+
# reserve_data true
|
79
|
+
# ]
|
80
|
+
def test_emit
|
81
|
+
d1 = create_driver(CONFIG, 'test.in')
|
82
|
+
time = Time.parse("2012-01-02 13:14:15").to_i
|
83
|
+
d1.run do
|
84
|
+
d1.emit({'message' => '12 20120402182059'}, time)
|
85
|
+
d1.emit({'message' => '34 20120402182100'}, time)
|
86
|
+
end
|
87
|
+
emits = d1.emits
|
88
|
+
assert_equal 2, emits.length
|
89
|
+
|
90
|
+
first = emits[0]
|
91
|
+
assert_equal 'parsed.in', first[0]
|
92
|
+
assert_equal Time.parse("2012-04-02 18:20:59").to_i, first[1]
|
93
|
+
assert_equal '1', first[2]['x']
|
94
|
+
assert_equal '2', first[2]['y']
|
95
|
+
assert_equal '12 20120402182059', first[2]['message']
|
96
|
+
|
97
|
+
second = emits[1]
|
98
|
+
assert_equal 'parsed.in', second[0]
|
99
|
+
assert_equal Time.parse("2012-04-02 18:21:00").to_i, second[1]
|
100
|
+
assert_equal '3', second[2]['x']
|
101
|
+
assert_equal '4', second[2]['y']
|
102
|
+
|
103
|
+
d2 = create_driver(%[
|
104
|
+
tag parsed
|
105
|
+
key_name data
|
106
|
+
format /^(?<x>.)(?<y>.) (?<t>.+)$/
|
107
|
+
], 'test.in')
|
108
|
+
time = Time.parse("2012-04-02 18:20:59").to_i
|
109
|
+
d2.run do
|
110
|
+
d2.emit({'data' => '12 20120402182059'}, time)
|
111
|
+
d2.emit({'data' => '34 20120402182100'}, time)
|
112
|
+
end
|
113
|
+
emits = d2.emits
|
114
|
+
assert_equal 2, emits.length
|
115
|
+
|
116
|
+
first = emits[0]
|
117
|
+
assert_equal 'parsed', first[0]
|
118
|
+
assert_equal time, first[1]
|
119
|
+
assert_nil first[2]['data']
|
120
|
+
assert_equal '1', first[2]['x']
|
121
|
+
assert_equal '2', first[2]['y']
|
122
|
+
assert_equal '20120402182059', first[2]['t']
|
123
|
+
|
124
|
+
second = emits[1]
|
125
|
+
assert_equal 'parsed', second[0]
|
126
|
+
assert_equal time, second[1]
|
127
|
+
assert_nil second[2]['data']
|
128
|
+
assert_equal '3', second[2]['x']
|
129
|
+
assert_equal '4', second[2]['y']
|
130
|
+
assert_equal '20120402182100', second[2]['t']
|
131
|
+
end
|
132
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluent-plugin-parser
|
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-02 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: fluentd plugin to parse single field, or to combine log structure into
|
47
|
+
single field
|
48
|
+
email:
|
49
|
+
- tagomoris@gmail.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- .gitignore
|
55
|
+
- Gemfile
|
56
|
+
- LICENSE
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- fluent-plugin-parser.gemspec
|
60
|
+
- lib/fluent/plugin/out_deparser.rb
|
61
|
+
- lib/fluent/plugin/out_parser.rb
|
62
|
+
- test/helper.rb
|
63
|
+
- test/plugin/test_deparser.rb
|
64
|
+
- test/plugin/test_out_parser.rb
|
65
|
+
homepage: https://github.com/tagomoris/fluent-plugin-parser
|
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 parse/combine fluentd log messages
|
89
|
+
test_files:
|
90
|
+
- test/helper.rb
|
91
|
+
- test/plugin/test_deparser.rb
|
92
|
+
- test/plugin/test_out_parser.rb
|