fluent-plugin-tail-multiline-ex 0.0.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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +32 -0
- data/Rakefile +1 -0
- data/fluent-plugin-tail-multiline-ex.gemspec +27 -0
- data/lib/fluent/plugin/in_tail_multiline_ex.rb +351 -0
- metadata +165 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 TODO: Write your name
|
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,32 @@
|
|
1
|
+
# fluent-tail-multiline-ex
|
2
|
+
|
3
|
+
Now under construction.
|
4
|
+
|
5
|
+
TODO: Write a gem description
|
6
|
+
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem 'fluent-plugin-tail-multiline-ex'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install fluent-plugin-tail-multiline-ex
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
TODO: Write usage instructions here
|
25
|
+
|
26
|
+
## Contributing
|
27
|
+
|
28
|
+
1. Fork it
|
29
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
30
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
31
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
32
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "fluent-plugin-tail-multiline-ex"
|
7
|
+
spec.version = "0.0.1"
|
8
|
+
spec.authors = ["Yoshiharu Mori"]
|
9
|
+
spec.email = ["y-mori@sraoss.co.jp"]
|
10
|
+
spec.description = %q{merge tail_ex and tail_multiline input plugin}
|
11
|
+
spec.summary = %q{merge tail_ex and tail_multiline input plugin}
|
12
|
+
spec.homepage = "https://github.com/y-mori0110/fluent-plugin-multiline-ex"
|
13
|
+
spec.license = "Apache 2.0"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
requires = ['fluentd', 'fluent-mixin-config-placeholders']
|
21
|
+
requires.each {|name| spec.add_runtime_dependency name}
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
|
25
|
+
requires += ['rake', 'flexmock']
|
26
|
+
requires.each {|name| spec.add_development_dependency name}
|
27
|
+
end
|
@@ -0,0 +1,351 @@
|
|
1
|
+
module Fluent
|
2
|
+
require 'fluent/plugin/in_tail'
|
3
|
+
require 'fluent/mixin/config_placeholders'
|
4
|
+
|
5
|
+
class TailMultilineInput_EX < TailInput
|
6
|
+
|
7
|
+
class MultilineTextParser_EX < TextParser
|
8
|
+
def configure(conf, required=true)
|
9
|
+
format = conf['format']
|
10
|
+
if format == nil
|
11
|
+
raise ConfigError, "'format' parameter is required"
|
12
|
+
elsif format[0] != ?/ || format[format.length-1] != ?/
|
13
|
+
raise ConfigError, "'format' should be RegEx. Template is not supported in multiline mode"
|
14
|
+
end
|
15
|
+
|
16
|
+
begin
|
17
|
+
@regex = Regexp.new(format[1..-2],Regexp::MULTILINE)
|
18
|
+
if @regex.named_captures.empty?
|
19
|
+
raise "No named captures"
|
20
|
+
end
|
21
|
+
rescue
|
22
|
+
raise ConfigError, "Invalid regexp in format '#{format[1..-2]}': #{$!}"
|
23
|
+
end
|
24
|
+
|
25
|
+
@parser = RegexpParser.new(@regex, conf)
|
26
|
+
|
27
|
+
format_firstline = conf['format_firstline']
|
28
|
+
if format_firstline
|
29
|
+
# Use custom matcher for 1st line
|
30
|
+
if format_firstline[0] == '/' && format_firstline[format_firstline.length-1] == '/'
|
31
|
+
@regex = Regexp.new(format_firstline[1..-2])
|
32
|
+
else
|
33
|
+
raise ConfigError, "Invalid regexp in format_firstline '#{format_firstline[1..-2]}': #{$!}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
return true
|
38
|
+
end
|
39
|
+
|
40
|
+
def match_firstline(text)
|
41
|
+
@regex.match(text)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
Plugin.register_input('tail_multiline_ex', self)
|
46
|
+
|
47
|
+
FORMAT_MAX_NUMS = 20
|
48
|
+
|
49
|
+
config_param :format, :string
|
50
|
+
config_param :format_firstline, :string, :default => nil
|
51
|
+
config_param :rawdata_key, :string, :default => nil
|
52
|
+
config_param :auto_flush_sec, :integer, :default => 1
|
53
|
+
config_param :read_newfile_from_head, :bool, :default => false
|
54
|
+
(1..FORMAT_MAX_NUMS).each do |i|
|
55
|
+
config_param "format#{i}".to_sym, :string, :default => nil
|
56
|
+
end
|
57
|
+
|
58
|
+
config_param :expand_date, :bool, :default => true
|
59
|
+
config_param :read_all, :bool, :default => true
|
60
|
+
config_param :refresh_interval, :integer, :default => 3600
|
61
|
+
|
62
|
+
include Fluent::Mixin::ConfigPlaceholders
|
63
|
+
|
64
|
+
def initialize
|
65
|
+
super
|
66
|
+
@locker = Monitor.new
|
67
|
+
@logbuf = nil
|
68
|
+
@logbuf_flusher = CallLater_EX::new
|
69
|
+
@ready = false
|
70
|
+
end
|
71
|
+
|
72
|
+
def configure(conf)
|
73
|
+
if conf['format'].nil?
|
74
|
+
invalids = conf.keys.select{|k| k =~ /^format(\d+)$/ and not (1..FORMAT_MAX_NUMS).include?($1.to_i)}
|
75
|
+
if invalids.size > 0
|
76
|
+
raise ConfigError, "invalid number formats (valid format number:1-#{FORMAT_MAX_NUMS}):" + invalids.join(",")
|
77
|
+
end
|
78
|
+
format_index_list = conf.keys.select{|s| s =~ /^format\d+$/}.map{|v| (/^format(\d+)$/.match(v))[1].to_i}
|
79
|
+
if (1..format_index_list.max).map{|i| conf["format#{i}"]}.include?(nil)
|
80
|
+
raise Fluent::ConfigError, "jump of format index found"
|
81
|
+
end
|
82
|
+
formats = (1..FORMAT_MAX_NUMS).map {|i|
|
83
|
+
conf["format#{i}"]
|
84
|
+
}.delete_if {|format|
|
85
|
+
format.nil?
|
86
|
+
}.map {|format|
|
87
|
+
format[1..-2]
|
88
|
+
}.join
|
89
|
+
conf['format'] = '/' + formats + '/'
|
90
|
+
end
|
91
|
+
super
|
92
|
+
if @tag.index('*')
|
93
|
+
@tag_prefix, @tag_suffix = @tag.split('*')
|
94
|
+
@tag_suffix ||= ''
|
95
|
+
else
|
96
|
+
@tag_prefix = nil
|
97
|
+
@tag_suffix = nil
|
98
|
+
end
|
99
|
+
@watchers = {}
|
100
|
+
@refresh_trigger = TailWatcher::TimerWatcher.new(@refresh_interval, true, &method(:refresh_watchers))
|
101
|
+
if read_newfile_from_head and @pf
|
102
|
+
# Tread new file as rotated file
|
103
|
+
# Use temp file inode number as previos logfile
|
104
|
+
@paths.map {|path|
|
105
|
+
pe = @pf[path]
|
106
|
+
if pe.read_inode == 0
|
107
|
+
require 'tempfile'
|
108
|
+
tmpfile = Tempfile.new('gettempinode')
|
109
|
+
pe.update(File.stat(tmpfile).ino, 0)
|
110
|
+
tmpfile.unlink
|
111
|
+
end
|
112
|
+
}
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def expand_paths
|
117
|
+
date = Time.now
|
118
|
+
paths = []
|
119
|
+
for path in @paths
|
120
|
+
if @expand_date
|
121
|
+
path = date.strftime(path)
|
122
|
+
end
|
123
|
+
paths += Dir.glob(path)
|
124
|
+
end
|
125
|
+
paths
|
126
|
+
end
|
127
|
+
|
128
|
+
def refresh_watchers
|
129
|
+
paths = expand_paths
|
130
|
+
missing = @watchers.keys - paths
|
131
|
+
added = paths - @watchers.keys
|
132
|
+
|
133
|
+
stop_watch(missing) unless missing.empty?
|
134
|
+
start_watch(added) unless added.empty?
|
135
|
+
end
|
136
|
+
|
137
|
+
def start_watch(paths)
|
138
|
+
paths.each do |path|
|
139
|
+
if @pf
|
140
|
+
pe = @pf[path]
|
141
|
+
if @read_all && pe.read_inode == 0
|
142
|
+
inode = File::Stat.new(path).ino
|
143
|
+
pe.update(inode, 0)
|
144
|
+
end
|
145
|
+
else
|
146
|
+
pe = nil
|
147
|
+
end
|
148
|
+
|
149
|
+
watcher = TailExWatcher_EX.new(path, @rotate_wait, pe, &method(:receive_lines))
|
150
|
+
watcher.attach(@loop)
|
151
|
+
@watchers[path] = watcher
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def stop_watch(paths, immediate=false)
|
156
|
+
paths.each do |path|
|
157
|
+
watcher = @watchers.delete(path)
|
158
|
+
if watcher
|
159
|
+
watcher.close(immediate ? nil : @loop)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def start
|
165
|
+
paths, @paths = @paths, []
|
166
|
+
super
|
167
|
+
@thread.join
|
168
|
+
@paths = paths
|
169
|
+
refresh_watchers
|
170
|
+
@refresh_trigger.attach(@loop)
|
171
|
+
@ready = true
|
172
|
+
@thread = Thread.new(&method(:run))
|
173
|
+
end
|
174
|
+
|
175
|
+
def run
|
176
|
+
# don't run unless ready to avoid coolio error
|
177
|
+
if @ready
|
178
|
+
super
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def configure_parser(conf)
|
183
|
+
@parser = MultilineTextParser_EX.new
|
184
|
+
@parser.configure(conf)
|
185
|
+
end
|
186
|
+
|
187
|
+
def receive_lines(lines,tag)
|
188
|
+
if @tag_prefix || @tag_suffix
|
189
|
+
@tag = @tag_prefix + tag + @tag_suffix
|
190
|
+
end
|
191
|
+
@logbuf_flusher.cancel()
|
192
|
+
es = MultiEventStream.new
|
193
|
+
@locker.synchronize do
|
194
|
+
lines.each {|line|
|
195
|
+
if @parser.match_firstline(line)
|
196
|
+
time, record = parse_logbuf(@logbuf)
|
197
|
+
if time && record
|
198
|
+
es.add(time, record)
|
199
|
+
end
|
200
|
+
@logbuf = line
|
201
|
+
else
|
202
|
+
@logbuf += line if(@logbuf)
|
203
|
+
end
|
204
|
+
}
|
205
|
+
end
|
206
|
+
unless es.empty?
|
207
|
+
begin
|
208
|
+
Engine.emit_stream(@tag, es)
|
209
|
+
rescue
|
210
|
+
# ignore errors. Engine shows logs and backtraces.
|
211
|
+
end
|
212
|
+
end
|
213
|
+
@logbuf_flusher.call_later(@auto_flush_sec) do
|
214
|
+
flush_logbuf()
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def shutdown
|
219
|
+
@refresh_trigger.detach
|
220
|
+
stop_watch(@watchers.keys, true)
|
221
|
+
@loop.stop
|
222
|
+
@thread.join
|
223
|
+
@pf_file.close if @pf_file
|
224
|
+
flush_logbuf()
|
225
|
+
@logbuf_flusher.shutdown()
|
226
|
+
end
|
227
|
+
|
228
|
+
def flush_logbuf
|
229
|
+
time, record = nil,nil
|
230
|
+
@locker.synchronize do
|
231
|
+
time, record = parse_logbuf(@logbuf)
|
232
|
+
@logbuf = nil
|
233
|
+
end
|
234
|
+
if time && record
|
235
|
+
Engine.emit(@tag, time, record)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def parse_logbuf(buf)
|
240
|
+
return nil,nil unless buf
|
241
|
+
buf.chomp!
|
242
|
+
begin
|
243
|
+
time, record = @parser.parse(buf)
|
244
|
+
rescue
|
245
|
+
$log.warn buf.dump, :error=>$!.to_s
|
246
|
+
$log.debug_backtrace
|
247
|
+
end
|
248
|
+
return nil,nil unless time && record
|
249
|
+
record[@rawdata_key] = buf if @rawdata_key
|
250
|
+
return time, record
|
251
|
+
end
|
252
|
+
|
253
|
+
class TailExWatcher_EX < TailWatcher
|
254
|
+
def initialize(path, rotate_wait, pe, &receive_lines)
|
255
|
+
@parent_receive_lines = receive_lines
|
256
|
+
super(path, rotate_wait, pe, &method(:_receive_lines))
|
257
|
+
#super(path, rotate_wait, pe, &receive_lines)
|
258
|
+
@close_trigger = TimerWatcher.new(rotate_wait * 2, false, &method(:_close))
|
259
|
+
end
|
260
|
+
|
261
|
+
def _receive_lines(lines)
|
262
|
+
tag = @path.tr('/', '.').gsub(/\.+/, '.').gsub(/^\./, '')
|
263
|
+
@parent_receive_lines.call(lines, tag)
|
264
|
+
end
|
265
|
+
|
266
|
+
def close(loop=nil)
|
267
|
+
detach # detach first to avoid timer conflict
|
268
|
+
if loop
|
269
|
+
@close_trigger.attach(loop)
|
270
|
+
else
|
271
|
+
_close
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def _close
|
276
|
+
@close_trigger.detach if @close_trigger.attached?
|
277
|
+
self.class.superclass.instance_method(:close).bind(self).call
|
278
|
+
|
279
|
+
@io_handler.on_notify
|
280
|
+
@io_handler.close
|
281
|
+
$log.info "stop following of #{@path}"
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
class CallLater_EX
|
287
|
+
def initialize
|
288
|
+
@locker = Monitor::new
|
289
|
+
initExecBlock()
|
290
|
+
@thread = Thread.new(&method(:run))
|
291
|
+
end
|
292
|
+
|
293
|
+
def call_later(delay,&block)
|
294
|
+
@locker.synchronize do
|
295
|
+
@exec_time = Engine.now + delay
|
296
|
+
@exec_block = block
|
297
|
+
end
|
298
|
+
@thread.run
|
299
|
+
end
|
300
|
+
|
301
|
+
def run
|
302
|
+
@running = true
|
303
|
+
while true
|
304
|
+
sleepSec = -1
|
305
|
+
@locker.synchronize do
|
306
|
+
now = Engine.now
|
307
|
+
if @exec_block && @exec_time <= now
|
308
|
+
@exec_block.call()
|
309
|
+
initExecBlock()
|
310
|
+
end
|
311
|
+
return unless @running
|
312
|
+
unless(@exec_time == -1)
|
313
|
+
sleepSec = @exec_time - now
|
314
|
+
end
|
315
|
+
end
|
316
|
+
if (sleepSec == -1)
|
317
|
+
sleep()
|
318
|
+
else
|
319
|
+
sleep(sleepSec)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
rescue => e
|
323
|
+
puts e
|
324
|
+
end
|
325
|
+
|
326
|
+
def cancel()
|
327
|
+
initExecBlock()
|
328
|
+
end
|
329
|
+
|
330
|
+
def shutdown()
|
331
|
+
@locker.synchronize do
|
332
|
+
@running = false
|
333
|
+
end
|
334
|
+
if(@thread)
|
335
|
+
@thread.run
|
336
|
+
@thread.join
|
337
|
+
@thread = nil
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
private
|
342
|
+
|
343
|
+
def initExecBlock()
|
344
|
+
@locker.synchronize do
|
345
|
+
@exec_time = -1
|
346
|
+
@exec_block = nil
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
end
|
351
|
+
end
|
metadata
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluent-plugin-tail-multiline-ex
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Yoshiharu Mori
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-04-14 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: :runtime
|
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: fluent-mixin-config-placeholders
|
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
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bundler
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.3'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.3'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: fluentd
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: fluent-mixin-config-placeholders
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rake
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: flexmock
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: merge tail_ex and tail_multiline input plugin
|
127
|
+
email:
|
128
|
+
- y-mori@sraoss.co.jp
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- .gitignore
|
134
|
+
- Gemfile
|
135
|
+
- LICENSE.txt
|
136
|
+
- README.md
|
137
|
+
- Rakefile
|
138
|
+
- fluent-plugin-tail-multiline-ex.gemspec
|
139
|
+
- lib/fluent/plugin/in_tail_multiline_ex.rb
|
140
|
+
homepage: https://github.com/y-mori0110/fluent-plugin-multiline-ex
|
141
|
+
licenses:
|
142
|
+
- Apache 2.0
|
143
|
+
post_install_message:
|
144
|
+
rdoc_options: []
|
145
|
+
require_paths:
|
146
|
+
- lib
|
147
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
148
|
+
none: false
|
149
|
+
requirements:
|
150
|
+
- - ! '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
|
+
none: false
|
155
|
+
requirements:
|
156
|
+
- - ! '>='
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
159
|
+
requirements: []
|
160
|
+
rubyforge_project:
|
161
|
+
rubygems_version: 1.8.23
|
162
|
+
signing_key:
|
163
|
+
specification_version: 3
|
164
|
+
summary: merge tail_ex and tail_multiline input plugin
|
165
|
+
test_files: []
|