logger 1.2.8.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +8 -0
- data/.travis.yml +6 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +39 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/logger.rb +375 -281
- data/logger.gemspec +27 -0
- metadata +57 -8
- 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: 6b7218297d498d773ba9c79d57749a01f259d336a4e9b5acbfe51274d2442500
|
4
|
+
data.tar.gz: c455593961790cfaf065b7b6b7798d9c184900e1ccdd0aa4f89411e33400e4b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5db2250788d1a9c583685501312a5f6ef277b3148b164b5ed1c2b123a88560ff795b40eee1ac70cf5f466e3dbd771e3d93773c387599b7f7c9916dd7319a99a6
|
7
|
+
data.tar.gz: 94a96a2fba4c2e56e18e2e40f4ca0d0238edd355f65b95182e9ffdfb9a380bd7f932cf9734c0baa2a4c7b79df182cf27151972bfdd3efc6829f58530cd28c287
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
4
|
+
modification, are permitted provided that the following conditions
|
5
|
+
are met:
|
6
|
+
1. Redistributions of source code must retain the above copyright
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
8
|
+
2. Redistributions in binary form must reproduce the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
10
|
+
documentation and/or other materials provided with the distribution.
|
11
|
+
|
12
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
13
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
14
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
15
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
16
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
17
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
18
|
+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
19
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
20
|
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
21
|
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
22
|
+
SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# Logger
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/logger`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'logger'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install logger
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/logger.
|
36
|
+
|
37
|
+
## License
|
38
|
+
|
39
|
+
The gem is available as open source under the terms of the [BSD-2-Clause](LICENSE.txt).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "logger"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/lib/logger.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: false
|
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,42 @@
|
|
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
|
-
|
16
15
|
# == Description
|
17
16
|
#
|
18
17
|
# 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
|
18
|
+
# you can use to output messages.
|
19
|
+
#
|
20
|
+
# The messages have associated levels, such as +INFO+ or +ERROR+ that indicate
|
21
|
+
# their importance. You can then give the Logger a level, and only messages
|
22
|
+
# at that level or higher will be printed.
|
23
|
+
#
|
24
|
+
# The levels are:
|
25
|
+
#
|
26
|
+
# +UNKNOWN+:: An unknown message that should always be logged.
|
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
|
+
# For instance, in a production system, you may have your Logger set to
|
34
|
+
# +INFO+ or even +WARN+.
|
35
|
+
# When you are developing the system, however, you probably
|
36
|
+
# want to know about the program's internal state, and would set the Logger to
|
41
37
|
# +DEBUG+.
|
42
38
|
#
|
43
|
-
#
|
39
|
+
# *Note*: Logger does not escape or sanitize any messages passed to it.
|
44
40
|
# Developers should be aware of when potentially malicious data (user-input)
|
45
41
|
# is passed to Logger, and manually escape the untrusted data:
|
46
42
|
#
|
47
43
|
# logger.info("User-input: #{input.dump}")
|
48
44
|
# logger.info("User-input: %p" % input)
|
49
45
|
#
|
50
|
-
# You can use
|
46
|
+
# You can use #formatter= for escaping all data.
|
51
47
|
#
|
52
48
|
# original_formatter = Logger::Formatter.new
|
53
49
|
# logger.formatter = proc { |severity, datetime, progname, msg|
|
@@ -57,24 +53,29 @@ require 'monitor'
|
|
57
53
|
#
|
58
54
|
# === Example
|
59
55
|
#
|
60
|
-
#
|
56
|
+
# This creates a Logger that outputs to the standard output stream, with a
|
57
|
+
# level of +WARN+:
|
58
|
+
#
|
59
|
+
# require 'logger'
|
61
60
|
#
|
62
|
-
#
|
63
|
-
#
|
61
|
+
# logger = Logger.new(STDOUT)
|
62
|
+
# logger.level = Logger::WARN
|
64
63
|
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
64
|
+
# logger.debug("Created logger")
|
65
|
+
# logger.info("Program started")
|
66
|
+
# logger.warn("Nothing to do!")
|
67
|
+
#
|
68
|
+
# path = "a_non_existent_file"
|
68
69
|
#
|
69
70
|
# begin
|
70
|
-
# File.
|
71
|
+
# File.foreach(path) do |line|
|
71
72
|
# unless line =~ /^(\w+) = (.*)$/
|
72
|
-
#
|
73
|
+
# logger.error("Line in wrong format: #{line.chomp}")
|
73
74
|
# end
|
74
75
|
# end
|
75
76
|
# rescue => err
|
76
|
-
#
|
77
|
-
#
|
77
|
+
# logger.fatal("Caught exception; exiting")
|
78
|
+
# logger.fatal(err)
|
78
79
|
# end
|
79
80
|
#
|
80
81
|
# Because the Logger's level is set to +WARN+, only the warning, error, and
|
@@ -108,16 +109,16 @@ require 'monitor'
|
|
108
109
|
# 3. Create a logger for the specified file.
|
109
110
|
#
|
110
111
|
# file = File.open('foo.log', File::WRONLY | File::APPEND)
|
111
|
-
# # To create new
|
112
|
-
# #
|
112
|
+
# # To create new logfile, add File::CREAT like:
|
113
|
+
# # file = File.open('foo.log', File::WRONLY | File::APPEND | File::CREAT)
|
113
114
|
# logger = Logger.new(file)
|
114
115
|
#
|
115
|
-
# 4. Create a logger which ages logfile once it reaches a certain size.
|
116
|
-
# 10 "old log files
|
116
|
+
# 4. Create a logger which ages the logfile once it reaches a certain size.
|
117
|
+
# Leave 10 "old" log files where each file is about 1,024,000 bytes.
|
117
118
|
#
|
118
119
|
# logger = Logger.new('foo.log', 10, 1024000)
|
119
120
|
#
|
120
|
-
# 5. Create a logger which ages logfile daily/weekly/monthly.
|
121
|
+
# 5. Create a logger which ages the logfile daily/weekly/monthly.
|
121
122
|
#
|
122
123
|
# logger = Logger.new('foo.log', 'daily')
|
123
124
|
# logger = Logger.new('foo.log', 'weekly')
|
@@ -126,17 +127,17 @@ require 'monitor'
|
|
126
127
|
# === How to log a message
|
127
128
|
#
|
128
129
|
# Notice the different methods (+fatal+, +error+, +info+) being used to log
|
129
|
-
# messages of various levels
|
130
|
+
# messages of various levels? Other methods in this family are +warn+ and
|
130
131
|
# +debug+. +add+ is used below to log a message of an arbitrary (perhaps
|
131
132
|
# dynamic) level.
|
132
133
|
#
|
133
|
-
# 1. Message in block.
|
134
|
+
# 1. Message in a block.
|
134
135
|
#
|
135
136
|
# logger.fatal { "Argument 'foo' not given." }
|
136
137
|
#
|
137
138
|
# 2. Message as a string.
|
138
139
|
#
|
139
|
-
# logger.error "Argument #{
|
140
|
+
# logger.error "Argument #{@foo} mismatch."
|
140
141
|
#
|
141
142
|
# 3. With progname.
|
142
143
|
#
|
@@ -146,6 +147,20 @@ require 'monitor'
|
|
146
147
|
#
|
147
148
|
# logger.add(Logger::FATAL) { 'Fatal error!' }
|
148
149
|
#
|
150
|
+
# The block form allows you to create potentially complex log messages,
|
151
|
+
# but to delay their evaluation until and unless the message is
|
152
|
+
# logged. For example, if we have the following:
|
153
|
+
#
|
154
|
+
# logger.debug { "This is a " + potentially + " expensive operation" }
|
155
|
+
#
|
156
|
+
# If the logger's level is +INFO+ or higher, no debug messages will be logged,
|
157
|
+
# and the entire block will not even be evaluated. Compare to this:
|
158
|
+
#
|
159
|
+
# logger.debug("This is a " + potentially + " expensive operation")
|
160
|
+
#
|
161
|
+
# Here, the string concatenation is done every time, even if the log
|
162
|
+
# level is not set to show the debug message.
|
163
|
+
#
|
149
164
|
# === How to close a logger
|
150
165
|
#
|
151
166
|
# logger.close
|
@@ -160,8 +175,20 @@ require 'monitor'
|
|
160
175
|
#
|
161
176
|
# logger.level = Logger::INFO
|
162
177
|
#
|
163
|
-
# DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
|
178
|
+
# # DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
|
179
|
+
#
|
180
|
+
# 3. Symbol or String (case insensitive)
|
164
181
|
#
|
182
|
+
# logger.level = :info
|
183
|
+
# logger.level = 'INFO'
|
184
|
+
#
|
185
|
+
# # :debug < :info < :warn < :error < :fatal < :unknown
|
186
|
+
#
|
187
|
+
# 4. Constructor
|
188
|
+
#
|
189
|
+
# Logger.new(logdev, level: Logger::INFO)
|
190
|
+
# Logger.new(logdev, level: :info)
|
191
|
+
# Logger.new(logdev, level: 'INFO')
|
165
192
|
#
|
166
193
|
# == Format
|
167
194
|
#
|
@@ -169,65 +196,123 @@ require 'monitor'
|
|
169
196
|
# default. The default format and a sample are shown below:
|
170
197
|
#
|
171
198
|
# Log format:
|
172
|
-
# SeverityID, [
|
199
|
+
# SeverityID, [DateTime #pid] SeverityLabel -- ProgName: message
|
173
200
|
#
|
174
201
|
# Log sample:
|
175
|
-
# I, [
|
202
|
+
# I, [1999-03-03T02:34:24.895701 #19074] INFO -- Main: info.
|
176
203
|
#
|
177
|
-
# You may change the date and time format
|
204
|
+
# You may change the date and time format via #datetime_format=.
|
178
205
|
#
|
179
|
-
# logger.datetime_format =
|
206
|
+
# logger.datetime_format = '%Y-%m-%d %H:%M:%S'
|
180
207
|
# # e.g. "2004-01-03 00:54:26"
|
181
208
|
#
|
182
|
-
#
|
209
|
+
# or via the constructor.
|
183
210
|
#
|
184
|
-
#
|
211
|
+
# Logger.new(logdev, datetime_format: '%Y-%m-%d %H:%M:%S')
|
212
|
+
#
|
213
|
+
# Or, you may change the overall format via the #formatter= method.
|
214
|
+
#
|
215
|
+
# logger.formatter = proc do |severity, datetime, progname, msg|
|
185
216
|
# "#{datetime}: #{msg}\n"
|
186
|
-
#
|
187
|
-
#
|
217
|
+
# end
|
218
|
+
# # e.g. "2005-09-22 08:51:08 +0900: hello world"
|
219
|
+
#
|
220
|
+
# or via the constructor.
|
221
|
+
#
|
222
|
+
# Logger.new(logdev, formatter: proc {|severity, datetime, progname, msg|
|
223
|
+
# "#{datetime}: #{msg}\n"
|
224
|
+
# })
|
188
225
|
#
|
189
|
-
|
190
|
-
|
191
226
|
class Logger
|
192
|
-
VERSION = "1.
|
193
|
-
|
227
|
+
VERSION = "1.3.0"
|
228
|
+
_, name, rev = %w$Id$
|
229
|
+
if name
|
230
|
+
name = name.chomp(",v")
|
231
|
+
else
|
232
|
+
name = File.basename(__FILE__)
|
233
|
+
end
|
234
|
+
rev ||= "v#{VERSION}"
|
235
|
+
ProgName = "#{name}/#{rev}".freeze
|
194
236
|
|
195
|
-
class Error < RuntimeError
|
196
|
-
|
237
|
+
class Error < RuntimeError # :nodoc:
|
238
|
+
end
|
239
|
+
# not used after 1.2.7. just for compat.
|
240
|
+
class ShiftingError < Error # :nodoc:
|
241
|
+
end
|
197
242
|
|
198
243
|
# Logging severity.
|
199
244
|
module Severity
|
245
|
+
# Low-level information, mostly for developers.
|
200
246
|
DEBUG = 0
|
247
|
+
# Generic (useful) information about system operation.
|
201
248
|
INFO = 1
|
249
|
+
# A warning.
|
202
250
|
WARN = 2
|
251
|
+
# A handleable error condition.
|
203
252
|
ERROR = 3
|
253
|
+
# An unhandleable error that results in a program crash.
|
204
254
|
FATAL = 4
|
255
|
+
# An unknown message that should always be logged.
|
205
256
|
UNKNOWN = 5
|
206
257
|
end
|
207
258
|
include Severity
|
208
259
|
|
209
260
|
# Logging severity threshold (e.g. <tt>Logger::INFO</tt>).
|
210
|
-
|
261
|
+
attr_reader :level
|
262
|
+
|
263
|
+
# Set logging severity threshold.
|
264
|
+
#
|
265
|
+
# +severity+:: The Severity of the log message.
|
266
|
+
def level=(severity)
|
267
|
+
if severity.is_a?(Integer)
|
268
|
+
@level = severity
|
269
|
+
else
|
270
|
+
case severity.to_s.downcase
|
271
|
+
when 'debug'
|
272
|
+
@level = DEBUG
|
273
|
+
when 'info'
|
274
|
+
@level = INFO
|
275
|
+
when 'warn'
|
276
|
+
@level = WARN
|
277
|
+
when 'error'
|
278
|
+
@level = ERROR
|
279
|
+
when 'fatal'
|
280
|
+
@level = FATAL
|
281
|
+
when 'unknown'
|
282
|
+
@level = UNKNOWN
|
283
|
+
else
|
284
|
+
raise ArgumentError, "invalid log level: #{severity}"
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
211
288
|
|
212
|
-
#
|
289
|
+
# Program name to include in log messages.
|
213
290
|
attr_accessor :progname
|
214
291
|
|
215
|
-
#
|
292
|
+
# Set date-time format.
|
293
|
+
#
|
294
|
+
# +datetime_format+:: A string suitable for passing to +strftime+.
|
216
295
|
def datetime_format=(datetime_format)
|
217
296
|
@default_formatter.datetime_format = datetime_format
|
218
297
|
end
|
219
298
|
|
220
|
-
# Returns the date format
|
221
|
-
# using datetime_format=)
|
299
|
+
# Returns the date format being used. See #datetime_format=
|
222
300
|
def datetime_format
|
223
301
|
@default_formatter.datetime_format
|
224
302
|
end
|
225
303
|
|
226
|
-
# Logging formatter
|
227
|
-
#
|
228
|
-
#
|
229
|
-
#
|
230
|
-
# when
|
304
|
+
# Logging formatter, as a +Proc+ that will take four arguments and
|
305
|
+
# return the formatted message. The arguments are:
|
306
|
+
#
|
307
|
+
# +severity+:: The Severity of the log message.
|
308
|
+
# +time+:: A Time instance representing when the message was logged.
|
309
|
+
# +progname+:: The #progname configured, or passed to the logger method.
|
310
|
+
# +msg+:: The _Object_ the user passed to the log message; not necessarily a
|
311
|
+
# String.
|
312
|
+
#
|
313
|
+
# The block should return an Object that can be written to the logging
|
314
|
+
# device via +write+. The default formatter is used when no formatter is
|
315
|
+
# set.
|
231
316
|
attr_accessor :formatter
|
232
317
|
|
233
318
|
alias sev_threshold level
|
@@ -254,10 +339,13 @@ class Logger
|
|
254
339
|
def fatal?; @level <= FATAL; end
|
255
340
|
|
256
341
|
#
|
257
|
-
#
|
258
|
-
#
|
259
|
-
# Logger.new(
|
260
|
-
# Logger.new(
|
342
|
+
# :call-seq:
|
343
|
+
# Logger.new(logdev, shift_age = 0, shift_size = 1048576)
|
344
|
+
# Logger.new(logdev, shift_age = 'weekly')
|
345
|
+
# Logger.new(logdev, level: :info)
|
346
|
+
# Logger.new(logdev, progname: 'progname')
|
347
|
+
# Logger.new(logdev, formatter: formatter)
|
348
|
+
# Logger.new(logdev, datetime_format: '%Y-%m-%d %H:%M:%S')
|
261
349
|
#
|
262
350
|
# === Args
|
263
351
|
#
|
@@ -266,29 +354,65 @@ class Logger
|
|
266
354
|
# +STDOUT+, +STDERR+, or an open file).
|
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.
|
270
358
|
# +shift_size+::
|
271
|
-
# Maximum logfile size (only applies when +shift_age+ is a number).
|
359
|
+
# Maximum logfile size in bytes (only applies when +shift_age+ is a number).
|
360
|
+
# Defaults to +1048576+ (1MB).
|
361
|
+
# +level+::
|
362
|
+
# Logging severity threshold. Default values is Logger::DEBUG.
|
363
|
+
# +progname+::
|
364
|
+
# Program name to include in log messages. Default value is nil.
|
365
|
+
# +formatter+::
|
366
|
+
# Logging formatter. Default values is an instance of Logger::Formatter.
|
367
|
+
# +datetime_format+::
|
368
|
+
# Date and time format. Default value is '%Y-%m-%d %H:%M:%S'.
|
369
|
+
# +shift_period_suffix+::
|
370
|
+
# The log file suffix format for +daily+, +weekly+ or +monthly+ rotation.
|
371
|
+
# Default is '%Y%m%d'.
|
272
372
|
#
|
273
373
|
# === Description
|
274
374
|
#
|
275
375
|
# Create an instance.
|
276
376
|
#
|
277
|
-
def initialize(logdev, shift_age = 0, shift_size = 1048576
|
278
|
-
|
279
|
-
|
377
|
+
def initialize(logdev, shift_age = 0, shift_size = 1048576, level: DEBUG,
|
378
|
+
progname: nil, formatter: nil, datetime_format: nil,
|
379
|
+
shift_period_suffix: '%Y%m%d')
|
380
|
+
self.level = level
|
381
|
+
self.progname = progname
|
280
382
|
@default_formatter = Formatter.new
|
281
|
-
|
383
|
+
self.datetime_format = datetime_format
|
384
|
+
self.formatter = formatter
|
282
385
|
@logdev = nil
|
283
386
|
if logdev
|
284
387
|
@logdev = LogDevice.new(logdev, :shift_age => shift_age,
|
285
|
-
:shift_size => shift_size
|
388
|
+
:shift_size => shift_size,
|
389
|
+
:shift_period_suffix => shift_period_suffix)
|
286
390
|
end
|
287
391
|
end
|
288
392
|
|
289
393
|
#
|
290
|
-
#
|
394
|
+
# :call-seq:
|
395
|
+
# Logger#reopen
|
396
|
+
# Logger#reopen(logdev)
|
397
|
+
#
|
398
|
+
# === Args
|
399
|
+
#
|
400
|
+
# +logdev+::
|
401
|
+
# The log device. This is a filename (String) or IO object (typically
|
402
|
+
# +STDOUT+, +STDERR+, or an open file). reopen the same filename if
|
403
|
+
# it is +nil+, do nothing for IO. Default is +nil+.
|
404
|
+
#
|
405
|
+
# === Description
|
406
|
+
#
|
407
|
+
# Reopen a log device.
|
291
408
|
#
|
409
|
+
def reopen(logdev = nil)
|
410
|
+
@logdev.reopen(logdev)
|
411
|
+
self
|
412
|
+
end
|
413
|
+
|
414
|
+
#
|
415
|
+
# :call-seq:
|
292
416
|
# Logger#add(severity, message = nil, progname = nil) { ... }
|
293
417
|
#
|
294
418
|
# === Args
|
@@ -306,10 +430,8 @@ class Logger
|
|
306
430
|
#
|
307
431
|
# === Return
|
308
432
|
#
|
309
|
-
#
|
310
|
-
#
|
311
|
-
# When the given severity is not high enough (for this particular logger), log
|
312
|
-
# no message, and return +true+.
|
433
|
+
# When the given severity is not high enough (for this particular logger),
|
434
|
+
# log no message, and return +true+.
|
313
435
|
#
|
314
436
|
# === Description
|
315
437
|
#
|
@@ -328,14 +450,16 @@ class Logger
|
|
328
450
|
#
|
329
451
|
# * Logfile is not locked.
|
330
452
|
# * Append open does not need to lock file.
|
331
|
-
# *
|
453
|
+
# * If the OS supports multi I/O, records possibly may be mixed.
|
332
454
|
#
|
333
|
-
def add(severity, message = nil, progname = nil
|
455
|
+
def add(severity, message = nil, progname = nil)
|
334
456
|
severity ||= UNKNOWN
|
335
457
|
if @logdev.nil? or severity < @level
|
336
458
|
return true
|
337
459
|
end
|
338
|
-
|
460
|
+
if progname.nil?
|
461
|
+
progname = @progname
|
462
|
+
end
|
339
463
|
if message.nil?
|
340
464
|
if block_given?
|
341
465
|
message = yield
|
@@ -355,9 +479,7 @@ class Logger
|
|
355
479
|
# device exists, return +nil+.
|
356
480
|
#
|
357
481
|
def <<(msg)
|
358
|
-
|
359
|
-
@logdev.write(msg)
|
360
|
-
end
|
482
|
+
@logdev&.write(msg)
|
361
483
|
end
|
362
484
|
|
363
485
|
#
|
@@ -369,12 +491,20 @@ class Logger
|
|
369
491
|
add(DEBUG, nil, progname, &block)
|
370
492
|
end
|
371
493
|
|
494
|
+
#
|
495
|
+
# :call-seq:
|
496
|
+
# info(message)
|
497
|
+
# info(progname, &block)
|
372
498
|
#
|
373
499
|
# Log an +INFO+ message.
|
374
500
|
#
|
375
|
-
# The message
|
376
|
-
#
|
377
|
-
#
|
501
|
+
# +message+:: The message to log; does not need to be a String.
|
502
|
+
# +progname+:: In the block form, this is the #progname to use in the
|
503
|
+
# log message. The default can be set with #progname=.
|
504
|
+
# +block+:: Evaluates to the message to log. This is not evaluated unless
|
505
|
+
# the logger's level is sufficient to log the message. This
|
506
|
+
# allows you to create potentially expensive logging messages that
|
507
|
+
# are only called when the logger is configured to show them.
|
378
508
|
#
|
379
509
|
# === Examples
|
380
510
|
#
|
@@ -385,7 +515,7 @@ class Logger
|
|
385
515
|
# logger.info { "User typed #{input}" }
|
386
516
|
#
|
387
517
|
# You'll probably stick to the second form above, unless you want to provide a
|
388
|
-
# program name (which you can do with
|
518
|
+
# program name (which you can do with #progname= as well).
|
389
519
|
#
|
390
520
|
# === Return
|
391
521
|
#
|
@@ -423,8 +553,8 @@ class Logger
|
|
423
553
|
end
|
424
554
|
|
425
555
|
#
|
426
|
-
# Log an +UNKNOWN+ message. This will be printed no matter what the logger
|
427
|
-
# level.
|
556
|
+
# Log an +UNKNOWN+ message. This will be printed no matter what the logger's
|
557
|
+
# level is.
|
428
558
|
#
|
429
559
|
# See #info for more information.
|
430
560
|
#
|
@@ -436,13 +566,13 @@ class Logger
|
|
436
566
|
# Close the logging device.
|
437
567
|
#
|
438
568
|
def close
|
439
|
-
@logdev
|
569
|
+
@logdev&.close
|
440
570
|
end
|
441
571
|
|
442
572
|
private
|
443
573
|
|
444
|
-
# Severity label for logging
|
445
|
-
SEV_LABEL = %w(DEBUG INFO WARN ERROR FATAL ANY)
|
574
|
+
# Severity label for logging (max 5 chars).
|
575
|
+
SEV_LABEL = %w(DEBUG INFO WARN ERROR FATAL ANY).each(&:freeze).freeze
|
446
576
|
|
447
577
|
def format_severity(severity)
|
448
578
|
SEV_LABEL[severity] || 'ANY'
|
@@ -453,8 +583,9 @@ private
|
|
453
583
|
end
|
454
584
|
|
455
585
|
|
586
|
+
# Default formatter for log messages.
|
456
587
|
class Formatter
|
457
|
-
Format = "%s, [%s#%d] %5s -- %s: %s\n"
|
588
|
+
Format = "%s, [%s#%d] %5s -- %s: %s\n".freeze
|
458
589
|
|
459
590
|
attr_accessor :datetime_format
|
460
591
|
|
@@ -470,11 +601,7 @@ private
|
|
470
601
|
private
|
471
602
|
|
472
603
|
def format_datetime(time)
|
473
|
-
|
474
|
-
time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec
|
475
|
-
else
|
476
|
-
time.strftime(@datetime_format)
|
477
|
-
end
|
604
|
+
time.strftime(@datetime_format || "%Y-%m-%dT%H:%M:%S.%6N ".freeze)
|
478
605
|
end
|
479
606
|
|
480
607
|
def msg2str(msg)
|
@@ -490,32 +617,73 @@ private
|
|
490
617
|
end
|
491
618
|
end
|
492
619
|
|
620
|
+
module Period
|
621
|
+
module_function
|
493
622
|
|
494
|
-
|
495
|
-
attr_reader :dev
|
496
|
-
attr_reader :filename
|
623
|
+
SiD = 24 * 60 * 60
|
497
624
|
|
498
|
-
|
499
|
-
|
625
|
+
def next_rotate_time(now, shift_age)
|
626
|
+
case shift_age
|
627
|
+
when 'daily'
|
628
|
+
t = Time.mktime(now.year, now.month, now.mday) + SiD
|
629
|
+
when 'weekly'
|
630
|
+
t = Time.mktime(now.year, now.month, now.mday) + SiD * (7 - now.wday)
|
631
|
+
when 'monthly'
|
632
|
+
t = Time.mktime(now.year, now.month, 1) + SiD * 32
|
633
|
+
return Time.mktime(t.year, t.month, 1)
|
634
|
+
else
|
635
|
+
return now
|
636
|
+
end
|
637
|
+
if t.hour.nonzero? or t.min.nonzero? or t.sec.nonzero?
|
638
|
+
hour = t.hour
|
639
|
+
t = Time.mktime(t.year, t.month, t.mday)
|
640
|
+
t += SiD if hour > 12
|
641
|
+
end
|
642
|
+
t
|
500
643
|
end
|
501
644
|
|
502
|
-
def
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
645
|
+
def previous_period_end(now, shift_age)
|
646
|
+
case shift_age
|
647
|
+
when 'daily'
|
648
|
+
t = Time.mktime(now.year, now.month, now.mday) - SiD / 2
|
649
|
+
when 'weekly'
|
650
|
+
t = Time.mktime(now.year, now.month, now.mday) - (SiD * now.wday + SiD / 2)
|
651
|
+
when 'monthly'
|
652
|
+
t = Time.mktime(now.year, now.month, 1) - SiD / 2
|
507
653
|
else
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
654
|
+
return now
|
655
|
+
end
|
656
|
+
Time.mktime(t.year, t.month, t.mday, 23, 59, 59)
|
657
|
+
end
|
658
|
+
end
|
659
|
+
|
660
|
+
# Device used for logging messages.
|
661
|
+
class LogDevice
|
662
|
+
include Period
|
663
|
+
|
664
|
+
attr_reader :dev
|
665
|
+
attr_reader :filename
|
666
|
+
include MonitorMixin
|
667
|
+
|
668
|
+
def initialize(log = nil, shift_age: nil, shift_size: nil, shift_period_suffix: nil)
|
669
|
+
@dev = @filename = @shift_age = @shift_size = @shift_period_suffix = nil
|
670
|
+
mon_initialize
|
671
|
+
set_dev(log)
|
672
|
+
if @filename
|
673
|
+
@shift_age = shift_age || 7
|
674
|
+
@shift_size = shift_size || 1048576
|
675
|
+
@shift_period_suffix = shift_period_suffix || '%Y%m%d'
|
676
|
+
|
677
|
+
unless @shift_age.is_a?(Integer)
|
678
|
+
base_time = @dev.respond_to?(:stat) ? @dev.stat.mtime : Time.now
|
679
|
+
@next_rotate_time = next_rotate_time(base_time, @shift_age)
|
680
|
+
end
|
513
681
|
end
|
514
682
|
end
|
515
683
|
|
516
684
|
def write(message)
|
517
685
|
begin
|
518
|
-
|
686
|
+
synchronize do
|
519
687
|
if @shift_age and @dev.respond_to?(:stat)
|
520
688
|
begin
|
521
689
|
check_shift_log
|
@@ -536,7 +704,7 @@ private
|
|
536
704
|
|
537
705
|
def close
|
538
706
|
begin
|
539
|
-
|
707
|
+
synchronize do
|
540
708
|
@dev.close rescue nil
|
541
709
|
end
|
542
710
|
rescue Exception
|
@@ -544,43 +712,110 @@ private
|
|
544
712
|
end
|
545
713
|
end
|
546
714
|
|
715
|
+
def reopen(log = nil)
|
716
|
+
# reopen the same filename if no argument, do nothing for IO
|
717
|
+
log ||= @filename if @filename
|
718
|
+
if log
|
719
|
+
synchronize do
|
720
|
+
if @filename and @dev
|
721
|
+
@dev.close rescue nil # close only file opened by Logger
|
722
|
+
@filename = nil
|
723
|
+
end
|
724
|
+
set_dev(log)
|
725
|
+
end
|
726
|
+
end
|
727
|
+
self
|
728
|
+
end
|
729
|
+
|
547
730
|
private
|
548
731
|
|
549
|
-
def
|
550
|
-
if (
|
551
|
-
|
732
|
+
def set_dev(log)
|
733
|
+
if log.respond_to?(:write) and log.respond_to?(:close)
|
734
|
+
@dev = log
|
552
735
|
else
|
736
|
+
@dev = open_logfile(log)
|
737
|
+
@dev.sync = true
|
738
|
+
@filename = log
|
739
|
+
end
|
740
|
+
end
|
741
|
+
|
742
|
+
def open_logfile(filename)
|
743
|
+
begin
|
744
|
+
File.open(filename, (File::WRONLY | File::APPEND))
|
745
|
+
rescue Errno::ENOENT
|
553
746
|
create_logfile(filename)
|
554
747
|
end
|
555
748
|
end
|
556
749
|
|
557
750
|
def create_logfile(filename)
|
558
|
-
|
559
|
-
|
560
|
-
|
751
|
+
begin
|
752
|
+
logdev = File.open(filename, (File::WRONLY | File::APPEND | File::CREAT | File::EXCL))
|
753
|
+
logdev.flock(File::LOCK_EX)
|
754
|
+
logdev.sync = true
|
755
|
+
add_log_header(logdev)
|
756
|
+
logdev.flock(File::LOCK_UN)
|
757
|
+
rescue Errno::EEXIST
|
758
|
+
# file is created by another process
|
759
|
+
logdev = open_logfile(filename)
|
760
|
+
logdev.sync = true
|
761
|
+
end
|
561
762
|
logdev
|
562
763
|
end
|
563
764
|
|
564
765
|
def add_log_header(file)
|
565
766
|
file.write(
|
566
767
|
"# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName]
|
567
|
-
)
|
768
|
+
) if file.size == 0
|
568
769
|
end
|
569
770
|
|
570
|
-
SiD = 24 * 60 * 60
|
571
|
-
|
572
771
|
def check_shift_log
|
573
772
|
if @shift_age.is_a?(Integer)
|
574
773
|
# Note: always returns false if '0'.
|
575
774
|
if @filename && (@shift_age > 0) && (@dev.stat.size > @shift_size)
|
576
|
-
shift_log_age
|
775
|
+
lock_shift_log { shift_log_age }
|
577
776
|
end
|
578
777
|
else
|
579
778
|
now = Time.now
|
580
|
-
|
581
|
-
|
582
|
-
shift_log_period(
|
779
|
+
if now >= @next_rotate_time
|
780
|
+
@next_rotate_time = next_rotate_time(now, @shift_age)
|
781
|
+
lock_shift_log { shift_log_period(previous_period_end(now, @shift_age)) }
|
782
|
+
end
|
783
|
+
end
|
784
|
+
end
|
785
|
+
|
786
|
+
if /mswin|mingw/ =~ RUBY_PLATFORM
|
787
|
+
def lock_shift_log
|
788
|
+
yield
|
789
|
+
end
|
790
|
+
else
|
791
|
+
def lock_shift_log
|
792
|
+
retry_limit = 8
|
793
|
+
retry_sleep = 0.1
|
794
|
+
begin
|
795
|
+
File.open(@filename, File::WRONLY | File::APPEND) do |lock|
|
796
|
+
lock.flock(File::LOCK_EX) # inter-process locking. will be unlocked at closing file
|
797
|
+
if File.identical?(@filename, lock) and File.identical?(lock, @dev)
|
798
|
+
yield # log shifting
|
799
|
+
else
|
800
|
+
# log shifted by another process (i-node before locking and i-node after locking are different)
|
801
|
+
@dev.close rescue nil
|
802
|
+
@dev = open_logfile(@filename)
|
803
|
+
@dev.sync = true
|
804
|
+
end
|
805
|
+
end
|
806
|
+
rescue Errno::ENOENT
|
807
|
+
# @filename file would not exist right after #rename and before #create_logfile
|
808
|
+
if retry_limit <= 0
|
809
|
+
warn("log rotation inter-process lock failed. #{$!}")
|
810
|
+
else
|
811
|
+
sleep retry_sleep
|
812
|
+
retry_limit -= 1
|
813
|
+
retry_sleep *= 2
|
814
|
+
retry
|
815
|
+
end
|
583
816
|
end
|
817
|
+
rescue
|
818
|
+
warn("log rotation inter-process lock failed. #{$!}")
|
584
819
|
end
|
585
820
|
end
|
586
821
|
|
@@ -597,15 +832,15 @@ private
|
|
597
832
|
end
|
598
833
|
|
599
834
|
def shift_log_period(period_end)
|
600
|
-
|
601
|
-
age_file = "#{@filename}.#{
|
835
|
+
suffix = period_end.strftime(@shift_period_suffix)
|
836
|
+
age_file = "#{@filename}.#{suffix}"
|
602
837
|
if FileTest.exist?(age_file)
|
603
838
|
# try to avoid filename crash caused by Timestamp change.
|
604
839
|
idx = 0
|
605
840
|
# .99 can be overridden; avoid too much file search with 'loop do'
|
606
841
|
while idx < 100
|
607
842
|
idx += 1
|
608
|
-
age_file = "#{@filename}.#{
|
843
|
+
age_file = "#{@filename}.#{suffix}.#{idx}"
|
609
844
|
break unless FileTest.exist?(age_file)
|
610
845
|
end
|
611
846
|
end
|
@@ -614,146 +849,5 @@ private
|
|
614
849
|
@dev = create_logfile(@filename)
|
615
850
|
return true
|
616
851
|
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
852
|
end
|
759
853
|
end
|