madvertise-logging 1.2.1-java
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/.travis.yml +6 -0
- data/.yardopts +1 -0
- data/Gemfile +13 -0
- data/LICENSE +22 -0
- data/README.md +33 -0
- data/Rakefile +3 -0
- data/lib/madvertise-logging.rb +1 -0
- data/lib/madvertise/logging.rb +5 -0
- data/lib/madvertise/logging/airbrake.rb +21 -0
- data/lib/madvertise/logging/document_logger.rb +49 -0
- data/lib/madvertise/logging/improved_io.rb +159 -0
- data/lib/madvertise/logging/improved_logger.rb +457 -0
- data/lib/madvertise/logging/mask.reek +14 -0
- data/lib/madvertise/logging/multi_logger.rb +34 -0
- data/madvertise-logging.gemspec +22 -0
- data/spec/improved_logger_spec.rb +322 -0
- data/spec/multi_logger_spec.rb +27 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +19 -0
- data/tasks/reek.rake +8 -0
- data/tasks/rspec.rake +14 -0
- data/tasks/yard.rake +9 -0
- metadata +105 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
module Madvertise
|
2
|
+
module Logging
|
3
|
+
|
4
|
+
##
|
5
|
+
# MultiLogger is a simple class for multiplexing ImprovedLogger objects. It
|
6
|
+
# support attach/detach to send messages to any number of loggers.
|
7
|
+
|
8
|
+
class MultiLogger
|
9
|
+
def initialize(*loggers)
|
10
|
+
@loggers = loggers
|
11
|
+
end
|
12
|
+
|
13
|
+
# Attach an ImprovedLogger object.
|
14
|
+
def attach(logger)
|
15
|
+
logger.token = @loggers.first.token rescue nil
|
16
|
+
@loggers << logger
|
17
|
+
end
|
18
|
+
|
19
|
+
# Detach an ImprovedLogger object.
|
20
|
+
def detach(logger)
|
21
|
+
@loggers.delete(logger)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Delegate all method calls to all attached loggers.
|
25
|
+
#
|
26
|
+
# @private
|
27
|
+
def method_missing(name, *args, &block)
|
28
|
+
@loggers.map do |logger|
|
29
|
+
logger.send(name, *args, &block)
|
30
|
+
end.first
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "madvertise-logging"
|
5
|
+
spec.version = "1.2.1"
|
6
|
+
spec.authors = ["madvertise Mobile Advertising GmbH"]
|
7
|
+
spec.email = ["tech@madvertise.com"]
|
8
|
+
spec.description = %q{Advanced logging classes with buffer backend, transactions, multi logger, etc}
|
9
|
+
spec.summary = %q{Advanced logging classes with buffer backend, transactions, multi logger, etc}
|
10
|
+
spec.homepage = "https://github.com/madvertise/logging"
|
11
|
+
|
12
|
+
spec.files = `git ls-files`.split($/)
|
13
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
14
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
15
|
+
spec.require_paths = ["lib"]
|
16
|
+
|
17
|
+
if RUBY_PLATFORM == "java"
|
18
|
+
spec.platform = 'java'
|
19
|
+
spec.add_dependency "log4jruby", "~> 1.0.0.rc1"
|
20
|
+
spec.add_dependency "slyphon-log4j", "~> 1.2.15"
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,322 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe ImprovedLogger do
|
4
|
+
|
5
|
+
let(:logger) { ImprovedLogger.new(:document) }
|
6
|
+
|
7
|
+
before(:each) { logger.level = :debug }
|
8
|
+
|
9
|
+
subject { logger.messages }
|
10
|
+
|
11
|
+
ImprovedLogger.severities.keys.each do |level|
|
12
|
+
describe level do
|
13
|
+
before { logger.send(level, "testing #{level}") }
|
14
|
+
let(:prefix) { level == :unknown ? "ANY" : level.to_s.upcase }
|
15
|
+
it "logs #{level} messages" do
|
16
|
+
subject.last[:message].should == "testing #{level}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "logs info level messages with <<" do
|
22
|
+
logger << "Info test <<"
|
23
|
+
subject.last[:message].should == "Info test <<"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "logs info level messages with write" do
|
27
|
+
logger.write("Info test write")
|
28
|
+
subject.last[:message].should == "Info test write"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "supports additional attributes" do
|
32
|
+
logger.info("foo", key: "value", test: "with space")
|
33
|
+
subject.last[:message].should == 'foo key=value test="with space"'
|
34
|
+
end
|
35
|
+
|
36
|
+
it "supports lazy-evaluation via blocks" do
|
37
|
+
logger.debug { "debug message" }
|
38
|
+
subject.last[:message].should == "debug message"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "supports lazy-evaluation with attributes" do
|
42
|
+
logger.debug { ["debug message", {key: "value"}] }
|
43
|
+
subject.last[:message].should == "debug message key=value"
|
44
|
+
end
|
45
|
+
|
46
|
+
it "accepts a different backend" do
|
47
|
+
l = Logger.new('/dev/null')
|
48
|
+
logger.logger = l
|
49
|
+
logger.logger.should == l
|
50
|
+
end
|
51
|
+
|
52
|
+
describe :log_caller do
|
53
|
+
it "logs the caller file and line number" do
|
54
|
+
f = __FILE__
|
55
|
+
l = __LINE__ + 3
|
56
|
+
|
57
|
+
logger.log_caller = true
|
58
|
+
logger.info("Caller test")
|
59
|
+
subject.last[:message].should == "Caller test file=#{f} line=#{l}"
|
60
|
+
end
|
61
|
+
|
62
|
+
it "does not log the caller file and line number" do
|
63
|
+
f = File.basename(__FILE__)
|
64
|
+
l = __LINE__ + 3
|
65
|
+
|
66
|
+
logger.log_caller = false
|
67
|
+
logger.info("Caller test")
|
68
|
+
subject.last[:message].should_not == "Caller test file=#{f} line=#{l}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
let(:fake_trace) do
|
73
|
+
[
|
74
|
+
"/home/jdoe/app/libexec/app.rb:1:in `foo'",
|
75
|
+
"/usr/lib/ruby/gems/1.8/gems/madvertise-logging-0.1.0/lib/madvertise/logging/improved_logger.rb:42: in `info'"
|
76
|
+
]
|
77
|
+
end
|
78
|
+
|
79
|
+
describe :exceptions do
|
80
|
+
let(:exc) do
|
81
|
+
RuntimeError.new('Test error').tap do |exc|
|
82
|
+
exc.set_backtrace(fake_trace)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it "logs an exception object" do
|
87
|
+
logger.exception(exc)
|
88
|
+
subject.last[:message].should match(%r{exception class=RuntimeError reason=\"Test error\" message= backtrace=\"\['/home/jdoe/app/libexec/app\.rb:1:in `foo''\]\"})
|
89
|
+
end
|
90
|
+
|
91
|
+
it "logs an exception object and prefix" do
|
92
|
+
logger.exception(exc, "app failed to foo")
|
93
|
+
subject.last[:message].should match(%r{exception class=RuntimeError reason=\"Test error\" message=\"app failed to foo\" backtrace=\"\['/home/jdoe/app/libexec/app\.rb:1:in `foo''\]\"})
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe :clean_trace do
|
98
|
+
subject { logger.clean_trace(fake_trace) }
|
99
|
+
it { should include("/home/jdoe/app/libexec/app.rb:1:in `foo'") }
|
100
|
+
it { should_not include("/usr/lib/ruby/gems/1.8/gems/madvertise-logging-0.1.0/lib/madvertise/logging/improved_logger.rb:42: in `info'") }
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should support silencing" do
|
104
|
+
logger.silence do |logger|
|
105
|
+
logger.info "This should never be logged"
|
106
|
+
end
|
107
|
+
|
108
|
+
subject.last.should be_nil
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should not discard messages if silencer is disabled globally" do
|
112
|
+
ImprovedLogger.silencer = false
|
113
|
+
|
114
|
+
logger.silence do |logger|
|
115
|
+
logger.info "This should actually be logged"
|
116
|
+
end
|
117
|
+
|
118
|
+
subject.last[:message].should == "This should actually be logged"
|
119
|
+
|
120
|
+
ImprovedLogger.silencer = true
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should support a token" do
|
124
|
+
token = "3d5e27f7-b97c-4adc-b1fd-adf1bd4314e0"
|
125
|
+
|
126
|
+
logger.token = token
|
127
|
+
logger.info "This should include a token"
|
128
|
+
subject.last[:message].should match(token)
|
129
|
+
|
130
|
+
logger.token = nil
|
131
|
+
logger.info "This should not include a token"
|
132
|
+
subject.last[:message].should_not match(token)
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should support save/restore on tokens" do
|
136
|
+
token1 = "3d5e27f7-b97c-4adc-b1fd-adf1bd4314e0"
|
137
|
+
token2 = "1bdef605-34b9-4ec7-9a1c-cb58efc8a635"
|
138
|
+
|
139
|
+
obj = Object.new
|
140
|
+
|
141
|
+
logger.token = token1
|
142
|
+
logger.info "This should include token1"
|
143
|
+
subject.last[:message].should match(token1)
|
144
|
+
|
145
|
+
logger.save_token(obj)
|
146
|
+
logger.token = token2
|
147
|
+
logger.info "This should include token2"
|
148
|
+
subject.last[:message].should match(token2)
|
149
|
+
|
150
|
+
logger.restore_token(obj)
|
151
|
+
logger.info "This should include token1"
|
152
|
+
subject.last[:message].should match(token1)
|
153
|
+
|
154
|
+
logger.token = nil
|
155
|
+
logger.info "This should not include a token"
|
156
|
+
subject.last[:message].should_not match(token1)
|
157
|
+
subject.last[:message].should_not match(token2)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should fall back to stderr if logfile is not writable" do
|
161
|
+
$stderr.should_receive(:write).with(/not writable.*STDERR/)
|
162
|
+
|
163
|
+
@logfile = "/not/writable/spec.log"
|
164
|
+
logger = ImprovedLogger.new(@logfile)
|
165
|
+
logger.level = :debug
|
166
|
+
|
167
|
+
$stderr.should_receive(:write).with(/test/)
|
168
|
+
logger.info "test"
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should fallback to standard logger if syslogger gem is missing" do
|
172
|
+
syslogger_paths = $:.select { |p| p.match(/gems\/.*syslogger-/) }
|
173
|
+
$:.replace($: - syslogger_paths)
|
174
|
+
|
175
|
+
$stderr.should_receive(:write).with(/reverting to STDERR/)
|
176
|
+
logger = ImprovedLogger.new(:syslog)
|
177
|
+
logger.logger.should be_instance_of(Logger)
|
178
|
+
|
179
|
+
$:.replace($: + syslogger_paths)
|
180
|
+
end
|
181
|
+
|
182
|
+
context "should behave like write-only IO and" do
|
183
|
+
subject { logger }
|
184
|
+
|
185
|
+
it { should be_a IO }
|
186
|
+
its(:logger) { should_not be_nil }
|
187
|
+
its(:flush) { should == logger }
|
188
|
+
its(:set_encoding) { should == logger }
|
189
|
+
its(:sync) { should == true }
|
190
|
+
its(:tty?) { should == false }
|
191
|
+
|
192
|
+
it "should close on close_write" do
|
193
|
+
logger.should_receive(:close)
|
194
|
+
logger.close_write
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should not implement closed?" do
|
198
|
+
expect { logger.closed? }.to raise_error(NotImplementedError)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "should not implement sync=" do
|
202
|
+
expect { logger.sync = false }.to raise_error(NotImplementedError)
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should implement readbyte, readchar, readline" do
|
206
|
+
{
|
207
|
+
:readbyte => :getbyte,
|
208
|
+
:readchar => :getc,
|
209
|
+
:readline => :gets,
|
210
|
+
}.each do |m, should|
|
211
|
+
logger.should_receive(should)
|
212
|
+
expect { logger.send(m) }.to raise_error(IOError)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
[
|
217
|
+
:bytes,
|
218
|
+
:chars,
|
219
|
+
:codepoints,
|
220
|
+
:lines,
|
221
|
+
:eof?,
|
222
|
+
:getbyte,
|
223
|
+
:getc,
|
224
|
+
:gets,
|
225
|
+
:pos,
|
226
|
+
:pos=,
|
227
|
+
:read,
|
228
|
+
:readlines,
|
229
|
+
:readpartial,
|
230
|
+
:rewind,
|
231
|
+
:seek,
|
232
|
+
:ungetbyte,
|
233
|
+
:ungetc
|
234
|
+
].each do |m|
|
235
|
+
it "should raise IOError for method #{m}" do
|
236
|
+
expect { logger.send(m) }.to raise_error(IOError)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
context "print functions" do
|
241
|
+
subject { logger.messages }
|
242
|
+
|
243
|
+
it "should support printf" do
|
244
|
+
logger.printf("%.2f %s", 1.12345, "foo")
|
245
|
+
subject.last[:message].should == "1.12 foo"
|
246
|
+
end
|
247
|
+
|
248
|
+
it "should support print" do
|
249
|
+
$,, old = ' ', $,
|
250
|
+
logger.print("foo", "bar", 123, ["baz", 345])
|
251
|
+
subject.last[:message].should == "foo bar 123 baz 345"
|
252
|
+
$, = old
|
253
|
+
end
|
254
|
+
|
255
|
+
it "should support puts" do
|
256
|
+
logger.puts("a", "b")
|
257
|
+
subject.last[:message].should == "b"
|
258
|
+
logger.puts(["c", "d"])
|
259
|
+
subject.last[:message].should == "d"
|
260
|
+
logger.puts(1, 2, 3)
|
261
|
+
subject.last[:message].should == "3"
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
context "buffer backend" do
|
267
|
+
let(:logger) { ImprovedLogger.new(:buffer) }
|
268
|
+
subject { logger }
|
269
|
+
|
270
|
+
its(:sync) { should == false }
|
271
|
+
|
272
|
+
it "should support a buffered logger" do
|
273
|
+
logger.info "test"
|
274
|
+
logger.buffer.should match(/test/)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
context "document backend" do
|
279
|
+
let(:logger) { ImprovedLogger.new(:document) }
|
280
|
+
|
281
|
+
before do
|
282
|
+
@msg = "test"
|
283
|
+
|
284
|
+
@now = Time.now
|
285
|
+
Time.stub(:now).and_return(@now)
|
286
|
+
|
287
|
+
@expected = {
|
288
|
+
severity: Logger::INFO,
|
289
|
+
time: @now.to_f,
|
290
|
+
progname: "rspec",
|
291
|
+
message: @msg
|
292
|
+
}
|
293
|
+
end
|
294
|
+
|
295
|
+
it "should store all messages as documents" do
|
296
|
+
logger.info(@msg)
|
297
|
+
logger.messages.first.should == @expected
|
298
|
+
end
|
299
|
+
|
300
|
+
it "should add custom attributes" do
|
301
|
+
attrs = {txid: 1234}
|
302
|
+
logger.logger.attrs = attrs
|
303
|
+
logger.info(@msg)
|
304
|
+
logger.messages.first.should == attrs.merge(@expected)
|
305
|
+
end
|
306
|
+
|
307
|
+
end
|
308
|
+
|
309
|
+
context "syslog backend" do
|
310
|
+
let(:logger) { ImprovedLogger.new(:syslog) }
|
311
|
+
subject { logger }
|
312
|
+
its(:sync) { should == true }
|
313
|
+
its(:logger) { should be_instance_of(Syslogger) }
|
314
|
+
end
|
315
|
+
|
316
|
+
context "unknown backend" do
|
317
|
+
it "should raise for unknown backends " do
|
318
|
+
expect { ImprovedLogger.new(:unknown_logger) }.to raise_error(RuntimeError)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
include Madvertise::Logging
|
4
|
+
|
5
|
+
describe MultiLogger do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@logger = ImprovedLogger.new
|
9
|
+
@logger.level = :debug
|
10
|
+
@ml = MultiLogger.new(@logger)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should support attach/detach of loggers" do
|
14
|
+
buflog = ImprovedLogger.new(:buffer)
|
15
|
+
@ml.attach(buflog)
|
16
|
+
|
17
|
+
$stderr.should_receive(:write).with(/test1/)
|
18
|
+
@ml.info("test1")
|
19
|
+
buflog.buffer.should match(/test1/)
|
20
|
+
|
21
|
+
@ml.detach(buflog)
|
22
|
+
|
23
|
+
$stderr.should_receive(:write).with(/test2/)
|
24
|
+
@ml.info("test2")
|
25
|
+
buflog.buffer.should_not match(/test2/)
|
26
|
+
end
|
27
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rspec'
|
3
|
+
|
4
|
+
require 'simplecov'
|
5
|
+
SimpleCov.start
|
6
|
+
|
7
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
8
|
+
require 'madvertise-logging'
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
# == Mock Framework
|
12
|
+
#
|
13
|
+
# RSpec uses it's own mocking framework by default. If you prefer to
|
14
|
+
# use mocha, flexmock or RR, uncomment the appropriate line:
|
15
|
+
#
|
16
|
+
# config.mock_with :mocha
|
17
|
+
# config.mock_with :flexmock
|
18
|
+
# config.mock_with :rr
|
19
|
+
end
|