fluent-plugin-record-reformer 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +70 -25
- data/fluent-plugin-record-reformer.gemspec +2 -1
- data/lib/fluent/plugin/out_record_reformer.rb +72 -9
- data/spec/out_record_reformer_spec.rb +101 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2166c7d160e63370e3400203a591cbb9c2de22e7
|
4
|
+
data.tar.gz: e01e518088e90bdb32e4040ed5a2fbbca5ee6bf8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2e3901a0af27f800d9a81e34477409f8696b13bed9c2e47425911cd45ae0295455a5686e42969daa31a4a327c8cf779b821f0fe4a8b4625eedfe72fce58b0f9
|
7
|
+
data.tar.gz: 8812a93c884c231525b95bf29b14e5f9d4381ea0f9cece5a3109b3d7439b3cfff677c99831741d7aa7f509798bc4cea68348cbe4747a15676c45c471ff26cb62
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# fluent-plugin-record-reformer
|
2
2
|
|
3
|
-
[![Build Status](https://secure.travis-ci.org/sonots/fluent-plugin-record-reformer.png?branch=master)](http://travis-ci.org/sonots/fluent-plugin-record-reformer)
|
3
|
+
[![Build Status](https://secure.travis-ci.org/sonots/fluent-plugin-record-reformer.png?branch=master)](http://travis-ci.org/sonots/fluent-plugin-record-reformer)
|
4
4
|
|
5
5
|
Fluentd pluging to add or replace fields of a event record
|
6
6
|
|
@@ -16,63 +16,108 @@ Example:
|
|
16
16
|
|
17
17
|
<match foo.**>
|
18
18
|
type record_reformer
|
19
|
-
|
19
|
+
remove_keys remove_me
|
20
|
+
renew_record false
|
21
|
+
enable_ruby false
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
output_tag reformed.${tag}
|
24
|
+
<record>
|
25
|
+
hostname ${hostname}
|
26
|
+
input_tag ${tag}
|
27
|
+
message ${message}, ${tag_parts[-1]}
|
28
|
+
</record>
|
25
29
|
</match>
|
26
30
|
|
27
|
-
Assume following input is coming:
|
31
|
+
Assume following input is coming (indented):
|
28
32
|
|
29
33
|
```js
|
30
|
-
foo.bar {
|
34
|
+
foo.bar {
|
35
|
+
"remove_me":"bar",
|
36
|
+
"foo":"bar",
|
37
|
+
"message":"Hello world!"
|
38
|
+
}
|
31
39
|
```
|
32
40
|
|
33
41
|
then output becomes as below (indented):
|
34
42
|
|
35
43
|
```js
|
36
44
|
reformed.foo.bar {
|
37
|
-
"
|
38
|
-
"
|
39
|
-
"
|
40
|
-
"message":"
|
41
|
-
"foo":"bar"
|
45
|
+
"foo":"bar",
|
46
|
+
"hostname":"YOUR_HOSTNAME",
|
47
|
+
"input_tag":"foo.bar",
|
48
|
+
"message":"Hello world!, bar",
|
42
49
|
}
|
43
50
|
```
|
44
51
|
|
45
|
-
|
52
|
+
## Configuration (Classic Style)
|
53
|
+
|
54
|
+
Example:
|
55
|
+
|
56
|
+
<match foo.**>
|
57
|
+
type record_reformer
|
58
|
+
remove_keys remove_me
|
59
|
+
renew_record false
|
60
|
+
enable_ruby false
|
61
|
+
output_tag reformed.${tag}
|
62
|
+
|
63
|
+
hostname ${hostname}
|
64
|
+
input_tag ${tag}
|
65
|
+
message ${message}, ${tag_parts[-1]}
|
66
|
+
</match>
|
67
|
+
|
68
|
+
This results in same, but please note that following option parameters are reserved, so can not be used as a record key.
|
69
|
+
|
70
|
+
## Parameters
|
71
|
+
|
72
|
+
- output_tag
|
73
|
+
|
74
|
+
The output tag name
|
75
|
+
|
76
|
+
- remove_keys
|
77
|
+
|
78
|
+
Specify record keys to be removed by a string separated by , (comma) like
|
79
|
+
|
80
|
+
remove_keys message,foo
|
46
81
|
|
47
|
-
|
82
|
+
- renew_record *bool*
|
83
|
+
|
84
|
+
Set to `true` if you do not want to extend (or merge) the input record fields. Default is `false`.
|
85
|
+
|
86
|
+
- enable_ruby *bool*
|
87
|
+
|
88
|
+
Enable to use ruby codes in placeholders. See `Placeholders` section.
|
89
|
+
Default is `true` (just for lower version compatibility).
|
90
|
+
|
91
|
+
## Placeholders
|
48
92
|
|
49
93
|
The keys of input json are available as placeholders. In the above example,
|
50
94
|
|
51
95
|
* ${foo}
|
52
96
|
* ${message}
|
97
|
+
* ${remove_me}
|
53
98
|
|
54
99
|
shall be available. In addition, following placeholders are reserved:
|
55
100
|
|
56
101
|
* ${hostname} hostname
|
57
102
|
* ${tag} input tag
|
58
|
-
* ${tags} input tag splitted by '.' (obsolete. use tag_parts)
|
59
|
-
* ${tag_parts} input tag splitted by '.'
|
60
103
|
* ${time} time of the event
|
104
|
+
* ${tags[N]} input tag splitted by '.' (obsolete. use tag\_parts)
|
105
|
+
* ${tag\_parts[N]} input tag splitted by '.' indexed with N such as `${tag_parts[0]}`, `${tag_parts[-1]}`.
|
61
106
|
|
62
|
-
It is also possible to write a ruby code in placeholders, so you may write some codes as
|
107
|
+
It is also possible to write a ruby code in placeholders if you set `enable_ruby true` option, so you may write some codes as
|
63
108
|
|
64
109
|
* ${time.strftime('%Y-%m-%dT%H:%M:%S%z')}
|
65
|
-
* ${
|
66
|
-
* ${tag_parts.last}
|
67
|
-
|
68
|
-
## Notice
|
110
|
+
* ${tag\_parts.last}
|
69
111
|
|
70
|
-
|
112
|
+
but, please note that enabling ruby codes is not encouraged by security reasons and also in terms of the performance.
|
71
113
|
|
72
114
|
## Relatives
|
73
115
|
|
74
|
-
|
75
|
-
|
116
|
+
Following plugins look similar:
|
117
|
+
|
118
|
+
* [fluent-plugin-record-modifier](https://github.com/repeatedly/fluent-plugin-record-modifier)
|
119
|
+
* [fluent-plugin-format](https://github.com/mach/fluent-plugin-format)
|
120
|
+
* [fluent-plugin-add](https://github.com/yu-yamada/fluent-plugin-add)
|
76
121
|
|
77
122
|
## ChangeLog
|
78
123
|
|
@@ -3,7 +3,7 @@ $:.push File.expand_path('../lib', __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.name = "fluent-plugin-record-reformer"
|
6
|
-
gem.version = "0.
|
6
|
+
gem.version = "0.2.0"
|
7
7
|
gem.authors = ["Naotoshi Seo"]
|
8
8
|
gem.email = "sonots@gmail.com"
|
9
9
|
gem.homepage = "https://github.com/sonots/fluent-plugin-record-reformer"
|
@@ -21,4 +21,5 @@ Gem::Specification.new do |gem|
|
|
21
21
|
gem.add_development_dependency "rake"
|
22
22
|
gem.add_development_dependency "rspec"
|
23
23
|
gem.add_development_dependency "pry"
|
24
|
+
gem.add_development_dependency "pry-nav"
|
24
25
|
end
|
@@ -13,8 +13,11 @@ module Fluent
|
|
13
13
|
end
|
14
14
|
|
15
15
|
config_param :output_tag, :string
|
16
|
+
config_param :remove_keys, :string, :default => nil
|
17
|
+
config_param :renew_record, :bool, :default => false
|
18
|
+
config_param :enable_ruby, :bool, :default => true # true for lower version compatibility
|
16
19
|
|
17
|
-
BUILTIN_CONFIGURATIONS = %W(type output_tag)
|
20
|
+
BUILTIN_CONFIGURATIONS = %W(type output_tag remove_keys renew_record enable_ruby)
|
18
21
|
|
19
22
|
def configure(conf)
|
20
23
|
super
|
@@ -22,9 +25,34 @@ module Fluent
|
|
22
25
|
@map = {}
|
23
26
|
conf.each_pair { |k, v|
|
24
27
|
next if BUILTIN_CONFIGURATIONS.include?(k)
|
25
|
-
conf.has_key?(k)
|
28
|
+
conf.has_key?(k) # to suppress unread configuration warning
|
26
29
|
@map[k] = v
|
27
30
|
}
|
31
|
+
# <record></record> directive
|
32
|
+
conf.elements.select { |element| element.name == 'record' }.each { |element|
|
33
|
+
element.each_pair { |k, v|
|
34
|
+
element.has_key?(k) # to suppress unread configuration warning
|
35
|
+
@map[k] = v
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
if @remove_keys
|
40
|
+
@remove_keys = @remove_keys.split(',')
|
41
|
+
end
|
42
|
+
|
43
|
+
@expand_placeholder_proc =
|
44
|
+
if @enable_ruby
|
45
|
+
Proc.new {|str, record, tag, tag_parts, time| expand_ruby_placeholder(str, record, tag, tag_parts, time) }
|
46
|
+
else
|
47
|
+
Proc.new {|str, record, tag, tag_parts, time| expand_placeholder(str, record, tag, tag_parts, time) }
|
48
|
+
end
|
49
|
+
|
50
|
+
@time_proc = # hmm, want to remove ${time} placeholder ...
|
51
|
+
if @enable_ruby
|
52
|
+
Proc.new {|time| Time.at(time) }
|
53
|
+
else
|
54
|
+
Proc.new {|time| time }
|
55
|
+
end
|
28
56
|
|
29
57
|
@hostname = Socket.gethostname
|
30
58
|
end
|
@@ -32,23 +60,58 @@ module Fluent
|
|
32
60
|
def emit(tag, es, chain)
|
33
61
|
tag_parts = tag.split('.')
|
34
62
|
es.each { |time, record|
|
35
|
-
t_time =
|
36
|
-
output_tag =
|
63
|
+
t_time = @time_proc.call(time)
|
64
|
+
output_tag = @expand_placeholder_proc.call(@output_tag, record, tag, tag_parts, t_time)
|
37
65
|
Engine.emit(output_tag, time, replace_record(record, tag, tag_parts, t_time))
|
38
66
|
}
|
39
67
|
chain.next
|
40
68
|
rescue => e
|
41
|
-
$log.warn e.message
|
42
|
-
$log.warn e.backtrace.join(', ')
|
69
|
+
$log.warn "record_reformer: #{e.class} #{e.message} #{e.backtrace.join(', ')}"
|
43
70
|
end
|
44
71
|
|
45
72
|
private
|
46
73
|
|
47
74
|
def replace_record(record, tag, tag_parts, time)
|
75
|
+
new_record = @renew_record ? {} : record.dup
|
48
76
|
@map.each_pair { |k, v|
|
49
|
-
|
77
|
+
new_record[k] = @expand_placeholder_proc.call(v, record, tag, tag_parts, time)
|
50
78
|
}
|
51
|
-
|
79
|
+
@remove_keys.each { |k| new_record.delete(k) } if @remove_keys
|
80
|
+
new_record
|
81
|
+
end
|
82
|
+
|
83
|
+
def expand_placeholder(str, record, tag, tag_parts, time)
|
84
|
+
# referenced https://github.com/fluent/fluent-plugin-rewrite-tag-filter, thanks!
|
85
|
+
placeholders = get_placeholders(record, tag, tag_parts, time)
|
86
|
+
str.gsub(/(\${[a-z_]+(\[-?[0-9]+\])?}|__[A-Z_]+__)/) do
|
87
|
+
$log.warn "record_reformer: unknown placeholder `#{$1}` found in a tag `#{tag}`" unless placeholders.include?($1)
|
88
|
+
placeholders[$1]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def get_placeholders(record, tag, tag_parts, time)
|
93
|
+
placeholders = {
|
94
|
+
'${time}' => time,
|
95
|
+
'${tag}' => tag,
|
96
|
+
'${hostname}' => @hostname,
|
97
|
+
}
|
98
|
+
|
99
|
+
size = tag_parts.size
|
100
|
+
tag_parts.each_with_index do |t, idx|
|
101
|
+
placeholders.store("${tag_parts[#{idx}]}", t)
|
102
|
+
placeholders.store("${tag_parts[#{idx-size}]}", t) # support tag_parts[-1]
|
103
|
+
end
|
104
|
+
# tags is just for old version compatibility
|
105
|
+
tag_parts.each_with_index do |t, idx|
|
106
|
+
placeholders.store("${tags[#{idx}]}", t)
|
107
|
+
placeholders.store("${tags[#{idx-size}]}", t) # support tags[-1]
|
108
|
+
end
|
109
|
+
|
110
|
+
record.each {|k, v|
|
111
|
+
placeholders.store("${#{k}}", v)
|
112
|
+
}
|
113
|
+
|
114
|
+
return placeholders
|
52
115
|
end
|
53
116
|
|
54
117
|
# Replace placeholders in a string
|
@@ -58,7 +121,7 @@ module Fluent
|
|
58
121
|
# @param [String] tag the tag
|
59
122
|
# @param [Array] tag_parts the tag parts (tag splitted by .)
|
60
123
|
# @param [Time] time the time
|
61
|
-
def
|
124
|
+
def expand_ruby_placeholder(str, record, tag, tag_parts, time)
|
62
125
|
struct = UndefOpenStruct.new(record)
|
63
126
|
struct.tag = tag
|
64
127
|
struct.tags = struct.tag_parts = tag_parts # tags is for old version compatibility
|
@@ -75,5 +75,106 @@ describe Fluent::RecordReformerOutput do
|
|
75
75
|
end
|
76
76
|
it { emit }
|
77
77
|
end
|
78
|
+
|
79
|
+
context 'record directive' do
|
80
|
+
let(:config) {%[
|
81
|
+
type reformed
|
82
|
+
output_tag reformed.${tag}
|
83
|
+
|
84
|
+
<record>
|
85
|
+
hostname ${hostname}
|
86
|
+
output_tag ${tag}
|
87
|
+
time ${time.strftime('%S')}
|
88
|
+
message ${hostname} ${tag_parts.last} ${message}
|
89
|
+
</record>
|
90
|
+
]}
|
91
|
+
before do
|
92
|
+
Fluent::Engine.stub(:now).and_return(time)
|
93
|
+
Fluent::Engine.should_receive(:emit).with("reformed.#{tag}", time.to_i, {
|
94
|
+
'foo' => 'bar',
|
95
|
+
'hostname' => hostname,
|
96
|
+
'output_tag' => tag,
|
97
|
+
'time' => time.strftime('%S'),
|
98
|
+
'message' => "#{hostname} #{tag_parts.last} 1",
|
99
|
+
})
|
100
|
+
Fluent::Engine.should_receive(:emit).with("reformed.#{tag}", time.to_i, {
|
101
|
+
'foo' => 'bar',
|
102
|
+
'hostname' => hostname,
|
103
|
+
'output_tag' => tag,
|
104
|
+
'time' => time.strftime('%S'),
|
105
|
+
'message' => "#{hostname} #{tag_parts.last} 2",
|
106
|
+
})
|
107
|
+
end
|
108
|
+
it { emit }
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'remove_keys' do
|
112
|
+
let(:config) { CONFIG + %[remove_keys foo,message] }
|
113
|
+
before do
|
114
|
+
Fluent::Engine.stub(:now).and_return(time)
|
115
|
+
Fluent::Engine.should_receive(:emit).with("reformed.#{tag}", time.to_i, {
|
116
|
+
'hostname' => hostname,
|
117
|
+
'tag' => tag,
|
118
|
+
'time' => time.strftime('%S'),
|
119
|
+
})
|
120
|
+
Fluent::Engine.should_receive(:emit).with("reformed.#{tag}", time.to_i, {
|
121
|
+
'hostname' => hostname,
|
122
|
+
'tag' => tag,
|
123
|
+
'time' => time.strftime('%S'),
|
124
|
+
})
|
125
|
+
end
|
126
|
+
it { emit }
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'renew_record true' do
|
130
|
+
let(:config) { CONFIG + %[renew_record true] }
|
131
|
+
before do
|
132
|
+
Fluent::Engine.stub(:now).and_return(time)
|
133
|
+
Fluent::Engine.should_receive(:emit).with("reformed.#{tag}", time.to_i, {
|
134
|
+
'hostname' => hostname,
|
135
|
+
'tag' => tag,
|
136
|
+
'time' => time.strftime('%S'),
|
137
|
+
'message' => "#{hostname} #{tag_parts.last} 1",
|
138
|
+
})
|
139
|
+
Fluent::Engine.should_receive(:emit).with("reformed.#{tag}", time.to_i, {
|
140
|
+
'hostname' => hostname,
|
141
|
+
'tag' => tag,
|
142
|
+
'time' => time.strftime('%S'),
|
143
|
+
'message' => "#{hostname} #{tag_parts.last} 2",
|
144
|
+
})
|
145
|
+
end
|
146
|
+
it { emit }
|
147
|
+
end
|
148
|
+
|
149
|
+
context 'enable_ruby no' do
|
150
|
+
let(:config) {%[
|
151
|
+
type reformed
|
152
|
+
output_tag reformed.${tag}
|
153
|
+
enable_ruby no
|
154
|
+
|
155
|
+
hostname ${hostname}
|
156
|
+
tag ${tag}
|
157
|
+
time ${time}
|
158
|
+
message ${hostname} ${tag_parts[-1]} ${message}
|
159
|
+
]}
|
160
|
+
before do
|
161
|
+
Fluent::Engine.stub(:now).and_return(time)
|
162
|
+
Fluent::Engine.should_receive(:emit).with("reformed.#{tag}", time.to_i, {
|
163
|
+
'foo' => 'bar',
|
164
|
+
'hostname' => hostname,
|
165
|
+
'tag' => tag,
|
166
|
+
'time' => time.to_i.to_s, # hmm, want to remove ${time} placeholder
|
167
|
+
'message' => "#{hostname} #{tag_parts.last} 1",
|
168
|
+
})
|
169
|
+
Fluent::Engine.should_receive(:emit).with("reformed.#{tag}", time.to_i, {
|
170
|
+
'foo' => 'bar',
|
171
|
+
'hostname' => hostname,
|
172
|
+
'tag' => tag,
|
173
|
+
'time' => time.to_i.to_s, # hmm, want to remove ${time} placeholder
|
174
|
+
'message' => "#{hostname} #{tag_parts.last} 2",
|
175
|
+
})
|
176
|
+
end
|
177
|
+
it { emit }
|
178
|
+
end
|
78
179
|
end
|
79
180
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-record-reformer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Naotoshi Seo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-01-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fluentd
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - '>='
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry-nav
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
description: Output filter plugin for reforming each event record
|
70
84
|
email: sonots@gmail.com
|
71
85
|
executables: []
|