fluent-plugin-tail-multiline 0.1.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 +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -0
- data/README.md +83 -0
- data/Rakefile +10 -0
- data/fluent-plugin-tail-multiline.gemspec +21 -0
- data/lib/fluent/plugin/in_tail_multiline.rb +192 -0
- data/test/helper.rb +28 -0
- data/test/plugin/test_in_tail_multiline.rb +203 -0
- metadata +68 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 30ae0c4b7c3d24c02a04d5fef674ca5e24f133ae
|
4
|
+
data.tar.gz: 10ac8354faf5e40d599d9dd9f8753c4ce46ecc4d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 352fdfcb3089d9553db4930a7514cb0a843b47c73e8f0e3c7da5617b7faf090357340e807d2cc65fd4ef12acb6ad1b4c218f8627d33637ae10d124c1afcc9ec7
|
7
|
+
data.tar.gz: 663c365b32692660f80eb0c4b4c22985ed3733d73bf4020689d2b8c864fd37c9da1908265020e2de7c219b45f26aa0ace0152a93ac67a08aee8410675f1a98a6
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (c) 2013 - Tomohisa Ota
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# Fluent::Plugin::Tail-Multiline
|
2
|
+
|
3
|
+
Tail-Multiline plugin extends built-in tail plugin with following features
|
4
|
+
+ Support log with multiple line output such as stacktrace
|
5
|
+
+ RegEx parameter to detect first line
|
6
|
+
+ Save raw log data
|
7
|
+
|
8
|
+
**built-in templates are not supported. It does not support multiple line log anyway**
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Use ruby gem as :
|
13
|
+
|
14
|
+
gem 'fluent-plugin-tail-multiline'
|
15
|
+
|
16
|
+
Or, if you're using td-client, you can call td-client's gem
|
17
|
+
|
18
|
+
$ /usr/lib64/fluent/ruby/bin/gem install fluent-plugin-tail-multiline
|
19
|
+
|
20
|
+
## Base Configuration
|
21
|
+
Tail-Multiline extends [tail plugin](http://docs.fluentd.org/categories/in_tail).
|
22
|
+
|
23
|
+
## Configuration
|
24
|
+
### Additional Parameters
|
25
|
+
name | type | description
|
26
|
+
----------------------|---------------------------------|---------------------------
|
27
|
+
format_firstline | string(default = format) | RegEx to detect first line of multiple line log, no name capture required
|
28
|
+
rawdata_key | string(default = null) | Store raw data with given key
|
29
|
+
|
30
|
+
## Examples
|
31
|
+
### Java log with exception
|
32
|
+
#### Input
|
33
|
+
```
|
34
|
+
2013-3-03 14:27:33 [main] INFO Main - Start
|
35
|
+
2013-3-03 14:27:33 [main] ERROR Main - Exception
|
36
|
+
javax.management.RuntimeErrorException: null
|
37
|
+
at Main.main(Main.java:16) ~[bin/:na]
|
38
|
+
2013-3-03 14:27:33 [main] INFO Main - End
|
39
|
+
```
|
40
|
+
#### Parameters
|
41
|
+
```
|
42
|
+
tag test
|
43
|
+
format /^(?<time>\d{4}-\d{1,2}-\d{1,2} \d{1,2}:\d{1,2}:\d{1,2}) \[(?<thread>.*)\] (?<level>[^\s]+)(?<message>.*)/
|
44
|
+
```
|
45
|
+
#### Output
|
46
|
+
```
|
47
|
+
2013-03-03 14:27:33 +0900 test: {"thread":"main","level":"INFO","message":" Main - Start"}
|
48
|
+
2013-03-03 14:27:33 +0900 test: {"thread":"main","level":"ERROR","message":" Main - Exception\njavax.management.RuntimeErrorException: null\n\tat Main.main(Main.java:16) ~[bin/:na]"}
|
49
|
+
2013-03-03 14:27:33 +0900 test: {"thread":"main","level":"INFO","message":" Main - End\n"}
|
50
|
+
```
|
51
|
+
|
52
|
+
### Case where first line does not have any name capture
|
53
|
+
#### Input
|
54
|
+
```
|
55
|
+
----
|
56
|
+
time=2013-3-03 14:27:33
|
57
|
+
message=test1
|
58
|
+
----
|
59
|
+
time=2013-3-03 14:27:34
|
60
|
+
message=test2
|
61
|
+
```
|
62
|
+
|
63
|
+
#### Parameters
|
64
|
+
```
|
65
|
+
tag test
|
66
|
+
format /time=(?<time>\d{4}-\d{1,2}-\d{1,2} \d{1,2}:\d{1,2}:\d{1,2}).*message=(?<message>.*)/
|
67
|
+
format_firstline /----/
|
68
|
+
```
|
69
|
+
|
70
|
+
#### Output
|
71
|
+
```
|
72
|
+
2013-03-03 14:27:33 +0900 test: {"message":"test1"}
|
73
|
+
2013-03-03 14:27:34 +0900 test: {"message":"test2"}
|
74
|
+
```
|
75
|
+
|
76
|
+
## Contributing
|
77
|
+
|
78
|
+
1. Fork it
|
79
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
80
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
81
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
82
|
+
5. Create new Pull Request
|
83
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
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 |gem|
|
6
|
+
gem.name = "fluent-plugin-tail-multiline"
|
7
|
+
gem.version = "0.1.0"
|
8
|
+
gem.authors = ["Tomohisa Ota"]
|
9
|
+
gem.email = ["tomohisa.ota+github@gmail.com"]
|
10
|
+
gem.description = ""
|
11
|
+
gem.summary = gem.description
|
12
|
+
gem.homepage = "http://github.com/tomohisaota/fluent-plugin-tail-multiline"
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($/)
|
15
|
+
gem.files.reject! { |fn| fn.include? "doc/" }
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_runtime_dependency "fluentd"
|
21
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
module Fluent
|
2
|
+
require 'fluent/plugin/in_tail'
|
3
|
+
class TailMultilineInput < TailInput
|
4
|
+
|
5
|
+
class MultilineTextParser < TextParser
|
6
|
+
def configure(conf, required=true)
|
7
|
+
format = conf['format']
|
8
|
+
if format == nil
|
9
|
+
raise ConfigError, "'format' parameter is required"
|
10
|
+
elsif format[0] != ?/ || format[format.length-1] != ?/
|
11
|
+
raise ConfigError, "'format' should be RegEx. Template is not supported in multiline mode"
|
12
|
+
end
|
13
|
+
|
14
|
+
begin
|
15
|
+
@regex = Regexp.new(format[1..-2],Regexp::MULTILINE)
|
16
|
+
if @regex.named_captures.empty?
|
17
|
+
raise "No named captures"
|
18
|
+
end
|
19
|
+
rescue
|
20
|
+
raise ConfigError, "Invalid regexp in format '#{format[1..-2]}': #{$!}"
|
21
|
+
end
|
22
|
+
|
23
|
+
@parser = RegexpParser.new(@regex)
|
24
|
+
|
25
|
+
if @parser.respond_to?(:configure)
|
26
|
+
@parser.configure(conf)
|
27
|
+
end
|
28
|
+
|
29
|
+
format_firstline = conf['format_firstline']
|
30
|
+
if format_firstline
|
31
|
+
# Use custom matcher for 1st line
|
32
|
+
if format_firstline[0] == '/' && format_firstline[format_firstline.length-1] == '/'
|
33
|
+
@regex = Regexp.new(format_firstline[1..-2])
|
34
|
+
else
|
35
|
+
raise ConfigError, "Invalid regexp in format_firstline '#{format_firstline[1..-2]}': #{$!}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
return true
|
40
|
+
end
|
41
|
+
|
42
|
+
def match_firstline(text)
|
43
|
+
@regex.match(text)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
Plugin.register_input('tail_multiline', self)
|
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
|
+
|
54
|
+
def initialize
|
55
|
+
super
|
56
|
+
@locker = Monitor.new
|
57
|
+
@logbuf = nil
|
58
|
+
@logbuf_flusher = CallLater::new
|
59
|
+
end
|
60
|
+
|
61
|
+
def configure_parser(conf)
|
62
|
+
@parser = MultilineTextParser.new
|
63
|
+
@parser.configure(conf)
|
64
|
+
end
|
65
|
+
|
66
|
+
def receive_lines(lines)
|
67
|
+
@logbuf_flusher.cancel()
|
68
|
+
es = MultiEventStream.new
|
69
|
+
@locker.synchronize do
|
70
|
+
lines.each {|line|
|
71
|
+
if @parser.match_firstline(line)
|
72
|
+
time, record = parse_logbuf(@logbuf)
|
73
|
+
if time && record
|
74
|
+
es.add(time, record)
|
75
|
+
end
|
76
|
+
@logbuf = line
|
77
|
+
else
|
78
|
+
@logbuf += line if(@logbuf)
|
79
|
+
end
|
80
|
+
}
|
81
|
+
end
|
82
|
+
unless es.empty?
|
83
|
+
begin
|
84
|
+
Engine.emit_stream(@tag, es)
|
85
|
+
rescue
|
86
|
+
# ignore errors. Engine shows logs and backtraces.
|
87
|
+
end
|
88
|
+
end
|
89
|
+
@logbuf_flusher.call_later(@auto_flush_sec) do
|
90
|
+
flush_logbuf()
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def shutdown
|
95
|
+
super
|
96
|
+
flush_logbuf()
|
97
|
+
@logbuf_flusher.shutdown()
|
98
|
+
end
|
99
|
+
|
100
|
+
def flush_logbuf
|
101
|
+
time, record = nil,nil
|
102
|
+
@locker.synchronize do
|
103
|
+
time, record = parse_logbuf(@logbuf)
|
104
|
+
@logbuf = nil
|
105
|
+
end
|
106
|
+
if time && record
|
107
|
+
Engine.emit(@tag, time, record)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def parse_logbuf(buf)
|
112
|
+
return nil,nil unless buf
|
113
|
+
buf.chomp!
|
114
|
+
begin
|
115
|
+
time, record = @parser.parse(buf)
|
116
|
+
rescue
|
117
|
+
$log.warn line.dump, :error=>$!.to_s
|
118
|
+
$log.debug_backtrace
|
119
|
+
end
|
120
|
+
return nil,nil unless time && record
|
121
|
+
record[@rawdata_key] = buf if @rawdata_key
|
122
|
+
return time, record
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
class CallLater
|
128
|
+
def initialize
|
129
|
+
@locker = Monitor::new
|
130
|
+
@thread = Thread.new(&method(:run))
|
131
|
+
initExecBlock()
|
132
|
+
end
|
133
|
+
|
134
|
+
def call_later(delay,&block)
|
135
|
+
@locker.synchronize do
|
136
|
+
@exec_time = Engine.now + delay
|
137
|
+
@exec_block = block
|
138
|
+
end
|
139
|
+
@thread.run
|
140
|
+
end
|
141
|
+
|
142
|
+
def run
|
143
|
+
@running = true
|
144
|
+
while true
|
145
|
+
sleepSec = -1
|
146
|
+
@locker.synchronize do
|
147
|
+
now = Engine.now
|
148
|
+
if @exec_block && @exec_time <= now
|
149
|
+
@exec_block.call()
|
150
|
+
initExecBlock()
|
151
|
+
end
|
152
|
+
return unless @running
|
153
|
+
unless(@exec_time == -1)
|
154
|
+
sleepSec = @exec_time - now
|
155
|
+
end
|
156
|
+
end
|
157
|
+
if (sleepSec == -1)
|
158
|
+
sleep()
|
159
|
+
else
|
160
|
+
sleep(sleepSec)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
rescue => e
|
164
|
+
puts e
|
165
|
+
end
|
166
|
+
|
167
|
+
def cancel()
|
168
|
+
initExecBlock()
|
169
|
+
end
|
170
|
+
|
171
|
+
def shutdown()
|
172
|
+
@locker.synchronize do
|
173
|
+
@running = false
|
174
|
+
end
|
175
|
+
if(@thread)
|
176
|
+
@thread.run
|
177
|
+
@thread.join
|
178
|
+
@thread = nil
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
|
184
|
+
def initExecBlock()
|
185
|
+
@locker.synchronize do
|
186
|
+
@exec_time = -1
|
187
|
+
@exec_block = nil
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
13
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
14
|
+
require 'fluent/test'
|
15
|
+
unless ENV.has_key?('VERBOSE')
|
16
|
+
nulllogger = Object.new
|
17
|
+
nulllogger.instance_eval {|obj|
|
18
|
+
def method_missing(method, *args)
|
19
|
+
# pass
|
20
|
+
end
|
21
|
+
}
|
22
|
+
$log = nulllogger
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'fluent/plugin/in_tail_multiline'
|
26
|
+
|
27
|
+
class Test::Unit::TestCase
|
28
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
class TailMultilineInputTest < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
Fluent::Test.setup
|
8
|
+
end
|
9
|
+
|
10
|
+
CONFIG = %[
|
11
|
+
]
|
12
|
+
# CONFIG = %[
|
13
|
+
# path #{TMP_DIR}/out_file_test
|
14
|
+
# compress gz
|
15
|
+
# utc
|
16
|
+
# ]
|
17
|
+
|
18
|
+
def create_driver(conf = CONFIG)
|
19
|
+
Fluent::Test::InputTestDriver.new(Fluent::TailMultilineInput).configure(conf)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_emit_no_additional_option
|
23
|
+
tmpFile = Tempfile.new("in_tail_multiline-")
|
24
|
+
begin
|
25
|
+
d = create_driver %[
|
26
|
+
path #{tmpFile.path}
|
27
|
+
tag test
|
28
|
+
format /^[s|f] (?<message>.*)/
|
29
|
+
]
|
30
|
+
d.run do
|
31
|
+
File.open(tmpFile.path, "w") {|f|
|
32
|
+
f.puts "f test1"
|
33
|
+
f.puts "s test2"
|
34
|
+
f.puts "f test3"
|
35
|
+
f.puts "f test4"
|
36
|
+
f.puts "s test5"
|
37
|
+
f.puts "s test6"
|
38
|
+
f.puts "f test7"
|
39
|
+
f.puts "s test8"
|
40
|
+
}
|
41
|
+
sleep 1
|
42
|
+
end
|
43
|
+
|
44
|
+
emits = d.emits
|
45
|
+
assert_equal(true, emits.length > 0)
|
46
|
+
assert_equal({"message"=>"test1"}, emits[0][2])
|
47
|
+
assert_equal({"message"=>"test2"}, emits[1][2])
|
48
|
+
assert_equal({"message"=>"test3"}, emits[2][2])
|
49
|
+
assert_equal({"message"=>"test4"}, emits[3][2])
|
50
|
+
assert_equal({"message"=>"test5"}, emits[4][2])
|
51
|
+
assert_equal({"message"=>"test6"}, emits[5][2])
|
52
|
+
assert_equal({"message"=>"test7"}, emits[6][2])
|
53
|
+
assert_equal({"message"=>"test8"}, emits[7][2])
|
54
|
+
ensure
|
55
|
+
tmpFile.close(true)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_emit_with_rawdata
|
60
|
+
tmpFile = Tempfile.new("in_tail_multiline-")
|
61
|
+
begin
|
62
|
+
d = create_driver %[
|
63
|
+
path #{tmpFile.path}
|
64
|
+
tag test
|
65
|
+
format /^[s|f] (?<message>.*)/
|
66
|
+
rawdata_key rawdata
|
67
|
+
]
|
68
|
+
d.run do
|
69
|
+
File.open(tmpFile.path, "w") {|f|
|
70
|
+
f.puts "f test1"
|
71
|
+
f.puts "s test2"
|
72
|
+
f.puts "f test3"
|
73
|
+
f.puts "f test4"
|
74
|
+
f.puts "s test5"
|
75
|
+
f.puts "s test6"
|
76
|
+
f.puts "f test7"
|
77
|
+
f.puts "s test8"
|
78
|
+
}
|
79
|
+
sleep 1
|
80
|
+
end
|
81
|
+
|
82
|
+
emits = d.emits
|
83
|
+
assert_equal(true, emits.length > 0)
|
84
|
+
assert_equal({"message"=>"test1","rawdata"=>"f test1"}, emits[0][2])
|
85
|
+
assert_equal({"message"=>"test2","rawdata"=>"s test2"}, emits[1][2])
|
86
|
+
assert_equal({"message"=>"test3","rawdata"=>"f test3"}, emits[2][2])
|
87
|
+
assert_equal({"message"=>"test4","rawdata"=>"f test4"}, emits[3][2])
|
88
|
+
assert_equal({"message"=>"test5","rawdata"=>"s test5"}, emits[4][2])
|
89
|
+
assert_equal({"message"=>"test6","rawdata"=>"s test6"}, emits[5][2])
|
90
|
+
assert_equal({"message"=>"test7","rawdata"=>"f test7"}, emits[6][2])
|
91
|
+
assert_equal({"message"=>"test8","rawdata"=>"s test8"}, emits[7][2])
|
92
|
+
ensure
|
93
|
+
tmpFile.close(true)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
def test_emit_with_format_firstline
|
97
|
+
tmpFile = Tempfile.new("in_tail_multiline-")
|
98
|
+
begin
|
99
|
+
d = create_driver %[
|
100
|
+
path #{tmpFile.path}
|
101
|
+
tag test
|
102
|
+
format /^[s|f] (?<message>.*)/
|
103
|
+
format_firstline /^[s]/
|
104
|
+
]
|
105
|
+
d.run do
|
106
|
+
File.open(tmpFile.path, "w") {|f|
|
107
|
+
f.puts "f test1"
|
108
|
+
f.puts "s test2"
|
109
|
+
f.puts "f test3"
|
110
|
+
f.puts "f test4"
|
111
|
+
f.puts "s test5"
|
112
|
+
f.puts "s test6"
|
113
|
+
f.puts "f test7"
|
114
|
+
f.puts "s test8"
|
115
|
+
}
|
116
|
+
sleep 1
|
117
|
+
end
|
118
|
+
|
119
|
+
emits = d.emits
|
120
|
+
assert_equal(true, emits.length > 0)
|
121
|
+
n = -1
|
122
|
+
assert_equal({"message"=>"test2\nf test3\nf test4"}, emits[0][2])
|
123
|
+
assert_equal({"message"=>"test5"}, emits[1][2])
|
124
|
+
assert_equal({"message"=>"test6\nf test7"}, emits[2][2])
|
125
|
+
assert_equal({"message"=>"test8"}, emits[3][2])
|
126
|
+
ensure
|
127
|
+
tmpFile.close(true)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_emit_with_format_firstline_with_rawdata
|
132
|
+
tmpFile = Tempfile.new("in_tail_multiline-")
|
133
|
+
begin
|
134
|
+
d = create_driver %[
|
135
|
+
path #{tmpFile.path}
|
136
|
+
tag test
|
137
|
+
format /^[s|f] (?<message>.*)/
|
138
|
+
format_firstline /^[s]/
|
139
|
+
rawdata_key rawdata
|
140
|
+
]
|
141
|
+
d.run do
|
142
|
+
File.open(tmpFile.path, "w") {|f|
|
143
|
+
f.puts "f test1"
|
144
|
+
f.puts "s test2"
|
145
|
+
f.puts "f test3"
|
146
|
+
f.puts "f test4"
|
147
|
+
f.puts "s test5"
|
148
|
+
f.puts "s test6"
|
149
|
+
f.puts "f test7"
|
150
|
+
f.puts "s test8"
|
151
|
+
}
|
152
|
+
sleep 1
|
153
|
+
end
|
154
|
+
|
155
|
+
emits = d.emits
|
156
|
+
assert_equal(true, emits.length > 0)
|
157
|
+
n = -1
|
158
|
+
assert_equal({"message"=>"test2\nf test3\nf test4","rawdata"=>"s test2\nf test3\nf test4"}, emits[0][2])
|
159
|
+
assert_equal({"message"=>"test5","rawdata"=>"s test5"}, emits[1][2])
|
160
|
+
assert_equal({"message"=>"test6\nf test7","rawdata"=>"s test6\nf test7"}, emits[2][2])
|
161
|
+
assert_equal({"message"=>"test8","rawdata"=>"s test8"}, emits[3][2])
|
162
|
+
ensure
|
163
|
+
tmpFile.close(true)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_multilinelog
|
168
|
+
tmpFile = Tempfile.new("in_tail_multiline-")
|
169
|
+
begin
|
170
|
+
d = create_driver %[
|
171
|
+
path #{tmpFile.path}
|
172
|
+
tag test
|
173
|
+
format /^s (?<message1>[^\\n]+)(\\nf (?<message2>[^\\n]+))?(\\nf (?<message3>.*))?/
|
174
|
+
format_firstline /^[s]/
|
175
|
+
rawdata_key rawdata
|
176
|
+
]
|
177
|
+
d.run do
|
178
|
+
File.open(tmpFile.path, "w") {|f|
|
179
|
+
f.puts "f test1"
|
180
|
+
f.puts "s test2"
|
181
|
+
f.puts "f test3"
|
182
|
+
f.puts "f test4"
|
183
|
+
f.puts "s test5"
|
184
|
+
f.puts "s test6"
|
185
|
+
f.puts "f test7"
|
186
|
+
f.puts "s test8"
|
187
|
+
}
|
188
|
+
sleep 1
|
189
|
+
end
|
190
|
+
|
191
|
+
emits = d.emits
|
192
|
+
assert_equal(true, emits.length > 0)
|
193
|
+
n = -1
|
194
|
+
assert_equal({"message1"=>"test2","message2"=>"test3","message3"=>"test4","rawdata"=>"s test2\nf test3\nf test4"}, emits[0][2])
|
195
|
+
assert_equal({"message1"=>"test5","rawdata"=>"s test5"}, emits[1][2])
|
196
|
+
assert_equal({"message1"=>"test6","message2"=>"test7","rawdata"=>"s test6\nf test7"}, emits[2][2])
|
197
|
+
assert_equal({"message1"=>"test8","rawdata"=>"s test8"}, emits[3][2])
|
198
|
+
ensure
|
199
|
+
tmpFile.close(true)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluent-plugin-tail-multiline
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tomohisa Ota
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-03-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: fluentd
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: ''
|
28
|
+
email:
|
29
|
+
- tomohisa.ota+github@gmail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- Gemfile
|
36
|
+
- LICENSE.txt
|
37
|
+
- README.md
|
38
|
+
- Rakefile
|
39
|
+
- fluent-plugin-tail-multiline.gemspec
|
40
|
+
- lib/fluent/plugin/in_tail_multiline.rb
|
41
|
+
- test/helper.rb
|
42
|
+
- test/plugin/test_in_tail_multiline.rb
|
43
|
+
homepage: http://github.com/tomohisaota/fluent-plugin-tail-multiline
|
44
|
+
licenses: []
|
45
|
+
metadata: {}
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
requirements: []
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 2.0.0
|
63
|
+
signing_key:
|
64
|
+
specification_version: 4
|
65
|
+
summary: ''
|
66
|
+
test_files:
|
67
|
+
- test/helper.rb
|
68
|
+
- test/plugin/test_in_tail_multiline.rb
|