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 +4 -4
- data/lib/logger.rb +254 -425
- data/lib/logger/errors.rb +9 -0
- data/lib/logger/formatter.rb +36 -0
- data/lib/logger/log_device.rb +205 -0
- data/lib/logger/period.rb +47 -0
- data/lib/logger/severity.rb +19 -0
- data/lib/logger/version.rb +5 -0
- data/logger.gemspec +29 -0
- metadata +75 -12
- data/test/logger/test_logger.rb +0 -527
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be49be41055335fa90e79c26a714b7176077de04b414def791b1a9524169ad80
|
4
|
+
data.tar.gz: f064246f7c9e483f96e9838ace1d079ce0d9acea770eb68d5a492a792157bb83
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8dc3387289106d3f98eda8384351d9486b8ad5bb21e5c58a3932595b101705600b1b66c64666eabab90a11f1d90a4b7f01e6f0ce658249bfb9ede51d76414ef4
|
7
|
+
data.tar.gz: 693e411b78fc95e47d008b3ced6247606ebb641f7a17368321e8e0002fa450694267794a620fc76f949ac9cf2a7e5ac8b891416d7d4accb738f8c2c228421388
|
data/lib/logger.rb
CHANGED
@@ -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
|
-
#
|
20
|
-
#
|
21
|
-
# The
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
# levels
|
26
|
-
#
|
27
|
-
# +
|
28
|
-
# +
|
29
|
-
# +
|
30
|
-
# +
|
31
|
-
# +
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
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
|
-
#
|
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
|
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
|
-
#
|
62
|
+
# This creates a Logger that outputs to the standard output stream, with a
|
63
|
+
# level of +WARN+:
|
61
64
|
#
|
62
|
-
#
|
63
|
-
# log.level = Logger::WARN
|
65
|
+
# require 'logger'
|
64
66
|
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
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.
|
77
|
+
# File.foreach(path) do |line|
|
71
78
|
# unless line =~ /^(\w+) = (.*)$/
|
72
|
-
#
|
79
|
+
# logger.error("Line in wrong format: #{line.chomp}")
|
73
80
|
# end
|
74
81
|
# end
|
75
82
|
# rescue => err
|
76
|
-
#
|
77
|
-
#
|
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
|
112
|
-
# #
|
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.
|
116
|
-
# 10 "old log files
|
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
|
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 #{
|
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, [
|
205
|
+
# SeverityID, [DateTime #pid] SeverityLabel -- ProgName: message
|
173
206
|
#
|
174
207
|
# Log sample:
|
175
|
-
# I, [
|
208
|
+
# I, [1999-03-03T02:34:24.895701 #19074] INFO -- Main: info.
|
176
209
|
#
|
177
|
-
# You may change the date and time format
|
210
|
+
# You may change the date and time format via #datetime_format=.
|
178
211
|
#
|
179
|
-
# logger.datetime_format =
|
212
|
+
# logger.datetime_format = '%Y-%m-%d %H:%M:%S'
|
180
213
|
# # e.g. "2004-01-03 00:54:26"
|
181
214
|
#
|
182
|
-
#
|
215
|
+
# or via the constructor.
|
183
216
|
#
|
184
|
-
#
|
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
|
-
#
|
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
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
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
|
-
|
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
|
-
#
|
273
|
+
# Program name to include in log messages.
|
213
274
|
attr_accessor :progname
|
214
275
|
|
215
|
-
#
|
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
|
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
|
227
|
-
#
|
228
|
-
#
|
229
|
-
#
|
230
|
-
# when
|
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?;
|
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?;
|
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?;
|
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?;
|
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?;
|
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
|
-
#
|
258
|
-
#
|
259
|
-
# Logger.new(
|
260
|
-
# Logger.new(
|
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)
|
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
|
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
|
-
|
279
|
-
|
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
|
-
|
386
|
+
self.datetime_format = datetime_format
|
387
|
+
self.formatter = formatter
|
282
388
|
@logdev = nil
|
283
|
-
if logdev
|
284
|
-
@logdev = LogDevice.new(logdev, :
|
285
|
-
:
|
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
|
-
#
|
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
|
-
#
|
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
|
-
# *
|
457
|
+
# * If the OS supports multi I/O, records possibly may be mixed.
|
332
458
|
#
|
333
|
-
def add(severity, message = nil, progname = nil
|
459
|
+
def add(severity, message = nil, progname = nil)
|
334
460
|
severity ||= UNKNOWN
|
335
|
-
if @logdev.nil? or severity <
|
461
|
+
if @logdev.nil? or severity < level
|
336
462
|
return true
|
337
463
|
end
|
338
|
-
|
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
|
-
|
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
|
376
|
-
#
|
377
|
-
#
|
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
|
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
|
573
|
+
@logdev&.close
|
440
574
|
end
|
441
575
|
|
442
576
|
private
|
443
577
|
|
444
|
-
# Severity label for logging
|
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
|