fluent-plugin-record-reformer 0.1.1 → 0.2.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.
- 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
|
-
[](http://travis-ci.org/sonots/fluent-plugin-record-reformer)
|
3
|
+
[](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: []
|