logger 1.2.8.1 → 1.4.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fca3ed305eb759ed28a0ea917d653feee81fc4d399a53a44e6649d8e8389defb
4
- data.tar.gz: 90cef9d07cb58cb5049c1a869ae56ec609e0dfd1befcac6053b97602e11ee609
3
+ metadata.gz: be49be41055335fa90e79c26a714b7176077de04b414def791b1a9524169ad80
4
+ data.tar.gz: f064246f7c9e483f96e9838ace1d079ce0d9acea770eb68d5a492a792157bb83
5
5
  SHA512:
6
- metadata.gz: c2c96afb68a6e3a22e53673119f673bd6490d6d1c5c184da06677633648c0e86ce6c7eaacbb4dca55c4d3c800ea8d7708c55235a854987c98ed3f20feee185f9
7
- data.tar.gz: c5e39c441722c99cd6fee091939303d22448a329543699b1deac011d3500f3cf939987ff6c4c64db1cff526a746e1e6050f9af801f057f3aca5181ea734599fa
6
+ metadata.gz: 8dc3387289106d3f98eda8384351d9486b8ad5bb21e5c58a3932595b101705600b1b66c64666eabab90a11f1d90a4b7f01e6f0ce658249bfb9ede51d76414ef4
7
+ data.tar.gz: 693e411b78fc95e47d008b3ced6247606ebb641f7a17368321e8e0002fa450694267794a620fc76f949ac9cf2a7e5ac8b891416d7d4accb738f8c2c228421388
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # logger.rb - simple logging utility
2
3
  # Copyright (C) 2000-2003, 2005, 2008, 2011 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
3
4
  #
@@ -7,47 +8,48 @@
7
8
  # license; either the dual license version in 2003, or any later version.
8
9
  # Revision:: $Id$
9
10
  #
10
- # See Logger for documentation.
11
-
11
+ # A simple system for logging messages. See Logger for more documentation.
12
12
 
13
13
  require 'monitor'
14
14
 
15
+ require_relative 'logger/version'
16
+ require_relative 'logger/formatter'
17
+ require_relative 'logger/log_device'
18
+ require_relative 'logger/severity'
19
+ require_relative 'logger/errors'
15
20
 
16
21
  # == Description
17
22
  #
18
23
  # The Logger class provides a simple but sophisticated logging utility that
19
- # anyone can use because it's included in the Ruby 1.8.x standard library.
20
- #
21
- # The HOWTOs below give a code-based overview of Logger's usage, but the basic
22
- # concept is as follows. You create a Logger object (output to a file or
23
- # elsewhere), and use it to log messages. The messages will have varying
24
- # levels (+info+, +error+, etc), reflecting their varying importance. The
25
- # levels, and their meanings, are:
26
- #
27
- # +FATAL+:: an unhandleable error that results in a program crash
28
- # +ERROR+:: a handleable error condition
29
- # +WARN+:: a warning
30
- # +INFO+:: generic (useful) information about system operation
31
- # +DEBUG+:: low-level information for developers
32
- #
33
- # So each message has a level, and the Logger itself has a level, which acts
34
- # as a filter, so you can control the amount of information emitted from the
35
- # logger without having to remove actual messages.
36
- #
37
- # For instance, in a production system, you may have your logger(s) set to
38
- # +INFO+ (or +WARN+ if you don't want the log files growing large with
39
- # repetitive information). When you are developing it, though, you probably
40
- # want to know about the program's internal state, and would set them to
24
+ # you can use to output messages.
25
+ #
26
+ # The messages have associated levels, such as +INFO+ or +ERROR+ that indicate
27
+ # their importance. You can then give the Logger a level, and only messages
28
+ # at that level or higher will be printed.
29
+ #
30
+ # The levels are:
31
+ #
32
+ # +UNKNOWN+:: An unknown message that should always be logged.
33
+ # +FATAL+:: An unhandleable error that results in a program crash.
34
+ # +ERROR+:: A handleable error condition.
35
+ # +WARN+:: A warning.
36
+ # +INFO+:: Generic (useful) information about system operation.
37
+ # +DEBUG+:: Low-level information for developers.
38
+ #
39
+ # For instance, in a production system, you may have your Logger set to
40
+ # +INFO+ or even +WARN+.
41
+ # When you are developing the system, however, you probably
42
+ # want to know about the program's internal state, and would set the Logger to
41
43
  # +DEBUG+.
