mtn_log4r 1.1.11
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 +15 -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/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 +109 -0
- data/lib/log4r/formatter/log4jxmlformatter.rb +65 -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/rabbitoutputter.rb +70 -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/log4jxmlformatter +21 -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/version.rb +4 -0
- data/lib/log4r/yamlconfigurator.rb +198 -0
- data/lib/log4r.rb +18 -0
- data/tests/README +10 -0
- data/tests/testGDC.rb +24 -0
- data/tests/testMDC.rb +40 -0
- data/tests/testNDC.rb +25 -0
- data/tests/test_helper.rb +12 -0
- data/tests/testall.rb +6 -0
- data/tests/testbase.rb +48 -0
- data/tests/testchainsaw.rb +42 -0
- data/tests/testconf.xml +37 -0
- data/tests/testcustom.rb +30 -0
- data/tests/testformatter.rb +31 -0
- data/tests/testlogger.rb +200 -0
- data/tests/testoutputter.rb +143 -0
- data/tests/testpatternformatter.rb +76 -0
- data/tests/testthreads.rb +31 -0
- data/tests/testxmlconf.rb +48 -0
- data/tests/testyaml.rb +39 -0
- data/tests/testyaml_arrays.yaml +25 -0
- data/tests/testyaml_injection.yaml +22 -0
- metadata +193 -0
data/lib/log4r/logger.rb
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# :include: rdoc/logger
|
|
2
|
+
#
|
|
3
|
+
# == Other Info
|
|
4
|
+
#
|
|
5
|
+
# Version:: $Id$
|
|
6
|
+
# Author:: Leon Torres <leon(at)ugcs.caltech.edu>
|
|
7
|
+
|
|
8
|
+
require "log4r/outputter/outputter"
|
|
9
|
+
require "log4r/repository"
|
|
10
|
+
require "log4r/loggerfactory"
|
|
11
|
+
require "log4r/staticlogger"
|
|
12
|
+
|
|
13
|
+
module Log4r
|
|
14
|
+
|
|
15
|
+
# See log4r/logger.rb
|
|
16
|
+
class Logger
|
|
17
|
+
attr_reader :name, :fullname, :path, :level, :parent
|
|
18
|
+
attr_reader :additive, :trace, :outputters
|
|
19
|
+
|
|
20
|
+
# Logger requires a name. The last 3 parameters are:
|
|
21
|
+
#
|
|
22
|
+
# level:: Do I have a level? (Otherwise, I'll inherit my parent's)
|
|
23
|
+
# additive:: Am I additive?
|
|
24
|
+
# trace:: Do I record the execution trace? (slows things a wee bit)
|
|
25
|
+
|
|
26
|
+
def initialize(_fullname, _level=nil, _additive=true, _trace=false)
|
|
27
|
+
# validation
|
|
28
|
+
raise ArgumentError, "Logger must have a name", caller if _fullname.nil?
|
|
29
|
+
Log4rTools.validate_level(_level) unless _level.nil?
|
|
30
|
+
validate_name(_fullname)
|
|
31
|
+
|
|
32
|
+
# create the logger
|
|
33
|
+
@fullname = _fullname
|
|
34
|
+
@outputters = []
|
|
35
|
+
@additive = _additive
|
|
36
|
+
deal_with_inheritance(_level)
|
|
37
|
+
LoggerFactory.define_methods(self)
|
|
38
|
+
self.trace = _trace
|
|
39
|
+
Repository[@fullname] = self
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def validate_name(_fullname)
|
|
43
|
+
parts = _fullname.split Log4rConfig::LoggerPathDelimiter
|
|
44
|
+
for part in parts
|
|
45
|
+
raise ArgumentError, "Malformed path", caller[1..-1] if part.empty?
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
private :validate_name
|
|
49
|
+
|
|
50
|
+
# Parses name for location in heiarchy, sets the parent, and
|
|
51
|
+
# deals with level inheritance
|
|
52
|
+
|
|
53
|
+
def deal_with_inheritance(_level)
|
|
54
|
+
mypath = @fullname.split Log4rConfig::LoggerPathDelimiter
|
|
55
|
+
@name = mypath.pop
|
|
56
|
+
if mypath.empty? # then root is my daddy
|
|
57
|
+
@path = ""
|
|
58
|
+
# This is one of the guarantees that RootLogger gets created
|
|
59
|
+
@parent = Logger.root
|
|
60
|
+
else
|
|
61
|
+
@path = mypath.join(Log4rConfig::LoggerPathDelimiter)
|
|
62
|
+
@parent = Repository.find_ancestor(@path)
|
|
63
|
+
@parent = Logger.root if @parent.nil?
|
|
64
|
+
end
|
|
65
|
+
# inherit the level if no level defined
|
|
66
|
+
if _level.nil? then @level = @parent.level
|
|
67
|
+
else @level = _level end
|
|
68
|
+
Repository.reassign_any_children(self)
|
|
69
|
+
end
|
|
70
|
+
private :deal_with_inheritance
|
|
71
|
+
|
|
72
|
+
# Set the logger level dynamically. Does not affect children.
|
|
73
|
+
def level=(_level)
|
|
74
|
+
Log4rTools.validate_level(_level)
|
|
75
|
+
@level = _level
|
|
76
|
+
LoggerFactory.define_methods(self)
|
|
77
|
+
Logger.log_internal {"Logger '#{@fullname}' set to #{LNAMES[@level]}"}
|
|
78
|
+
@level
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Return array of defined levels.
|
|
82
|
+
def levels
|
|
83
|
+
LNAMES
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Set the additivity of the logger dynamically. True or false.
|
|
87
|
+
def additive=(_additive)
|
|
88
|
+
@additive = _additive
|
|
89
|
+
LoggerFactory.define_methods(self)
|
|
90
|
+
Logger.log_internal {"Logger '#{@fullname}' is additive"}
|
|
91
|
+
@additive
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Set whether the logger traces. Can be set dynamically. Defaults
|
|
95
|
+
# to false and understands the strings 'true' and 'false'.
|
|
96
|
+
def trace=(_trace)
|
|
97
|
+
@trace =
|
|
98
|
+
case _trace
|
|
99
|
+
when "true", true then true
|
|
100
|
+
else false end
|
|
101
|
+
LoggerFactory.define_methods(self)
|
|
102
|
+
Logger.log_internal {"Logger '#{@fullname}' is tracing"} if @trace
|
|
103
|
+
@trace
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Please don't reset the parent
|
|
107
|
+
def parent=(parent)
|
|
108
|
+
@parent = parent
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Set the Outputters dynamically by name or reference. Can be done any
|
|
112
|
+
# time.
|
|
113
|
+
def outputters=(_outputters)
|
|
114
|
+
@outputters.clear
|
|
115
|
+
add(*_outputters)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Add outputters by name or by reference. Can be done any time.
|
|
119
|
+
def add(*_outputters)
|
|
120
|
+
for thing in _outputters
|
|
121
|
+
o = (thing.kind_of?(Outputter) ? thing : Outputter[thing])
|
|
122
|
+
# some basic validation
|
|
123
|
+
if not o.kind_of?(Outputter)
|
|
124
|
+
raise TypeError, "Expected kind of Outputter, got #{o.class}", caller
|
|
125
|
+
elsif o.nil?
|
|
126
|
+
raise TypeError, "Couldn't find Outputter '#{thing}'", caller
|
|
127
|
+
end
|
|
128
|
+
@outputters.push o
|
|
129
|
+
Logger.log_internal {"Added outputter '#{o.name}' to '#{@fullname}'"}
|
|
130
|
+
end
|
|
131
|
+
@outputters
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Remove outputters from this logger by name only. Can be done any time.
|
|
135
|
+
def remove(*_outputters)
|
|
136
|
+
for name in _outputters
|
|
137
|
+
o = Outputter[name]
|
|
138
|
+
@outputters.delete o
|
|
139
|
+
Logger.log_internal {"Removed outputter '#{o.name}' from '#{@fullname}'"}
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def is_root?; false end
|
|
144
|
+
|
|
145
|
+
def ==(other)
|
|
146
|
+
return true if self.object_id == other.object_id
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# RootLogger should be retrieved with Logger.root or Logger.global.
|
|
152
|
+
# It's supposed to be transparent.
|
|
153
|
+
#--
|
|
154
|
+
# We must guarantee the creation of RootLogger before any other Logger
|
|
155
|
+
# or Outputter gets their logging methods defined. There are two
|
|
156
|
+
# guarantees in the code:
|
|
157
|
+
#
|
|
158
|
+
# * Logger#deal_with_inheritance - calls RootLogger.instance when
|
|
159
|
+
# a new Logger is created without a parent. Parents must exist, therefore
|
|
160
|
+
# RootLogger is forced to be created.
|
|
161
|
+
#
|
|
162
|
+
# * OutputterFactory.create_methods - Calls Logger.root first. So if
|
|
163
|
+
# an Outputter is created, RootLogger is also created.
|
|
164
|
+
#
|
|
165
|
+
# When RootLogger is created, it calls
|
|
166
|
+
# Log4r.define_levels(*Log4rConfig::LogLevels). This ensures that the
|
|
167
|
+
# default levels are loaded if no custom ones are.
|
|
168
|
+
|
|
169
|
+
class RootLogger < Logger
|
|
170
|
+
include Singleton
|
|
171
|
+
|
|
172
|
+
def initialize
|
|
173
|
+
Log4r.define_levels(*Log4rConfig::LogLevels) # ensure levels are loaded
|
|
174
|
+
@level = ALL
|
|
175
|
+
@outputters = []
|
|
176
|
+
Repository['root'] = self
|
|
177
|
+
Repository['global'] = self
|
|
178
|
+
LoggerFactory.undefine_methods(self)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def is_root?; true end
|
|
182
|
+
|
|
183
|
+
# Set the global level. Any loggers defined thereafter will
|
|
184
|
+
# not log below the global level regardless of their levels.
|
|
185
|
+
|
|
186
|
+
def level=(alevel); @level = alevel end
|
|
187
|
+
|
|
188
|
+
# Does nothing
|
|
189
|
+
def outputters=(foo); end
|
|
190
|
+
# Does nothing
|
|
191
|
+
def trace=(foo); end
|
|
192
|
+
# Does nothing
|
|
193
|
+
def additive=(foo); end
|
|
194
|
+
# Does nothing
|
|
195
|
+
def add(*foo); end
|
|
196
|
+
# Does nothing
|
|
197
|
+
def remove(*foo); end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# :nodoc:
|
|
2
|
+
# Version:: $Id$
|
|
3
|
+
|
|
4
|
+
require "log4r/base"
|
|
5
|
+
require "log4r/repository"
|
|
6
|
+
require 'log4r/logevent'
|
|
7
|
+
|
|
8
|
+
module Log4r
|
|
9
|
+
class Logger
|
|
10
|
+
class LoggerFactory #:nodoc:
|
|
11
|
+
|
|
12
|
+
# we want to log iff root.lev <= lev && logger.lev <= lev
|
|
13
|
+
# BTW, root is guaranteed to be defined by this point
|
|
14
|
+
def self.define_methods(logger)
|
|
15
|
+
return if logger.is_root?
|
|
16
|
+
undefine_methods(logger)
|
|
17
|
+
globlev = Repository['root'].level
|
|
18
|
+
return if logger.level == OFF or globlev == OFF
|
|
19
|
+
toggle_methods(globlev, logger)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# set logging methods to null defaults
|
|
23
|
+
def self.undefine_methods(logger)
|
|
24
|
+
for lname in LNAMES
|
|
25
|
+
next if lname == 'OFF'|| lname == 'ALL'
|
|
26
|
+
unset_log(logger, lname)
|
|
27
|
+
set_false(logger, lname)
|
|
28
|
+
end
|
|
29
|
+
set_false(logger, 'all')
|
|
30
|
+
set_true(logger, 'off')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# toggle methods >= globlev that are also >= level
|
|
34
|
+
def self.toggle_methods(globlev, logger)
|
|
35
|
+
for lev in globlev...LEVELS # satisfies >= globlev
|
|
36
|
+
next if lev < logger.level # satisfies >= level
|
|
37
|
+
next if LNAMES[lev] == 'OFF'
|
|
38
|
+
next if LNAMES[lev] == 'ALL'
|
|
39
|
+
set_log(logger, LNAMES[lev])
|
|
40
|
+
set_true(logger, LNAMES[lev])
|
|
41
|
+
end
|
|
42
|
+
if logger.level == ALL
|
|
43
|
+
set_true(logger, 'all')
|
|
44
|
+
end
|
|
45
|
+
if logger.level != OFF && globlev != OFF
|
|
46
|
+
set_false(logger, 'off')
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# And now, the weird dynamic method definitions! :)
|
|
51
|
+
|
|
52
|
+
def self.unset_log(logger, lname)
|
|
53
|
+
mstr="def logger.#{lname.downcase}(data=nil, propagated=false); end"
|
|
54
|
+
module_eval mstr
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Logger logging methods are defined here.
|
|
58
|
+
def self.set_log(logger, lname)
|
|
59
|
+
# invoke caller iff the logger invoked is tracing
|
|
60
|
+
tracercall = (logger.trace ? "caller" : "nil")
|
|
61
|
+
# maybe pass parent a logevent. second arg is the switch
|
|
62
|
+
if logger.additive && !logger.parent.is_root?
|
|
63
|
+
parentcall = "@parent.#{lname.downcase}(event, true)"
|
|
64
|
+
end
|
|
65
|
+
mstr = %-
|
|
66
|
+
def logger.#{lname.downcase}(data=nil, propagated=false)
|
|
67
|
+
if propagated then event = data
|
|
68
|
+
else
|
|
69
|
+
data = yield if block_given?
|
|
70
|
+
event = LogEvent.new(#{lname}, self, #{tracercall}, data)
|
|
71
|
+
end
|
|
72
|
+
@outputters.each {|o| o.#{lname.downcase}(event) }
|
|
73
|
+
#{parentcall}
|
|
74
|
+
end
|
|
75
|
+
-
|
|
76
|
+
module_eval mstr
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.set_false(logger, lname)
|
|
80
|
+
module_eval "def logger.#{lname.downcase}?; false end"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def self.set_true(logger, lname)
|
|
84
|
+
module_eval "def logger.#{lname.downcase}?; true end"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# :include: rdoc/logserver
|
|
2
|
+
|
|
3
|
+
require 'log4r/logger'
|
|
4
|
+
require 'log4r/lib/drbloader'
|
|
5
|
+
|
|
6
|
+
module Log4r
|
|
7
|
+
# See log4r/logserver.rb
|
|
8
|
+
class LogServer < Logger
|
|
9
|
+
attr_reader :uri
|
|
10
|
+
|
|
11
|
+
# A valid ROMP uri must be specified.
|
|
12
|
+
def initialize(_fullname, _uri, _level=nil,
|
|
13
|
+
_additive=true, _trace=false, &accept)
|
|
14
|
+
super(_fullname, _level, _additive, _trace)
|
|
15
|
+
@uri = _uri
|
|
16
|
+
start_server(_uri, accept)
|
|
17
|
+
Logger.log_internal {"LogServer started at #{@uri}"}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
if HAVE_ROMP
|
|
21
|
+
include ROMPServer
|
|
22
|
+
else
|
|
23
|
+
def initialize(*args)
|
|
24
|
+
raise RuntimeError, "LogServer not supported. ROMP is required", caller
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# :nodoc:
|
|
2
|
+
require "log4r/outputter/iooutputter"
|
|
3
|
+
|
|
4
|
+
module Log4r
|
|
5
|
+
# Same as IOOutputter(name, $stdout)
|
|
6
|
+
class StdoutOutputter < IOOutputter
|
|
7
|
+
def initialize(_name, hash={})
|
|
8
|
+
super(_name, $stdout, hash)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Same as IOOutputter(name, $stderr)
|
|
13
|
+
class StderrOutputter < IOOutputter
|
|
14
|
+
def initialize(_name, hash={})
|
|
15
|
+
super(_name, $stderr, hash)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# = DateFileOutputter
|
|
2
|
+
#
|
|
3
|
+
# Subclass of FileOutputter that changes the log file daily. When a new
|
|
4
|
+
# day begins, a new file is created with the date included in the name.
|
|
5
|
+
#
|
|
6
|
+
# == Usage
|
|
7
|
+
#
|
|
8
|
+
# df_out = DateFileOutputter.new('name',
|
|
9
|
+
# :dirname="/tmp", :date_pattern=>"%m-%d"
|
|
10
|
+
# )
|
|
11
|
+
#
|
|
12
|
+
# == Rate of Change
|
|
13
|
+
#
|
|
14
|
+
# A new logfile is created whenever the current time as formatted by the date
|
|
15
|
+
# pattern no longer matches the previous time. (This is a simple String
|
|
16
|
+
# comparison.) So, in order to change the frequency of the rollover, just
|
|
17
|
+
# alter the date pattern to match how fast the files should be generated.
|
|
18
|
+
# For instance, to generate files by the minute,
|
|
19
|
+
#
|
|
20
|
+
# df_out.date_pattern = "%M"
|
|
21
|
+
#
|
|
22
|
+
# This causes the following files to show up one minute apart, asuming the
|
|
23
|
+
# script starts at the 4th minute of the hour:
|
|
24
|
+
#
|
|
25
|
+
# file_04.rb
|
|
26
|
+
# file_05.rb
|
|
27
|
+
# file_06.rb
|
|
28
|
+
# ...
|
|
29
|
+
#
|
|
30
|
+
# The only limitation of this approach is that the precise time cannot be
|
|
31
|
+
# recorded as the smallest time interval equals the rollover period for this
|
|
32
|
+
# system.
|
|
33
|
+
|
|
34
|
+
require "log4r/outputter/fileoutputter"
|
|
35
|
+
require "log4r/staticlogger"
|
|
36
|
+
|
|
37
|
+
module Log4r
|
|
38
|
+
|
|
39
|
+
# Additional hash arguments are:
|
|
40
|
+
#
|
|
41
|
+
# [<tt>:dirname</tt>] Directory of the log file
|
|
42
|
+
# [<tt>:date_pattern</tt>] Time.strftime format string (default is "%Y-%m-%d")
|
|
43
|
+
|
|
44
|
+
class DateFileOutputter < FileOutputter
|
|
45
|
+
DEFAULT_DATE_FMT = "%Y-%m-%d"
|
|
46
|
+
|
|
47
|
+
def initialize(_name, hash={})
|
|
48
|
+
@DatePattern = (hash[:date_pattern] or hash['date_pattern'] or
|
|
49
|
+
DEFAULT_DATE_FMT)
|
|
50
|
+
@DateStamp = Time.now.strftime( @DatePattern);
|
|
51
|
+
_dirname = (hash[:dirname] or hash['dirname'])
|
|
52
|
+
# hash[:dirname] masks hash[:filename]
|
|
53
|
+
if _dirname
|
|
54
|
+
if not FileTest.directory?( _dirname)
|
|
55
|
+
raise StandardError, "'#{_dirname}' must be a valid directory", caller
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
_filename = (hash[:filename] or hash['filename'])
|
|
60
|
+
if _filename.nil?
|
|
61
|
+
@filebase = File.basename( $0, '.rb') + ".log"
|
|
62
|
+
else
|
|
63
|
+
@filebase = File.basename((hash[:filename] or hash['filename'] or ""))
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Get rid of the 'nil' in the path
|
|
67
|
+
path = [_dirname, @filebase.sub(/(\.\w*)$/, "_#{@DateStamp}" + '\1')].compact
|
|
68
|
+
hash[:filename] = hash['filename'] = File.join(path)
|
|
69
|
+
|
|
70
|
+
super(_name, hash)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
#######
|
|
74
|
+
private
|
|
75
|
+
#######
|
|
76
|
+
|
|
77
|
+
# perform the write
|
|
78
|
+
def write(data)
|
|
79
|
+
change if requiresChange
|
|
80
|
+
super
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# construct a new filename from the DateStamp
|
|
84
|
+
def makeNewFilename
|
|
85
|
+
@DateStamp = Time.now.strftime( @DatePattern);
|
|
86
|
+
@filename = File.join(File.dirname(@filename),
|
|
87
|
+
@filebase.sub(/(\.\w*)$/, "_#{@DateStamp}" + '\1'))
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# does the file require a change?
|
|
91
|
+
def requiresChange
|
|
92
|
+
_DateStamp = Time.now.strftime( @DatePattern);
|
|
93
|
+
if not _DateStamp == @DateStamp
|
|
94
|
+
@DateStamp = _DateStamp
|
|
95
|
+
return true
|
|
96
|
+
end
|
|
97
|
+
false
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# change the file
|
|
101
|
+
def change
|
|
102
|
+
begin
|
|
103
|
+
@out.close
|
|
104
|
+
rescue
|
|
105
|
+
Logger.log_internal {
|
|
106
|
+
"DateFileOutputter '#{@name}' could not close #{@filename}"
|
|
107
|
+
}
|
|
108
|
+
end
|
|
109
|
+
makeNewFilename
|
|
110
|
+
@out = File.new(@filename, (@trunc ? "w" : "a"))
|
|
111
|
+
Logger.log_internal {
|
|
112
|
+
"DateFileOutputter '#{@name}' now writing to #{@filename}"
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# :include: ../rdoc/emailoutputter
|
|
2
|
+
|
|
3
|
+
require 'log4r/outputter/outputter'
|
|
4
|
+
require 'log4r/staticlogger'
|
|
5
|
+
require 'net/smtp'
|
|
6
|
+
|
|
7
|
+
module Log4r
|
|
8
|
+
|
|
9
|
+
class EmailOutputter < Outputter
|
|
10
|
+
attr_reader :server, :port, :domain, :acct, :authtype, :subject, :tls
|
|
11
|
+
|
|
12
|
+
def initialize(_name, hash={})
|
|
13
|
+
super(_name, hash)
|
|
14
|
+
validate(hash)
|
|
15
|
+
@buff = []
|
|
16
|
+
begin
|
|
17
|
+
Logger.log_internal {
|
|
18
|
+
"EmailOutputter '#{@name}' running SMTP client on #{@server}:#{@port}"
|
|
19
|
+
}
|
|
20
|
+
rescue Exception => e
|
|
21
|
+
Logger.log_internal(-2) {
|
|
22
|
+
"EmailOutputter '#{@name}' failed to start SMTP client!"
|
|
23
|
+
}
|
|
24
|
+
Logger.log_internal {e}
|
|
25
|
+
self.level = OFF
|
|
26
|
+
raise e
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# send out an email with the current buffer
|
|
31
|
+
def flush
|
|
32
|
+
synch { send_mail }
|
|
33
|
+
Logger.log_internal {"Flushed EmailOutputter '#{@name}'"}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def validate(hash)
|
|
39
|
+
@buffsize = (hash[:buffsize] or hash['buffsize'] or 100).to_i
|
|
40
|
+
@formatfirst = Log4rTools.decode_bool(hash, :formatfirst, false)
|
|
41
|
+
decode_immediate_at(hash)
|
|
42
|
+
validate_smtp_params(hash)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def decode_immediate_at(hash)
|
|
46
|
+
@immediate = Hash.new
|
|
47
|
+
_at = (hash[:immediate_at] or hash['immediate_at'])
|
|
48
|
+
return if _at.nil?
|
|
49
|
+
Log4rTools.comma_split(_at).each {|lname|
|
|
50
|
+
level = LNAMES.index(lname)
|
|
51
|
+
if level.nil?
|
|
52
|
+
Logger.log_internal(-2) do
|
|
53
|
+
"EmailOutputter: skipping bad immediate_at level name '#{lname}'"
|
|
54
|
+
end
|
|
55
|
+
next
|
|
56
|
+
end
|
|
57
|
+
@immediate[level] = true
|
|
58
|
+
}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def validate_smtp_params(hash)
|
|
62
|
+
@from = (hash[:from] or hash['from'])
|
|
63
|
+
raise ArgumentError, "Must specify from address" if @from.nil?
|
|
64
|
+
_to = (hash[:to] or hash['to'] or "")
|
|
65
|
+
@to = Log4rTools.comma_split(_to)
|
|
66
|
+
raise ArgumentError, "Must specify recepients" if @to.empty?
|
|
67
|
+
@server = (hash[:server] or hash['server'] or 'localhost')
|
|
68
|
+
@port = (hash[:port] or hash['port'] or 25).to_i
|
|
69
|
+
@domain = (hash[:domain] or hash['domain'] or ENV['HOSTNAME'])
|
|
70
|
+
@acct = (hash[:acct] or hash['acct'])
|
|
71
|
+
@passwd = (hash[:passwd] or hash['passwd'])
|
|
72
|
+
@authtype = (hash[:authtype] or hash['authtype'] or :cram_md5).to_s.to_sym
|
|
73
|
+
@subject = (hash[:subject] or hash['subject'] or "Message of #{$0}")
|
|
74
|
+
@tls = (hash[:tls] or hash['tls'] or nil)
|
|
75
|
+
@params = [@server, @port, @domain, @acct, @passwd, @authtype]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def canonical_log(event)
|
|
79
|
+
synch {
|
|
80
|
+
@buff.push case @formatfirst
|
|
81
|
+
when true then @formatter.format event
|
|
82
|
+
else event
|
|
83
|
+
end
|
|
84
|
+
send_mail if @buff.size >= @buffsize or @immediate[event.level]
|
|
85
|
+
}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def send_mail
|
|
89
|
+
msg =
|
|
90
|
+
case @formatfirst
|
|
91
|
+
when true then @buff.join
|
|
92
|
+
else @buff.collect{|e| @formatter.format e}.join
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
### build a mail header for RFC 822
|
|
96
|
+
rfc822msg =
|
|
97
|
+
"From: #{@from}\n" +
|
|
98
|
+
"To: #{@to}\n" +
|
|
99
|
+
"Subject: #{@subject}\n" +
|
|
100
|
+
"Date: #{Time.now.strftime( "%a, %d %b %Y %H:%M:%S %z %Z")}\n" +
|
|
101
|
+
"Message-Id: <#{"%.8f" % Time.now.to_f}@#{@domain}>\n\n" +
|
|
102
|
+
"#{msg}"
|
|
103
|
+
|
|
104
|
+
### send email
|
|
105
|
+
begin
|
|
106
|
+
|
|
107
|
+
smtp = Net::SMTP.new( @server, @port )
|
|
108
|
+
|
|
109
|
+
if ( @tls )
|
|
110
|
+
|
|
111
|
+
# >1.8.7 has smtp_tls built in, 1.8.6 requires smtp_tls
|
|
112
|
+
if RUBY_VERSION < "1.8.7" then
|
|
113
|
+
begin
|
|
114
|
+
require 'rubygems'
|
|
115
|
+
require 'smtp_tls'
|
|
116
|
+
smtp.enable_starttls if smtp.respond_to?(:enable_starttls)
|
|
117
|
+
rescue LoadError => e
|
|
118
|
+
Logger.log_internal(-2) {
|
|
119
|
+
"EmailOutputter '#{@name}' unable to load smtp_tls, needed to support TLS on Ruby versions < 1.8.7"
|
|
120
|
+
}
|
|
121
|
+
raise e
|
|
122
|
+
end
|
|
123
|
+
else # RUBY_VERSION >= 1.8.7
|
|
124
|
+
smtp.enable_starttls_auto if smtp.respond_to?(:enable_starttls_auto)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
end # if @tls
|
|
128
|
+
|
|
129
|
+
smtp.start(@domain, @acct, @passwd, @authtype) do |s|
|
|
130
|
+
s.send_message(rfc822msg, @from, @to)
|
|
131
|
+
end
|
|
132
|
+
rescue Exception => e
|
|
133
|
+
Logger.log_internal(-2) {
|
|
134
|
+
"EmailOutputter '#{@name}' couldn't send email!"
|
|
135
|
+
}
|
|
136
|
+
Logger.log_internal {e}
|
|
137
|
+
self.level = OFF
|
|
138
|
+
raise e
|
|
139
|
+
ensure @buff.clear
|
|
140
|
+
end # begin
|
|
141
|
+
end # def send_mail
|
|
142
|
+
end # class EmailOutputter
|
|
143
|
+
end # module Log4r
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# :nodoc:
|
|
2
|
+
# Version:: $Id$
|
|
3
|
+
|
|
4
|
+
require "log4r/outputter/iooutputter"
|
|
5
|
+
require "log4r/staticlogger"
|
|
6
|
+
|
|
7
|
+
module Log4r
|
|
8
|
+
|
|
9
|
+
# Convenience wrapper for File. Additional hash arguments are:
|
|
10
|
+
#
|
|
11
|
+
# [<tt>:filename</tt>] Name of the file to log to.
|
|
12
|
+
# [<tt>:trunc</tt>] Truncate the file?
|
|
13
|
+
class FileOutputter < IOOutputter
|
|
14
|
+
attr_reader :trunc, :filename
|
|
15
|
+
|
|
16
|
+
def initialize(_name, hash={})
|
|
17
|
+
super(_name, nil, hash)
|
|
18
|
+
|
|
19
|
+
@trunc = Log4rTools.decode_bool(hash, :trunc, false)
|
|
20
|
+
_filename = (hash[:filename] or hash['filename'])
|
|
21
|
+
@create = Log4rTools.decode_bool(hash, :create, true)
|
|
22
|
+
|
|
23
|
+
if _filename.class != String
|
|
24
|
+
raise TypeError, "Argument 'filename' must be a String", caller
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# file validation
|
|
28
|
+
if FileTest.exist?( _filename )
|
|
29
|
+
if not FileTest.file?( _filename )
|
|
30
|
+
raise StandardError, "'#{_filename}' is not a regular file", caller
|
|
31
|
+
elsif not FileTest.writable?( _filename )
|
|
32
|
+
raise StandardError, "'#{_filename}' is not writable!", caller
|
|
33
|
+
end
|
|
34
|
+
else # ensure directory is writable
|
|
35
|
+
dir = File.dirname( _filename )
|
|
36
|
+
if not FileTest.writable?( dir )
|
|
37
|
+
raise StandardError, "'#{dir}' is not writable!"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
@filename = _filename
|
|
42
|
+
if ( @create == true ) then
|
|
43
|
+
@out = File.new(@filename, (@trunc ? "wb" : "ab"))
|
|
44
|
+
Logger.log_internal {
|
|
45
|
+
"FileOutputter '#{@name}' writing to #{@filename}"
|
|
46
|
+
}
|
|
47
|
+
else
|
|
48
|
+
Logger.log_internal {
|
|
49
|
+
"FileOutputter '#{@name}' called with :create == false, #{@filename}"
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end
|
|
@@ -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
|