fluent-plugin-records-merger 0.1.0 → 0.1.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 +4 -4
- data/.rubocop.yml +18 -0
- data/example/README.md +22 -0
- data/example/fluent.conf +22 -0
- data/example/word1.json +1 -0
- data/example/word2.json +1 -0
- data/example/word3.json +1 -0
- data/fluent-plugin-records-merger.gemspec +2 -2
- data/lib/fluent/plugin/out_records_merger.rb +26 -95
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8eb7fbf8e02b7ce0c3f820c4ddbaa27526d6d43e409912d14f19ce5807220787
|
4
|
+
data.tar.gz: 192ade91d03b5233185f84dae69a206efb4a211ecc9bb87ed8c728d34ccfdeea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc2af7b4a0929a32fce8b0fbc296428cdc81da53153e31ba92928865698e9dcfd58fb11780b836899ff00d01e1e8a5189093ceeab203674420ac4203ebc50688
|
7
|
+
data.tar.gz: 5c0f1ea3554704a93b4987767b51b88c8141c6463266fb644b766fc094b55caa35122d379119278895299df6ca12c9ef5bb0d125e3e6ed92d1188cd0c91141de
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Style/AsciiComments:
|
2
|
+
Enabled: false
|
3
|
+
Metrics/LineLength:
|
4
|
+
Max: 140
|
5
|
+
Naming/MethodParameterName:
|
6
|
+
Enabled: false
|
7
|
+
Metrics/AbcSize:
|
8
|
+
Max: 30
|
9
|
+
Metrics/MethodLength:
|
10
|
+
Max: 30
|
11
|
+
Metrics/PerceivedComplexity:
|
12
|
+
Max: 10
|
13
|
+
Metrics/CyclomaticComplexity:
|
14
|
+
Max: 10
|
15
|
+
Metrics/BlockLength:
|
16
|
+
Max: 45
|
17
|
+
Metrics/ClassLength:
|
18
|
+
Max: 250
|
data/example/README.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
## 動かす
|
2
|
+
|
3
|
+
|
4
|
+
```cassandraql
|
5
|
+
fluentd -c example/fluent.conf -p lib/fluent/plugin
|
6
|
+
```
|
7
|
+
|
8
|
+
## 動作を確認する
|
9
|
+
|
10
|
+
```cassandraql
|
11
|
+
cat worw2.json | fluent-cat w.sub_word1
|
12
|
+
cat word1.json | fluent-cat w.main_word
|
13
|
+
|
14
|
+
# 条件にマッチするのでemitされる
|
15
|
+
|
16
|
+
|
17
|
+
cat worw3.json | fluent-cat w.sub_word1
|
18
|
+
cat word1.json | fluent-cat w.main_word
|
19
|
+
|
20
|
+
# 条件にマッチしないのでemitされない
|
21
|
+
|
22
|
+
```
|
data/example/fluent.conf
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
<source>
|
2
|
+
@type forward
|
3
|
+
port 24224
|
4
|
+
</source>
|
5
|
+
|
6
|
+
<match w.**>
|
7
|
+
@type records_merger
|
8
|
+
tag result
|
9
|
+
merge_timing before
|
10
|
+
main_tag w.main_word
|
11
|
+
sub_tag1 w.sub_word1
|
12
|
+
condition sub1["word"] == "hello"
|
13
|
+
<record>
|
14
|
+
main ${main["word"]}
|
15
|
+
sub ${sub1["word"]}
|
16
|
+
</record>
|
17
|
+
</match>
|
18
|
+
|
19
|
+
|
20
|
+
<match result>
|
21
|
+
@type stdout
|
22
|
+
</match>
|
data/example/word1.json
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
{"word": "aaaaaaaaa"}
|
data/example/word2.json
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
{"word": "hello"}
|
data/example/word3.json
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
{"word": "not_hello"}
|
@@ -5,13 +5,13 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = 'fluent-plugin-records-merger'
|
8
|
-
spec.version = '0.1.
|
8
|
+
spec.version = '0.1.1'
|
9
9
|
spec.authors = ['Nobuyuki Oishi', 'WallyNegima']
|
10
10
|
spec.email = ['u.str.gm@gmail.com']
|
11
11
|
|
12
12
|
spec.summary = 'this is fluentd plugin. some records merge and out.'
|
13
13
|
spec.description = 'Parallels data is merged and out.'
|
14
|
-
spec.homepage =
|
14
|
+
spec.homepage = 'https://github.com/WallyNegima/fluent-plugin-records-merger'
|
15
15
|
spec.license = 'Apache-2.0'
|
16
16
|
|
17
17
|
test_files, files = `git ls-files -z`.split("\x0").partition do |f|
|
@@ -19,6 +19,7 @@ require 'fluent/plugin/output'
|
|
19
19
|
|
20
20
|
module Fluent
|
21
21
|
module Plugin
|
22
|
+
# fluentd records merger
|
22
23
|
class RecordsMergerOutput < Fluent::Plugin::Output
|
23
24
|
Fluent::Plugin.register_output('records_merger', self)
|
24
25
|
helpers :event_emitter
|
@@ -28,67 +29,56 @@ module Fluent
|
|
28
29
|
desc: 'Specify the output tag'
|
29
30
|
config_param :main_tag, :string,
|
30
31
|
desc: 'Specify main_tag name'
|
31
|
-
|
32
|
-
desc: 'Specify sub_tag name'
|
33
|
-
(2..PATTERN_MAX_NUM).each do |i|
|
32
|
+
(1..PATTERN_MAX_NUM).each do |i|
|
34
33
|
config_param ('sub_tag' + i.to_s).to_sym, :string, default: nil, # NAME REGEXP
|
35
34
|
desc: 'Specify sub_tag name'
|
36
35
|
end
|
37
36
|
config_param :auto_typecast, :bool, default: true, # false for lower version compatibility
|
38
37
|
desc: 'Automatically cast the field types.'
|
39
|
-
config_param :merge_timing, :enum, list: %i[before after simple], default: '
|
38
|
+
config_param :merge_timing, :enum, list: %i[before after simple], default: 'simple',
|
40
39
|
desc: 'Choose merge timing before/after'
|
41
40
|
# ↓ これ、なんかエラー出てるっぽい。修正する。
|
42
41
|
config_param :tolerable_time_range, :integer, default: nil,
|
43
42
|
desc: 'Specify tolerable time range for merging'
|
43
|
+
|
44
|
+
config_param :condition, :string, default: nil, desc: 'emit if condition is true '
|
45
|
+
|
44
46
|
# 以下、未実装
|
45
47
|
config_param :force_emit, :bool, default: false,
|
46
48
|
desc: 'Specify to emit or not when exceeded the tolerable_time_range'
|
47
49
|
|
48
50
|
def configure(conf)
|
49
51
|
super
|
50
|
-
# p 'in configure()'
|
51
|
-
# p conf
|
52
52
|
# conf: {"@type"=>"records_merger", "tag"=>"merged1", "main_tag"=>"accesslog.1.main", "sub_tag1"=>"accesslog.1.sub1", "sub_tag2"=>"accesslog.1.sub2"}, []
|
53
53
|
@tag = conf['tag'] # これも,結局 ${tag} とかを有効にする関係で使わなくなりそう.
|
54
54
|
# set the tags set by the user
|
55
|
+
@last_records = {}
|
55
56
|
@main_tag = conf['main_tag']
|
57
|
+
|
56
58
|
@sub_tags = []
|
57
59
|
(1..PATTERN_MAX_NUM).each do |i|
|
58
60
|
next unless conf["sub_tag#{i}"]
|
59
61
|
|
60
62
|
@sub_tags.push(conf["sub_tag#{i}"])
|
61
63
|
end
|
64
|
+
|
62
65
|
if conf['merge_timing']
|
63
66
|
@merge_timing = conf['merge_timing']
|
64
|
-
p '@merge_timing is ' + @merge_timing.to_s
|
65
|
-
else
|
66
|
-
p 'Specify merge_timing before/after'
|
67
67
|
end
|
68
68
|
|
69
69
|
@tolerable_time_range = conf['tolerable_time_range'].to_i
|
70
70
|
|
71
|
-
@last_records = {} # main を流した後に削除する? (未実装)
|
72
|
-
|
73
|
-
p 'size of @sub_tags is ' + @sub_tags.size.to_s
|
74
71
|
@flags4merge = Array.new(@sub_tags.size + 1, 0)
|
75
|
-
# p @flags4merge # [0, 0,..., 0]
|
76
|
-
|
77
72
|
@force_emit = conf['force_emit']
|
78
73
|
|
79
74
|
# from out-record-reformer
|
80
75
|
map = {}
|
81
|
-
# load <record></record> directive
|
82
76
|
conf.elements.select { |element| element.name == 'record' }.each do |element|
|
83
77
|
element.each_pair do |k, v|
|
84
|
-
# p k
|
85
|
-
# p v
|
86
78
|
element.key?(k) # to suppress unread configuration warning
|
87
79
|
map[k] = parse_value(v)
|
88
80
|
end
|
89
81
|
end
|
90
|
-
# p 'print map below'
|
91
|
-
# p map
|
92
82
|
|
93
83
|
placeholder_expander_params = {
|
94
84
|
log: log,
|
@@ -98,6 +88,7 @@ module Fluent
|
|
98
88
|
@map = @placeholder_expander.preprocess_map(map)
|
99
89
|
@tag = @placeholder_expander.preprocess_map(@tag)
|
100
90
|
@hostname = Socket.gethostname
|
91
|
+
@condition = conf['condition']
|
101
92
|
end
|
102
93
|
|
103
94
|
def start
|
@@ -109,18 +100,12 @@ module Fluent
|
|
109
100
|
end
|
110
101
|
|
111
102
|
def process(tag, es)
|
112
|
-
# p 'in process(), merge_timing is '+@merge_timing.to_s
|
113
103
|
if @merge_timing == 'before'
|
114
|
-
# p 'merge timing is before'
|
115
104
|
if @main_tag == tag
|
116
|
-
# p 'main tag has come'
|
117
105
|
store_to_lastrecords(tag, es)
|
118
106
|
@flags4merge[0] = 1
|
119
|
-
# p 'store_to_lastrecords() seems to have no problems'
|
120
107
|
check_timegap if @tolerable_time_range > 0
|
121
|
-
# pp @flags4merge
|
122
108
|
if @flags4merge.all? { |flag| flag == 1 }
|
123
|
-
# pp 'Great! Now you can omit merged event!'
|
124
109
|
emit_new_event(es)
|
125
110
|
else
|
126
111
|
(0..@flags4merge.length - 1).each do |i|
|
@@ -131,53 +116,35 @@ module Fluent
|
|
131
116
|
store_to_lastrecords(tag, es)
|
132
117
|
@flags4merge[@sub_tags.index(tag) + 1] = 1
|
133
118
|
check_timegap if @tolerable_time_range > 0
|
134
|
-
# p @flags4merge
|
135
119
|
end
|
136
120
|
elsif @merge_timing == 'after'
|
137
|
-
# p 'merge timing is after'
|
138
121
|
if @main_tag == tag
|
139
122
|
store_to_lastrecords(tag, es)
|
140
123
|
@flags4merge[0] = 1
|
141
124
|
check_timegap if @tolerable_time_range > 0
|
142
|
-
# pp @flags4merge
|
143
125
|
elsif @sub_tags.include?(tag)
|
144
126
|
if @flags4merge[0] == 1
|
145
127
|
store_to_lastrecords(tag, es)
|
146
128
|
@flags4merge[@sub_tags.index(tag) + 1] = 1
|
147
129
|
check_timegap if @tolerable_time_range > 0
|
148
|
-
|
149
|
-
if @flags4merge.all? { |flag| flag == 1 }
|
150
|
-
# pp 'Great! Now you can omit merged event!'
|
151
|
-
emit_new_event(es)
|
152
|
-
end
|
130
|
+
emit_new_event(es) if @flags4merge.all? {|flag| flag == 1}
|
153
131
|
else
|
154
132
|
(0..@flags4merge.length - 1).each do |i|
|
155
133
|
@flags4merge[i] = 0
|
156
134
|
end
|
157
|
-
# p @flags4merge
|
158
135
|
end
|
159
136
|
else
|
160
137
|
p 'something else has come! check it!' + tag.to_s
|
161
138
|
end
|
162
139
|
elsif @merge_timing == 'simple'
|
163
|
-
# p 'merge timing is simple'
|
164
140
|
if @main_tag == tag
|
165
|
-
# p "main_tag_came"
|
166
141
|
store_to_lastrecords(tag, es)
|
167
|
-
# p 'after store_to_lastrecords()'
|
168
142
|
@flags4merge[0] = 1
|
169
143
|
check_timegap if @tolerable_time_range > 0
|
170
|
-
|
171
|
-
if @flags4merge.all? { |flag| flag == 1 }
|
172
|
-
# p 'before emit_new event()'
|
173
|
-
emit_new_event(es)
|
174
|
-
# p 'after emit_new event()'
|
175
|
-
|
176
|
-
end
|
144
|
+
emit_new_event(es) if @flags4merge.all? { |flag| flag == 1 }
|
177
145
|
elsif @sub_tags.include?(tag)
|
178
146
|
store_to_lastrecords(tag, es)
|
179
147
|
@flags4merge[@sub_tags.index(tag) + 1] = 1
|
180
|
-
# p @flags4merge
|
181
148
|
check_timegap if @tolerable_time_range > 0
|
182
149
|
emit_new_event(es) if @flags4merge.all? { |flag| flag == 1 }
|
183
150
|
else
|
@@ -186,7 +153,7 @@ module Fluent
|
|
186
153
|
end
|
187
154
|
rescue StandardError => e
|
188
155
|
log.warn "record_reformer: #{e.class} #{e.message} #{e.backtrace.first}"
|
189
|
-
log.debug "record_reformer: tag:#{@tag} map:#{@map} record:#{last_records} placeholder_values
|
156
|
+
log.debug "record_reformer: tag:#{@tag} map:#{@map} record:#{@last_records} placeholder_values"
|
190
157
|
end
|
191
158
|
|
192
159
|
# そのまま持ってきてしまっているので,rewriteしたほうが良さそう?
|
@@ -195,26 +162,17 @@ module Fluent
|
|
195
162
|
|
196
163
|
def store_to_lastrecords(tag, es)
|
197
164
|
es.each do |time, record|
|
198
|
-
# p 'in store_to_lastrecords()'
|
199
|
-
# print 'time: '
|
200
|
-
# p time
|
201
|
-
# print 'record: '
|
202
|
-
# p record
|
203
165
|
record.store('time', time)
|
204
166
|
@last_records.store(tag, record)
|
205
|
-
# これで,last_recordsにはsub_tagの情報がそのまま入ってる.ちなみに,storeはkey, value
|
206
|
-
# ここはそのままでも問題なくて,mainが来たときにまとめて調整すれば良い.
|
167
|
+
# これで,last_recordsにはsub_tagの情報がそのまま入ってる.ちなみに,storeはkey, valueの形でハッシュに値を入れるために使う.´
|
207
168
|
end
|
208
169
|
end
|
209
170
|
|
210
171
|
def generate_placeholders
|
211
172
|
# here, tag can be used directly only because it is the main tag
|
212
173
|
main_tag_parts = @main_tag.split('.')
|
213
|
-
# p tag_parts
|
214
174
|
main_tag_prefix = tag_prefix(main_tag_parts)
|
215
|
-
# p tag_prefix
|
216
175
|
main_tag_suffix = tag_suffix(main_tag_parts)
|
217
|
-
# p tag_suffix
|
218
176
|
|
219
177
|
placeholder_values = {
|
220
178
|
'main' => @last_records[@main_tag],
|
@@ -248,46 +206,43 @@ module Fluent
|
|
248
206
|
end
|
249
207
|
|
250
208
|
def emit_new_event(es)
|
251
|
-
# p "in emit_new_event"
|
252
209
|
es.each do |time, _record|
|
253
210
|
placeholder_values = generate_placeholders
|
254
211
|
message = @last_records
|
255
|
-
# print "message:"
|
256
|
-
# p @last_records
|
257
212
|
next if message.keys.empty?
|
258
213
|
|
259
214
|
new_tag, new_record = reform(@tag, @last_records, placeholder_values)
|
260
215
|
next unless new_tag
|
261
216
|
|
217
|
+
unless @condition.nil?
|
218
|
+
# conditionがOKじゃなければemiしない
|
219
|
+
next unless condition_is_ok?(placeholder_values)
|
220
|
+
end
|
221
|
+
|
262
222
|
router.emit(new_tag, time, new_record)
|
263
223
|
# flagの初期化
|
264
224
|
(0..@flags4merge.length - 1).each do |i|
|
265
225
|
@flags4merge[i] = 0
|
266
226
|
end
|
267
|
-
# p @flags4merge
|
268
|
-
# router.emit(@tag, time, message)
|
269
227
|
end
|
270
228
|
end
|
271
229
|
|
230
|
+
def condition_is_ok?(placeholder_values)
|
231
|
+
placeholders = @placeholder_expander.prepare_placeholders(placeholder_values)
|
232
|
+
instance_eval(@condition.gsub(/((main\["\w+"\])|(sub[0-9]{1,2}\["\w+"\]))/, 'placeholders[\'${\1}\']'))
|
233
|
+
end
|
234
|
+
|
272
235
|
def check_timegap
|
273
|
-
p @last_records.size
|
274
236
|
tag_time_hash = {}
|
275
237
|
if @last_records.size > 1
|
276
238
|
@last_records.each do |k, v|
|
277
|
-
# p k
|
278
|
-
# p v['time'].sec
|
279
|
-
# p v['time'].nsec # ナノセカンドでの比較は余裕があったら実装する
|
280
239
|
tag_time_hash.store(k, v['time'].sec)
|
281
240
|
end
|
282
241
|
# ここでtag_nameと一緒に保持しておきたい
|
283
242
|
latest = tag_time_hash.max { |a, b| a[1] <=> b[1] }
|
284
|
-
# p latest
|
285
243
|
oldest = tag_time_hash.min { |a, b| a[1] <=> b[1] }
|
286
|
-
# p oldest
|
287
|
-
# p @last_records
|
288
244
|
|
289
245
|
if latest[1] - oldest[1] > @tolerable_time_range
|
290
|
-
p oldest[0].to_s + ' is too old! so deleted'
|
291
246
|
@last_records.delete(oldest[0])
|
292
247
|
if oldest[0] == @main_tag
|
293
248
|
@flags4merge[0] = 0
|
@@ -310,37 +265,21 @@ module Fluent
|
|
310
265
|
end
|
311
266
|
|
312
267
|
def reform(tag, _record, placeholder_values)
|
313
|
-
# p 'in reform()'
|
314
|
-
# p 'tag: '
|
315
|
-
# pp tag
|
316
|
-
# p 'record'
|
317
|
-
# pp record
|
318
268
|
placeholders = @placeholder_expander.prepare_placeholders(placeholder_values)
|
319
|
-
# p 'placeholders'
|
320
|
-
# pp placeholders
|
321
269
|
|
322
270
|
new_tag = expand_placeholders(tag, placeholders) # 新しいtagを生成してる
|
323
|
-
# p 'new_tag'
|
324
|
-
# p new_tag
|
325
271
|
|
326
272
|
# 基本的に,全部ゼロから作るからここでdupする必要はない.
|
327
273
|
# new_record = @renew_record ? {} : record.dup # dupは新しいのを作ってコピーするやつ
|
328
274
|
# @keep_keys.each {|k| new_record[k] = record[k]} if @keep_keys and @renew_record
|
329
275
|
|
330
276
|
new_record = {}
|
331
|
-
# p '@map' # ここには,<record></record>で指定したものが入ってる.
|
332
|
-
# pp @map
|
333
277
|
new_record.merge!(expand_placeholders(@map, placeholders))
|
334
|
-
# p 'new_record'
|
335
|
-
# pp new_record
|
336
278
|
|
337
279
|
[new_tag, new_record]
|
338
280
|
end
|
339
281
|
|
340
282
|
def expand_placeholders(value, placeholders)
|
341
|
-
# p 'in expand_placeholders()'
|
342
|
-
# pp value
|
343
|
-
# pp placeholders
|
344
283
|
if value.is_a?(String)
|
345
284
|
new_value = @placeholder_expander.expand(value, placeholders)
|
346
285
|
elsif value.is_a?(Hash)
|
@@ -403,11 +342,9 @@ module Fluent
|
|
403
342
|
end
|
404
343
|
|
405
344
|
def prepare_placeholders(placeholder_values)
|
406
|
-
# p 'in prepare_placeholders()'
|
407
345
|
placeholders = {}
|
408
346
|
|
409
347
|
placeholder_values.each do |key, value|
|
410
|
-
# p key
|
411
348
|
if value.is_a?(Array) # tag_parts, etc
|
412
349
|
size = value.size
|
413
350
|
value.each_with_index do |v, idx|
|
@@ -416,17 +353,13 @@ module Fluent
|
|
416
353
|
end
|
417
354
|
elsif value.is_a?(Hash) # record, etc
|
418
355
|
value.each do |k, v|
|
419
|
-
unless placeholder_values.key?(k) # prevent overwriting the reserved keys such as tag
|
420
|
-
placeholders.store("${#{k}}", v)
|
421
|
-
end
|
356
|
+
placeholders.store("${#{k}}", v) unless placeholder_values.key?(k) # prevent overwriting the reserved keys such as tag
|
422
357
|
placeholders.store(%(${#{key}["#{k}"]}), v) # record["foo"]
|
423
358
|
end
|
424
359
|
else # string, interger, float, and others?
|
425
360
|
placeholders.store("${#{key}}", value)
|
426
361
|
end
|
427
362
|
end
|
428
|
-
# pp placeholders
|
429
|
-
# p 'end of prepare_placeholders()'
|
430
363
|
placeholders
|
431
364
|
end
|
432
365
|
|
@@ -451,9 +384,7 @@ module Fluent
|
|
451
384
|
private
|
452
385
|
|
453
386
|
def log_if_unknown_placeholder(placeholder, placeholders)
|
454
|
-
unless placeholders.include?(placeholder)
|
455
|
-
log.warn "record_reformer: unknown placeholder `#{placeholder}` found"
|
456
|
-
end
|
387
|
+
log.warn "record_reformer: unknown placeholder `#{placeholder}` found" unless placeholders.include?(placeholder)
|
457
388
|
end
|
458
389
|
end
|
459
390
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-records-merger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nobuyuki Oishi
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2020-
|
12
|
+
date: 2020-03-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -81,10 +81,16 @@ extensions: []
|
|
81
81
|
extra_rdoc_files: []
|
82
82
|
files:
|
83
83
|
- ".gitignore"
|
84
|
+
- ".rubocop.yml"
|
84
85
|
- Gemfile
|
85
86
|
- LICENSE
|
86
87
|
- README.md
|
87
88
|
- Rakefile
|
89
|
+
- example/README.md
|
90
|
+
- example/fluent.conf
|
91
|
+
- example/word1.json
|
92
|
+
- example/word2.json
|
93
|
+
- example/word3.json
|
88
94
|
- fluent-plugin-records-merger.gemspec
|
89
95
|
- lib/fluent/plugin/out_records_merger.rb
|
90
96
|
- test/helper.rb
|