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.
Files changed (106) hide show
  1. checksums.yaml +15 -0
  2. data/doc/content/contact.html +22 -0
  3. data/doc/content/contribute.html +21 -0
  4. data/doc/content/index.html +90 -0
  5. data/doc/content/license.html +56 -0
  6. data/doc/content/manual.html +449 -0
  7. data/doc/dev/README.developers +55 -0
  8. data/doc/dev/checklist +23 -0
  9. data/doc/dev/things-to-do +5 -0
  10. data/doc/images/log4r-logo.png +0 -0
  11. data/doc/images/logo2.png +0 -0
  12. data/doc/log4r.css +111 -0
  13. data/doc/rdoc-log4r.css +696 -0
  14. data/doc/templates/main.html +147 -0
  15. data/examples/README +19 -0
  16. data/examples/ancestors.rb +53 -0
  17. data/examples/chainsaw_settings.xml +7 -0
  18. data/examples/customlevels.rb +34 -0
  19. data/examples/filelog.rb +25 -0
  20. data/examples/fileroll.rb +40 -0
  21. data/examples/gmail.rb +30 -0
  22. data/examples/gmail.yaml +95 -0
  23. data/examples/log4r_yaml.yaml +0 -0
  24. data/examples/logclient.rb +25 -0
  25. data/examples/logserver.rb +18 -0
  26. data/examples/moderate.xml +29 -0
  27. data/examples/moderateconfig.rb +66 -0
  28. data/examples/myformatter.rb +23 -0
  29. data/examples/outofthebox.rb +21 -0
  30. data/examples/rdoc-gen +2 -0
  31. data/examples/rrconfig.xml +63 -0
  32. data/examples/rrsetup.rb +42 -0
  33. data/examples/simpleconfig.rb +39 -0
  34. data/examples/syslogcustom.rb +52 -0
  35. data/examples/xmlconfig.rb +25 -0
  36. data/examples/yaml.rb +30 -0
  37. data/lib/log4r/GDC.rb +41 -0
  38. data/lib/log4r/MDC.rb +59 -0
  39. data/lib/log4r/NDC.rb +86 -0
  40. data/lib/log4r/base.rb +74 -0
  41. data/lib/log4r/config.rb +9 -0
  42. data/lib/log4r/configurator.rb +224 -0
  43. data/lib/log4r/formatter/formatter.rb +109 -0
  44. data/lib/log4r/formatter/log4jxmlformatter.rb +65 -0
  45. data/lib/log4r/formatter/patternformatter.rb +145 -0
  46. data/lib/log4r/lib/drbloader.rb +52 -0
  47. data/lib/log4r/lib/xmlloader.rb +24 -0
  48. data/lib/log4r/logevent.rb +28 -0
  49. data/lib/log4r/logger.rb +199 -0
  50. data/lib/log4r/loggerfactory.rb +89 -0
  51. data/lib/log4r/logserver.rb +28 -0
  52. data/lib/log4r/outputter/consoleoutputters.rb +18 -0
  53. data/lib/log4r/outputter/datefileoutputter.rb +117 -0
  54. data/lib/log4r/outputter/emailoutputter.rb +143 -0
  55. data/lib/log4r/outputter/fileoutputter.rb +56 -0
  56. data/lib/log4r/outputter/iooutputter.rb +55 -0
  57. data/lib/log4r/outputter/outputter.rb +134 -0
  58. data/lib/log4r/outputter/outputterfactory.rb +61 -0
  59. data/lib/log4r/outputter/rabbitoutputter.rb +70 -0
  60. data/lib/log4r/outputter/remoteoutputter.rb +40 -0
  61. data/lib/log4r/outputter/rollingfileoutputter.rb +234 -0
  62. data/lib/log4r/outputter/scribeoutputter.rb +37 -0
  63. data/lib/log4r/outputter/staticoutputter.rb +30 -0
  64. data/lib/log4r/outputter/syslogoutputter.rb +130 -0
  65. data/lib/log4r/outputter/udpoutputter.rb +53 -0
  66. data/lib/log4r/rdoc/GDC +14 -0
  67. data/lib/log4r/rdoc/MDC +16 -0
  68. data/lib/log4r/rdoc/NDC +41 -0
  69. data/lib/log4r/rdoc/configurator +243 -0
  70. data/lib/log4r/rdoc/emailoutputter +103 -0
  71. data/lib/log4r/rdoc/formatter +39 -0
  72. data/lib/log4r/rdoc/log4jxmlformatter +21 -0
  73. data/lib/log4r/rdoc/log4r +89 -0
  74. data/lib/log4r/rdoc/logger +175 -0
  75. data/lib/log4r/rdoc/logserver +85 -0
  76. data/lib/log4r/rdoc/outputter +108 -0
  77. data/lib/log4r/rdoc/patternformatter +128 -0
  78. data/lib/log4r/rdoc/scribeoutputter +16 -0
  79. data/lib/log4r/rdoc/syslogoutputter +29 -0
  80. data/lib/log4r/rdoc/win32eventoutputter +7 -0
  81. data/lib/log4r/rdoc/yamlconfigurator +20 -0
  82. data/lib/log4r/repository.rb +88 -0
  83. data/lib/log4r/staticlogger.rb +49 -0
  84. data/lib/log4r/version.rb +4 -0
  85. data/lib/log4r/yamlconfigurator.rb +198 -0
  86. data/lib/log4r.rb +18 -0
  87. data/tests/README +10 -0
  88. data/tests/testGDC.rb +24 -0
  89. data/tests/testMDC.rb +40 -0
  90. data/tests/testNDC.rb +25 -0
  91. data/tests/test_helper.rb +12 -0
  92. data/tests/testall.rb +6 -0
  93. data/tests/testbase.rb +48 -0
  94. data/tests/testchainsaw.rb +42 -0
  95. data/tests/testconf.xml +37 -0
  96. data/tests/testcustom.rb +30 -0
  97. data/tests/testformatter.rb +31 -0
  98. data/tests/testlogger.rb +200 -0
  99. data/tests/testoutputter.rb +143 -0
  100. data/tests/testpatternformatter.rb +76 -0
  101. data/tests/testthreads.rb +31 -0
  102. data/tests/testxmlconf.rb +48 -0
  103. data/tests/testyaml.rb +39 -0
  104. data/tests/testyaml_arrays.yaml +25 -0
  105. data/tests/testyaml_injection.yaml +22 -0
  106. metadata +193 -0
@@ -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