42
44
  #
43
- # **Note**: Logger does not escape or sanitize any messages passed to it.
45
+ # *Note*: Logger does not escape or sanitize any messages passed to it.
44
46
  # Developers should be aware of when potentially malicious data (user-input)
45
47
  # is passed to Logger, and manually escape the untrusted data:
46
48
  #
47
49
  # logger.info("User-input: #{input.dump}")
48
50
  # logger.info("User-input: %p" % input)
49
51
  #
50
- # You can use Logger#formatter= for escaping all data.
52
+ # You can use #formatter= for escaping all data.
51
53
  #
52
54
  # original_formatter = Logger::Formatter.new
53
55
  # logger.formatter = proc { |severity, datetime, progname, msg|
@@ -57,24 +59,29 @@ require 'monitor'
57
59
  #
58
60
  # === Example
59
61
  #
60
- # A simple example demonstrates the above explanation:
62
+ # This creates a Logger that outputs to the standard output stream, with a
63
+ # level of +WARN+:
61
64
  #
62
- # log = Logger.new(STDOUT)
63
- # log.level = Logger::WARN
65
+ # require 'logger'
64
66
  #
65
- # log.debug("Created logger")
66
- # log.info("Program started")
67
- # log.warn("Nothing to do!")
67
+ # logger = Logger.new(STDOUT)
68
+ # logger.level = Logger::WARN
69
+ #
70
+ # logger.debug("Created logger")
71
+ # logger.info("Program started")
72
+ # logger.warn("Nothing to do!")
73
+ #
74
+ # path = "a_non_existent_file"
68
75
  #
69
76
  # begin
70
- # File.each_line(path) do |line|
77
+ # File.foreach(path) do |line|
71
78
  # unless line =~ /^(\w+) = (.*)$/
72
- # log.error("Line in wrong format: #{line}")
79
+ # logger.error("Line in wrong format: #{line.chomp}")
73
80
  # end
74
81
  # end
75
82
  # rescue => err
76
- # log.fatal("Caught exception; exiting")
77
- # log.fatal(err)
83
+ # logger.fatal("Caught exception; exiting")
84
+ # logger.fatal(err)
78
85
  # end
79
86
  #
80
87
  # Because the Logger's level is set to +WARN+, only the warning, error, and
@@ -108,16 +115,16 @@ require 'monitor'
108
115
  # 3. Create a logger for the specified file.
109
116
  #
110
117
  # file = File.open('foo.log', File::WRONLY | File::APPEND)
111
- # # To create new (and to remove old) logfile, add File::CREAT like;
112
- # # file = open('foo.log', File::WRONLY | File::APPEND | File::CREAT)
118
+ # # To create new logfile, add File::CREAT like:
119
+ # # file = File.open('foo.log', File::WRONLY | File::APPEND | File::CREAT)
113
120
  # logger = Logger.new(file)
114
121
  #
115
- # 4. Create a logger which ages logfile once it reaches a certain size. Leave
116
- # 10 "old log files" and each file is about 1,024,000 bytes.
122
+ # 4. Create a logger which ages the logfile once it reaches a certain size.
123
+ # Leave 10 "old" log files where each file is about 1,024,000 bytes.
117
124
  #
118
125
  # logger = Logger.new('foo.log', 10, 1024000)
119
126
  #
120
- # 5. Create a logger which ages logfile daily/weekly/monthly.
127
+ # 5. Create a logger which ages the logfile daily/weekly/monthly.
121
128
  #
122
129
  # logger = Logger.new('foo.log', 'daily')
123
130
  # logger = Logger.new('foo.log', 'weekly')
@@ -126,17 +133,17 @@ require 'monitor'
126
133
  # === How to log a message
127
134
  #
128
135
  # Notice the different methods (+fatal+, +error+, +info+) being used to log
129
- # messages of various levels. Other methods in this family are +warn+ and
136
+ # messages of various levels? Other methods in this family are +warn+ and
130
137
  # +debug+. +add+ is used below to log a message of an arbitrary (perhaps
131
138
  # dynamic) level.
132
139
  #
133
- # 1. Message in block.
140
+ # 1. Message in a block.
134
141
  #
135
142
  # logger.fatal { "Argument 'foo' not given." }
136
143
  #
137
144
  # 2. Message as a string.
138
145
  #
