logging 2.0.0 → 2.3.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.
Files changed (55) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +8 -5
  4. data/History.txt +59 -0
  5. data/LICENSE +22 -0
  6. data/README.md +20 -41
  7. data/Rakefile +2 -2
  8. data/examples/appenders.rb +1 -1
  9. data/examples/layouts.rb +1 -1
  10. data/examples/lazy.rb +1 -1
  11. data/examples/mdc.rb +2 -2
  12. data/examples/rails4.rb +21 -0
  13. data/examples/reusing_layouts.rb +51 -0
  14. data/lib/logging.rb +99 -9
  15. data/lib/logging/appender.rb +13 -34
  16. data/lib/logging/appenders/buffering.rb +130 -59
  17. data/lib/logging/appenders/console.rb +68 -57
  18. data/lib/logging/appenders/file.rb +43 -22
  19. data/lib/logging/appenders/io.rb +22 -16
  20. data/lib/logging/appenders/rolling_file.rb +60 -26
  21. data/lib/logging/appenders/string_io.rb +1 -1
  22. data/lib/logging/appenders/syslog.rb +3 -4
  23. data/lib/logging/color_scheme.rb +1 -1
  24. data/lib/logging/diagnostic_context.rb +100 -73
  25. data/lib/logging/layout.rb +144 -16
  26. data/lib/logging/layouts/parseable.rb +50 -12
  27. data/lib/logging/layouts/pattern.rb +8 -9
  28. data/lib/logging/log_event.rb +19 -12
  29. data/lib/logging/logger.rb +117 -95
  30. data/lib/logging/proxy.rb +1 -1
  31. data/lib/logging/rails_compat.rb +4 -13
  32. data/lib/logging/version.rb +1 -1
  33. data/logging.gemspec +31 -32
  34. data/script/console +8 -0
  35. data/test/appenders/{test_periodic_flushing.rb → test_async_flushing.rb} +67 -14
  36. data/test/appenders/test_buffered_io.rb +19 -18
  37. data/test/appenders/test_console.rb +55 -12
  38. data/test/appenders/test_file.rb +48 -28
  39. data/test/appenders/test_rolling_file.rb +18 -12
  40. data/test/appenders/test_syslog.rb +6 -0
  41. data/test/benchmark.rb +42 -18
  42. data/test/layouts/test_json.rb +14 -1
  43. data/test/layouts/test_nested_exceptions.rb +124 -0
  44. data/test/layouts/test_pattern.rb +16 -3
  45. data/test/layouts/test_yaml.rb +15 -1
  46. data/test/performance.rb +66 -0
  47. data/test/setup.rb +26 -30
  48. data/test/test_appender.rb +2 -4
  49. data/test/test_layout.rb +49 -0
  50. data/test/test_log_event.rb +10 -2
  51. data/test/test_logger.rb +20 -3
  52. data/test/test_logging.rb +75 -4
  53. data/test/test_mapped_diagnostic_context.rb +15 -6
  54. data/test/test_nested_diagnostic_context.rb +6 -1
  55. metadata +23 -17
@@ -13,9 +13,15 @@ module TestAppenders
13
13
  super
14
14
  Logging.init
15
15
 
16
- @fn = File.expand_path('test.log', TMP)
17
- @fn_fmt = File.expand_path('test.%d.log', TMP)
18
- @glob = File.expand_path('*.log', TMP)
16
+ @fn = File.expand_path('test.log', @tmpdir)
17
+ @fn_fmt = File.expand_path('test.%d.log', @tmpdir)
18
+ @glob = File.expand_path('*.log', @tmpdir)
19
+ end
20
+
21
+ def test_factory_method_validates_input
22
+ assert_raise(ArgumentError) do
23
+ Logging.appenders.rolling_file
24
+ end
19
25
  end
20
26
 
21
27
  def test_initialize
@@ -87,8 +93,8 @@ module TestAppenders
87
93
  end
88
94
 
89
95
  def test_age
90
- d_glob = File.join(TMP, 'test.*.log')
91
- dt_glob = File.join(TMP, 'test.*-*.log')
96
+ d_glob = File.join(@tmpdir, 'test.*.log')
97
+ dt_glob = File.join(@tmpdir, 'test.*-*.log')
92
98
  age_fn = @fn + '.age'
93
99
 
94
100
  assert_equal [], Dir.glob(@glob)
@@ -199,7 +205,7 @@ module TestAppenders
199
205
 
