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
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Logger
|
4
|
+
# Default formatter for log messages.
|
5
|
+
class Formatter
|
6
|
+
Format = "%s, [%s#%d] %5s -- %s: %s\n"
|
7
|
+
|
8
|
+
attr_accessor :datetime_format
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@datetime_format = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(severity, time, progname, msg)
|
15
|
+
Format % [severity[0..0], format_datetime(time), Process.pid, severity, progname,
|
16
|
+
msg2str(msg)]
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def format_datetime(time)
|
22
|
+
time.strftime(@datetime_format || "%Y-%m-%dT%H:%M:%S.%6N ")
|
23
|
+
end
|
24
|
+
|
25
|
+
def msg2str(msg)
|
26
|
+
case msg
|
27
|
+
when ::String
|
28
|
+
msg
|
29
|
+
when ::Exception
|
30
|
+
"#{ msg.message } (#{ msg.class })\n#{ msg.backtrace.join("\n") if msg.backtrace }"
|
31
|
+
else
|
32
|
+
msg.inspect
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'period'
|
4
|
+
|
5
|
+
class Logger
|
6
|
+
# Device used for logging messages.
|
7
|
+
class LogDevice
|
8
|
+
include Period
|
9
|
+
|
10
|
+
attr_reader :dev
|
11
|
+
attr_reader :filename
|
12
|
+
include MonitorMixin
|
13
|
+
|
14
|
+
def initialize(log = nil, shift_age: nil, shift_size: nil, shift_period_suffix: nil, binmode: false)
|
15
|
+
@dev = @filename = @shift_age = @shift_size = @shift_period_suffix = nil
|
16
|
+
@binmode = binmode
|
17
|
+
mon_initialize
|
18
|
+
set_dev(log)
|
19
|
+
if @filename
|
20
|
+
@shift_age = shift_age || 7
|
21
|
+
@shift_size = shift_size || 1048576
|
22
|
+
@shift_period_suffix = shift_period_suffix || '%Y%m%d'
|
23
|
+
|
24
|
+
unless @shift_age.is_a?(Integer)
|
25
|
+
base_time = @dev.respond_to?(:stat) ? @dev.stat.mtime : Time.now
|
26
|
+
@next_rotate_time = next_rotate_time(base_time, @shift_age)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def write(message)
|
32
|
+
begin
|
33
|
+
synchronize do
|
34
|
+
if @shift_age and @dev.respond_to?(:stat)
|
35
|
+
begin
|
36
|
+
check_shift_log
|
37
|
+
rescue
|
38
|
+
warn("log shifting failed. #{$!}")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
begin
|
42
|
+
@dev.write(message)
|
43
|
+
rescue
|
44
|
+
warn("log writing failed. #{$!}")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
rescue Exception => ignored
|
48
|
+
warn("log writing failed. #{ignored}")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def close
|
53
|
+
begin
|
54
|
+
synchronize do
|
55
|
+
@dev.close rescue nil
|
56
|
+
end
|
57
|
+
rescue Exception
|
58
|
+
@dev.close rescue nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def reopen(log = nil)
|
63
|
+
# reopen the same filename if no argument, do nothing for IO
|
64
|
+
log ||= @filename if @filename
|
65
|
+
if log
|
66
|
+
synchronize do
|
67
|
+
if @filename and @dev
|
68
|
+
@dev.close rescue nil # close only file opened by Logger
|
69
|
+
@filename = nil
|
70
|
+
end
|
71
|
+
set_dev(log)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def set_dev(log)
|
80
|
+
if log.respond_to?(:write) and log.respond_to?(:close)
|
81
|
+
@dev = log
|
82
|
+
if log.respond_to?(:path)
|
83
|
+
@filename = log.path
|
84
|
+
end
|
85
|
+
else
|
86
|
+
@dev = open_logfile(log)
|
87
|
+
@dev.sync = true
|
88
|
+
@dev.binmode if @binmode
|
89
|
+
@filename = log
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def open_logfile(filename)
|
94
|
+
begin
|
95
|
+
File.open(filename, (File::WRONLY | File::APPEND))
|
96
|
+
rescue Errno::ENOENT
|
97
|
+
create_logfile(filename)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def create_logfile(filename)
|
102
|
+
begin
|
103
|
+
logdev = File.open(filename, (File::WRONLY | File::APPEND | File::CREAT | File::EXCL))
|
104
|
+
logdev.flock(File::LOCK_EX)
|
105
|
+
logdev.sync = true
|
106
|
+
logdev.binmode if @binmode
|
107
|
+
add_log_header(logdev)
|
108
|
+
logdev.flock(File::LOCK_UN)
|
109
|
+
rescue Errno::EEXIST
|
110
|
+
# file is created by another process
|
111
|
+
logdev = open_logfile(filename)
|
112
|
+
logdev.sync = true
|
113
|
+
end
|
114
|
+
logdev
|
115
|
+
end
|
116
|
+
|
117
|
+
def add_log_header(file)
|
118
|
+
file.write(
|
119
|
+
"# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName]
|
120
|
+
) if file.size == 0
|
121
|
+
end
|
122
|
+
|
123
|
+
def check_shift_log
|
124
|
+
if @shift_age.is_a?(Integer)
|
125
|
+
# Note: always returns false if '0'.
|
126
|
+
if @filename && (@shift_age > 0) && (@dev.stat.size > @shift_size)
|
127
|
+
lock_shift_log { shift_log_age }
|
128
|
+
end
|
129
|
+
else
|
130
|
+
now = Time.now
|
131
|
+
if now >= @next_rotate_time
|
132
|
+
@next_rotate_time = next_rotate_time(now, @shift_age)
|
133
|
+
lock_shift_log { shift_log_period(previous_period_end(now, @shift_age)) }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
if /mswin|mingw|cygwin/ =~ RUBY_PLATFORM
|
139
|
+
def lock_shift_log
|
140
|
+
yield
|
141
|
+
end
|
142
|
+
else
|
143
|
+
def lock_shift_log
|
144
|
+
retry_limit = 8
|
145
|
+
retry_sleep = 0.1
|
146
|
+
begin
|
147
|
+
File.open(@filename, File::WRONLY | File::APPEND) do |lock|
|
148
|
+
lock.flock(File::LOCK_EX) # inter-process locking. will be unlocked at closing file
|
149
|
+
if File.identical?(@filename, lock) and File.identical?(lock, @dev)
|
150
|
+
yield # log shifting
|
151
|
+
else
|
152
|
+
# log shifted by another process (i-node before locking and i-node after locking are different)
|
153
|
+
@dev.close rescue nil
|
154
|
+
@dev = open_logfile(@filename)
|
155
|
+
@dev.sync = true
|
156
|
+
end
|
157
|
+
end
|
158
|
+
rescue Errno::ENOENT
|
159
|
+
# @filename file would not exist right after #rename and before #create_logfile
|
160
|
+
if retry_limit <= 0
|
161
|
+
warn("log rotation inter-process lock failed. #{$!}")
|
162
|
+
else
|
163
|
+
sleep retry_sleep
|
164
|
+
retry_limit -= 1
|
165
|
+
retry_sleep *= 2
|
166
|
+
retry
|
167
|
+
end
|
168
|
+
end
|
169
|
+
rescue
|
170
|
+
warn("log rotation inter-process lock failed. #{$!}")
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def shift_log_age
|
175
|
+
(@shift_age-3).downto(0) do |i|
|
176
|
+
if FileTest.exist?("#{@filename}.#{i}")
|
177
|
+
File.rename("#{@filename}.#{i}", "#{@filename}.#{i+1}")
|
178
|
+
end
|
179
|
+
end
|
180
|
+
@dev.close rescue nil
|
181
|
+
File.rename("#{@filename}", "#{@filename}.0")
|
182
|
+
@dev = create_logfile(@filename)
|
183
|
+
return true
|
184
|
+
end
|
185
|
+
|
186
|
+
def shift_log_period(period_end)
|
187
|
+
suffix = period_end.strftime(@shift_period_suffix)
|
188
|
+
age_file = "#{@filename}.#{suffix}"
|
189
|
+
if FileTest.exist?(age_file)
|
190
|
+
# try to avoid filename crash caused by Timestamp change.
|
191
|
+
idx = 0
|
192
|
+
# .99 can be overridden; avoid too much file search with 'loop do'
|
193
|
+
while idx < 100
|
194
|
+
idx += 1
|
195
|
+
age_file = "#{@filename}.#{suffix}.#{idx}"
|
196
|
+
break unless FileTest.exist?(age_file)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
@dev.close rescue nil
|
200
|
+
File.rename("#{@filename}", age_file)
|
201
|
+
@dev = create_logfile(@filename)
|
202
|
+
return true
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Logger
|
4
|
+
module Period
|
5
|
+
module_function
|
6
|
+
|
7
|
+
SiD = 24 * 60 * 60
|
8
|
+
|
9
|
+
def next_rotate_time(now, shift_age)
|
10
|
+
case shift_age
|
11
|
+
when 'daily'
|
12
|
+
t = Time.mktime(now.year, now.month, now.mday) + SiD
|
13
|
+
when 'weekly'
|
14
|
+
t = Time.mktime(now.year, now.month, now.mday) + SiD * (7 - now.wday)
|
15
|
+
when 'monthly'
|
16
|
+
t = Time.mktime(now.year, now.month, 1) + SiD * 32
|
17
|
+
return Time.mktime(t.year, t.month, 1)
|
18
|
+
when 'now', 'everytime'
|
19
|
+
return now
|
20
|
+
else
|
21
|
+
raise ArgumentError, "invalid :shift_age #{shift_age.inspect}, should be daily, weekly, monthly, or everytime"
|
22
|
+
end
|
23
|
+
if t.hour.nonzero? or t.min.nonzero? or t.sec.nonzero?
|
24
|
+
hour = t.hour
|
25
|
+
t = Time.mktime(t.year, t.month, t.mday)
|
26
|
+
t += SiD if hour > 12
|
27
|
+
end
|
28
|
+
t
|
29
|
+
end
|
30
|
+
|
31
|
+
def previous_period_end(now, shift_age)
|
32
|
+
case shift_age
|
33
|
+
when 'daily'
|
34
|
+
t = Time.mktime(now.year, now.month, now.mday) - SiD / 2
|
35
|
+
when 'weekly'
|
36
|
+
t = Time.mktime(now.year, now.month, now.mday) - (SiD * now.wday + SiD / 2)
|
37
|
+
when 'monthly'
|
38
|
+
t = Time.mktime(now.year, now.month, 1) - SiD / 2
|
39
|
+
when 'now', 'everytime'
|
40
|
+
return now
|
41
|
+
else
|
42
|
+
raise ArgumentError, "invalid :shift_age #{shift_age.inspect}, should be daily, weekly, monthly, or everytime"
|
43
|
+
end
|
44
|
+
Time.mktime(t.year, t.month, t.mday, 23, 59, 59)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Logger
|
4
|
+
# Logging severity.
|
5
|
+
module Severity
|
6
|
+
# Low-level information, mostly for developers.
|
7
|
+
DEBUG = 0
|
8
|
+
# Generic (useful) information about system operation.
|
9
|
+
INFO = 1
|
10
|
+
# A warning.
|
11
|
+
WARN = 2
|
12
|
+
# A handleable error condition.
|
13
|
+
ERROR = 3
|
14
|
+
# An unhandleable error that results in a program crash.
|
15
|
+
FATAL = 4
|
16
|
+
# An unknown message that should always be logged.
|
17
|
+
UNKNOWN = 5
|
18
|
+
end
|
19
|
+
end
|
data/logger.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
begin
|
2
|
+
require_relative "lib/logger/version"
|
3
|
+
rescue LoadError # Fallback to load version file in ruby core repository
|
4
|
+
require_relative "version"
|
5
|
+
end
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "logger"
|
9
|
+
spec.version = Logger::VERSION
|
10
|
+
spec.authors = ["Naotoshi Seo", "SHIBATA Hiroshi"]
|
11
|
+
spec.email = ["sonots@gmail.com", "hsbt@ruby-lang.org"]
|
12
|
+
|
13
|
+
spec.summary = %q{Provides a simple logging utility for outputting messages.}
|
14
|
+
spec.description = %q{Provides a simple logging utility for outputting messages.}
|
15
|
+
spec.homepage = "https://github.com/ruby/logger"
|
16
|
+
spec.licenses = ["Ruby", "BSD-2-Clause"]
|
17
|
+
|
18
|
+
spec.files = Dir.glob("lib/**/*.rb") + ["logger.gemspec"]
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.required_ruby_version = ">= 2.3.0"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", ">= 0"
|
26
|
+
spec.add_development_dependency "rake", ">= 12.3.3"
|
27
|
+
spec.add_development_dependency "test-unit"
|
28
|
+
spec.add_development_dependency "rdoc"
|
29
|
+
end
|
metadata
CHANGED
@@ -1,31 +1,94 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Naotoshi Seo
|
8
8
|
- SHIBATA Hiroshi
|
9
|
-
autorequire:
|
10
|
-
bindir:
|
9
|
+
autorequire:
|
10
|
+
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
12
|
+
date: 2020-12-22 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rake
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 12.3.3
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 12.3.3
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: test-unit
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
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'
|
14
70
|
description: Provides a simple logging utility for outputting messages.
|
15
71
|
email:
|
16
|
-
-
|
72
|
+
- sonots@gmail.com
|
17
73
|
- hsbt@ruby-lang.org
|
18
74
|
executables: []
|
19
75
|
extensions: []
|
20
76
|
extra_rdoc_files: []
|
21
77
|
files:
|
22
78
|
- lib/logger.rb
|
23
|
-
-
|
79
|
+
- lib/logger/errors.rb
|
80
|
+
- lib/logger/formatter.rb
|
81
|
+
- lib/logger/log_device.rb
|
82
|
+
- lib/logger/period.rb
|
83
|
+
- lib/logger/severity.rb
|
84
|
+
- lib/logger/version.rb
|
85
|
+
- logger.gemspec
|
24
86
|
homepage: https://github.com/ruby/logger
|
25
87
|
licenses:
|
88
|
+
- Ruby
|
26
89
|
- BSD-2-Clause
|
27
90
|
metadata: {}
|
28
|
-
post_install_message:
|
91
|
+
post_install_message:
|
29
92
|
rdoc_options: []
|
30
93
|
require_paths:
|
31
94
|
- lib
|
@@ -33,15 +96,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
33
96
|
requirements:
|
34
97
|
- - ">="
|
35
98
|
- !ruby/object:Gem::Version
|
36
|
-
version:
|
99
|
+
version: 2.3.0
|
37
100
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
101
|
requirements:
|
39
102
|
- - ">="
|
40
103
|
- !ruby/object:Gem::Version
|
41
104
|
version: '0'
|
42
105
|
requirements: []
|
43
|
-
rubygems_version: 3.
|
44
|
-
signing_key:
|
106
|
+
rubygems_version: 3.2.2
|
107
|
+
signing_key:
|
45
108
|
specification_version: 4
|
46
109
|
summary: Provides a simple logging utility for outputting messages.
|
47
110
|
test_files: []
|