139
- # logger.error "Argument #{ @foo } mismatch."
146
+ # logger.error "Argument #{@foo} mismatch."
140
147
  #
141
148
  # 3. With progname.
142
149
  #
@@ -146,6 +153,20 @@ require 'monitor'
146
153
  #
147
154
  # logger.add(Logger::FATAL) { 'Fatal error!' }
148
155
  #
156
+ # The block form allows you to create potentially complex log messages,
157
+ # but to delay their evaluation until and unless the message is
158
+ # logged. For example, if we have the following:
159
+ #
160
+ # logger.debug { "This is a " + potentially + " expensive operation" }
161
+ #
162
+ # If the logger's level is +INFO+ or higher, no debug messages will be logged,
163
+ # and the entire block will not even be evaluated. Compare to this:
164
+ #
165
+ # logger.debug("This is a " + potentially + " expensive operation")
166
+ #
167
+ # Here, the string concatenation is done every time, even if the log
168
+ # level is not set to show the debug message.
169
+ #
149
170
  # === How to close a logger
150
171
  #
151
172
  # logger.close
@@ -160,8 +181,20 @@ require 'monitor'
160
181
  #
161
182
  # logger.level = Logger::INFO
162
183
  #
163
- # DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
184
+ # # DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
185
+ #
186
+ # 3. Symbol or String (case insensitive)
187
+ #
188
+ # logger.level = :info
189
+ # logger.level = 'INFO'
164
190
  #
191
+ # # :debug < :info < :warn < :error < :fatal < :unknown
192
+ #
193
+ # 4. Constructor
194
+ #
195
+ # Logger.new(logdev, level: Logger::INFO)
196
+ # Logger.new(logdev, level: :info)
197
+ # Logger.new(logdev, level: 'INFO')
165
198
  #
166
199
  # == Format
167
200
  #
@@ -169,65 +202,101 @@ require 'monitor'
169
202
  # default. The default format and a sample are shown below:
170
203
  #
171
204
  # Log format:
172
- # SeverityID, [Date Time mSec #pid] SeverityLabel -- ProgName: message
205
+ # SeverityID, [DateTime #pid] SeverityLabel -- ProgName: message
173
206
  #
174
207
  # Log sample:
175
- # I, [Wed Mar 03 02:34:24 JST 1999 895701 #19074] INFO -- Main: info.
208
+ # I, [1999-03-03T02:34:24.895701 #19074] INFO -- Main: info.
176
209
  #
177
- # You may change the date and time format in this manner:
210
+ # You may change the date and time format via #datetime_format=.
178
211
  #
179
- # logger.datetime_format = "%Y-%m-%d %H:%M:%S"
212
+ # logger.datetime_format = '%Y-%m-%d %H:%M:%S'
180
213
  # # e.g. "2004-01-03 00:54:26"
181
214
  #
182
- # You may change the overall format with Logger#formatter= method.
215
+ # or via the constructor.
183
216
  #
184
- # logger.formatter = proc { |severity, datetime, progname, msg|
217
+ # Logger.new(logdev, datetime_format: '%Y-%m-%d %H:%M:%S')
218
+ #
219
+ # Or, you may change the overall format via the #formatter= method.
220
+ #
221
+ # logger.formatter = proc do |severity, datetime, progname, msg|
185
222
  # "#{datetime}: #{msg}\n"
186
- # }
187
- # # e.g. "Thu Sep 22 08:51:08 GMT+9:00 2005: hello world"
223
+ # end
224
+ # # e.g. "2005-09-22 08:51:08 +0900: hello world"
225
+ #
226
+ # or via the constructor.
227
+ #
228
+ # Logger.new(logdev, formatter: proc {|severity, datetime, progname, msg|
229
+ # "#{datetime}: #{msg}\n"
230
+ # })
188
231
  #
189
-
190
-
191
232
  class Logger
192
- VERSION = "1.2.8.1"
193
- ProgName = "#{File.basename(__FILE__)}/#{VERSION}"
194
-
195
- class Error < RuntimeError; end
196
- class ShiftingError < Error; end # not used after 1.2.7. just for compat.
197
-
198
- # Logging severity.
199
- module Severity
200
- DEBUG = 0
201
- INFO = 1
202
- WARN = 2
203
- ERROR = 3
204
- FATAL = 4
205
- UNKNOWN = 5
233
+ _, name, rev = %w$Id$
234
+ if name
235
+ name = name.chomp(",v")
236
+ else
237
+ name = File.basename(__FILE__)
206
238
  end