200
206
  begin
201
207
  pwd = Dir.pwd
202
- Dir.chdir TMP
208
+ Dir.chdir @tmpdir
203
209
 
204
210
  ap << 'X' * 100; ap.flush
205
211
  assert_equal 1, Dir.glob(@glob).length
@@ -243,9 +249,9 @@ module TestAppenders
243
249
  end
244
250
 
245
251
  def test_custom_numberd_filename
246
- fn = File.expand_path('test.log{{.%d}}', TMP)
247
- filename = File.expand_path('test.log', TMP)
248
- glob = File.expand_path('test.log.*', TMP)
252
+ fn = File.expand_path('test.log{{.%d}}', @tmpdir)
253
+ filename = File.expand_path('test.log', @tmpdir)
254
+ glob = File.expand_path('test.log.*', @tmpdir)
249
255
 
250
256
  assert_equal [], Dir.glob(glob)
251
257
  ap = Logging.appenders.rolling_file(NAME, :filename => fn, :size => 100, :keep => 2)
@@ -279,10 +285,10 @@ module TestAppenders
279
285
  end
280
286
 
281
287
  def test_custom_timestamp_filename
282
- fn = File.expand_path('test{{.%S:%M}}.log', TMP)
283
- filename = File.expand_path('test.log', TMP)
288
+ fn = File.expand_path('test{{.%S:%M}}.log', @tmpdir)
289
+ filename = File.expand_path('test.log', @tmpdir)
284
290
  age_file = filename + '.age'
285
- glob = File.expand_path('test.*.log', TMP)
291
+ glob = File.expand_path('test.*.log', @tmpdir)
286
292
 
287
293
  assert_equal [], Dir.glob(glob)
288
294
  ap = Logging.appenders.rolling_file(NAME, :filename => fn, :age => 1, :keep => 2)
@@ -19,6 +19,12 @@ module TestAppenders
19
19
  @logopt |= ::Syslog::LOG_PERROR if defined?(::Syslog::LOG_PERROR)
20
20
  end
21
21
 
22
+ def test_factory_method_validates_input
23
+ assert_raise(ArgumentError) do
24
+ Logging.appenders.syslog
25
+ end
26
+ end
27
+
22
28
  def test_append
23
29
  return if RUBY_PLATFORM =~ %r/cygwin|java/i
24
30
 
@@ -1,4 +1,3 @@
1
-
2
1
  require 'rubygems'
3
2
 
4
3
  libpath = File.expand_path('../../lib', __FILE__)
@@ -22,16 +21,14 @@ module Logging
22
21
  def run
23
22
  this_many = 300_000
24
23
 
25
- Logging.appenders.string_io(
26
- 'sio',
27
- :layout => Logging.layouts.pattern(
28
- :pattern => '%.1l, [%d] %5l -- %c: %m\n',
29
- :date_pattern => "%Y-%m-%dT%H:%M:%S.%s"
30
- )
31
- )
24
+ pattern = Logging.layouts.pattern \
25
+ :pattern => '%.1l, [%d #%p] %5l -- %c: %m\n',
26
+ :date_pattern => "%Y-%m-%dT%H:%M:%S.%s"
27
+
28
+ Logging.appenders.string_io('sio', :layout => pattern)
32
29
  sio = Logging.appenders['sio'].sio
33
30
 
34
- logging = ::Logging.logger('benchmark')
31
+ logging = ::Logging.logger['benchmark']
35
32
  logging.level = :warn
36
33
  logging.appenders = 'sio'
37
34
 
@@ -39,16 +36,16 @@ module Logging
39
36
  logger.level = ::Logger::WARN
40
37
 
41
38
  log4r = if $log4r
