fluent-plugin-split_record 0.12.1
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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +23 -0
- data/README.md +74 -0
- data/Rakefile +13 -0
- data/fluent-plugin-split_record.gemspec +18 -0
- data/lib/fluent/plugin/filter_split_record.rb +105 -0
- data/test/helper.rb +32 -0
- data/test/plugin/test_filter_split_record.rb +109 -0
- metadata +77 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0887090cebdf0de99fa03fcd83c7702a9013cf44
|
4
|
+
data.tar.gz: ffd3606d5bb4a9f0a563102b1d6e39fbd0243e40
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 84593232bdea75447f1d3dba891f464eca40392902918796efd28dfcadad6ee8b23d5f49a76ea909f9a3ce4e4cfd468db4dddbd1af08e70ddbe576aeacc276ac
|
7
|
+
data.tar.gz: 0888ea9208dad158790fce11fc0fe4168f996f754ad07af53fb1d430f8b73f96330131cf75cb7f9d982a2f2e9ca050010c7034053d90c28bcea2f0a03607d52d
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2013 Masahiro Sano
|
2
|
+
Copyright (c) 2017 Michael Adams
|
3
|
+
|
4
|
+
MIT License
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# fluent-plugin-split_record
|
2
|
+
|
3
|
+
Fluentd filter plugin to split a record into multiple records with key/value pair. Compatible with 0.12 and 0.14 versions of fluentd.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
This plugin splits a record and parses each results to make key/value pairs; Logstash's [kv filter](https://www.elastic.co/guide/en/logstash/current/plugins-filters-kv.html) is a good example of this. It is a successor to [fluent-plugin-split](https://github.com/kazegusuri/fluent-plugin-split/); a 0.10 output plugin. This is NOT the current 0.12+ [fluent-plugin-split](https://github.com/toyama0919/fluent-plugin-split/): that one is what currently installs with ruby-gem, and splits CSV-style content.
|
7
|
+
|
8
|
+
Normally you can use a regular expression to parse a record. It is difficult to parse a record which has ambiguous numbers of data like a following record.
|
9
|
+
|
10
|
+
**Before**
|
11
|
+
```json
|
12
|
+
{"message":"key1=val1 key2=val2 key3=val3"}
|
13
|
+
```
|
14
|
+
|
15
|
+
**After**
|
16
|
+
```json
|
17
|
+
{"key1":"val1","key2":"val2","key3":"val3"}
|
18
|
+
```
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
### Local/Build
|
23
|
+
```
|
24
|
+
$ git clone https://github.com/unquietwiki/fluent-plugin-split_record.git && cd fluent-plugin-split_record
|
25
|
+
$ td-agent-gem build fluent-plugin-split_record.gemspec
|
26
|
+
$ td-agent-gem install fluent-plugin-split_record-0.12.1.gem
|
27
|
+
```
|
28
|
+
|
29
|
+
### Online
|
30
|
+
```
|
31
|
+
$ td-agent-gem install fluent-plugin-split_record
|
32
|
+
```
|
33
|
+
|
34
|
+
## Configuration
|
35
|
+
|
36
|
+
### Parameters
|
37
|
+
|
38
|
+
|parameter|description|default|
|
39
|
+
|---|---|---|
|
40
|
+
|tag| key name for tag | |
|
41
|
+
|format| regexp to parse a record after split | '(?<key>\S*)=(?<value>\S*)' |
|
42
|
+
|substring_format| regexp used to identify substrings | '(?<key>\S*)=\\"(?<value>.*?)\\"' |
|
43
|
+
|key_name| key name to be split | |
|
44
|
+
|out_key| key name of json object which includes divided records | nil |
|
45
|
+
|reserve_msg| if original message is reserved or not | nil |
|
46
|
+
|keys_prefix| if set, all extracted keys names will be preceded by this string | nil |
|
47
|
+
|
48
|
+
### Example
|
49
|
+
|
50
|
+
You may want to pre-process with the [regexp parser](https://docs.fluentd.org/v0.12/articles/parser_regexp) to remove/tag other elements first; this is a requirement if working with [SonicWall syslog input](http://software.sonicwall.com/manual/232-001835-00_rev_a_sonicos_log_event_reference_guide.pdf), which is otherwise an array of key-value pairs.
|
51
|
+
|
52
|
+
```
|
53
|
+
<source>
|
54
|
+
@type udp
|
55
|
+
port 514
|
56
|
+
format /\<(?<prefix>[0-9]{1,3})\>(?<extradata>.+)$\z/
|
57
|
+
tag FW
|
58
|
+
</source>
|
59
|
+
|
60
|
+
<filter FW.**>
|
61
|
+
@type split_record
|
62
|
+
tag FW
|
63
|
+
key_name extradata
|
64
|
+
reserve_msg no
|
65
|
+
</filter>
|
66
|
+
```
|
67
|
+
|
68
|
+
## References
|
69
|
+
|
70
|
+
* https://github.com/repeatedly/fluent-plugin-record-modifier/
|
71
|
+
* https://docs.fluentd.org/v0.12/articles/plugin-development
|
72
|
+
* https://ruby-doc.org/core-2.1.5/
|
73
|
+
* https://stackoverflow.com/questions/tagged/ruby
|
74
|
+
* http://rubular.com/
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = "fluent-plugin-split_record"
|
6
|
+
spec.version = "0.12.1"
|
7
|
+
spec.authors = ["Masahiro Sano","Michael Adams"]
|
8
|
+
spec.email = ["sabottenda@gmail.com","unquietwiki@gmail.com"]
|
9
|
+
spec.description = %q{Fluentd filter plugin to split a record into multiple records with key/value pair.}
|
10
|
+
spec.summary = %q{Successor to original fluent-plugin-split. Updated for fluentd 0.12 and 0.14, with non-conflicting name.}
|
11
|
+
spec.homepage = "https://github.com/unquietwiki/fluent-plugin-split_record"
|
12
|
+
spec.license = "MIT"
|
13
|
+
spec.files = `git ls-files`.split("\n")
|
14
|
+
spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
+
spec.require_paths = ["lib"]
|
17
|
+
spec.add_dependency "fluentd",">= 0.12.39","< 0.16"
|
18
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# Derived from https://github.com/kazegusuri/fluent-plugin-split/blob/master/lib/fluent/plugin/out_split.rb
|
2
|
+
# Aug 30, 2017; Michael Adams; unquietwiki@gmail.com
|
3
|
+
|
4
|
+
# References
|
5
|
+
# https://github.com/repeatedly/fluent-plugin-record-modifier/
|
6
|
+
# https://docs.fluentd.org/v0.12/articles/plugin-development
|
7
|
+
# https://ruby-doc.org/core-2.1.5/
|
8
|
+
# https://stackoverflow.com/questions/tagged/ruby
|
9
|
+
|
10
|
+
require 'fluent/filter'
|
11
|
+
|
12
|
+
module Fluent
|
13
|
+
class SplitRecordFilter < Filter
|
14
|
+
Fluent::Plugin.register_filter("split_record", self)
|
15
|
+
|
16
|
+
# Parameters
|
17
|
+
config_param :tag, :string
|
18
|
+
config_param :key_name, :string
|
19
|
+
config_param :out_key, :string, :default => nil
|
20
|
+
config_param :reserve_msg, :bool, :default => nil
|
21
|
+
config_param :keys_prefix, :string, :default => nil
|
22
|
+
config_param :format, :string, :default => '(?<key>\S*)=(?<value>\S*)'
|
23
|
+
config_param :substring_format, :string, :default => '(?<key>\S*)=\\"(?<value>.*?)\\"'
|
24
|
+
|
25
|
+
# Configuration
|
26
|
+
def configure(conf)
|
27
|
+
super
|
28
|
+
@format_regex = Regexp.new(@format)
|
29
|
+
@format_regex_substring = Regexp.new(@substring_format)
|
30
|
+
unless @format_regex.names.include?("key") and @format_regex.names.include?("value")
|
31
|
+
raise ConfigError, "split_record: format must have named_captures of key and value"
|
32
|
+
end
|
33
|
+
if (!keys_prefix.nil? && keys_prefix.is_a?(String))
|
34
|
+
@store_fun = method(:store_with_prefix)
|
35
|
+
else
|
36
|
+
@store_fun = method(:store)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# ===== Required API methods =====
|
41
|
+
def start
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def shutdown
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
def filter(tag, time, record)
|
50
|
+
record
|
51
|
+
end
|
52
|
+
|
53
|
+
def filter_stream(tag, es)
|
54
|
+
mes = MultiEventStream.new
|
55
|
+
es.each { |time, record|
|
56
|
+
begin
|
57
|
+
msg = record[@key_name]
|
58
|
+
record.delete(@key_name) unless @reserve_msg
|
59
|
+
data = split_message(msg)
|
60
|
+
if @out_key.nil?
|
61
|
+
record.merge!(data)
|
62
|
+
else
|
63
|
+
record[@out_key] = data
|
64
|
+
end
|
65
|
+
mes.add(time, record)
|
66
|
+
rescue => e
|
67
|
+
router.emit_error_event(tag, time, record, e)
|
68
|
+
end
|
69
|
+
}
|
70
|
+
mes
|
71
|
+
end
|
72
|
+
|
73
|
+
# ===== Private methods =====
|
74
|
+
private
|
75
|
+
|
76
|
+
# Message splitter
|
77
|
+
def split_message(message)
|
78
|
+
return {} unless message.is_a?(String)
|
79
|
+
# Convert key-pairs as found
|
80
|
+
if @format_regex_substring.nil?
|
81
|
+
key_values = message.scan @format_regex
|
82
|
+
# Pop off substrings; get their key-pairs; then scan the leftovers
|
83
|
+
else
|
84
|
+
key_values = message.scan @format_regex_substring
|
85
|
+
leftovers = message.gsub(@format_regex_substring,'').scan(@format_regex)
|
86
|
+
leftovers.each { |e| key_values << e }
|
87
|
+
end
|
88
|
+
# Store key pairs
|
89
|
+
data = {}
|
90
|
+
key_values.each { |e| @store_fun.call(data,e[0],e[1]) }
|
91
|
+
data
|
92
|
+
end
|
93
|
+
|
94
|
+
# Store key/value pair
|
95
|
+
def store(data, key, value)
|
96
|
+
data.store(key, value)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Store key/value pair, with prefix
|
100
|
+
def store_with_prefix(data, key, value)
|
101
|
+
data.store(@keys_prefix+key, value)
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
begin
|
5
|
+
Bundler.setup(:default, :development)
|
6
|
+
rescue Bundler::BundlerError => e
|
7
|
+
$stderr.puts e.message
|
8
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
9
|
+
exit e.status_code
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'test/unit'
|
13
|
+
|
14
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
15
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
16
|
+
|
17
|
+
require 'fluent/test'
|
18
|
+
|
19
|
+
unless ENV.has_key?('VERBOSE')
|
20
|
+
nulllogger = Object.new
|
21
|
+
nulllogger.instance_eval {|obj|
|
22
|
+
def method_missing(method, *args)
|
23
|
+
# pass
|
24
|
+
end
|
25
|
+
}
|
26
|
+
$log = nulllogger
|
27
|
+
end
|
28
|
+
|
29
|
+
require 'fluent/plugin/filter_split_record'
|
30
|
+
|
31
|
+
class Test::Unit::TestCase
|
32
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'fluent/test'
|
2
|
+
require 'fluent/plugin/filter_split_record'
|
3
|
+
|
4
|
+
class SplitRecordFilterTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
Fluent::Test.setup
|
7
|
+
end
|
8
|
+
|
9
|
+
CONFIG = %[
|
10
|
+
type split_record
|
11
|
+
tag foo.filtered
|
12
|
+
key_name message
|
13
|
+
]
|
14
|
+
|
15
|
+
def create_driver(conf = CONFIG, tag='test')
|
16
|
+
Fluent::Test:::OutputTestDriver.new(Fluent::SplitRecordFilter, tag).configure(conf)
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_hostname
|
20
|
+
require 'socket'
|
21
|
+
Socket.gethostname.chomp
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_configure
|
25
|
+
assert_raise(Fluent::ConfigError) {
|
26
|
+
create_driver(CONFIG + %[
|
27
|
+
format /aa/
|
28
|
+
])
|
29
|
+
}
|
30
|
+
assert_raise(Fluent::ConfigError) {
|
31
|
+
create_driver(CONFIG + %[
|
32
|
+
format (?<key>a)
|
33
|
+
])
|
34
|
+
}
|
35
|
+
assert_raise(Fluent::ConfigError) {
|
36
|
+
create_driver(CONFIG + %[
|
37
|
+
format (?<value>a)
|
38
|
+
])
|
39
|
+
}
|
40
|
+
assert_nothing_raised(Fluent::ConfigError) {
|
41
|
+
create_driver(CONFIG + %[
|
42
|
+
format (?<value>a) (?<key>b)
|
43
|
+
])
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_format_1
|
48
|
+
d = create_driver
|
49
|
+
|
50
|
+
d.run do
|
51
|
+
d.emit({"message" => "key1=val1 key2=val2"})
|
52
|
+
d.emit({"message" => " key1=val1 "})
|
53
|
+
d.emit({"message" => " "})
|
54
|
+
d.emit({"message" => 1})
|
55
|
+
end
|
56
|
+
|
57
|
+
mapped = {'gen_host' => get_hostname, 'foo' => 'bar', 'included_tag' => 'test'}
|
58
|
+
assert_equal [
|
59
|
+
{"key1" => "val1", "key2" => "val2"},
|
60
|
+
{"key1" => "val1"},
|
61
|
+
{},
|
62
|
+
{},
|
63
|
+
], d.records
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_format_2
|
67
|
+
d = create_driver(CONFIG + %[
|
68
|
+
separator ,
|
69
|
+
reserve_msg true
|
70
|
+
])
|
71
|
+
|
72
|
+
d.run do
|
73
|
+
d.emit({"message" => "key1=val1,key2=val2"})
|
74
|
+
end
|
75
|
+
|
76
|
+
assert_equal [
|
77
|
+
{"message" => "key1=val1,key2=val2", "key1" => "val1", "key2" => "val2"},
|
78
|
+
], d.records
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_format_3
|
82
|
+
d = create_driver(CONFIG + %[
|
83
|
+
out_key data
|
84
|
+
])
|
85
|
+
|
86
|
+
d.run do
|
87
|
+
d.emit({"message" => "key1=val1 key2=val2"})
|
88
|
+
end
|
89
|
+
|
90
|
+
assert_equal [
|
91
|
+
{"data" => {"key1" => "val1", "key2" => "val2"}},
|
92
|
+
], d.records
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_format_keysprefix
|
96
|
+
d = create_driver(CONFIG + %[
|
97
|
+
out_key data
|
98
|
+
keys_prefix extracted_
|
99
|
+
])
|
100
|
+
|
101
|
+
d.run do
|
102
|
+
d.emit({"message" => "key1=val1 key2=val2"})
|
103
|
+
end
|
104
|
+
|
105
|
+
assert_equal [
|
106
|
+
{"data" => {"extracted_key1" => "val1", "extracted_key2" => "val2"}},
|
107
|
+
], d.records
|
108
|
+
end
|
109
|
+
end
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluent-plugin-split_record
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.12.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Masahiro Sano
|
8
|
+
- Michael Adams
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2017-08-31 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: fluentd
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 0.12.39
|
21
|
+
- - "<"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '0.16'
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: 0.12.39
|
31
|
+
- - "<"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.16'
|
34
|
+
description: Fluentd filter plugin to split a record into multiple records with key/value
|
35
|
+
pair.
|
36
|
+
email:
|
37
|
+
- sabottenda@gmail.com
|
38
|
+
- unquietwiki@gmail.com
|
39
|
+
executables: []
|
40
|
+
extensions: []
|
41
|
+
extra_rdoc_files: []
|
42
|
+
files:
|
43
|
+
- ".gitignore"
|
44
|
+
- Gemfile
|
45
|
+
- LICENSE.txt
|
46
|
+
- README.md
|
47
|
+
- Rakefile
|
48
|
+
- fluent-plugin-split_record.gemspec
|
49
|
+
- lib/fluent/plugin/filter_split_record.rb
|
50
|
+
- test/helper.rb
|
51
|
+
- test/plugin/test_filter_split_record.rb
|
52
|
+
homepage: https://github.com/unquietwiki/fluent-plugin-split_record
|
53
|
+
licenses:
|
54
|
+
- MIT
|
55
|
+
metadata: {}
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
requirements: []
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 2.4.8
|
73
|
+
signing_key:
|
74
|
+
specification_version: 4
|
75
|
+
summary: Successor to original fluent-plugin-split. Updated for fluentd 0.12 and 0.14,
|
76
|
+
with non-conflicting name.
|
77
|
+
test_files: []
|