239
+ rev ||= "v#{VERSION}"
240
+ ProgName = "#{name}/#{rev}"
241
+
207
242
  include Severity
208
243
 
209
244
  # Logging severity threshold (e.g. <tt>Logger::INFO</tt>).
210
- attr_accessor :level
245
+ attr_reader :level
246
+
247
+ # Set logging severity threshold.
248
+ #
249
+ # +severity+:: The Severity of the log message.
250
+ def level=(severity)
251
+ if severity.is_a?(Integer)
252
+ @level = severity
253
+ else
254
+ case severity.to_s.downcase
255
+ when 'debug'
256
+ @level = DEBUG
257
+ when 'info'
258
+ @level = INFO
259
+ when 'warn'
260
+ @level = WARN
261
+ when 'error'
262
+ @level = ERROR
263
+ when 'fatal'
264
+ @level = FATAL
265
+ when 'unknown'
266
+ @level = UNKNOWN
267
+ else
268
+ raise ArgumentError, "invalid log level: #{severity}"
269
+ end
270
+ end
271
+ end
211
272
 
212
- # Logging program name.
273
+ # Program name to include in log messages.
213
274
  attr_accessor :progname
214
275
 
215
- # Logging date-time format (string passed to +strftime+).
276
+ # Set date-time format.
277
+ #
278
+ # +datetime_format+:: A string suitable for passing to +strftime+.
216
279
  def datetime_format=(datetime_format)
217
280
  @default_formatter.datetime_format = datetime_format
218
281
  end
219
282
 
220
- # Returns the date format (string passed to +strftime+) being used (it's set
221
- # using datetime_format=)
283
+ # Returns the date format being used. See #datetime_format=
222
284
  def datetime_format
223
285
  @default_formatter.datetime_format
224
286
  end
225
287
 
226
- # Logging formatter. formatter#call is invoked with 4 arguments; severity,
227
- # time, progname and msg for each log. Bear in mind that time is a Time and
228
- # msg is an Object that user passed and it could not be a String. It is
229
- # expected to return a logdev#write-able Object. Default formatter is used
230
- # when no formatter is set.
288
+ # Logging formatter, as a +Proc+ that will take four arguments and
289
+ # return the formatted message. The arguments are:
290
+ #
291
+ # +severity+:: The Severity of the log message.
292
+ # +time+:: A Time instance representing when the message was logged.
293
+ # +progname+:: The #progname configured, or passed to the logger method.
294
+ # +msg+:: The _Object_ the user passed to the log message; not necessarily a
295
+ # String.
296
+ #
297
+ # The block should return an Object that can be written to the logging
298
+ # device via +write+. The default formatter is used when no formatter is
299
+ # set.
231
300
  attr_accessor :formatter
232
301
 
233
302
  alias sev_threshold level
@@ -235,60 +304,119 @@ class Logger
235
304
 
236
305
  # Returns +true+ iff the current severity level allows for the printing of
237
306
  # +DEBUG+ messages.
238
- def debug?; @level <= DEBUG; end
307
+ def debug?; level <= DEBUG; end
308
+
309
+ # Sets the severity to DEBUG.
310
+ def debug!; self.level = DEBUG; end
239
311
 
240
312
  # Returns +true+ iff the current severity level allows for the printing of
241
313
  # +INFO+ messages.
242
- def info?; @level <= INFO; end
314
+ def info?; level <= INFO; end
315
+
316
+ # Sets the severity to INFO.
317
+ def info!; self.level = INFO; end
243
318
 
244
319
  # Returns +true+ iff the current severity level allows for the printing of
245
320
  # +WARN+ messages.
246
- def warn?; @level <= WARN; end
321
+ def warn?; level <= WARN; end
322
+
323
+ # Sets the severity to WARN.
324
+ def warn!; self.level = WARN; end
247
325
 
248
326
  # Returns +true+ iff the current severity level allows for the printing of
249
327
  # +ERROR+ messages.
250
- def error?; @level <= ERROR; end
328
+ def error?; level <= ERROR; end
329
+
330
+ # Sets the severity to ERROR.
331
+ def error!; self.level = ERROR; end
251
332
 
252
333
  # Returns +true+ iff the current severity level allows for the printing of
