fluent-logger 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.gitmodules +3 -0
- data/.rspec +1 -0
- data/.travis.yml +10 -0
- data/AUTHORS +1 -0
- data/COPYING +14 -0
- data/ChangeLog +14 -0
- data/Gemfile +9 -0
- data/README.rdoc +16 -78
- data/Rakefile +18 -0
- data/VERSION +1 -0
- data/fluent-logger.gemspec +44 -0
- data/lib/fluent/logger.rb +8 -36
- data/lib/fluent/logger/{console.rb → console_logger.rb} +2 -1
- data/lib/fluent/logger/fluent_logger.rb +214 -0
- data/lib/fluent/logger/logger_base.rb +34 -0
- data/lib/fluent/logger/null_logger.rb +28 -0
- data/lib/fluent/logger/{test.rb → test_logger.rb} +4 -12
- data/lib/fluent/logger/text_logger.rb +40 -0
- data/lib/fluent/logger/version.rb +1 -1
- data/spec/console_logger_spec.rb +69 -0
- data/spec/fluent_logger_spec.rb +201 -0
- data/spec/logger_base_spec.rb +11 -0
- data/spec/logger_spec.rb +43 -0
- data/spec/null_logger_spec.rb +15 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/timecop.rb +8 -0
- data/spec/test_logger_spec.rb +31 -0
- metadata +121 -84
- data/lib/fluent/logger/base.rb +0 -89
- data/lib/fluent/logger/event.rb +0 -118
- data/lib/fluent/logger/fluent.rb +0 -121
- data/lib/fluent/logger/syslog.rb +0 -127
- data/test/event_test.rb +0 -183
- data/test/instance_test.rb +0 -34
- data/test/simple_test.rb +0 -78
- data/test/test_helper.rb +0 -14
@@ -0,0 +1,34 @@
|
|
1
|
+
#
|
2
|
+
# Fluent
|
3
|
+
#
|
4
|
+
# Copyright (C) 2011 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module Fluent
|
19
|
+
module Logger
|
20
|
+
|
21
|
+
class LoggerBase
|
22
|
+
def self.open(*args, &block)
|
23
|
+
Fluent::Logger.open(self, *args, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
#def post(tag, map, time=nil)
|
27
|
+
#end
|
28
|
+
|
29
|
+
#def close(map)
|
30
|
+
#end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#
|
2
|
+
# Fluent
|
3
|
+
#
|
4
|
+
# Copyright (C) 2011 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module Fluent
|
19
|
+
module Logger
|
20
|
+
|
21
|
+
class NullLogger < LoggerBase
|
22
|
+
def post(tag, map, time=nil)
|
23
|
+
false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -18,38 +18,30 @@
|
|
18
18
|
module Fluent
|
19
19
|
module Logger
|
20
20
|
|
21
|
-
|
22
21
|
class TestLogger < LoggerBase
|
23
22
|
def initialize(queue=[])
|
24
23
|
@queue = queue
|
25
24
|
@max = 1024
|
26
|
-
@close_called = 0
|
27
25
|
end
|
28
26
|
|
29
27
|
attr_accessor :max
|
30
28
|
attr_reader :queue
|
31
|
-
attr_reader :close_called
|
32
29
|
|
33
|
-
def post(tag, map)
|
34
|
-
while @queue.size > @max
|
30
|
+
def post(tag, map, time=nil)
|
31
|
+
while @queue.size > @max-1
|
35
32
|
@queue.shift
|
36
33
|
end
|
37
34
|
(class<<map;self;end).module_eval do
|
38
35
|
define_method(:tag) { tag }
|
36
|
+
define_method(:time) { time }
|
39
37
|
end
|
40
38
|
@queue << map
|
39
|
+
true
|
41
40
|
end
|
42
41
|
|
43
42
|
def close
|
44
|
-
@close_called += 1
|
45
|
-
end
|
46
|
-
|
47
|
-
def closed?
|
48
|
-
@close_called > 0
|
49
43
|
end
|
50
44
|
end
|
51
45
|
|
52
|
-
|
53
46
|
end
|
54
47
|
end
|
55
|
-
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#
|
2
|
+
# Fluent
|
3
|
+
#
|
4
|
+
# Copyright (C) 2011 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module Fluent
|
19
|
+
module Logger
|
20
|
+
|
21
|
+
class TextLogger < LoggerBase
|
22
|
+
def initialize
|
23
|
+
require 'yajl'
|
24
|
+
@time_format = "%b %e %H:%M:%S"
|
25
|
+
end
|
26
|
+
|
27
|
+
def post(tag, map, time=nil)
|
28
|
+
time ||= Time.now
|
29
|
+
a = [time.strftime(@time_format), " ", tag, ":"]
|
30
|
+
map.each_pair {|k,v|
|
31
|
+
a << " #{k}="
|
32
|
+
a << Yajl::Encoder.encode(v)
|
33
|
+
}
|
34
|
+
post_text a.join
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'stringio'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
describe Fluent::Logger::ConsoleLogger do
|
8
|
+
before(:each) {
|
9
|
+
Timecop.freeze Time.local(2008, 9, 1, 10, 5, 0)
|
10
|
+
}
|
11
|
+
after(:each) {
|
12
|
+
Timecop.return
|
13
|
+
}
|
14
|
+
|
15
|
+
context "IO output" do
|
16
|
+
let(:io) { StringIO.new }
|
17
|
+
let(:logger) { Fluent::Logger::ConsoleLogger.new(io) }
|
18
|
+
|
19
|
+
subject {
|
20
|
+
io
|
21
|
+
}
|
22
|
+
|
23
|
+
context "post and read" do
|
24
|
+
before do
|
25
|
+
logger.post('example', {:foo => :bar})
|
26
|
+
io.rewind
|
27
|
+
end
|
28
|
+
its(:read) { should eq %Q!Sep 1 10:05:00 example: foo="bar"\n! }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "Filename output" do
|
33
|
+
let(:path) {
|
34
|
+
@tmp = Tempfile.new('fluent-logger') # ref instance var because Tempfile.close(true) check GC
|
35
|
+
filename = @tmp.path
|
36
|
+
@tmp.close(true)
|
37
|
+
Pathname.new(filename)
|
38
|
+
}
|
39
|
+
let(:logger) { Fluent::Logger::ConsoleLogger.new(path.to_s) }
|
40
|
+
|
41
|
+
subject { path }
|
42
|
+
after { path.unlink }
|
43
|
+
|
44
|
+
context "post and read" do
|
45
|
+
before do
|
46
|
+
logger.post('example', {:foo => :bar})
|
47
|
+
logger.close
|
48
|
+
end
|
49
|
+
its(:read) { should eq %Q!Sep 1 10:05:00 example: foo="bar"\n! }
|
50
|
+
end
|
51
|
+
|
52
|
+
context "reopen" do
|
53
|
+
before do
|
54
|
+
logger.post('example', {:foo => :baz})
|
55
|
+
logger.close
|
56
|
+
logger.reopen!
|
57
|
+
end
|
58
|
+
its(:read) { should eq %Q!Sep 1 10:05:00 example: foo="baz"\n! }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "Invalid output" do
|
63
|
+
it {
|
64
|
+
expect {
|
65
|
+
Fluent::Logger::ConsoleLogger.new(nil)
|
66
|
+
}.to raise_error
|
67
|
+
}
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
|
2
|
+
require 'spec_helper'
|
3
|
+
if RUBY_VERSION < "1.9.2"
|
4
|
+
|
5
|
+
describe Fluent::Logger::FluentLogger do
|
6
|
+
pending "fluentd don't work RUBY < 1.9.2"
|
7
|
+
end
|
8
|
+
|
9
|
+
else
|
10
|
+
|
11
|
+
require 'fluent/load'
|
12
|
+
require 'tempfile'
|
13
|
+
require 'logger'
|
14
|
+
require 'socket'
|
15
|
+
require 'stringio'
|
16
|
+
|
17
|
+
$log = Fluent::Log.new(StringIO.new) # XXX should remove $log from fluentd
|
18
|
+
|
19
|
+
describe Fluent::Logger::FluentLogger do
|
20
|
+
let(:fluentd_port) {
|
21
|
+
port = 60001
|
22
|
+
loop do
|
23
|
+
begin
|
24
|
+
TCPServer.open('localhost', port).close
|
25
|
+
break
|
26
|
+
rescue Errno::EADDRINUSE
|
27
|
+
port += 1
|
28
|
+
end
|
29
|
+
end
|
30
|
+
port
|
31
|
+
}
|
32
|
+
|
33
|
+
let(:logger) {
|
34
|
+
@logger_io = StringIO.new
|
35
|
+
logger = ::Logger.new(@logger_io)
|
36
|
+
Fluent::Logger::FluentLogger.new('logger-test', {
|
37
|
+
:host => 'localhost',
|
38
|
+
:port => fluentd_port,
|
39
|
+
:logger => logger,
|
40
|
+
})
|
41
|
+
}
|
42
|
+
|
43
|
+
let(:logger_io) {
|
44
|
+
@logger_io
|
45
|
+
}
|
46
|
+
|
47
|
+
let(:output) {
|
48
|
+
sleep 0.0001 # next tick
|
49
|
+
Fluent::Engine.match('logger-test').output
|
50
|
+
}
|
51
|
+
|
52
|
+
let(:queue) {
|
53
|
+
queue = []
|
54
|
+
output.emits.each {|tag,events|
|
55
|
+
events.each {|time,record|
|
56
|
+
queue << [tag, record]
|
57
|
+
}
|
58
|
+
}
|
59
|
+
queue
|
60
|
+
}
|
61
|
+
|
62
|
+
after(:each) do
|
63
|
+
output.emits.clear rescue nil
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
context "running fluentd" do
|
68
|
+
before(:each) do
|
69
|
+
tmp = Tempfile.new('fluent-logger-config')
|
70
|
+
tmp.close(false)
|
71
|
+
|
72
|
+
File.open(tmp.path, 'w') {|f|
|
73
|
+
f.puts <<EOF
|
74
|
+
<source>
|
75
|
+
type tcp
|
76
|
+
port #{fluentd_port}
|
77
|
+
</source>
|
78
|
+
<match logger-test.**>
|
79
|
+
type test
|
80
|
+
</match>
|
81
|
+
EOF
|
82
|
+
}
|
83
|
+
Fluent::Test.setup
|
84
|
+
Fluent::Engine.read_config(tmp.path)
|
85
|
+
@coolio_default_loop = nil
|
86
|
+
Thread.new {
|
87
|
+
@coolio_default_loop = Coolio::Loop.default
|
88
|
+
Fluent::Engine.run
|
89
|
+
}
|
90
|
+
sleep 0.001 # next tick
|
91
|
+
end
|
92
|
+
|
93
|
+
after(:each) do
|
94
|
+
@coolio_default_loop.stop
|
95
|
+
Fluent::Engine.send :shutdown
|
96
|
+
end
|
97
|
+
|
98
|
+
context('post') do
|
99
|
+
it ('success') {
|
100
|
+
logger.post('tag', {'a' => 'b'}).should be_true
|
101
|
+
queue.last.should == ['logger-test.tag', {'a' => 'b'}]
|
102
|
+
}
|
103
|
+
|
104
|
+
it ('close after post') {
|
105
|
+
logger.should be_connect
|
106
|
+
logger.close
|
107
|
+
logger.should_not be_connect
|
108
|
+
|
109
|
+
logger.post('tag', {'b' => 'c'})
|
110
|
+
logger.should be_connect
|
111
|
+
queue.last.should == ['logger-test.tag', {'b' => 'c'}]
|
112
|
+
}
|
113
|
+
|
114
|
+
it ('large data') {
|
115
|
+
data = {'a' => ('b' * 1000000)}
|
116
|
+
logger.post('tag', data)
|
117
|
+
sleep 0.01 # wait write
|
118
|
+
queue.last.should == ['logger-test.tag', data]
|
119
|
+
}
|
120
|
+
|
121
|
+
it ('msgpack unsupport data') {
|
122
|
+
data = {
|
123
|
+
'time' => Time.utc(2008, 9, 1, 10, 5, 0),
|
124
|
+
'object' => Object.new,
|
125
|
+
'proc' => proc { 1 },
|
126
|
+
}
|
127
|
+
logger.post('tag', data)
|
128
|
+
logger_data = queue.last.last
|
129
|
+
logger_data['time'].should == '2008-09-01 10:05:00 UTC'
|
130
|
+
logger_data['proc'].should be
|
131
|
+
logger_data['object'].should be
|
132
|
+
}
|
133
|
+
|
134
|
+
it ('msgpack and JSON unsupport data') {
|
135
|
+
data = {
|
136
|
+
'time' => Time.utc(2008, 9, 1, 10, 5, 0),
|
137
|
+
'object' => Object.new,
|
138
|
+
'proc' => proc { 1 },
|
139
|
+
'NaN' => (0.0/0.0) # JSON don't convert
|
140
|
+
}
|
141
|
+
logger.post('tag', data)
|
142
|
+
queue.last.should be_nil
|
143
|
+
logger_io.rewind
|
144
|
+
logger_io.read =~ /FluentLogger: Can't convert to msgpack:/
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
context "initializer" do
|
149
|
+
it "backward compatible" do
|
150
|
+
port = fluentd_port
|
151
|
+
fluent_logger = Fluent::Logger::FluentLogger.new('logger-test', 'localhost', port)
|
152
|
+
fluent_logger.method_missing(:instance_eval) { # fluent_logger is delegetor
|
153
|
+
@host.should == 'localhost'
|
154
|
+
@port.should == port
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
158
|
+
it "hash argument" do
|
159
|
+
port = fluentd_port
|
160
|
+
fluent_logger = Fluent::Logger::FluentLogger.new('logger-test', {
|
161
|
+
:host => 'localhost',
|
162
|
+
:port => port
|
163
|
+
})
|
164
|
+
fluent_logger.method_missing(:instance_eval) { # fluent_logger is delegetor
|
165
|
+
@host.should == 'localhost'
|
166
|
+
@port.should == port
|
167
|
+
}
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context "not running fluentd" do
|
173
|
+
context('fluent logger interface') do
|
174
|
+
it ('post & close') {
|
175
|
+
logger.post('tag', {'a' => 'b'}).should be_false
|
176
|
+
queue.last.should be_nil
|
177
|
+
logger.close
|
178
|
+
logger_io.rewind
|
179
|
+
log = logger_io.read
|
180
|
+
log.should =~ /Failed to connect/
|
181
|
+
log.should =~ /Can't send logs to/
|
182
|
+
}
|
183
|
+
|
184
|
+
it ('post limit over') do
|
185
|
+
logger.limit = 100
|
186
|
+
logger.post('tag', {'a' => 'b'})
|
187
|
+
queue.last.should be_nil
|
188
|
+
|
189
|
+
logger_io.rewind
|
190
|
+
logger_io.read.should_not =~ /Can't send logs to/
|
191
|
+
|
192
|
+
logger.post('tag', {'a' => ('c' * 1000)})
|
193
|
+
logger_io.rewind
|
194
|
+
logger_io.read.should =~ /Can't send logs to/
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
data/spec/logger_spec.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
describe Fluent::Logger do
|
6
|
+
context "default logger" do
|
7
|
+
let(:test_logger) {
|
8
|
+
Fluent::Logger::TestLogger.new
|
9
|
+
}
|
10
|
+
before(:each) do
|
11
|
+
Fluent::Logger.default = test_logger
|
12
|
+
end
|
13
|
+
|
14
|
+
it('post') {
|
15
|
+
time = Time.now
|
16
|
+
test_logger.should_receive(:post).with('tag1', {:foo => :bar}, time)
|
17
|
+
Fluent::Logger.post('tag1', {:foo => :bar}, time)
|
18
|
+
}
|
19
|
+
|
20
|
+
it('close') {
|
21
|
+
test_logger.should_receive(:close)
|
22
|
+
Fluent::Logger.close
|
23
|
+
}
|
24
|
+
|
25
|
+
it('open') {
|
26
|
+
test_logger.should_receive(:close)
|
27
|
+
klass = Class.new(Fluent::Logger::LoggerBase)
|
28
|
+
fluent_logger_logger_io = StringIO.new
|
29
|
+
Fluent::Logger.open('tag-prefix', {
|
30
|
+
:logger => ::Logger.new(fluent_logger_logger_io)
|
31
|
+
})
|
32
|
+
# Fluent::Logger::FluentLogger is delegator
|
33
|
+
Fluent::Logger.default.method_missing(:kind_of?, Fluent::Logger::FluentLogger).should be_true
|
34
|
+
}
|
35
|
+
|
36
|
+
it('open with BaseLogger class') {
|
37
|
+
test_logger.should_receive(:close)
|
38
|
+
klass = Class.new(Fluent::Logger::LoggerBase)
|
39
|
+
Fluent::Logger.open(klass)
|
40
|
+
Fluent::Logger.default.class.should == klass
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|