logger 1.5.0 → 1.5.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: fdee784517977e63d746c2ea95367978a203a68472dfdbaac094e49b291ce170
4
- data.tar.gz: a80c2295c6ea2d146f6a21f9d2c76320a0bedebac7261c88785a7e45f3a7f91e
3
+ metadata.gz: 299d68259bad0228e1e6519f73ddbef94902a8e04f230ad8bbc38f1cb7c45dee
4
+ data.tar.gz: 9178b4b52d2e025fefb9dcadebd94ff6a95392d5d381b949f8d2cb454d456bab
5
5
  SHA512:
6
- metadata.gz: 3b8af19f1a91db15b14c9a3db994184b0b918db2dae08380813495b135f5cbf7d55563d75a155ba3924b023b7123c7934343bad913880c2b5511e34c99e86972
7
- data.tar.gz: 0eb96b3097cc63f8fbcda65de469effd3355f6381152b1e8344c35f2222e91c51adc669bc8c1ccc28a5706efdbea0b07926326e35a9dfa8fad1c545101485b9b
6
+ metadata.gz: 1ee506486c7af0e574c4c4c58b28ca10c2d0d4a0493d0b405e43af96e93bb0c397dee7b128d0d89e7793724b6f0cb186c8e2d33aa2ff8cfc6e847e830e6c0127
7
+ data.tar.gz: 70c4a17f55e4eb5d2323ef5218151de9638a70b8690f6a40180b051554a17bd58b4a09521bbc8ca6a55b60afc9215d51b1c37873c0883e5ba3f42d89e4965a36
data/lib/logger/errors.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # not used after 1.2.7. just for compat.
4
3
  class Logger
4
+ # not used after 1.2.7. just for compat.
5
5
  class Error < RuntimeError # :nodoc:
6
6
  end
7
7
  class ShiftingError < Error # :nodoc:
@@ -3,7 +3,7 @@
3
3
  class Logger
4
4
  # Default formatter for log messages.
5
5
  class Formatter
6
- Format = "%s, [%s #%d] %5s -- %s: %s\n"
6
+ Format = "%.1s, [%s #%d] %5s -- %s: %s\n"
7
7
  DatetimeFormat = "%Y-%m-%dT%H:%M:%S.%6N"
8
8
 
9
9
  attr_accessor :datetime_format
@@ -13,8 +13,7 @@ class Logger
13
13
  end
14
14
 
15
15
  def call(severity, time, progname, msg)
16
- Format % [severity[0..0], format_datetime(time), Process.pid, severity, progname,
17
- msg2str(msg)]
16
+ sprintf(Format, severity, format_datetime(time), Process.pid, severity, progname, msg2str(msg))
18
17
  end
19
18
 
20
19
  private
@@ -79,8 +79,10 @@ class Logger
79
79
  def set_dev(log)
80
80
  if log.respond_to?(:write) and log.respond_to?(:close)
81
81
  @dev = log
82
- if log.respond_to?(:path)
83
- @filename = log.path
82
+ if log.respond_to?(:path) and path = log.path
83
+ if File.exist?(path)
84
+ @filename = path
85
+ end
84
86
  end
85
87
  else
86
88
  @dev = open_logfile(log)
@@ -135,7 +137,7 @@ class Logger
135
137
  end
136
138
  end
137
139
 
138
- if /mswin|mingw|cygwin/ =~ RUBY_PLATFORM
140
+ if /mswin|mingw|cygwin/ =~ RbConfig::CONFIG['host_os']
139
141
  def lock_shift_log
140
142
  yield
141
143
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Logger
4
- VERSION = "1.5.0"
4
+ VERSION = "1.5.3"
5
5
  end
data/lib/logger.rb CHANGED
@@ -11,6 +11,7 @@
11
11
  # A simple system for logging messages. See Logger for more documentation.
12
12
 
13
13
  require 'monitor'
14
+ require 'rbconfig'
14
15
 
15
16
  require_relative 'logger/version'
16
17
  require_relative 'logger/formatter'
@@ -18,216 +19,353 @@ require_relative 'logger/log_device'
18
19
  require_relative 'logger/severity'
19
20
  require_relative 'logger/errors'
20
21
 
21
- # == Description
22
+ # \Class \Logger provides a simple but sophisticated logging utility that
23
+ # you can use to create one or more
24
+ # {event logs}[https://en.wikipedia.org/wiki/Logging_(software)#Event_logs]
25
+ # for your program.
26
+ # Each such log contains a chronological sequence of entries
27
+ # that provides a record of the program's activities.
22
28
  #
23
- # The Logger class provides a simple but sophisticated logging utility that
24
- # you can use to output messages.
29
+ # == About the Examples
25
30
  #
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.
31
+ # All examples on this page assume that \Logger has been required:
29
32
  #
30
- # The levels are:
33
+ # require 'logger'
31
34
  #
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.
35
+ # == Synopsis
38
36
  #
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
43
- # +DEBUG+.
37
+ # Create a log with Logger.new:
44
38
  #
45
- # *Note*: Logger does not escape or sanitize any messages passed to it.
46
- # Developers should be aware of when potentially malicious data (user-input)
47
- # is passed to Logger, and manually escape the untrusted data:
39
+ # # Single log file.
40
+ # logger = Logger.new('t.log')
41
+ # # Size-based rotated logging: 3 10-megabyte files.
42
+ # logger = Logger.new('t.log', 3, 10485760)
43
+ # # Period-based rotated logging: daily (also allowed: 'weekly', 'monthly').
44
+ # logger = Logger.new('t.log', 'daily')
45
+ # # Log to an IO stream.
46
+ # logger = Logger.new($stdout)
48
47
  #