253
334
  # +FATAL+ messages.
254
- def fatal?; @level <= FATAL; end
335
+ def fatal?; level <= FATAL; end
336
+
337
+ # Sets the severity to FATAL.
338
+ def fatal!; self.level = FATAL; end
255
339
 
256
340
  #
257
- # === Synopsis
258
- #
259
- # Logger.new(name, shift_age = 7, shift_size = 1048576)
260
- # Logger.new(name, shift_age = 'weekly')
341
+ # :call-seq:
342
+ # Logger.new(logdev, shift_age = 0, shift_size = 1048576)
343
+ # Logger.new(logdev, shift_age = 'weekly')
344
+ # Logger.new(logdev, level: :info)
345
+ # Logger.new(logdev, progname: 'progname')
346
+ # Logger.new(logdev, formatter: formatter)
347
+ # Logger.new(logdev, datetime_format: '%Y-%m-%d %H:%M:%S')
261
348
  #
262
349
  # === Args
263
350
  #
264
351
  # +logdev+::
265
- # The log device. This is a filename (String) or IO object (typically
266
- # +STDOUT+, +STDERR+, or an open file).
352
+ # The log device. This is a filename (String), IO object (typically
353
+ # +STDOUT+, +STDERR+, or an open file), +nil+ (it writes nothing) or
354
+ # +File::NULL+ (same as +nil+).
267
355
  # +shift_age+::
268
356
  # Number of old log files to keep, *or* frequency of rotation (+daily+,
269
- # +weekly+ or +monthly+).
357
+ # +weekly+ or +monthly+). Default value is 0, which disables log file
358
+ # rotation.
270
359
  # +shift_size+::
271
- # Maximum logfile size (only applies when +shift_age+ is a number).
360
+ # Maximum logfile size in bytes (only applies when +shift_age+ is a positive
361
+ # Integer). Defaults to +1048576+ (1MB).
362
+ # +level+::
363
+ # Logging severity threshold. Default values is Logger::DEBUG.
364
+ # +progname+::
365
+ # Program name to include in log messages. Default value is nil.
366
+ # +formatter+::
367
+ # Logging formatter. Default values is an instance of Logger::Formatter.
368
+ # +datetime_format+::
369
+ # Date and time format. Default value is '%Y-%m-%d %H:%M:%S'.
370
+ # +binmode+::
371
+ # Use binary mode on the log device. Default value is false.
372
+ # +shift_period_suffix+::
373
+ # The log file suffix format for +daily+, +weekly+ or +monthly+ rotation.
374
+ # Default is '%Y%m%d'.
272
375
  #
273
376
  # === Description
274
377
  #
275
378
  # Create an instance.
276
379
  #
277
- def initialize(logdev, shift_age = 0, shift_size = 1048576)
278
- @progname = nil
279
- @level = DEBUG
380
+ def initialize(logdev, shift_age = 0, shift_size = 1048576, level: DEBUG,
381
+ progname: nil, formatter: nil, datetime_format: nil,
382
+ binmode: false, shift_period_suffix: '%Y%m%d')
383
+ self.level = level
384
+ self.progname = progname
280
385
  @default_formatter = Formatter.new
281
- @formatter = nil
386
+ self.datetime_format = datetime_format
387
+ self.formatter = formatter
282
388
  @logdev = nil
283
- if logdev
284
- @logdev = LogDevice.new(logdev, :shift_age => shift_age,
285
- :shift_size => shift_size)
389
+ if logdev && logdev != File::NULL
390
+ @logdev = LogDevice.new(logdev, shift_age: shift_age,
391
+ shift_size: shift_size,
392
+ shift_period_suffix: shift_period_suffix,
393
+ binmode: binmode)
286
394
  end
287
395
  end
288
396
 
289
397
  #
290
- # === Synopsis
398
+ # :call-seq:
399
+ # Logger#reopen
400
+ # Logger#reopen(logdev)
401
+ #
402
+ # === Args
403
+ #
404
+ # +logdev+::
405
+ # The log device. This is a filename (String) or IO object (typically
406
+ # +STDOUT+, +STDERR+, or an open file). reopen the same filename if
407
+ # it is +nil+, do nothing for IO. Default is +nil+.
408
+ #
409
+ # === Description
410
+ #
411
+ # Reopen a log device.
412
+ #
413
+ def reopen(logdev = nil)
414
+ @logdev&.reopen(logdev)
415
+ self
416
+ end
417
+
291
418
  #
