fluent-logger 0.3.1 → 0.4.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.
@@ -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