49
- # logger.info("User-input: #{input.dump}")
50
- # logger.info("User-input: %p" % input)
48
+ # Add entries (level, message) with Logger#add:
51
49
  #
52
- # You can use #formatter= for escaping all data.
50
+ # logger.add(Logger::DEBUG, 'Maximal debugging info')
51
+ # logger.add(Logger::INFO, 'Non-error information')
52
+ # logger.add(Logger::WARN, 'Non-error warning')
53
+ # logger.add(Logger::ERROR, 'Non-fatal error')
54
+ # logger.add(Logger::FATAL, 'Fatal error')
55
+ # logger.add(Logger::UNKNOWN, 'Most severe')
53
56
  #
54
- # original_formatter = Logger::Formatter.new
55
- # logger.formatter = proc { |severity, datetime, progname, msg|
56
- # original_formatter.call(severity, datetime, progname, msg.dump)
57
- # }
58
- # logger.info(input)
57
+ # Close the log with Logger#close:
59
58
  #
60
- # === Example
59
+ # logger.close
61
60
  #
62
- # This creates a Logger that outputs to the standard output stream, with a
63
- # level of +WARN+:
61
+ # == Entries
64
62
  #
65
- # require 'logger'
63
+ # You can add entries with method Logger#add:
64
+ #
65
+ # logger.add(Logger::DEBUG, 'Maximal debugging info')
66
+ # logger.add(Logger::INFO, 'Non-error information')
67
+ # logger.add(Logger::WARN, 'Non-error warning')
68
+ # logger.add(Logger::ERROR, 'Non-fatal error')
69
+ # logger.add(Logger::FATAL, 'Fatal error')
70
+ # logger.add(Logger::UNKNOWN, 'Most severe')
71
+ #
72
+ # These shorthand methods also add entries:
73
+ #
74
+ # logger.debug('Maximal debugging info')
75
+ # logger.info('Non-error information')
76
+ # logger.warn('Non-error warning')
77
+ # logger.error('Non-fatal error')
78
+ # logger.fatal('Fatal error')
79
+ # logger.unknown('Most severe')
80
+ #
81
+ # When you call any of these methods,
82
+ # the entry may or may not be written to the log,
83
+ # depending on the entry's severity and on the log level;
84
+ # see {Log Level}[rdoc-ref:Logger@Log+Level]
85
+ #
86
+ # An entry always has:
87
+ #
88
+ # - A severity (the required argument to #add).
89
+ # - An automatically created timestamp.
90
+ #
91
+ # And may also have:
92
+ #
93
+ # - A message.
94
+ # - A program name.
95
+ #
96
+ # Example:
97
+ #
98
+ # logger = Logger.new($stdout)
99
+ # logger.add(Logger::INFO, 'My message.', 'mung')
100
+ # # => I, [2022-05-07T17:21:46.536234 #20536] INFO -- mung: My message.
101
+ #
102
+ # The default format for an entry is:
103
+ #
104
+ # "%s, [%s #%d] %5s -- %s: %s\n"
105
+ #
106
+ # where the values to be formatted are:
107
+ #
108
+ # - \Severity (one letter).
109
+ # - Timestamp.
110
+ # - Process id.
111
+ # - \Severity (word).
112
+ # - Program name.
113
+ # - Message.
114
+ #
115
+ # You can use a different entry format by:
116
+ #
117
+ # - Setting a custom format proc (affects following entries);
118
+ # see {formatter=}[Logger.html#attribute-i-formatter].
119
+ # - Calling any of the methods above with a block
120
+ # (affects only the one entry).
121
+ # Doing so can have two benefits:
122
+ #
123
+ # - Context: the block can evaluate the entire program context
124
+ # and create a context-dependent message.
125
+ # - Performance: the block is not evaluated unless the log level
126
+ # permits the entry actually to be written:
127
+ #
128
+ # logger.error { my_slow_message_generator }
129
+ #
130
+ # Contrast this with the string form, where the string is
131
+ # always evaluated, regardless of the log level:
132
+ #
133
+ # logger.error("#{my_slow_message_generator}")
134
+ #
135
+ # === \Severity
136
+ #
137
+ # The severity of a log entry has two effects:
138
+ #
139
+ # - Determines whether the entry is selected for inclusion in the log;
140
+ # see {Log Level}[rdoc-ref:Logger@Log+Level].
141
+ # - Indicates to any log reader (whether a person or a program)
142
+ # the relative importance of the entry.
143
+ #
144
+ # === Timestamp
66
145
  #
67
- # logger = Logger.new(STDOUT)
68
- # logger.level = Logger::WARN
146
+ # The timestamp for a log entry is generated automatically
147
+ # when the entry is created.
69
148
  #
70
- # logger.debug("Created logger")
71
- # logger.info("Program started")
72
- # logger.warn("Nothing to do!")
149
+ # The logged timestamp is formatted by method
150
+ # {Time#strftime}[https://docs.ruby-lang.org/en/master/Time.html#method-i-strftime]
151
+ # using this format string:
73
152
  #
74
- # path = "a_non_existent_file"
153
+ # '%Y-%m-%dT%H:%M:%S.%6N'
75
154
  #
76
- # begin
77
- # File.foreach(path) do |line|
78
- # unless line =~ /^(\w+) = (.*)$/
79
- # logger.error("Line in wrong format: #{line.chomp}")
80
- # end
81
- # end
82
- # rescue => err
83
- # logger.fatal("Caught exception; exiting")
84
- # logger.fatal(err)
85
- # end
155
+ # Example:
86
156
  #