419
+ # :call-seq:
292
420
  # Logger#add(severity, message = nil, progname = nil) { ... }
293
421
  #
294
422
  # === Args
@@ -306,10 +434,8 @@ class Logger
306
434
  #
307
435
  # === Return
308
436
  #
309
- # +true+ if successful, +false+ otherwise.
310
- #
311
- # When the given severity is not high enough (for this particular logger), log
312
- # no message, and return +true+.
437
+ # When the given severity is not high enough (for this particular logger),
438
+ # log no message, and return +true+.
313
439
  #
314
440
  # === Description
315
441
  #
@@ -328,14 +454,16 @@ class Logger
328
454
  #
329
455
  # * Logfile is not locked.
330
456
  # * Append open does not need to lock file.
331
- # * But on the OS which supports multi I/O, records possibly be mixed.
457
+ # * If the OS supports multi I/O, records possibly may be mixed.
332
458
  #
333
- def add(severity, message = nil, progname = nil, &block)
459
+ def add(severity, message = nil, progname = nil)
334
460
  severity ||= UNKNOWN
335
- if @logdev.nil? or severity < @level
461
+ if @logdev.nil? or severity < level
336
462
  return true
337
463
  end
338
- progname ||= @progname
464
+ if progname.nil?
465
+ progname = @progname
466
+ end
339
467
  if message.nil?
340
468
  if block_given?
341
469
  message = yield
@@ -355,9 +483,7 @@ class Logger
355
483
  # device exists, return +nil+.
356
484
  #
357
485
  def <<(msg)
358
- unless @logdev.nil?
359
- @logdev.write(msg)
360
- end
486
+ @logdev&.write(msg)
361
487
  end
362
488
 
363
489
  #
@@ -369,12 +495,20 @@ class Logger
369
495
  add(DEBUG, nil, progname, &block)
370
496
  end
371
497
 
498
+ #
499
+ # :call-seq:
500
+ # info(message)
501
+ # info(progname, &block)
372
502
  #
373
503
  # Log an +INFO+ message.
374
504
  #
375
- # The message can come either from the +progname+ argument or the +block+. If
376
- # both are provided, then the +block+ is used as the message, and +progname+
377
- # is used as the program name.
505
+ # +message+:: The message to log; does not need to be a String.
506
+ # +progname+:: In the block form, this is the #progname to use in the
507
+ # log message. The default can be set with #progname=.
508
+ # +block+:: Evaluates to the message to log. This is not evaluated unless
509
+ # the logger's level is sufficient to log the message. This
510
+ # allows you to create potentially expensive logging messages that
511
+ # are only called when the logger is configured to show them.
378
512
  #
379
513
  # === Examples
380
514
  #
@@ -385,7 +519,7 @@ class Logger
385
519
  # logger.info { "User typed #{input}" }
386
520
  #
387
521
  # You'll probably stick to the second form above, unless you want to provide a
388
- # program name (which you can do with <tt>Logger#progname=</tt> as well).
522
+ # program name (which you can do with #progname= as well).
389
523
  #
390
524
  # === Return
391
525
  #
@@ -423,8 +557,8 @@ class Logger
423
557
  end
424
558
 
425
559
  #
426
- # Log an +UNKNOWN+ message. This will be printed no matter what the logger
427
- # level.
560
+ # Log an +UNKNOWN+ message. This will be printed no matter what the logger's
561
+ # level is.
428
562
  #
429
563
  # See #info for more information.
430
564
  #
@@ -436,13 +570,13 @@ class Logger
436
570
  # Close the logging device.
437
571
  #
438
572
  def close
439
- @logdev.close if @logdev
573
+ @logdev&.close
440
574
  end
441
575
 
442
576
  private
443
577
 
444
- # Severity label for logging. (max 5 char)
445
- SEV_LABEL = %w(DEBUG INFO WARN ERROR FATAL ANY)
578
+ # Severity label for logging (max 5 chars).
579
+ SEV_LABEL = %w(DEBUG INFO WARN ERROR FATAL ANY).freeze
446
580
 
447
581
  def format_severity(severity)
448
582
  SEV_LABEL[severity] || 'ANY'
@@ -451,309 +585,4 @@ private
451
585
  def format_message(severity, datetime, progname, msg)
