path-log4r 1.1.10
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.
- data/INSTALL +11 -0
- data/LICENSE +90 -0
- data/LICENSE.LGPLv3 +165 -0
- data/README +95 -0
- data/Rakefile +74 -0
- data/TODO +2 -0
- data/doc/content/contact.html +22 -0
- data/doc/content/contribute.html +21 -0
- data/doc/content/index.html +90 -0
- data/doc/content/license.html +56 -0
- data/doc/content/manual.html +449 -0
- data/doc/dev/README.developers +55 -0
- data/doc/dev/checklist +23 -0
- data/doc/dev/things-to-do +5 -0
- data/doc/images/log4r-logo.png +0 -0
- data/doc/images/logo2.png +0 -0
- data/doc/log4r.css +111 -0
- data/doc/rdoc-log4r.css +696 -0
- data/doc/templates/main.html +147 -0
- data/examples/README +19 -0
- data/examples/ancestors.rb +53 -0
- data/examples/chainsaw_settings.xml +7 -0
- data/examples/customlevels.rb +34 -0
- data/examples/filelog.rb +25 -0
- data/examples/fileroll.rb +40 -0
- data/examples/gmail.rb +30 -0
- data/examples/gmail.yaml +95 -0
- data/examples/log4r_yaml.yaml +0 -0
- data/examples/logclient.rb +25 -0
- data/examples/logserver.rb +18 -0
- data/examples/moderate.xml +29 -0
- data/examples/moderateconfig.rb +66 -0
- data/examples/myformatter.rb +23 -0
- data/examples/outofthebox.rb +21 -0
- data/examples/rdoc-gen +2 -0
- data/examples/rrconfig.xml +63 -0
- data/examples/rrsetup.rb +42 -0
- data/examples/simpleconfig.rb +39 -0
- data/examples/syslogcustom.rb +52 -0
- data/examples/xmlconfig.rb +25 -0
- data/examples/yaml.rb +30 -0
- data/lib/log4r.rb +20 -0
- data/lib/log4r/GDC.rb +41 -0
- data/lib/log4r/MDC.rb +59 -0
- data/lib/log4r/NDC.rb +86 -0
- data/lib/log4r/base.rb +74 -0
- data/lib/log4r/config.rb +9 -0
- data/lib/log4r/configurator.rb +224 -0
- data/lib/log4r/formatter/formatter.rb +105 -0
- data/lib/log4r/formatter/log4jxmlformatter.rb +61 -0
- data/lib/log4r/formatter/patternformatter.rb +145 -0
- data/lib/log4r/lib/drbloader.rb +52 -0
- data/lib/log4r/lib/xmlloader.rb +24 -0
- data/lib/log4r/logevent.rb +28 -0
- data/lib/log4r/logger.rb +199 -0
- data/lib/log4r/loggerfactory.rb +89 -0
- data/lib/log4r/logserver.rb +28 -0
- data/lib/log4r/outputter/consoleoutputters.rb +18 -0
- data/lib/log4r/outputter/datefileoutputter.rb +117 -0
- data/lib/log4r/outputter/emailoutputter.rb +143 -0
- data/lib/log4r/outputter/fileoutputter.rb +56 -0
- data/lib/log4r/outputter/iooutputter.rb +55 -0
- data/lib/log4r/outputter/outputter.rb +134 -0
- data/lib/log4r/outputter/outputterfactory.rb +61 -0
- data/lib/log4r/outputter/remoteoutputter.rb +40 -0
- data/lib/log4r/outputter/rollingfileoutputter.rb +234 -0
- data/lib/log4r/outputter/scribeoutputter.rb +37 -0
- data/lib/log4r/outputter/staticoutputter.rb +30 -0
- data/lib/log4r/outputter/syslogoutputter.rb +130 -0
- data/lib/log4r/outputter/udpoutputter.rb +53 -0
- data/lib/log4r/rdoc/GDC +14 -0
- data/lib/log4r/rdoc/MDC +16 -0
- data/lib/log4r/rdoc/NDC +41 -0
- data/lib/log4r/rdoc/configurator +243 -0
- data/lib/log4r/rdoc/emailoutputter +103 -0
- data/lib/log4r/rdoc/formatter +39 -0
- data/lib/log4r/rdoc/log4r +89 -0
- data/lib/log4r/rdoc/logger +175 -0
- data/lib/log4r/rdoc/logserver +85 -0
- data/lib/log4r/rdoc/outputter +108 -0
- data/lib/log4r/rdoc/patternformatter +128 -0
- data/lib/log4r/rdoc/scribeoutputter +16 -0
- data/lib/log4r/rdoc/syslogoutputter +29 -0
- data/lib/log4r/rdoc/win32eventoutputter +7 -0
- data/lib/log4r/rdoc/yamlconfigurator +20 -0
- data/lib/log4r/repository.rb +88 -0
- data/lib/log4r/staticlogger.rb +49 -0
- data/lib/log4r/yamlconfigurator.rb +196 -0
- data/tests/README +10 -0
- data/tests/testGDC.rb +26 -0
- data/tests/testMDC.rb +42 -0
- data/tests/testNDC.rb +27 -0
- data/tests/testall.rb +6 -0
- data/tests/testbase.rb +49 -0
- data/tests/testchainsaw.rb +48 -0
- data/tests/testconf.xml +37 -0
- data/tests/testcustom.rb +27 -0
- data/tests/testformatter.rb +27 -0
- data/tests/testlogger.rb +196 -0
- data/tests/testoutputter.rb +132 -0
- data/tests/testpatternformatter.rb +78 -0
- data/tests/testthreads.rb +35 -0
- data/tests/testxmlconf.rb +45 -0
- metadata +184 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# :nodoc:
|
|
2
|
+
require "log4r/outputter/outputter"
|
|
3
|
+
require "log4r/staticlogger"
|
|
4
|
+
|
|
5
|
+
module Log4r
|
|
6
|
+
|
|
7
|
+
##
|
|
8
|
+
# IO Outputter invokes print then flush on the wrapped IO
|
|
9
|
+
# object. If the IO stream dies, IOOutputter sets itself to OFF
|
|
10
|
+
# and the system continues on its merry way.
|
|
11
|
+
#
|
|
12
|
+
# To find out why an IO stream died, create a logger named 'log4r'
|
|
13
|
+
# and look at the output.
|
|
14
|
+
|
|
15
|
+
class IOOutputter < Outputter
|
|
16
|
+
|
|
17
|
+
# IOOutputter needs an IO object to write to.
|
|
18
|
+
def initialize(_name, _out, hash={})
|
|
19
|
+
super(_name, hash)
|
|
20
|
+
@out = _out
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def closed?
|
|
24
|
+
@out.closed?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Close the IO and sets level to OFF
|
|
28
|
+
def close
|
|
29
|
+
@out.close unless @out.nil?
|
|
30
|
+
@level = OFF
|
|
31
|
+
OutputterFactory.create_methods(self)
|
|
32
|
+
Logger.log_internal {"Outputter '#{@name}' closed IO and set to OFF"}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
#######
|
|
36
|
+
private
|
|
37
|
+
#######
|
|
38
|
+
|
|
39
|
+
# perform the write
|
|
40
|
+
def write(data)
|
|
41
|
+
begin
|
|
42
|
+
@out.print data
|
|
43
|
+
@out.flush
|
|
44
|
+
rescue IOError => ioe # recover from this instead of crash
|
|
45
|
+
Logger.log_internal {"IOError in Outputter '#{@name}'!"}
|
|
46
|
+
Logger.log_internal {ioe}
|
|
47
|
+
close
|
|
48
|
+
rescue NameError => ne
|
|
49
|
+
Logger.log_internal {"Outputter '#{@name}' IO is #{@out.class}!"}
|
|
50
|
+
Logger.log_internal {ne}
|
|
51
|
+
close
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# :include: ../rdoc/outputter
|
|
2
|
+
#
|
|
3
|
+
# == Other Info
|
|
4
|
+
#
|
|
5
|
+
# Version:: $Id$
|
|
6
|
+
# Author:: Leon Torres <leon@ugcs.caltech.edu>
|
|
7
|
+
|
|
8
|
+
require "thread"
|
|
9
|
+
|
|
10
|
+
require "log4r/outputter/outputterfactory"
|
|
11
|
+
require "log4r/formatter/formatter"
|
|
12
|
+
require "log4r/staticlogger"
|
|
13
|
+
|
|
14
|
+
require 'monitor'
|
|
15
|
+
|
|
16
|
+
module Log4r
|
|
17
|
+
|
|
18
|
+
class Outputter < Monitor
|
|
19
|
+
attr_reader :name, :level, :formatter
|
|
20
|
+
@@outputters = Hash.new
|
|
21
|
+
|
|
22
|
+
# An Outputter needs a name. RootLogger will be loaded if not already
|
|
23
|
+
# done. The hash arguments are as follows:
|
|
24
|
+
#
|
|
25
|
+
# [<tt>:level</tt>] Logger level. Optional, defaults to root level
|
|
26
|
+
# [<tt>:formatter</tt>] A Formatter. Defaults to DefaultFormatter
|
|
27
|
+
|
|
28
|
+
def initialize(_name, hash={})
|
|
29
|
+
super()
|
|
30
|
+
if _name.nil?
|
|
31
|
+
raise ArgumentError, "Bad arguments. Name and IO expected.", caller
|
|
32
|
+
end
|
|
33
|
+
@name = _name
|
|
34
|
+
validate_hash(hash)
|
|
35
|
+
@@outputters[@name] = self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# dynamically change the level
|
|
39
|
+
def level=(_level)
|
|
40
|
+
Log4rTools.validate_level(_level)
|
|
41
|
+
@level = _level
|
|
42
|
+
OutputterFactory.create_methods(self)
|
|
43
|
+
Logger.log_internal {"Outputter '#{@name}' level is #{LNAMES[_level]}"}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Set the levels to log. All others will be ignored
|
|
47
|
+
def only_at(*levels)
|
|
48
|
+
raise ArgumentError, "Gimme some levels!", caller if levels.empty?
|
|
49
|
+
raise ArgumentError, "Can't log only_at ALL", caller if levels.include? ALL
|
|
50
|
+
levels.each {|level| Log4rTools.validate_level(level)}
|
|
51
|
+
@level = levels.sort.first
|
|
52
|
+
OutputterFactory.create_methods self, levels
|
|
53
|
+
Logger.log_internal {
|
|
54
|
+
"Outputter '#{@name}' writes only on " +\
|
|
55
|
+
levels.collect{|l| LNAMES[l]}.join(", ")
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Dynamically change the formatter. You can just specify a Class
|
|
60
|
+
# object and the formatter will invoke +new+ or +instance+
|
|
61
|
+
# on it as appropriate.
|
|
62
|
+
|
|
63
|
+
def formatter=(_formatter)
|
|
64
|
+
if _formatter.kind_of?(Formatter)
|
|
65
|
+
@formatter = _formatter
|
|
66
|
+
elsif _formatter.kind_of?(Class) and _formatter <= Formatter
|
|
67
|
+
if _formatter.respond_to? :instance
|
|
68
|
+
@formatter = _formatter.instance
|
|
69
|
+
else
|
|
70
|
+
@formatter = _formatter.new
|
|
71
|
+
end
|
|
72
|
+
else
|
|
73
|
+
raise TypeError, "Argument was not a Formatter!", caller
|
|
74
|
+
end
|
|
75
|
+
Logger.log_internal {"Outputter '#{@name}' using #{@formatter.class}"}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Call flush to force an outputter to write out any buffered
|
|
79
|
+
# log events. Similar to IO#flush, so use in a similar fashion.
|
|
80
|
+
|
|
81
|
+
def flush
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
#########
|
|
85
|
+
protected
|
|
86
|
+
#########
|
|
87
|
+
|
|
88
|
+
# Validates the common hash arguments. For now, that would be
|
|
89
|
+
# +:level+, +:formatter+ and the string equivalents
|
|
90
|
+
def validate_hash(hash)
|
|
91
|
+
# default to root level and DefaultFormatter
|
|
92
|
+
if hash.empty?
|
|
93
|
+
self.level = Logger.root.level
|
|
94
|
+
@formatter = DefaultFormatter.new
|
|
95
|
+
return
|
|
96
|
+
end
|
|
97
|
+
self.level = (hash[:level] or hash['level'] or Logger.root.level)
|
|
98
|
+
self.formatter = (hash[:formatter] or hash['formatter'] or DefaultFormatter.new)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
#######
|
|
102
|
+
private
|
|
103
|
+
#######
|
|
104
|
+
|
|
105
|
+
# This method handles all log events passed to a typical Outputter.
|
|
106
|
+
# Overload this to change the overall behavior of an outputter. Make
|
|
107
|
+
# sure that the new behavior is thread safe.
|
|
108
|
+
|
|
109
|
+
def canonical_log(logevent)
|
|
110
|
+
synch { write(format(logevent)) }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Common method to format data. All it does is call the resident
|
|
114
|
+
# formatter's format method. If a different formatting behavior is
|
|
115
|
+
# needed, then overload this method.
|
|
116
|
+
|
|
117
|
+
def format(logevent)
|
|
118
|
+
# @formatter is guaranteed to be DefaultFormatter if no Formatter
|
|
119
|
+
# was specified
|
|
120
|
+
@formatter.format(logevent)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Abstract method to actually write the data to a destination.
|
|
124
|
+
# Custom outputters should overload this to specify how the
|
|
125
|
+
# formatted data should be written and to where.
|
|
126
|
+
|
|
127
|
+
def write(data)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def synch; synchronize { yield } end
|
|
131
|
+
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# :nodoc:
|
|
2
|
+
# Version: $Id$
|
|
3
|
+
|
|
4
|
+
require "log4r/base"
|
|
5
|
+
require "log4r/repository"
|
|
6
|
+
require "log4r/logger"
|
|
7
|
+
|
|
8
|
+
require 'monitor'
|
|
9
|
+
|
|
10
|
+
module Log4r
|
|
11
|
+
class Outputter < Monitor
|
|
12
|
+
|
|
13
|
+
class OutputterFactory #:nodoc:
|
|
14
|
+
include Singleton
|
|
15
|
+
|
|
16
|
+
# handles two cases: logging above a level (no second arg specified)
|
|
17
|
+
# or logging a set of levels (passed into the second argument)
|
|
18
|
+
def self.create_methods(out, levels=nil)
|
|
19
|
+
Logger.root # force levels to be loaded
|
|
20
|
+
|
|
21
|
+
# first, undefine all the log levels
|
|
22
|
+
for mname in LNAMES
|
|
23
|
+
undefine_log(mname.downcase, out)
|
|
24
|
+
end
|
|
25
|
+
if not levels.nil? and levels.include? OFF
|
|
26
|
+
raise TypeError, "Can't log only_at OFF", caller[1..-1]
|
|
27
|
+
end
|
|
28
|
+
return out if out.level == OFF
|
|
29
|
+
|
|
30
|
+
if levels.nil? # then define the log methods for lev >= outlev
|
|
31
|
+
for lev in out.level...LEVELS
|
|
32
|
+
define_log(LNAMES[lev].downcase, lev, out)
|
|
33
|
+
end
|
|
34
|
+
else # define the logs only for assigned levels
|
|
35
|
+
for lev in levels
|
|
36
|
+
define_log(LNAMES[lev].downcase, lev, out)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
return out
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# we need to synch the actual write/format for thread safteyness
|
|
43
|
+
def self.define_log(mname, level, out)
|
|
44
|
+
return if mname == 'off' || mname == 'all'
|
|
45
|
+
mstr = %-
|
|
46
|
+
def out.#{mname}(logevent)
|
|
47
|
+
canonical_log(logevent)
|
|
48
|
+
end
|
|
49
|
+
-
|
|
50
|
+
module_eval mstr
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.undefine_log(mname, out)
|
|
54
|
+
return if mname == 'off' || mname == 'all'
|
|
55
|
+
mstr = "def out.#{mname}(logevent); end"
|
|
56
|
+
module_eval mstr
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# :nodoc:
|
|
2
|
+
require 'log4r/lib/drbloader'
|
|
3
|
+
require 'log4r/outputter/outputter'
|
|
4
|
+
|
|
5
|
+
module Log4r
|
|
6
|
+
# See log4r/logserver.rb
|
|
7
|
+
class RemoteOutputter < Outputter
|
|
8
|
+
|
|
9
|
+
def initialize(_name, hash={})
|
|
10
|
+
super(_name, hash)
|
|
11
|
+
@uri = (hash[:uri] or hash['uri'])
|
|
12
|
+
@buffsize = (hash[:buffsize] or hash['buffsize'] or 1).to_i
|
|
13
|
+
@buff = []
|
|
14
|
+
connect
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
if HAVE_ROMP
|
|
18
|
+
include ROMPClient
|
|
19
|
+
else
|
|
20
|
+
def initialize(*args)
|
|
21
|
+
raise RuntimeError, "LogServer not supported. ROMP is required", caller
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Call flush to send any remaining LogEvents to the remote server.
|
|
27
|
+
def flush
|
|
28
|
+
synch { send_buffer }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def canonical_log(logevent)
|
|
34
|
+
synch {
|
|
35
|
+
@buff.push logevent
|
|
36
|
+
send_buffer if @buff.size >= @buffsize
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
|
|
2
|
+
# :nodoc:
|
|
3
|
+
# Version:: $Id: rollingfileoutputter.rb,v 1.2 2009/09/29 18:13:13 colbygk Exp $
|
|
4
|
+
|
|
5
|
+
require "log4r/outputter/fileoutputter"
|
|
6
|
+
require "log4r/staticlogger"
|
|
7
|
+
|
|
8
|
+
require 'fileutils'
|
|
9
|
+
|
|
10
|
+
module Log4r
|
|
11
|
+
|
|
12
|
+
# RollingFileOutputter - subclass of FileOutputter that rolls files on size
|
|
13
|
+
# or time. So, given a filename of "error.log", the first log file will be "error000001.log".
|
|
14
|
+
# When its check condition is exceeded, it'll create and log to "error000002.log", etc.
|
|
15
|
+
#
|
|
16
|
+
# Additional hash arguments are:
|
|
17
|
+
#
|
|
18
|
+
# [<tt>:maxsize</tt>] Maximum size of the file in bytes.
|
|
19
|
+
# [<tt>:maxtime</tt>] Maximum age of the file in seconds.
|
|
20
|
+
# [<tt>:max_backups</tt>] Maxium number of prior log files to maintain. If max_backups is a positive number,
|
|
21
|
+
# then each time a roll happens, RollingFileOutputter will delete the oldest backup log files in excess
|
|
22
|
+
# of this number (if any). So, if max_backups is 10, then a maximum of 11 files will be maintained (the current
|
|
23
|
+
# log, plus 10 backups). If max_backups is 0, no backups will be kept. If it is negative (the default),
|
|
24
|
+
# there will be no limit on the number of files created. Note that the sequence numbers will continue to escalate;
|
|
25
|
+
# old sequence numbers are not reused.
|
|
26
|
+
# [<tt>:trunc</tt>] If true, deletes ALL existing log files (based on :filename) upon initialization,
|
|
27
|
+
# and the sequence numbering will start over at 000001. Otherwise continues logging where it left off
|
|
28
|
+
# last time (i.e. either to the file with the highest sequence number, or a new file, as appropriate).
|
|
29
|
+
class RollingFileOutputter < FileOutputter
|
|
30
|
+
|
|
31
|
+
attr_reader :current_sequence_number, :maxsize, :maxtime, :start_time, :max_backups
|
|
32
|
+
|
|
33
|
+
def initialize(_name, hash={})
|
|
34
|
+
super( _name, hash.merge({:create => false}) )
|
|
35
|
+
if hash.has_key?(:maxsize) || hash.has_key?('maxsize')
|
|
36
|
+
_maxsize = (hash[:maxsize] or hash['maxsize']).to_i
|
|
37
|
+
if _maxsize.class != Fixnum
|
|
38
|
+
raise TypeError, "Argument 'maxsize' must be an Fixnum", caller
|
|
39
|
+
end
|
|
40
|
+
if _maxsize == 0
|
|
41
|
+
raise TypeError, "Argument 'maxsize' must be > 0", caller
|
|
42
|
+
end
|
|
43
|
+
@maxsize = _maxsize
|
|
44
|
+
end
|
|
45
|
+
if hash.has_key?(:maxtime) || hash.has_key?('maxtime')
|
|
46
|
+
_maxtime = (hash[:maxtime] or hash['maxtime']).to_i
|
|
47
|
+
if _maxtime.class != Fixnum
|
|
48
|
+
raise TypeError, "Argument 'maxtime' must be an Fixnum", caller
|
|
49
|
+
end
|
|
50
|
+
if _maxtime == 0
|
|
51
|
+
raise TypeError, "Argument 'maxtime' must be > 0", caller
|
|
52
|
+
end
|
|
53
|
+
@maxtime = _maxtime
|
|
54
|
+
end
|
|
55
|
+
if hash.has_key?(:max_backups) || hash.has_key?('max_backups')
|
|
56
|
+
_max_backups = (hash[:max_backups] or hash['max_backups']).to_i
|
|
57
|
+
if _max_backups.class != Fixnum
|
|
58
|
+
raise TypeError, "Argument 'max_backups' must be an Fixnum", caller
|
|
59
|
+
end
|
|
60
|
+
@max_backups = _max_backups
|
|
61
|
+
else
|
|
62
|
+
@max_backups = -1
|
|
63
|
+
end
|
|
64
|
+
# @filename starts out as the file (including path) provided by the user, e.g. "\usr\logs\error.log".
|
|
65
|
+
# It will get assigned the current log file (including sequence number)
|
|
66
|
+
# @log_dir is the directory in which we'll log, e.g. "\usr\logs"
|
|
67
|
+
# @file_extension is the file's extension (if any) including any period, e.g. ".log"
|
|
68
|
+
# @core_file_name is the part of the log file's name, sans sequence digits or extension, e.g. "error"
|
|
69
|
+
@log_dir = File.dirname(@filename)
|
|
70
|
+
@file_extension = File.extname(@filename) # Note: the File API doc comment states that this doesn't include the period, but its examples and behavior do include it. We'll depend on the latter.
|
|
71
|
+
@core_file_name = File.basename(@filename, @file_extension)
|
|
72
|
+
if (@trunc)
|
|
73
|
+
purge_log_files(0)
|
|
74
|
+
end
|
|
75
|
+
@current_sequence_number = get_current_sequence_number()
|
|
76
|
+
makeNewFilename
|
|
77
|
+
# Now @filename points to a properly sequenced filename, which may or may not yet exist.
|
|
78
|
+
open_log_file('a')
|
|
79
|
+
|
|
80
|
+
# Note: it's possible we're already in excess of our time or size constraint for the current file;
|
|
81
|
+
# no worries -- if a new file needs to be started, it'll happen during the write() call.
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
#######
|
|
85
|
+
private
|
|
86
|
+
#######
|
|
87
|
+
|
|
88
|
+
# Delete all but the latest number_to_keep log files.
|
|
89
|
+
def purge_log_files(number_to_keep)
|
|
90
|
+
Dir.chdir(@log_dir) do
|
|
91
|
+
# Make a list of the log files to delete. Start with all of the matching log files...
|
|
92
|
+
glob = "#{@core_file_name}[0-9][0-9][0-9][0-9][0-9][0-9]#{@file_extension}"
|
|
93
|
+
files = Dir.glob(glob)
|
|
94
|
+
|
|
95
|
+
# ... if there are fewer than our threshold, just return...
|
|
96
|
+
if (files.size() <= number_to_keep )
|
|
97
|
+
# Logger.log_internal {"No log files need purging."}
|
|
98
|
+
return
|
|
99
|
+
end
|
|
100
|
+
# ...then remove those that we want to keep (i.e. the most recent #{number_to_keep} files).
|
|
101
|
+
files.sort!().slice!(-number_to_keep, number_to_keep)
|
|
102
|
+
|
|
103
|
+
# Delete the files. We use force (rm_f), so in case any files can't be deleted (e.g. someone's got one
|
|
104
|
+
# open in an editor), we'll swallow the error and keep going.
|
|
105
|
+
FileUtils.rm_f(files)
|
|
106
|
+
Logger.log_internal { "Purged #{files.length} log files: #{files}" }
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Get the highest existing log file sequence number, or 1 if there are no existing log files.
|
|
111
|
+
def get_current_sequence_number()
|
|
112
|
+
max_seq_no = 0
|
|
113
|
+
Dir.foreach(@log_dir) do |child|
|
|
114
|
+
if child =~ /^#{@core_file_name}(\d+)#{@file_extension}$/
|
|
115
|
+
seq_no = $1.to_i
|
|
116
|
+
if (seq_no > max_seq_no)
|
|
117
|
+
max_seq_no = seq_no
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
return [max_seq_no, 1].max
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# perform the write
|
|
125
|
+
def write(data)
|
|
126
|
+
# we have to keep track of the file size ourselves - File.size doesn't
|
|
127
|
+
# seem to report the correct size when the size changes rapidly
|
|
128
|
+
@datasize += data.size + 1 # the 1 is for newline
|
|
129
|
+
roll if requiresRoll
|
|
130
|
+
super
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Constructs a new filename from the @current_sequence_number, @core_file_name, and @file_extension,
|
|
134
|
+
# and assigns it to @filename
|
|
135
|
+
def makeNewFilename
|
|
136
|
+
# note use of hard coded 6 digit sequence width - is this enough files?
|
|
137
|
+
padded_seq_no = "0" * (6 - @current_sequence_number.to_s.length) + @current_sequence_number.to_s
|
|
138
|
+
newbase = "#{@core_file_name}#{padded_seq_no}#{@file_extension}"
|
|
139
|
+
@filename = File.join(@log_dir, newbase)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Open @filename with the given mode:
|
|
143
|
+
# 'a' - appends to the end of the file if it exists; otherwise creates it.
|
|
144
|
+
# 'w' - truncates the file to zero length if it exists, otherwise creates it.
|
|
145
|
+
# Re-initializes @datasize and @startime appropriately.
|
|
146
|
+
def open_log_file(mode)
|
|
147
|
+
# It appears that if a file has been recently deleted then recreated, calls like
|
|
148
|
+
# File.ctime can return the erstwhile creation time. File.size? can similarly return
|
|
149
|
+
# old information. So instead of simply doing ctime and size checks after File.new, we
|
|
150
|
+
# do slightly more complicated checks beforehand:
|
|
151
|
+
if (mode == 'w' || !File.exists?(@filename))
|
|
152
|
+
@start_time = Time.now()
|
|
153
|
+
@datasize = 0
|
|
154
|
+
else
|
|
155
|
+
@start_time = File.ctime(@filename)
|
|
156
|
+
@datasize = File.size?(@filename) || 0 # File.size? returns nil even if the file exists but is empty; we convert it to 0.
|
|
157
|
+
end
|
|
158
|
+
@out = File.new(@filename, mode)
|
|
159
|
+
Logger.log_internal {"File #{@filename} opened with mode #{mode}"}
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# does the file require a roll?
|
|
163
|
+
def requiresRoll
|
|
164
|
+
if !@maxsize.nil? && @datasize > @maxsize
|
|
165
|
+
Logger.log_internal { "Rolling because #{@filename} (#{@datasize} bytes) has exceded the maxsize limit (#{@maxsize} bytes)." }
|
|
166
|
+
return true
|
|
167
|
+
end
|
|
168
|
+
if !@maxtime.nil? && (Time.now - @start_time) > @maxtime
|
|
169
|
+
Logger.log_internal { "Rolling because #{@filename} (created: #{@start_time}) has exceded the maxtime age (#{@maxtime} seconds)." }
|
|
170
|
+
return true
|
|
171
|
+
end
|
|
172
|
+
false
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# roll the file
|
|
176
|
+
def roll
|
|
177
|
+
begin
|
|
178
|
+
# If @baseFilename == @filename, then this method is about to
|
|
179
|
+
# try to close out a file that is not actually opened because
|
|
180
|
+
# fileoutputter has been called with the parameter roll=true
|
|
181
|
+
# TODO: Is this check valid any more? I suspect not. Am commenting out...:
|
|
182
|
+
#if ( @baseFilename != @filename ) then
|
|
183
|
+
@out.close
|
|
184
|
+
#end
|
|
185
|
+
rescue
|
|
186
|
+
Logger.log_internal {
|
|
187
|
+
"RollingFileOutputter '#{@name}' could not close #{@filename}"
|
|
188
|
+
}
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Prepare the next file. (Note: if max_backups is zero, we can skip this; we'll
|
|
192
|
+
# just overwrite the existing log file)
|
|
193
|
+
if (@max_backups != 0)
|
|
194
|
+
@current_sequence_number += 1
|
|
195
|
+
makeNewFilename
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
open_log_file('w')
|
|
199
|
+
|
|
200
|
+
# purge any excess log files (unless max_backups is negative, which means don't purge).
|
|
201
|
+
if (@max_backups >= 0)
|
|
202
|
+
purge_log_files(@max_backups + 1)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# this can be found in examples/fileroll.rb as well
|
|
212
|
+
if __FILE__ == $0
|
|
213
|
+
require 'log4r'
|
|
214
|
+
include Log4r
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
timeLog = Logger.new 'WbExplorer'
|
|
218
|
+
timeLog.outputters = RollingFileOutputter.new("WbExplorer", { "filename" => "TestTime.log", "maxtime" => 10, "trunc" => true })
|
|
219
|
+
timeLog.level = DEBUG
|
|
220
|
+
|
|
221
|
+
100.times { |t|
|
|
222
|
+
timeLog.info "blah #{t}"
|
|
223
|
+
sleep(1.0)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
sizeLog = Logger.new 'WbExplorer'
|
|
227
|
+
sizeLog.outputters = RollingFileOutputter.new("WbExplorer", { "filename" => "TestSize.log", "maxsize" => 16000, "trunc" => true })
|
|
228
|
+
sizeLog.level = DEBUG
|
|
229
|
+
|
|
230
|
+
10000.times { |t|
|
|
231
|
+
sizeLog.info "blah #{t}"
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
end
|