87
- # Because the Logger's level is set to +WARN+, only the warning, error, and
88
- # fatal messages are recorded. The debug and info messages are silently
89
- # discarded.
157
+ # logger = Logger.new($stdout)
158
+ # logger.add(Logger::INFO)
159
+ # # => I, [2022-05-07T17:04:32.318331 #20536] INFO -- : nil
90
160
  #
91
- # === Features
161
+ # You can set a different format using method #datetime_format=.
92
162
  #
93
- # There are several interesting features that Logger provides, like
94
- # auto-rolling of log files, setting the format of log messages, and
95
- # specifying a program name in conjunction with the message. The next section
96
- # shows you how to achieve these things.
163
+ # === Message
97
164
  #
165
+ # The message is an optional argument to an entry method:
98
166
  #
99
- # == HOWTOs
167
+ # logger = Logger.new($stdout)
168
+ # logger.add(Logger::INFO, 'My message')
169
+ # # => I, [2022-05-07T18:15:37.647581 #20536] INFO -- : My message
100
170
  #
101
- # === How to create a logger
171
+ # For the default entry formatter, <tt>Logger::Formatter</tt>,
172
+ # the message object may be:
102
173
  #
103
- # The options below give you various choices, in more or less increasing
104
- # complexity.
174
+ # - A string: used as-is.
175
+ # - An Exception: <tt>message.message</tt> is used.
176
+ # - Anything else: <tt>message.inspect</tt> is used.
105
177
  #
106
- # 1. Create a logger which logs messages to STDERR/STDOUT.
178
+ # *Note*: Logger::Formatter does not escape or sanitize
179
+ # the message passed to it.
180
+ # Developers should be aware that malicious data (user input)
181
+ # may be in the message, and should explicitly escape untrusted data.
107
182
  #
108
- # logger = Logger.new(STDERR)
109
- # logger = Logger.new(STDOUT)
183
+ # You can use a custom formatter to escape message data;
184
+ # see the example at {formatter=}[Logger.html#attribute-i-formatter].
110
185
  #
111
- # 2. Create a logger for the file which has the specified name.
186
+ # === Program Name
112
187
  #
113
- # logger = Logger.new('logfile.log')
188
+ # The program name is an optional argument to an entry method:
114
189
  #
115
- # 3. Create a logger for the specified file.
190
+ # logger = Logger.new($stdout)
191
+ # logger.add(Logger::INFO, 'My message', 'mung')
192
+ # # => I, [2022-05-07T18:17:38.084716 #20536] INFO -- mung: My message
116
193
  #
117
- # file = File.open('foo.log', File::WRONLY | File::APPEND)
118
- # # To create new logfile, add File::CREAT like:
119
- # # file = File.open('foo.log', File::WRONLY | File::APPEND | File::CREAT)
120
- # logger = Logger.new(file)
194
+ # The default program name for a new logger may be set in the call to
195
+ # Logger.new via optional keyword argument +progname+:
121
196
  #
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.
197
+ # logger = Logger.new('t.log', progname: 'mung')
124
198
  #
125
- # logger = Logger.new('foo.log', 10, 1024000)
199
+ # The default program name for an existing logger may be set
200
+ # by a call to method #progname=:
126
201
  #
127
- # 5. Create a logger which ages the logfile daily/weekly/monthly.
202
+ # logger.progname = 'mung'
128
203
  #
129
- # logger = Logger.new('foo.log', 'daily')
130
- # logger = Logger.new('foo.log', 'weekly')
131
- # logger = Logger.new('foo.log', 'monthly')
204
+ # The current program name may be retrieved with method
205
+ # {progname}[Logger.html#attribute-i-progname]:
132
206
  #
133
- # === How to log a message
207
+ # logger.progname # => "mung"
134
208
  #
135
- # Notice the different methods (+fatal+, +error+, +info+) being used to log
136
- # messages of various levels? Other methods in this family are +warn+ and
137
- # +debug+. +add+ is used below to log a message of an arbitrary (perhaps
138
- # dynamic) level.
209
+ # == Log Level
139
210
  #
140
- # 1. Message in a block.
211
+ # The log level setting determines whether an entry is actually
212
+ # written to the log, based on the entry's severity.
141
213
  #
142
- # logger.fatal { "Argument 'foo' not given." }
214
+ # These are the defined severities (least severe to most severe):
143
215
  #
144
- # 2. Message as a string.
216
+ # logger = Logger.new($stdout)
217
+ # logger.add(Logger::DEBUG, 'Maximal debugging info')
218
+ # # => D, [2022-05-07T17:57:41.776220 #20536] DEBUG -- : Maximal debugging info
219
+ # logger.add(Logger::INFO, 'Non-error information')
220
+ # # => I, [2022-05-07T17:59:14.349167 #20536] INFO -- : Non-error information
221
+ # logger.add(Logger::WARN, 'Non-error warning')
222
+ # # => W, [2022-05-07T18:00:45.337538 #20536] WARN -- : Non-error warning
223
+ # logger.add(Logger::ERROR, 'Non-fatal error')
224
+ # # => E, [2022-05-07T18:02:41.592912 #20536] ERROR -- : Non-fatal error
225
+ # logger.add(Logger::FATAL, 'Fatal error')
226
+ # # => F, [2022-05-07T18:05:24.703931 #20536] FATAL -- : Fatal error
227
+ # logger.add(Logger::UNKNOWN, 'Most severe')
228
+ # # => A, [2022-05-07T18:07:54.657491 #20536] ANY -- : Most severe
145
229
  #
