loggability 0.14.0 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/History.rdoc +55 -0
  5. data/Manifest.txt +15 -4
  6. data/{README.rdoc → README.md} +78 -71
  7. data/Rakefile +5 -77
  8. data/lib/loggability.rb +19 -27
  9. data/lib/loggability/constants.rb +2 -2
  10. data/lib/loggability/formatter.rb +13 -67
  11. data/lib/loggability/formatter/color.rb +2 -2
  12. data/lib/loggability/formatter/default.rb +69 -6
  13. data/lib/loggability/formatter/html.rb +5 -5
  14. data/lib/loggability/formatter/structured.rb +35 -0
  15. data/lib/loggability/log_device.rb +86 -0
  16. data/lib/loggability/log_device/appending.rb +34 -0
  17. data/lib/loggability/log_device/datadog.rb +90 -0
  18. data/lib/loggability/log_device/file.rb +37 -0
  19. data/lib/loggability/log_device/http.rb +310 -0
  20. data/lib/loggability/logclient.rb +2 -1
  21. data/lib/loggability/logger.rb +45 -42
  22. data/lib/loggability/loghost.rb +2 -1
  23. data/lib/loggability/override.rb +2 -1
  24. data/lib/loggability/spechelpers.rb +1 -1
  25. data/spec/helpers.rb +6 -12
  26. data/spec/loggability/formatter/color_spec.rb +3 -7
  27. data/spec/loggability/formatter/default_spec.rb +50 -0
  28. data/spec/loggability/formatter/html_spec.rb +7 -7
  29. data/spec/loggability/formatter/structured_spec.rb +61 -0
  30. data/spec/loggability/formatter_spec.rb +42 -29
  31. data/spec/loggability/log_device/appending_spec.rb +27 -0
  32. data/spec/loggability/log_device/datadog_spec.rb +67 -0
  33. data/spec/loggability/log_device/file_spec.rb +27 -0
  34. data/spec/loggability/log_device/http_spec.rb +217 -0
  35. data/spec/loggability/logger_spec.rb +46 -5
  36. data/spec/loggability/loghost_spec.rb +3 -1
  37. data/spec/loggability/override_spec.rb +18 -5
  38. data/spec/loggability/spechelpers_spec.rb +3 -4
  39. data/spec/loggability_spec.rb +16 -13
  40. metadata +77 -105
  41. metadata.gz.sig +0 -0
  42. data/ChangeLog +0 -621
@@ -1,5 +1,6 @@
1
1
  # -*- ruby -*-
2
- #encoding: utf-8
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # frozen_string_literal: true
3
4
 
4
5
  require 'loggability' unless defined?( Loggability )
5
6
 
@@ -1,6 +1,6 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
2
  # vim: set nosta noet ts=4 sw=4:
3
- # encoding: utf-8
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
- numeric_level = super
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 = AppendingLogDevice.new( target )
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 the
286
- ### logger to fall back to its default formatter. If it's a Symbol other than +:default+, it looks
287
- ### for a similarly-named formatter under loggability/formatter/ and uses that. If +formatter+ is
288
- ### an object that responds to #call (e.g., a Proc or a Method object), that object is used directly.
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
  ###
@@ -1,5 +1,6 @@
1
1
  # -*- ruby -*-
2
- #encoding: utf-8
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # frozen_string_literal: true
3
4
 
4
5
  require 'loggability' unless defined?( Loggability )
5
6
 
@@ -1,5 +1,6 @@
1
1
  # -*- ruby -*-
2
- #encoding: utf-8
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # frozen_string_literal: true
3
4
 
4
5
  require 'monitor'
5
6
  require 'loggability' unless defined?( Loggability )
@@ -1,6 +1,6 @@
1
1
  # -*- ruby -*-
2
2
  # vim: set nosta noet ts=4 sw=4:
3
- # encoding: utf-8
3
+ # frozen_string_literal: true
4
4
 
5
5
  require 'loggability' unless defined?( Loggability )
6
6
 
@@ -1,15 +1,6 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
2
  # vim: set nosta noet ts=4 sw=4:
3
- # encoding: utf-8
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
- # -*- rspec -*-
2
- #encoding: utf-8
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
- # -*- rspec -*-
2
- #encoding: utf-8
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\">#&lt;Object:0x[[:xdigit:]]+&gt;</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
- # -*- rspec -*-
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
- it "formats messages with the pattern it's constructed with" do
16
- formatter = described_class.new( '[%5$s] %7$s' )
17
- result = formatter.call( 'INFO', Time.at(1336286481), nil, 'Foom.' )
18
- expect( result ).to match(/\[INFO\] Foom./i)
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
- it "formats exceptions into useful messages" do
23
- formatter = described_class.new( '[%5$s] %7$s' )
24
- msg = nil
21
+ describe "concrete subclasses" do
25
22
 
26
- begin
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
- expect( result ).to match(/\[INFO\] #<Object:0x[[:xdigit:]]+>/i)
41
- end
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
- it "includes the thread ID if logging from a thread other than the main thread" do
45
- formatter = described_class.new( '%4$d' )
46
- thr = Thread.new do
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
- expect( thr.value ).to eq( thr.object_id.to_s )
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