loggability 0.14.0 → 0.18.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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/History.rdoc +55 -0
- data/Manifest.txt +15 -4
- data/{README.rdoc → README.md} +78 -71
- data/Rakefile +5 -77
- data/lib/loggability.rb +19 -27
- data/lib/loggability/constants.rb +2 -2
- data/lib/loggability/formatter.rb +13 -67
- data/lib/loggability/formatter/color.rb +2 -2
- data/lib/loggability/formatter/default.rb +69 -6
- data/lib/loggability/formatter/html.rb +5 -5
- data/lib/loggability/formatter/structured.rb +35 -0
- data/lib/loggability/log_device.rb +86 -0
- data/lib/loggability/log_device/appending.rb +34 -0
- data/lib/loggability/log_device/datadog.rb +90 -0
- data/lib/loggability/log_device/file.rb +37 -0
- data/lib/loggability/log_device/http.rb +310 -0
- data/lib/loggability/logclient.rb +2 -1
- data/lib/loggability/logger.rb +45 -42
- data/lib/loggability/loghost.rb +2 -1
- data/lib/loggability/override.rb +2 -1
- data/lib/loggability/spechelpers.rb +1 -1
- data/spec/helpers.rb +6 -12
- data/spec/loggability/formatter/color_spec.rb +3 -7
- data/spec/loggability/formatter/default_spec.rb +50 -0
- data/spec/loggability/formatter/html_spec.rb +7 -7
- data/spec/loggability/formatter/structured_spec.rb +61 -0
- data/spec/loggability/formatter_spec.rb +42 -29
- data/spec/loggability/log_device/appending_spec.rb +27 -0
- data/spec/loggability/log_device/datadog_spec.rb +67 -0
- data/spec/loggability/log_device/file_spec.rb +27 -0
- data/spec/loggability/log_device/http_spec.rb +217 -0
- data/spec/loggability/logger_spec.rb +46 -5
- data/spec/loggability/loghost_spec.rb +3 -1
- data/spec/loggability/override_spec.rb +18 -5
- data/spec/loggability/spechelpers_spec.rb +3 -4
- data/spec/loggability_spec.rb +16 -13
- metadata +77 -105
- metadata.gz.sig +0 -0
- data/ChangeLog +0 -621
data/lib/loggability/logger.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
2
|
# vim: set nosta noet ts=4 sw=4:
|
3
|
-
#
|
3
|
+
# frozen_string_literal: true
|
4
4
|
|
5
5
|
require 'logger'
|
6
6
|
require 'loggability' unless defined?( Loggability )
|
@@ -24,35 +24,6 @@ class Loggability::Logger < ::Logger
|
|
24
24
|
DEFAULT_SHIFT_SIZE = 1048576
|
25
25
|
|
26
26
|
|
27
|
-
# A log device that appends to the object it's constructed with instead of writing
|
28
|
-
# to a file descriptor or a file.
|
29
|
-
class AppendingLogDevice
|
30
|
-
|
31
|
-
### Create a new AppendingLogDevice that will append content to +array+.
|
32
|
-
def initialize( target )
|
33
|
-
@target = target
|
34
|
-
end
|
35
|
-
|
36
|
-
|
37
|
-
######
|
38
|
-
public
|
39
|
-
######
|
40
|
-
|
41
|
-
# The target of the log device
|
42
|
-
attr_reader :target
|
43
|
-
|
44
|
-
|
45
|
-
### Append the specified +message+ to the target.
|
46
|
-
def write( message )
|
47
|
-
@target << message
|
48
|
-
end
|
49
|
-
|
50
|
-
### No-op -- this is here just so Logger doesn't complain
|
51
|
-
def close; end
|
52
|
-
|
53
|
-
end # class AppendingLogDevice
|
54
|
-
|
55
|
-
|
56
27
|
# Proxy for the Logger that injects the name of the object it wraps as the 'progname'
|
57
28
|
# of each log message.
|
58
29
|
class ObjectNameProxy
|
@@ -182,6 +153,28 @@ class Loggability::Logger < ::Logger
|
|
182
153
|
end
|
183
154
|
|
184
155
|
|
156
|
+
### Log a message if the +severity+ is high enough. Overridden to account for
|
157
|
+
### the overridden #level.
|
158
|
+
def add( severity, message=nil, progname=nil )
|
159
|
+
return true if severity < self.sev_threshold
|
160
|
+
progname ||= @progname
|
161
|
+
|
162
|
+
unless message
|
163
|
+
if block_given?
|
164
|
+
message = yield
|
165
|
+
else
|
166
|
+
message = progname
|
167
|
+
progname = @progname
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
msg = self.format_message( self.format_severity(severity), Time.now, progname, message )
|
172
|
+
self.logdev.write( msg )
|
173
|
+
|
174
|
+
return true
|
175
|
+
end
|
176
|
+
|
177
|
+
|
185
178
|
### Append operator -- Override Logger's append so log messages always have
|
186
179
|
### formatting, and are always appended at :debug level.
|
187
180
|
def <<( message )
|
@@ -224,8 +217,7 @@ class Loggability::Logger < ::Logger
|
|
224
217
|
|
225
218
|
### Return the logger's level as a Symbol.
|
226
219
|
def level
|
227
|
-
|
228
|
-
return LOG_LEVEL_NAMES[ numeric_level ]
|
220
|
+
return LOG_LEVEL_NAMES[ self.sev_threshold ]
|
229
221
|
end
|
230
222
|
|
231
223
|
|
@@ -251,14 +243,17 @@ class Loggability::Logger < ::Logger
|
|
251
243
|
### logging to IO objects and files (given a filename in a String), this method can also
|
252
244
|
### set up logging to any object that responds to #<<.
|
253
245
|
def output_to( target, *args )
|
254
|
-
if target.is_a?( Logger::LogDevice ) ||
|
255
|
-
target.is_a?( Loggability::Logger::AppendingLogDevice )
|
246
|
+
if target.is_a?( Logger::LogDevice ) || target.is_a?( Loggability::LogDevice )
|
256
247
|
self.logdev = target
|
257
248
|
elsif target.respond_to?( :write ) || target.is_a?( String )
|
258
249
|
opts = { :shift_age => args.shift || 0, :shift_size => args.shift || 1048576 }
|
259
|
-
self.logdev = Logger::LogDevice.new( target, opts )
|
250
|
+
self.logdev = Logger::LogDevice.new( target, **opts )
|
251
|
+
elsif target.respond_to?( :any? ) && target.any?( Loggability::LogDevice )
|
252
|
+
self.logdev = MultiDevice.new( target )
|
260
253
|
elsif target.respond_to?( :<< )
|
261
|
-
self.logdev =
|
254
|
+
self.logdev = Loggability::LogDevice.create( :appending, target )
|
255
|
+
elsif target.is_a?( Symbol )
|
256
|
+
self.logdev = Loggability::LogDevice.create( target, *args )
|
262
257
|
else
|
263
258
|
raise ArgumentError, "don't know how to output to %p (a %p)" % [ target, target.class ]
|
264
259
|
end
|
@@ -278,14 +273,22 @@ class Loggability::Logger < ::Logger
|
|
278
273
|
|
279
274
|
### Format a log message using the current formatter and return it.
|
280
275
|
def format_message( severity, datetime, progname, msg )
|
281
|
-
self.formatter.call(severity, datetime, progname, msg)
|
276
|
+
self.formatter.call( severity, datetime, progname, msg )
|
277
|
+
end
|
278
|
+
|
279
|
+
|
280
|
+
### Return the formatted name of the given +severity+.
|
281
|
+
def format_severity( severity )
|
282
|
+
name = LOG_LEVEL_NAMES[ severity ] || severity.to_s
|
283
|
+
return name.upcase
|
282
284
|
end
|
283
285
|
|
284
286
|
|
285
|
-
### Set a new +formatter+ for the logger. If +formatter+ is +nil+ or +:default+, this causes
|
286
|
-
### logger to fall back to its default formatter. If it's a Symbol other than +:default+,
|
287
|
-
### for a similarly-named formatter under loggability/formatter/ and uses that. If
|
288
|
-
### an object that responds to #call (e.g., a Proc or a Method object), that
|
287
|
+
### Set a new +formatter+ for the logger. If +formatter+ is +nil+ or +:default+, this causes
|
288
|
+
### the logger to fall back to its default formatter. If it's a Symbol other than +:default+,
|
289
|
+
### it looks for a similarly-named formatter under loggability/formatter/ and uses that. If
|
290
|
+
### +formatter+ is an object that responds to #call (e.g., a Proc or a Method object), that
|
291
|
+
### object is used directly.
|
289
292
|
###
|
290
293
|
### Procs and methods should have the method signature: (severity, datetime, progname, msg).
|
291
294
|
###
|
data/lib/loggability/loghost.rb
CHANGED
data/lib/loggability/override.rb
CHANGED
data/spec/helpers.rb
CHANGED
@@ -1,15 +1,6 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
2
|
# vim: set nosta noet ts=4 sw=4:
|
3
|
-
#
|
4
|
-
|
5
|
-
BEGIN {
|
6
|
-
require 'pathname'
|
7
|
-
basedir = Pathname.new( __FILE__ ).dirname.parent
|
8
|
-
|
9
|
-
libdir = basedir + "lib"
|
10
|
-
|
11
|
-
$LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
|
12
|
-
}
|
3
|
+
# frozen_string_literal: true
|
13
4
|
|
14
5
|
# SimpleCov test coverage reporting; enable this using the :coverage rake task
|
15
6
|
if ENV['COVERAGE']
|
@@ -25,10 +16,10 @@ begin
|
|
25
16
|
rescue LoadError
|
26
17
|
end
|
27
18
|
|
19
|
+
require 'timecop'
|
28
20
|
require 'loggability'
|
29
21
|
require 'loggability/spechelpers'
|
30
22
|
|
31
|
-
|
32
23
|
# Helpers specific to Loggability specs
|
33
24
|
module SpecHelpers
|
34
25
|
|
@@ -107,10 +98,13 @@ RSpec.configure do |c|
|
|
107
98
|
c.mock_with( :rspec ) do |mock|
|
108
99
|
mock.syntax = :expect
|
109
100
|
end
|
101
|
+
c.warnings = true
|
102
|
+
c.profile_examples = 5
|
110
103
|
|
111
104
|
c.include( SpecHelpers )
|
112
105
|
c.include( Loggability::SpecHelpers )
|
113
106
|
c.filter_run_excluding( :configurability ) unless defined?( Configurability )
|
114
107
|
|
108
|
+
|
115
109
|
end
|
116
110
|
|
@@ -1,13 +1,9 @@
|
|
1
|
-
# -*-
|
2
|
-
#
|
1
|
+
# -*- ruby -*-
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
# frozen_string_literal: true
|
3
4
|
|
4
5
|
require_relative '../../helpers'
|
5
6
|
|
6
|
-
require 'tempfile'
|
7
|
-
require 'rspec'
|
8
|
-
|
9
|
-
require 'loggability/logger'
|
10
|
-
require 'loggability/formatter'
|
11
7
|
require 'loggability/formatter/color'
|
12
8
|
|
13
9
|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require_relative '../../helpers'
|
6
|
+
|
7
|
+
require 'loggability/formatter/default'
|
8
|
+
|
9
|
+
|
10
|
+
describe Loggability::Formatter::Default do
|
11
|
+
|
12
|
+
it "formats messages with the pattern it's constructed with" do
|
13
|
+
formatter = described_class.new( '[%5$s] %7$s' )
|
14
|
+
result = formatter.call( 'INFO', Time.at(1336286481), nil, 'Foom.' )
|
15
|
+
expect( result ).to match(/\[INFO\] Foom./i)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
it "formats exceptions into useful messages" do
|
20
|
+
formatter = described_class.new( '[%5$s] %7$s' )
|
21
|
+
msg = nil
|
22
|
+
|
23
|
+
begin
|
24
|
+
raise ArgumentError, "invalid argument"
|
25
|
+
rescue => err
|
26
|
+
msg = formatter.call( 'INFO', Time.at(1336286481), nil, err )
|
27
|
+
end
|
28
|
+
|
29
|
+
expect( msg ).to match(/\[INFO\] ArgumentError: invalid argument/i)
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
it "formats regular objects into useful messages" do
|
34
|
+
formatter = described_class.new( '[%5$s] %7$s' )
|
35
|
+
result = formatter.call( 'INFO', Time.at(1336286481), nil, Object.new )
|
36
|
+
|
37
|
+
expect( result ).to match(/\[INFO\] #<Object:0x[[:xdigit:]]+>/i)
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
it "includes the thread ID if logging from a thread other than the main thread" do
|
42
|
+
formatter = described_class.new( '%4$d' )
|
43
|
+
thr = Thread.new do
|
44
|
+
formatter.call( 'INFO', Time.now, nil, 'Foom.' )
|
45
|
+
end
|
46
|
+
expect( thr.value ).to eq( thr.object_id.to_s )
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
@@ -1,13 +1,9 @@
|
|
1
|
-
# -*-
|
2
|
-
#
|
1
|
+
# -*- ruby -*-
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
# frozen_string_literal: true
|
3
4
|
|
4
5
|
require_relative '../../helpers'
|
5
6
|
|
6
|
-
require 'tempfile'
|
7
|
-
require 'rspec'
|
8
|
-
|
9
|
-
require 'loggability/logger'
|
10
|
-
require 'loggability/formatter'
|
11
7
|
require 'loggability/formatter/html'
|
12
8
|
|
13
9
|
|
@@ -15,12 +11,14 @@ describe Loggability::Formatter::HTML do
|
|
15
11
|
|
16
12
|
subject { described_class.new }
|
17
13
|
|
14
|
+
|
18
15
|
it "formats messages as HTML" do
|
19
16
|
expect(
|
20
17
|
subject.call( 'INFO', Time.at(1336286481), nil, "Foom." )
|
21
18
|
).to match( %r{<span class="log-message-text">Foom.</span>}i )
|
22
19
|
end
|
23
20
|
|
21
|
+
|
24
22
|
it "formats exceptions into useful messages" do
|
25
23
|
msg = nil
|
26
24
|
|
@@ -35,12 +33,14 @@ describe Loggability::Formatter::HTML do
|
|
35
33
|
expect( msg ).to match( %r{ from <span class=\"log-exc-firstframe\">}i )
|
36
34
|
end
|
37
35
|
|
36
|
+
|
38
37
|
it "formats regular objects into useful messages" do
|
39
38
|
expect(
|
40
39
|
subject.call( 'INFO', Time.at(1336286481), nil, Object.new )
|
41
40
|
).to match( %r{<span class=\"log-message-text\">#<Object:0x[[:xdigit:]]+></span>} )
|
42
41
|
end
|
43
42
|
|
43
|
+
|
44
44
|
it "escapes the 'progname' part of log messages" do
|
45
45
|
progname = "#<Class:0x007f9efa153d08>:0x7f9efa153c18"
|
46
46
|
expect(
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require_relative '../../helpers'
|
6
|
+
|
7
|
+
require 'loggability/formatter/structured'
|
8
|
+
|
9
|
+
|
10
|
+
describe Loggability::Formatter::Structured do
|
11
|
+
|
12
|
+
before( :each ) do
|
13
|
+
ENV['TZ'] = 'UTC'
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
it "outputs a stream of JSON objects" do
|
18
|
+
expect(
|
19
|
+
subject.call('INFO', Time.at(1336286481), nil, "Foom.")
|
20
|
+
).to eq(
|
21
|
+
%q|{"@version":1,"@timestamp":"2012-05-06T06:41:21.000+00:00"| +
|
22
|
+
%q|,"level":"INFO","progname":null,"message":"Foom."}|
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
it "includes a time even if called without one" do
|
28
|
+
Timecop.freeze( Time.at(1563114765.123) ) do
|
29
|
+
expect(
|
30
|
+
subject.call('WARN', nil, nil, "Right.")
|
31
|
+
).to match( %r(
|
32
|
+
\{
|
33
|
+
"@version":1,
|
34
|
+
"@timestamp":"2019-07-14T14:32:45\.\d{3}\+00:00",
|
35
|
+
"level":"WARN",
|
36
|
+
"progname":null,
|
37
|
+
"message":"Right\."
|
38
|
+
\}
|
39
|
+
)x )
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
it "defaults to DEBUG severity" do
|
45
|
+
Timecop.freeze( Time.at(1563114765.123) ) do
|
46
|
+
expect(
|
47
|
+
subject.call(nil, nil, nil, "Crane.")
|
48
|
+
).to match( %r(
|
49
|
+
\{
|
50
|
+
"@version":1,
|
51
|
+
"@timestamp":"2019-07-14T14:32:45\.\d{3}\+00:00",
|
52
|
+
"level":"DEBUG",
|
53
|
+
"progname":null,
|
54
|
+
"message":"Crane\."
|
55
|
+
\}
|
56
|
+
)x )
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
@@ -1,52 +1,65 @@
|
|
1
|
-
# -*-
|
1
|
+
# -*- ruby -*-
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
# frozen_string_literal: true
|
2
4
|
|
3
5
|
require_relative '../helpers'
|
4
6
|
|
5
|
-
require 'tempfile'
|
6
|
-
require 'rspec'
|
7
|
-
|
8
|
-
require 'loggability/logger'
|
9
7
|
require 'loggability/formatter'
|
10
|
-
require 'loggability/formatter/default'
|
11
8
|
|
12
9
|
|
13
10
|
describe Loggability::Formatter do
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
before( :all ) do
|
13
|
+
@actual_derivatives = described_class.derivatives.dup
|
14
|
+
end
|
15
|
+
|
16
|
+
after( :all ) do
|
17
|
+
described_class.derivatives.replace( @actual_derivatives )
|
19
18
|
end
|
20
19
|
|
21
20
|
|
22
|
-
|
23
|
-
formatter = described_class.new( '[%5$s] %7$s' )
|
24
|
-
msg = nil
|
21
|
+
describe "concrete subclasses" do
|
25
22
|
|
26
|
-
|
27
|
-
raise ArgumentError, "invalid argument"
|
28
|
-
rescue => err
|
29
|
-
msg = formatter.call( 'INFO', Time.at(1336286481), nil, err )
|
23
|
+
class Test < described_class
|
30
24
|
end
|
31
25
|
|
32
|
-
expect( msg ).to match(/\[INFO\] ArgumentError: invalid argument/i)
|
33
|
-
end
|
34
26
|
|
27
|
+
it "must implement #call" do
|
28
|
+
expect {
|
29
|
+
Test.new.call( 'INFO', Time.now, nil, "A message." )
|
30
|
+
}.to raise_error( /doesn't implement required method/i )
|
31
|
+
end
|
35
32
|
|
36
|
-
it "formats regular objects into useful messages" do
|
37
|
-
formatter = described_class.new( '[%5$s] %7$s' )
|
38
|
-
result = formatter.call( 'INFO', Time.at(1336286481), nil, Object.new )
|
39
33
|
|
40
|
-
|
41
|
-
|
34
|
+
it "is tracked by the base class" do
|
35
|
+
expect( described_class.derivatives ).to include( test: Test )
|
36
|
+
end
|
42
37
|
|
43
38
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
formatter.call( 'INFO', Time.now, nil, 'Foom.' )
|
39
|
+
it "is tracked if its anonymous" do
|
40
|
+
subclass = Class.new( described_class )
|
41
|
+
expect( described_class.derivatives.values ).to include( subclass )
|
48
42
|
end
|
49
|
-
|
43
|
+
|
44
|
+
|
45
|
+
it "can be loaded by name" do
|
46
|
+
expect( described_class ).to receive( :require ).
|
47
|
+
with( "loggability/formatter/test" )
|
48
|
+
|
49
|
+
expect( described_class.create(:test) ).to be_an_instance_of( Test )
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
it "raises a LoadError if loading doesn't work" do
|
54
|
+
expect( described_class ).to receive( :require ).
|
55
|
+
with( "loggability/formatter/circus" )
|
56
|
+
|
57
|
+
expect {
|
58
|
+
described_class.create( :circus )
|
59
|
+
}.to raise_error( LoadError, /didn't load a class/i )
|
60
|
+
end
|
61
|
+
|
62
|
+
|
50
63
|
end
|
51
64
|
|
52
65
|
end
|