146
- # logger.error "Argument #{@foo} mismatch."
230
+ # The default initial level setting is Logger::DEBUG, the lowest level,
231
+ # which means that all entries are to be written, regardless of severity:
147
232
  #
148
- # 3. With progname.
233
+ # logger = Logger.new($stdout)
234
+ # logger.level # => 0
235
+ # logger.add(0, "My message")
236
+ # # => D, [2022-05-11T15:10:59.773668 #20536] DEBUG -- : My message
149
237
  #
150
- # logger.info('initialize') { "Initializing..." }
238
+ # You can specify a different setting in a new logger
239
+ # using keyword argument +level+ with an appropriate value:
151
240
  #
152
- # 4. With severity.
241
+ # logger = Logger.new($stdout, level: Logger::ERROR)
242
+ # logger = Logger.new($stdout, level: 'error')
243
+ # logger = Logger.new($stdout, level: :error)
244
+ # logger.level # => 3
153
245
  #
154
- # logger.add(Logger::FATAL) { 'Fatal error!' }
246
+ # With this level, entries with severity Logger::ERROR and higher
247
+ # are written, while those with lower severities are not written:
155
248
  #
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:
249
+ # logger = Logger.new($stdout, level: Logger::ERROR)
250
+ # logger.add(3)
251
+ # # => E, [2022-05-11T15:17:20.933362 #20536] ERROR -- : nil
252
+ # logger.add(2) # Silent.
159
253
  #
160
- # logger.debug { "This is a " + potentially + " expensive operation" }
254
+ # You can set the log level for an existing logger
255
+ # with method #level=:
161
256
  #
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:
257
+ # logger.level = Logger::ERROR
164
258
  #
165
- # logger.debug("This is a " + potentially + " expensive operation")
259
+ # These shorthand methods also set the level:
166
260
  #
167
- # Here, the string concatenation is done every time, even if the log
168
- # level is not set to show the debug message.
261
+ # logger.debug! # => 0
262
+ # logger.info! # => 1
263
+ # logger.warn! # => 2
264
+ # logger.error! # => 3
265
+ # logger.fatal! # => 4
169
266
  #
170
- # === How to close a logger
267
+ # You can retrieve the log level with method
268
+ # {level}[Logger.html#attribute-i-level]:
171
269
  #
172
- # logger.close
270
+ # logger.level = Logger::ERROR
271
+ # logger.level # => 3
173
272
  #
174
- # === Setting severity threshold
273
+ # These methods return whether a given
274
+ # level is to be written:
175
275
  #
176
- # 1. Original interface.
276
+ # logger.level = Logger::ERROR
277
+ # logger.debug? # => false
278
+ # logger.info? # => false
279
+ # logger.warn? # => false
280
+ # logger.error? # => true
281
+ # logger.fatal? # => true
177
282
  #
178
- # logger.sev_threshold = Logger::WARN
283
+ # == Log File Rotation
179
284
  #
180
- # 2. Log4r (somewhat) compatible interface.
285
+ # By default, a log file is a single file that grows indefinitely
286
+ # (until explicitly closed); there is no file rotation.
181
287
  #
182
- # logger.level = Logger::INFO
288
+ # To keep log files to a manageable size,
289
+ # you can use _log_ _file_ _rotation_, which uses multiple log files:
183
290
  #
184
- # # DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
291
+ # - Each log file has entries for a non-overlapping
292
+ # time interval.
293
+ # - Only the most recent log file is open and active;
294
+ # the others are closed and inactive.
185
295
  #
186
- # 3. Symbol or String (case insensitive)
296
+ # === Size-Based Rotation
187
297
  #
188
- # logger.level = :info
189
- # logger.level = 'INFO'
298
+ # For size-based log file rotation, call Logger.new with:
190
299
  #
191
- # # :debug < :info < :warn < :error < :fatal < :unknown
300
+ # - Argument +logdev+ as a file path.
301
+ # - Argument +shift_age+ with a positive integer:
302
+ # the number of log files to be in the rotation.
303
+ # - Argument +shift_size+ as a positive integer:
304
+ # the maximum size (in bytes) of each log file;
305
+ # defaults to 1048576 (1 megabyte).
192
306
  #
193
- # 4. Constructor
307
+ # Examples:
194
308
  #
195
- # Logger.new(logdev, level: Logger::INFO)
196
- # Logger.new(logdev, level: :info)
197
- # Logger.new(logdev, level: 'INFO')
309
+ # logger = Logger.new('t.log', 3) # Three 1-megabyte files.
310
+ # logger = Logger.new('t.log', 5, 10485760) # Five 10-megabyte files.
198
311
  #
199
- # == Format
312
+ # For these examples, suppose:
200
313
  #
201
- # Log messages are rendered in the output stream in a certain format by
202
- # default. The default format and a sample are shown below:
314
+ # logger = Logger.new('t.log', 3)
203
315
  #
204
- # Log format:
205
- # SeverityID, [DateTime #pid] SeverityLabel -- ProgName: message
316
+ # Logging begins in the new log file, +t.log+;
317
+ # the log file is "full" and ready for rotation
318
+ # when a new entry would cause its size to exceed +shift_size+.
206
319
  #
207
- # Log sample:
208
- # I, [1999-03-03T02:34:24.895701 #19074] INFO -- Main: info.
320
+ # The first time +t.log+ is full:
209
321
  #
210
- # You may change the date and time format via #datetime_format=.
322
+ # - +t.log+ is closed and renamed to +t.log.0+.
323
+ # - A new file +t.log+ is opened.
211
324
  #