42
- x = ::Log4r::Logger.new('benchmark')
43
- x.level = ::Log4r::WARN
44
- x.add ::Log4r::IOOutputter.new(
39
+ l4r = ::Log4r::Logger.new('benchmark')
40
+ l4r.level = ::Log4r::WARN
41
+ l4r.add ::Log4r::IOOutputter.new(
45
42
  'benchmark', sio,
46
43
  :formatter => ::Log4r::PatternFormatter.new(
47
44
  :pattern => "%.1l, [%d #\#{Process.pid}] %5l : %M\n",
48
- :date_pattern => "%Y-%m-%dT%H:%M:%S.\#{Time.now.usec}"
45
+ :date_pattern => "%Y-%m-%dT%H:%M:%S.%6N"
49
46
  )
50
47
  )
51
- x
48
+ l4r
52
49
  end
53
50
 
54
51
  puts "== Debug (not logged) ==\n"
@@ -76,14 +73,41 @@ module Logging
76
73
  bm.report('Logger:') {this_many.times {logger << 'logged'}}
77
74
  puts "Log4r: not supported" if log4r
78
75
  end
79
- end
80
76
 
81
- end # class Benchmark
82
- end # module Logging
77
+ write_size = 250
78
+ auto_flushing_size = 500
79
+
80
+ logging_async = ::Logging.logger['AsyncFile']
81
+ logging_async.level = :info
82
+ logging_async.appenders = Logging.appenders.file \
83
+ 'benchmark_async.log',
84
+ :layout => pattern,
85
+ :write_size => write_size,
86
+ :auto_flushing => auto_flushing_size,
87
+ :async => true
88
+
89
+ logging_sync = ::Logging.logger['SyncFile']
90
+ logging_sync.level = :info
91
+ logging_sync.appenders = Logging.appenders.file \
92
+ 'benchmark_sync.log',
93
+ :layout => pattern,
94
+ :write_size => write_size,
95
+ :auto_flushing => auto_flushing_size,
96
+ :async => false
97
+
98
+ puts "\n== File ==\n"
99
+ ::Benchmark.bm(20) do |bm|
100
+ bm.report('Logging (Async):') {this_many.times { |n| logging_async.info "Iteration #{n}"}}
101
+ bm.report('Logging (Sync):') {this_many.times { |n| logging_sync.info "Iteration #{n}"}}
102
+ end
83
103
 
104
+ File.delete('benchmark_async.log')
105
+ File.delete('benchmark_sync.log')
106
+ end
107
+ end
108
+ end
84
109
 
85
110
  if __FILE__ == $0
86
111
  bm = ::Logging::Benchmark.new
87
112
  bm.run
88
113
  end
89
-
@@ -78,7 +78,7 @@ module TestLayouts
78
78
  'log message', false)
79
79
  event.file = 'test_file.rb'
80
80
  event.line = 123
81
- event.method = 'method_name'
81
+ event.method_name = 'method_name'
82
82
 
83
83
  @layout.items = %w[logger]
84
84
  assert_equal %Q[{"logger":"TestLogger"}\n], @layout.format(event)
@@ -166,6 +166,19 @@ module TestLayouts
166
166
  assert_match %r/"ndc":\[\]/, format
167
167
  end
168
168
 
169
+ def test_utc_offset
170
+ layout = Logging.layouts.json(:items => %w[timestamp])
171
+ event = Logging::LogEvent.new('TimestampLogger', @levels['info'], 'log message', false)
172
+ event.time = Time.utc(2016, 12, 1, 12, 0, 0).freeze
173
+
174
+ assert_equal %Q/{"timestamp":"2016-12-01T12:00:00.000000Z"}\n/, layout.format(event)
175
+
176
+ layout.utc_offset = "-06:00"
177
+ assert_equal %Q/{"timestamp":"2016-12-01T06:00:00.000000-06:00"}\n/, layout.format(event)
178
+
179
+ layout.utc_offset = "+01:00"
180
+ assert_equal %Q/{"timestamp":"2016-12-01T13:00:00.000000+01:00"}\n/, layout.format(event)
181
+ end
169
182
  end # class TestJson
170
183
  end # module TestLayouts
171
184
  end # module TestLogging