452
586
  (@formatter || @default_formatter).call(severity, datetime, progname, msg)
453
587
  end
454
-
455
-
456
- class Formatter
457
- Format = "%s, [%s#%d] %5s -- %s: %s\n"
458
-
459
- attr_accessor :datetime_format
460
-
461
- def initialize
462
- @datetime_format = nil
463
- end
464
-
465
- def call(severity, time, progname, msg)
466
- Format % [severity[0..0], format_datetime(time), $$, severity, progname,
467
- msg2str(msg)]
468
- end
469
-
470
- private
471
-
472
- def format_datetime(time)
473
- if @datetime_format.nil?
474
- time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec
475
- else
476
- time.strftime(@datetime_format)
477
- end
478
- end
479
-
480
- def msg2str(msg)
481
- case msg
482
- when ::String
483
- msg
484
- when ::Exception
485
- "#{ msg.message } (#{ msg.class })\n" <<
486
- (msg.backtrace || []).join("\n")
487
- else
488
- msg.inspect
489
- end
490
- end
491
- end
492
-
493
-
494
- class LogDevice
495
- attr_reader :dev
496
- attr_reader :filename
497
-
498
- class LogDeviceMutex
499
- include MonitorMixin
500
- end
501
-
502
- def initialize(log = nil, opt = {})
503
- @dev = @filename = @shift_age = @shift_size = nil
504
- @mutex = LogDeviceMutex.new
505
- if log.respond_to?(:write) and log.respond_to?(:close)
506
- @dev = log
507
- else
508
- @dev = open_logfile(log)
509
- @dev.sync = true
510
- @filename = log
511
- @shift_age = opt[:shift_age] || 7
512
- @shift_size = opt[:shift_size] || 1048576
513
- end
514
- end
515
-
516
- def write(message)
517
- begin
518
- @mutex.synchronize do
519
- if @shift_age and @dev.respond_to?(:stat)
520
- begin
521
- check_shift_log
522
- rescue
523
- warn("log shifting failed. #{$!}")
524
- end
525
- end
526
- begin
527
- @dev.write(message)
528
- rescue
529
- warn("log writing failed. #{$!}")
530
- end
531
- end
532
- rescue Exception => ignored
533
- warn("log writing failed. #{ignored}")
534
- end
535
- end
536
-
537
- def close
538
- begin
539
- @mutex.synchronize do
540
- @dev.close rescue nil
541
- end
542
- rescue Exception
543
- @dev.close rescue nil
544
- end
545
- end
546
-
547
- private
548
-
549
- def open_logfile(filename)
550
- if (FileTest.exist?(filename))
551
- open(filename, (File::WRONLY | File::APPEND))
552
- else
553
- create_logfile(filename)
554
- end
555
- end
556
-
557
- def create_logfile(filename)
558
- logdev = open(filename, (File::WRONLY | File::APPEND | File::CREAT))
559
- logdev.sync = true
560
- add_log_header(logdev)
561
- logdev
562
- end
563
-
564
- def add_log_header(file)
565
- file.write(
566
- "# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName]
567
- )
568
- end
569
-
570
- SiD = 24 * 60 * 60
571
-
572
- def check_shift_log
573
- if @shift_age.is_a?(Integer)
574
- # Note: always returns false if '0'.
575
- if @filename && (@shift_age > 0) && (@dev.stat.size > @shift_size)
576
- shift_log_age
577
- end
578
- else
579
- now = Time.now
580
- period_end = previous_period_end(now)
581
- if @dev.stat.mtime <= period_end
582
- shift_log_period(period_end)
583
- end
584
- end
585
- end
586
-
587
- def shift_log_age
588
- (@shift_age-3).downto(0) do |i|
589
- if FileTest.exist?("#{@filename}.#{i}")
590
- File.rename("#{@filename}.#{i}", "#{@filename}.#{i+1}")
591
- end
592
- end
593
- @dev.close rescue nil
594
- File.rename("#{@filename}", "#{@filename}.0")
595
- @dev = create_logfile(@filename)
596
- return true
597
- end
598
-
599
- def shift_log_period(period_end)
600
- postfix = period_end.strftime("%Y%m%d") # YYYYMMDD
601
- age_file = "#{@filename}.#{postfix}"
602
- if FileTest.exist?(age_file)
603
- # try to avoid filename crash caused by Timestamp change.
604
- idx = 0
605
- # .99 can be overridden; avoid too much file search with 'loop do'
606
- while idx < 100
607
- idx += 1
608
- age_file = "#{@filename}.#{postfix}.#{idx}"
609
- break unless FileTest.exist?(age_file)
610
- end
611
- end
612
- @dev.close rescue nil
613
- File.rename("#{@filename}", age_file)
614
- @dev = create_logfile(@filename)
615
- return true
616
- end
617
-
618
- def previous_period_end(now)
619
- case @shift_age
620
- when /^daily$/
621
- eod(now - 1 * SiD)
622
- when /^weekly$/
623
- eod(now - ((now.wday + 1) * SiD))
624
- when /^monthly$/
625
- eod(now - now.mday * SiD)
626
- else
627
- now
628
- end
629
- end
630
-
631
- def eod(t)
632
- Time.mktime(t.year, t.month, t.mday, 23, 59, 59)
633
- end
634
- end
635
-
636
-
637
- #
638
- # == Description
639
- #
640
- # Application -- Add logging support to your application.
641
- #
642
- # == Usage
643
- #
644
- # 1. Define your application class as a sub-class of this class.
645
- # 2. Override 'run' method in your class to do many things.
646
- # 3. Instantiate it and invoke 'start'.
647
- #
648
- # == Example
649
- #
650
- # class FooApp < Application
651
- # def initialize(foo_app, application_specific, arguments)
652
- # super('FooApp') # Name of the application.
653
- # end
654
- #
655
- # def run
656
- # ...
657
- # log(WARN, 'warning', 'my_method1')
658
- # ...
659
- # @log.error('my_method2') { 'Error!' }
660
- # ...
661
- # end
662
- # end
663
- #
664
- # status = FooApp.new(....).start
665
- #
666
- class Application
667
- include Logger::Severity
668
-
669
- # Name of the application given at initialize.
670
- attr_reader :appname
671
-
672
- #
673
- # == Synopsis
674
- #
675
- # Application.new(appname = '')
676
- #
677
- # == Args
678
- #
679
- # +appname+:: Name of the application.
680
- #
681
- # == Description
682
- #
683
- # Create an instance. Log device is +STDERR+ by default. This can be
684
- # changed with #set_log.
685
- #
686
- def initialize(appname = nil)
687
- @appname = appname
688
- @log = Logger.new(STDERR)
689
- @log.progname = @appname
690
- @level = @log.level
691
- end
692
-
693
- #
694
- # Start the application. Return the status code.
695
- #
696
- def start
697
- status = -1
698
- begin
699
- log(INFO, "Start of #{ @appname }.")
700
- status = run
701
- rescue
702
- log(FATAL, "Detected an exception. Stopping ... #{$!} (#{$!.class})\n" << $@.join("\n"))
703
- ensure
704
- log(INFO, "End of #{ @appname }. (status: #{ status.to_s })")
705
- end
706
- status
707
- end
708
-
709
- # Logger for this application. See the class Logger for an explanation.
710
- def logger
711
- @log
712
- end
713
-
714
- #
715
- # Sets the logger for this application. See the class Logger for an explanation.
716
- #
717
- def logger=(logger)
718
- @log = logger
719
- @log.progname = @appname
720
- @log.level = @level
721
- end
722
-
723
- #
724
- # Sets the log device for this application. See <tt>Logger.new</tt> for an explanation
725
- # of the arguments.
726
- #
727
- def set_log(logdev, shift_age = 0, shift_size = 1024000)
728
- @log = Logger.new(logdev, shift_age, shift_size)
729
- @log.progname = @appname
730
- @log.level = @level
731
- end
732
-
733
- def log=(logdev)
734
- set_log(logdev)
735
- end
736
-
737
- #
738
- # Set the logging threshold, just like <tt>Logger#level=</tt>.
739
- #
740
- def level=(level)
741
- @level = level
742
- @log.level = @level
743
- end
744
-
745
- #
746
- # See Logger#add. This application's +appname+ is used.
747
- #
748
- def log(severity, message = nil, &block)
749
- @log.add(severity, message, @appname, &block) if @log
750
- end
751
-
752
- private
753
-
754
- def run
755
- # TODO: should be an NotImplementedError
756
- raise RuntimeError.new('Method run must be defined in the derived class.')
757
- end
758
- end
759
588
  end