212
- # logger.datetime_format = '%Y-%m-%d %H:%M:%S'
213
- # # e.g. "2004-01-03 00:54:26"
325
+ # The second time +t.log+ is full:
214
326
  #
215
- # or via the constructor.
327
+ # - +t.log.0 is renamed as +t.log.1+.
328
+ # - +t.log+ is closed and renamed to +t.log.0+.
329
+ # - A new file +t.log+ is opened.
216
330
  #
217
- # Logger.new(logdev, datetime_format: '%Y-%m-%d %H:%M:%S')
331
+ # Each subsequent time that +t.log+ is full,
332
+ # the log files are rotated:
218
333
  #
219
- # Or, you may change the overall format via the #formatter= method.
334
+ # - +t.log.1+ is removed.
335
+ # - +t.log.0 is renamed as +t.log.1+.
336
+ # - +t.log+ is closed and renamed to +t.log.0+.
337
+ # - A new file +t.log+ is opened.
220
338
  #
221
- # logger.formatter = proc do |severity, datetime, progname, msg|
222
- # "#{datetime}: #{msg}\n"
223
- # end
224
- # # e.g. "2005-09-22 08:51:08 +0900: hello world"
339
+ # === Periodic Rotation
225
340
  #
226
- # or via the constructor.
341
+ # For periodic rotation, call Logger.new with:
227
342
  #
228
- # Logger.new(logdev, formatter: proc {|severity, datetime, progname, msg|
229
- # "#{datetime}: #{msg}\n"
230
- # })
343
+ # - Argument +logdev+ as a file path.
344
+ # - Argument +shift_age+ as a string period indicator.
345
+ #
346
+ # Examples:
347
+ #
348
+ # logger = Logger.new('t.log', 'daily') # Rotate log files daily.
349
+ # logger = Logger.new('t.log', 'weekly') # Rotate log files weekly.
350
+ # logger = Logger.new('t.log', 'monthly') # Rotate log files monthly.
351
+ #
352
+ # Example:
353
+ #
354
+ # logger = Logger.new('t.log', 'daily')
355
+ #
356
+ # When the given period expires:
357
+ #
358
+ # - The base log file, +t.log+ is closed and renamed
359
+ # with a date-based suffix such as +t.log.20220509+.
360
+ # - A new log file +t.log+ is opened.
361
+ # - Nothing is removed.
362
+ #
363
+ # The default format for the suffix is <tt>'%Y%m%d'</tt>,
364
+ # which produces a suffix similar to the one above.
365
+ # You can set a different format using create-time option
366
+ # +shift_period_suffix+;
367
+ # see details and suggestions at
368
+ # {Time#strftime}[https://docs.ruby-lang.org/en/master/Time.html#method-i-strftime].
231
369
  #
232
370
  class Logger
233
371
  _, name, rev = %w$Id$
@@ -244,9 +382,18 @@ class Logger
244
382
  # Logging severity threshold (e.g. <tt>Logger::INFO</tt>).
245
383
  attr_reader :level
246
384
 
247
- # Set logging severity threshold.
385
+ # Sets the log level; returns +severity+.
386
+ # See {Log Level}[rdoc-ref:Logger@Log+Level].
387
+ #
388
+ # Argument +severity+ may be an integer, a string, or a symbol:
389
+ #
390
+ # logger.level = Logger::ERROR # => 3
391
+ # logger.level = 3 # => 3
392
+ # logger.level = 'error' # => "error"
393
+ # logger.level = :error # => :error
394
+ #
395
+ # Logger#sev_threshold= is an alias for Logger#level=.
248
396
  #
249
- # +severity+:: The Severity of the log message.
250
397
  def level=(severity)
251
398
  if severity.is_a?(Integer)
252
399
  @level = severity
@@ -273,109 +420,159 @@ class Logger
273
420
  # Program name to include in log messages.
274
421
  attr_accessor :progname
275
422
 
276
- # Set date-time format.
423
+ # Sets the date-time format.
424
+ #
425
+ # Argument +datetime_format+ should be either of these:
426
+ #
427
+ # - A string suitable for use as a format for method
428
+ # {Time#strftime}[https://docs.ruby-lang.org/en/master/Time.html#method-i-strftime].
429
+ # - +nil+: the logger uses <tt>'%Y-%m-%dT%H:%M:%S.%6N'</tt>.
277
430
  #
278
- # +datetime_format+:: A string suitable for passing to +strftime+.
279
431
  def datetime_format=(datetime_format)
280
432
  @default_formatter.datetime_format = datetime_format
281
433
  end
282
434
 
283
- # Returns the date format being used. See #datetime_format=
435
+ # Returns the date-time format; see #datetime_format=.
436
+ #
284
437
  def datetime_format
285
438
  @default_formatter.datetime_format
286
439
  end
287
440
 