@@ -0,0 +1,124 @@
1
+
2
+ require_relative '../setup'
3
+
4
+ module TestLogging
5
+ module TestLayouts
6
+ class TestNestedExceptions < Test::Unit::TestCase
7
+ include LoggingTestCase
8
+
9
+ def test_basic_format_obj
10
+ err = nil
11
+ begin
12
+ begin
13
+ raise ArgumentError, 'nested exception'
14
+ rescue
15
+ raise StandardError, 'root exception'
16
+ end
17
+ rescue => e
18
+ err = e
19
+ end
20
+
21
+ layout = Logging.layouts.basic({})
22
+ log = layout.format_obj(err)
23
+ assert_not_nil log.index('<StandardError> root exception')
24
+
25
+ if err.respond_to?(:cause)
26
+ assert_not_nil log.index('<ArgumentError> nested exception')
27
+ assert(log.index('<StandardError> root exception') < log.index('<ArgumentError> nested exception'))
28
+ end
29
+ end
30
+
31
+ def test_cause_depth_limiting
32
+ err = nil
33
+ begin
34
+ begin
35
+ begin
36
+ raise TypeError, 'nested exception 2'
37
+ rescue
38
+ raise ArgumentError, 'nested exception 1'
39
+ end
40
+ rescue
41
+ raise StandardError, 'root exception'
42
+ end
43
+ rescue => e
44
+ err = e
45
+ end
46
+
47
+ layout = Logging.layouts.basic(cause_depth: 1)
48
+ log = layout.format_obj(err)
49
+ assert_not_nil log.index('<StandardError> root exception')
50
+
51
+ if err.respond_to?(:cause)
52
+ assert_not_nil log.index('<ArgumentError> nested exception 1')
53
+ assert_nil log.index('<TypeError> nested exception 2')
54
+ assert_equal '--- Further #cause backtraces were omitted ---', log.split("\n\t").last
55
+ end
56
+ end
57
+
58
+ def test_parseable_format_obj
59
+ err = nil
60
+ begin
61
+ begin
62
+ raise ArgumentError, 'nested exception'
63
+ rescue
64
+ raise StandardError, 'root exception'
65
+ end
66
+ rescue => e
67
+ err = e
68
+ end
69
+
70
+ layout = Logging.layouts.parseable.new
71
+ log = layout.format_obj(err)
72
+ assert_equal 'StandardError', log[:class]
73
+ assert_equal 'root exception', log[:message]
74
+ assert log[:backtrace].size > 0
75
+
76
+ if err.respond_to?(:cause)
77
+ assert_not_nil log[:cause]
78
+
79
+ log = log[:cause]
80
+ assert_equal 'ArgumentError', log[:class]
81
+ assert_equal 'nested exception', log[:message]
82
+ assert_nil log[:cause]
83
+ assert log[:backtrace].size > 0
84
+ end
85
+ end
86
+
87
+ def test_parseable_cause_depth_limiting
88
+ err = nil
89
+ begin
90
+ begin
91
+ begin
92
+ raise TypeError, 'nested exception 2'
93
+ rescue
94
+ raise ArgumentError, 'nested exception 1'
95
+ end
96
+ rescue
97
+ raise StandardError, 'root exception'
98
+ end
99
+ rescue => e
100
+ err = e
101
+ end
102
+
103
+ layout = Logging.layouts.parseable.new(cause_depth: 1)
104
+ log = layout.format_obj(err)
105
+
106
+ assert_equal 'StandardError', log[:class]
107
+ assert_equal 'root exception', log[:message]
108
+ assert log[:backtrace].size > 0
109
+
110
+ if err.respond_to?(:cause)
111
+ assert_not_nil log[:cause]
112
+
113
+ log = log[:cause]
114
+ assert_equal 'ArgumentError', log[:class]
115
+ assert_equal 'nested exception 1', log[:message]
116
+ assert_equal({message: "Further #cause backtraces were omitted"}, log[:cause])
117
+ assert log[:backtrace].size > 0
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ require 'pp'
@@ -26,7 +26,7 @@ module TestLayouts
26
26
 
27
27
  @layout.date_method = 'usec'
28
28
  assert_equal 'usec', @layout.date_method
29
- assert_instance_of Fixnum, @layout.format_date(Time.now)
29
+ assert_kind_of Integer, @layout.format_date(Time.now)
30
30
 
31
31
  @layout.date_method = :to_s
32
32
  assert_equal :to_s, @layout.date_method
@@ -38,7 +38,7 @@ module TestLayouts
38
38
 
39
39
  @layout.date_method = 'usec'
40
40
  assert_equal 'usec', @layout.date_method
41
- assert_instance_of Fixnum, @layout.format_date(Time.now)
41
+ assert_kind_of Integer, @layout.format_date(Time.now)
42
42
  end
43
43
 
44
44
  def test_date_pattern
@@ -105,7 +105,7 @@ module TestLayouts
105
105
  'log message', false)
106
106
  event.file = 'test_file.rb'
107
107
  event.line = '123'
108
- event.method = 'method_name'
108
+ event.method_name = 'method_name'
109
109
 
110
110
  @layout.pattern = '%c'
111
111
  assert_equal 'TestLogger', @layout.format(event)
@@ -230,6 +230,19 @@ module TestLayouts
230
230
  assert_equal 'context a', @layout.format(event)
231
231
  end
232
232
 
