logger 1.2.8.1 → 1.4.3

Sign up to get free protection for your applications and to get access to all the features.
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