288
- # Logging formatter, as a +Proc+ that will take four arguments and
289
- # return the formatted message. The arguments are:
441
+ # Sets or retrieves the logger entry formatter proc.
290
442
  #
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.
443
+ # When +formatter+ is +nil+, the logger uses Logger::Formatter.
444
+ #
445
+ # When +formatter+ is a proc, a new entry is formatted by the proc,
446
+ # which is called with four arguments:
447
+ #
448
+ # - +severity+: The severity of the entry.
449
+ # - +time+: A Time object representing the entry's timestamp.
450
+ # - +progname+: The program name for the entry.
451
+ # - +msg+: The message for the entry (string or string-convertible object).
452
+ #
453
+ # The proc should return a string containing the formatted entry.
454
+ #
455
+ # This custom formatter uses
456
+ # {String#dump}[https://docs.ruby-lang.org/en/master/String.html#method-i-dump]
457
+ # to escape the message string:
458
+ #
459
+ # logger = Logger.new($stdout, progname: 'mung')
460
+ # original_formatter = logger.formatter || Logger::Formatter.new
461
+ # logger.formatter = proc { |severity, time, progname, msg|
462
+ # original_formatter.call(severity, time, progname, msg.dump)
463
+ # }
464
+ # logger.add(Logger::INFO, "hello \n ''")
465
+ # logger.add(Logger::INFO, "\f\x00\xff\\\"")
466
+ #
467
+ # Output:
468
+ #
469
+ # I, [2022-05-13T13:16:29.637488 #8492] INFO -- mung: "hello \n ''"
470
+ # I, [2022-05-13T13:16:29.637610 #8492] INFO -- mung: "\f\x00\xFF\\\""
296
471
  #
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.
300
472
  attr_accessor :formatter
301
473
 
302
474
  alias sev_threshold level
303
475
  alias sev_threshold= level=
304
476
 
305
- # Returns +true+ if and only if the current severity level allows for the printing of
306
- # +DEBUG+ messages.
477
+ # Returns +true+ if the log level allows entries with severity
478
+ # Logger::DEBUG to be written, +false+ otherwise.
479
+ # See {Log Level}[rdoc-ref:Logger@Log+Level].
480
+ #
307
481
  def debug?; level <= DEBUG; end
308
482
 
309
- # Sets the severity to DEBUG.
483
+ # Sets the log level to Logger::DEBUG.
484
+ # See {Log Level}[rdoc-ref:Logger@Log+Level].
485
+ #
310
486
  def debug!; self.level = DEBUG; end
311
487
 
312
- # Returns +true+ if and only if the current severity level allows for the printing of
313
- # +INFO+ messages.
488
+ # Returns +true+ if the log level allows entries with severity
489
+ # Logger::INFO to be written, +false+ otherwise.
490
+ # See {Log Level}[rdoc-ref:Logger@Log+Level].
491
+ #
314
492
  def info?; level <= INFO; end
315
493
 
316
- # Sets the severity to INFO.
494
+ # Sets the log level to Logger::INFO.
495
+ # See {Log Level}[rdoc-ref:Logger@Log+Level].
496
+ #
317
497
  def info!; self.level = INFO; end
318
498
 
319
- # Returns +true+ if and only if the current severity level allows for the printing of
320
- # +WARN+ messages.
499
+ # Returns +true+ if the log level allows entries with severity
500
+ # Logger::WARN to be written, +false+ otherwise.
501
+ # See {Log Level}[rdoc-ref:Logger@Log+Level].
502
+ #
321
503
  def warn?; level <= WARN; end
322
504
 
323
- # Sets the severity to WARN.
505
+ # Sets the log level to Logger::WARN.
506
+ # See {Log Level}[rdoc-ref:Logger@Log+Level].
507
+ #
324
508
  def warn!; self.level = WARN; end
325
509
 
326
- # Returns +true+ if and only if the current severity level allows for the printing of
327
- # +ERROR+ messages.
510
+ # Returns +true+ if the log level allows entries with severity
511
+ # Logger::ERROR to be written, +false+ otherwise.
512
+ # See {Log Level}[rdoc-ref:Logger@Log+Level].
513
+ #
328
514
  def error?; level <= ERROR; end
329
515
 
330
- # Sets the severity to ERROR.
516
+ # Sets the log level to Logger::ERROR.
517
+ # See {Log Level}[rdoc-ref:Logger@Log+Level].
518
+ #
331
519
  def error!; self.level = ERROR; end
332
520
 
333
- # Returns +true+ if and only if the current severity level allows for the printing of
334
- # +FATAL+ messages.
521
+ # Returns +true+ if the log level allows entries with severity
522
+ # Logger::FATAL to be written, +false+ otherwise.
523
+ # See {Log Level}[rdoc-ref:Logger@Log+Level].
524
+ #
335
525
  def fatal?; level <= FATAL; end
336
526
 
337
- # Sets the severity to FATAL.
527
+ # Sets the log level to Logger::FATAL.
528
+ # See {Log Level}[rdoc-ref:Logger@Log+Level].
529
+ #
338
530
  def fatal!; self.level = FATAL; end
339
531
 
340
- #
341
532
  # :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')