233
+ def test_utc_offset
234
+ layout = Logging.layouts.pattern(:pattern => "%d", :utc_offset => "UTC")
235
+ event = Logging::LogEvent.new('DateLogger', @levels['info'], 'log message', false)
236
+ event.time = Time.utc(2016, 12, 1, 12, 0, 0).freeze
237
+
238
+ assert_equal "2016-12-01T12:00:00", layout.format(event)
239
+
240
+ layout.utc_offset = "-06:00"
241
+ assert_equal "2016-12-01T06:00:00", layout.format(event)
242
+
243
+ layout.utc_offset = "+01:00"
244
+ assert_equal "2016-12-01T13:00:00", layout.format(event)
245
+ end
233
246
  end # TestBasic
234
247
  end # TestLayouts
235
248
  end # TestLogging
@@ -68,7 +68,7 @@ module TestLayouts
68
68
  'log message', false)
69
69
  event.file = 'test_file.rb'
70
70
  event.line = 123
71
- event.method = 'method_name'
71
+ event.method_name = 'method_name'
72
72
 
73
73
  @layout.items = %w[logger]
74
74
  assert_match %r/\A--- ?\nlogger: TestLogger\n/, @layout.format(event)
@@ -148,6 +148,20 @@ module TestLayouts
148
148
  assert_match %r/\nndc: \[\]\n/, format
149
149
  end
150
150
 
151
+ def test_utc_offset
152
+ layout = Logging.layouts.yaml(:items => %w[timestamp])
153
+ event = Logging::LogEvent.new('TimestampLogger', @levels['info'], 'log message', false)
154
+ event.time = Time.utc(2016, 12, 1, 12, 0, 0).freeze
155
+
156
+ assert_equal %Q{---\ntimestamp: '2016-12-01T12:00:00.000000Z'\n}, layout.format(event)
157
+
158
+ layout.utc_offset = "-06:00"
159
+ assert_equal %Q{---\ntimestamp: '2016-12-01T06:00:00.000000-06:00'\n}, layout.format(event)
160
+
161
+ layout.utc_offset = "+01:00"
162
+ assert_equal %Q{---\ntimestamp: '2016-12-01T13:00:00.000000+01:00'\n}, layout.format(event)
163
+ end
164
+
151
165
  private
152
166
 
153
167
  def assert_yaml_match( expected, actual )
@@ -0,0 +1,66 @@
1
+ #
2
+ # The peformance script is used to output a performance analysis page for the
3
+ # Logging framework. You can run this script simply:
4
+ #
5
+ # ruby test/performance.rb
6
+ #
7
+ # This will write a file called "performance.html" that you can open in your web
8
+ # browser. You will need the `ruby-prof` gem installed in order to run this
9
+ # script.
10
+ # ------------------------------------------------------------------------------
11
+ require 'rubygems'
12
+
13
+ libpath = File.expand_path('../../lib', __FILE__)
14
+ $:.unshift libpath
15
+ require 'logging'
16
+
17
+ begin
18
+ gem 'log4r'
19
+ require 'log4r'
20
+ $log4r = true
21
+ rescue LoadError
22
+ $log4r = false
23
+ end
24
+
25
+ require 'logger'
26
+ require 'ruby-prof'
27
+
28
+ module Logging
29
+ class Performance
30
+
31
+ # number of iterations
32
+ attr_reader :this_many
33
+
34
+ # performance output file name
35
+ attr_reader :output_file
36
+
37
+ def initialize
38
+ @this_many = 300_000
39
+ @output_file = "performance.html"
40
+ end
41
+
42
+ def run
43
+ pattern = Logging.layouts.pattern \
44
+ :pattern => '%.1l, [%d#%p] %5l -- %c: %m\n',
45
+ :date_pattern => "%Y-%m-%dT%H:%M:%S.%s"
46
+
47
+ Logging.appenders.string_io("sio", :layout => pattern)
48
+
49
+ logger = ::Logging.logger["Performance"]
50
+ logger.level = :warn
51
+ logger.appenders = "sio"
52
+
53
+ result = RubyProf.profile do
54
+ this_many.times {logger.warn 'logged'}
55
+ end
56
+
57
+ printer = RubyProf::GraphHtmlPrinter.new(result)
58
+ File.open(output_file, "w") { |fd| printer.print(fd) }
59
+ end
60
+ end
61
+ end
62
+
63
+ if __FILE__ == $0
64
+ perf = Logging::Performance.new
65
+ perf.run
66
+ end