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.
@@ -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
@@ -1,7 +1,7 @@
1
1
  module Fluent
2
2
  module Logger
3
3
 
4
- VERSION = '0.3.1'
4
+ VERSION = '0.4.0'
5
5
 
6
6
  end
7
7
  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
@@ -0,0 +1,11 @@
1
+
2
+ require 'spec_helper'
3
+
4
+ describe Fluent::Logger::LoggerBase do
5
+ context "subclass" do
6
+ subject { Class.new(Fluent::Logger::LoggerBase) }
7
+ its(:open) {
8
+ should be_kind_of(Fluent::Logger::LoggerBase)
9
+ }
10
+ end
11
+ end
@@ -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