348
- #
349
- # === Args
350
- #
351
- # +logdev+::
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+).
355
- # +shift_age+::
356
- # Number of old log files to keep, *or* frequency of rotation (+daily+,
357
- # +weekly+ or +monthly+). Default value is 0, which disables log file
358
- # rotation.
359
- # +shift_size+::
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'.
375
- #
376
- # === Description
377
- #
378
- # Create an instance.
533
+ # Logger.new(logdev, shift_age = 0, shift_size = 1048576, **options)
534
+ #
535
+ # With the single argument +logdev+,
536
+ # returns a new logger with all default options:
537
+ #
538
+ # Logger.new('t.log') # => #<Logger:0x000001e685dc6ac8>
539
+ #
540
+ # Argument +logdev+ must be one of:
541
+ #
542
+ # - A string filepath: entries are to be written
543
+ # to the file at that path; if the file at that path exists,
544
+ # new entries are appended.
545
+ # - An IO stream (typically +$stdout+, +$stderr+. or an open file):
546
+ # entries are to be written to the given stream.
547
+ # - +nil+ or +File::NULL+: no entries are to be written.
548
+ #
549
+ # Examples:
550
+ #
551
+ # Logger.new('t.log')
552
+ # Logger.new($stdout)
553
+ #
554
+ # The keyword options are:
555
+ #
556
+ # - +level+: sets the log level; default value is Logger::DEBUG.
557
+ # See {Log Level}[rdoc-ref:Logger@Log+Level]:
558
+ #
559
+ # Logger.new('t.log', level: Logger::ERROR)
560
+ #
561
+ # - +progname+: sets the default program name; default is +nil+.
562
+ # See {Program Name}[rdoc-ref:Logger@Program+Name]:
563
+ #
564
+ # Logger.new('t.log', progname: 'mung')
565
+ #
566
+ # - +formatter+: sets the entry formatter; default is +nil+.
567
+ # See {formatter=}[Logger.html#attribute-i-formatter].
568
+ # - +datetime_format+: sets the format for entry timestamp;
569
+ # default is +nil+.
570
+ # See #datetime_format=.
571
+ # - +binmode+: sets whether the logger writes in binary mode;
572
+ # default is +false+.
573
+ # - +shift_period_suffix+: sets the format for the filename suffix
574
+ # for periodic log file rotation; default is <tt>'%Y%m%d'</tt>.
575
+ # See {Periodic Rotation}[rdoc-ref:Logger@Periodic+Rotation].
379
576
  #
380
577
  def initialize(logdev, shift_age = 0, shift_size = 1048576, level: DEBUG,
381
578
  progname: nil, formatter: nil, datetime_format: nil,
@@ -394,67 +591,60 @@ class Logger
394
591
  end
395
592
  end
396
593
 
397
- #
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.
594
+ # Sets the logger's output stream:
595
+ #
596
+ # - If +logdev+ is +nil+, reopens the current output stream.
597
+ # - If +logdev+ is a filepath, opens the indicated file for append.
598
+ # - If +logdev+ is an IO stream
599
+ # (usually <tt>$stdout</tt>, <tt>$stderr</tt>, or an open File object),
600
+ # opens the stream for append.
601
+ #
602
+ # Example:
603
+ #
604
+ # logger = Logger.new('t.log')
605
+ # logger.add(Logger::ERROR, 'one')
606
+ # logger.close
607
+ # logger.add(Logger::ERROR, 'two') # Prints 'log writing failed. closed stream'
608
+ # logger.reopen
609
+ # logger.add(Logger::ERROR, 'three')
610
+ # logger.close
611
+ # File.readlines('t.log')
612
+ # # =>
613
+ # # ["# Logfile created on 2022-05-12 14:21:19 -0500 by logger.rb/v1.5.0\n",
614
+ # # "E, [2022-05-12T14:21:27.596726 #22428] ERROR -- : one\n",
615
+ # # "E, [2022-05-12T14:23:05.847241 #22428] ERROR -- : three\n"]
412
616
  #
413
617
  def reopen(logdev = nil)
414
618
  @logdev&.reopen(logdev)
415
619
  self
416
620
  end
417
621
 
622
+ # Creates a log entry, which may or may not be written to the log,
623
+ # depending on the entry's severity and on the log level.
624
+ # See {Log Level}[rdoc-ref:Logger@Log+Level]
625
+ # and {Entries}[rdoc-ref:Logger@Entries] for details.
418
626
  #
419
- # :call-seq:
420
- # Logger#add(severity, message = nil, progname = nil) { ... }
421
- #
422
- # === Args
423
- #
424
- # +severity+::
425
- # Severity. Constants are defined in Logger namespace: +DEBUG+, +INFO+,
426
- # +WARN+, +ERROR+, +FATAL+, or +UNKNOWN+.
427
- # +message+::
428
- # The log message. A String or Exception.
429
- # +progname+::
430
- # Program name string. Can be omitted. Treated as a message if no
431
- # +message+ and +block+ are given.
432
- # +block+::
433
- # Can be omitted. Called to get a message string if +message+ is nil.
434
- #
435
- # === Return
436
- #
437
- # When the given severity is not high enough (for this particular logger),
438
- # log no message, and return +true+.
627
+ # Examples:
439
628
  #
440
- # === Description
629
+ # logger = Logger.new($stdout, progname: 'mung')
630
+ # logger.add(Logger::INFO)
631
+ # logger.add(Logger::ERROR, 'No good')
632
+ # logger.add(Logger::ERROR, 'No good', 'gnum')
441
633
  #
442
- # Log a message if the given severity is high enough. This is the generic
443
- # logging method. Users will be more inclined to use #debug, #info, #warn,
444
- # #error, and #fatal.
634
+ # Output:
445
635
  #
446
- # <b>Message format</b>: +message+ can be any object, but it has to be
447
- # converted to a String in order to log it. Generally, +inspect+ is used
448
- # if the given object is not a String.
449
- # A special case is an +Exception+ object, which will be printed in detail,
450
- # including message, class, and backtrace. See #msg2str for the
451
- # implementation if required.
636
+ # I, [2022-05-12T16:25:31.469726 #36328] INFO -- mung: mung
637
+ # E, [2022-05-12T16:25:55.349414 #36328] ERROR -- mung: No good
638
+ # E, [2022-05-12T16:26:35.841134 #36328] ERROR -- gnum: No good
452
639
  #
453
- # === Bugs
640
+ # These convenience methods have implicit severity:
454
641
  #
455
- # * Logfile is not locked.
456
- # * Append open does not need to lock file.
457
- # * If the OS supports multi I/O, records possibly may be mixed.
642
+ # - #debug.
643
+ # - #info.
644
+ # - #warn.
645
+ # - #error.
646
+ # - #fatal.
647
+ # - #unknown.
458
648
  #
459
649
  def add(severity, message = nil, progname = nil)
460
650
  severity ||= UNKNOWN
@@ -478,104 +668,71 @@ class Logger
478
668
  end
479
669
  alias log add
480
670
 
671
+ # Writes the given +msg+ to the log with no formatting;
672
+ # returns the number of characters written,
673
+ # or +nil+ if no log device exists:
481
674
  #
482
- # Dump given message to the log device without any formatting. If no log
483
- # device exists, return +nil+.
675
+ # logger = Logger.new($stdout)
676
+ # logger << 'My message.' # => 10
677
+ #
678
+ # Output:
679
+ #
680
+ # My message.
484
681
  #
485
682
  def <<(msg)
486
683
  @logdev&.write(msg)
487
684
  end
488
685
 
489
- #
490
- # Log a +DEBUG+ message.
491
- #
492
- # See #info for more information.
686
+ # Equivalent to calling #add with severity <tt>Logger::DEBUG</tt>.
493
687
  #
494
688
  def debug(progname = nil, &block)
495
689
  add(DEBUG, nil, progname, &block)
496
690
  end
497
691
 
498
- #
499
- # :call-seq:
500
- # info(message)
501
- # info(progname, &block)
502
- #
503
- # Log an +INFO+ message.
504
- #
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.
512
- #
513
- # === Examples
514
- #
515
- # logger.info("MainApp") { "Received connection from #{ip}" }
516
- # # ...
517
- # logger.info "Waiting for input from user"
518
- # # ...
519
- # logger.info { "User typed #{input}" }
520
- #
521
- # You'll probably stick to the second form above, unless you want to provide a
522
- # program name (which you can do with #progname= as well).
523
- #
524
- # === Return
525
- #
526
- # See #add.
692
+ # Equivalent to calling #add with severity <tt>Logger::INFO</tt>.
527
693
  #
528
694
  def info(progname = nil, &block)
529
695
  add(INFO, nil, progname, &block)
530
696
  end
531
697
 
532
- #
533
- # Log a +WARN+ message.
534
- #
535
- # See #info for more information.
698
+ # Equivalent to calling #add with severity <tt>Logger::WARN</tt>.
536
699
  #
537
700
  def warn(progname = nil, &block)
538
701
  add(WARN, nil, progname, &block)
539
702
  end
540
703
 
541
- #
542
- # Log an +ERROR+ message.
543
- #
544
- # See #info for more information.
704
+ # Equivalent to calling #add with severity <tt>Logger::ERROR</tt>.
545
705
  #
546
706
  def error(progname = nil, &block)
547
707
  add(ERROR, nil, progname, &block)
548
708
  end
549
709
 
550
- #
551
- # Log a +FATAL+ message.
552
- #
553
- # See #info for more information.
710
+ # Equivalent to calling #add with severity <tt>Logger::FATAL</tt>.
554
711
  #
555
712
  def fatal(progname = nil, &block)
556
713
  add(FATAL, nil, progname, &block)
557
714
  end
558
715
 
559
- #
560
- # Log an +UNKNOWN+ message. This will be printed no matter what the logger's
561
- # level is.
562
- #
563
- # See #info for more information.
716
+ # Equivalent to calling #add with severity <tt>Logger::UNKNOWN</tt>.
564
717
  #
565
718
  def unknown(progname = nil, &block)
566
719
  add(UNKNOWN, nil, progname, &block)
567
720
  end
568
721
 
722
+ # Closes the logger; returns +nil+:
569
723
  #
570
- # Close the logging device.
724
+ # logger = Logger.new('t.log')
725
+ # logger.close # => nil
726
+ # logger.info('foo') # Prints "log writing failed. closed stream"
571
727
  #
728
+ # Related: Logger#reopen.
572
729
  def close
573
730
  @logdev&.close
574
731
  end
575
732
 
576
733
  private
577
734
 
578
- # Severity label for logging (max 5 chars).
735
+ # \Severity label for logging (max 5 chars).
579
736
  SEV_LABEL = %w(DEBUG INFO WARN ERROR FATAL ANY).freeze
580
737
 
581
738
  def format_severity(severity)
data/logger.gemspec CHANGED
@@ -23,5 +23,4 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency "bundler", ">= 0"
24
24
  spec.add_development_dependency "rake", ">= 12.3.3"
25
25
  spec.add_development_dependency "test-unit"
26
- spec.add_development_dependency "rdoc"
27
26
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Naotoshi Seo
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-12-20 00:00:00.000000000 Z
12
+ date: 2022-12-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -53,20 +53,6 @@ dependencies:
53
53
  - - ">="
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
- - !ruby/object:Gem::Dependency
57
- name: rdoc
58
- requirement: !ruby/object:Gem::Requirement
59
- requirements:
60
- - - ">="
61
- - !ruby/object:Gem::Version
62
- version: '0'
63
- type: :development
64
- prerelease: false
65
- version_requirements: !ruby/object:Gem::Requirement
66
- requirements:
67
- - - ">="
68
- - !ruby/object:Gem::Version
69
- version: '0'
70
56
  description: Provides a simple logging utility for outputting messages.
71
57
  email:
72
58
  - sonots@gmail.com
@@ -103,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
89
  - !ruby/object:Gem::Version
104
90
  version: '0'
105
91
  requirements: []
106
- rubygems_version: 3.3.0.dev
92
+ rubygems_version: 3.3.26
107
93
  signing_key:
108
94
  specification_version: 4
109
95
  summary: Provides a simple logging utility for outputting messages.