logsly 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -3
  3. data/lib/logsly/colors.rb +2 -2
  4. data/lib/logsly/logging182/appender.rb +290 -0
  5. data/lib/logsly/logging182/appenders/buffering.rb +398 -0
  6. data/lib/logsly/logging182/appenders/console.rb +81 -0
  7. data/lib/logsly/logging182/appenders/email.rb +178 -0
  8. data/lib/logsly/logging182/appenders/file.rb +85 -0
  9. data/lib/logsly/logging182/appenders/growl.rb +200 -0
  10. data/lib/logsly/logging182/appenders/io.rb +84 -0
  11. data/lib/logsly/logging182/appenders/rolling_file.rb +338 -0
  12. data/lib/logsly/logging182/appenders/string_io.rb +92 -0
  13. data/lib/logsly/logging182/appenders/syslog.rb +215 -0
  14. data/lib/logsly/logging182/appenders.rb +64 -0
  15. data/lib/logsly/logging182/color_scheme.rb +248 -0
  16. data/lib/logsly/logging182/config/configurator.rb +187 -0
  17. data/lib/logsly/logging182/config/yaml_configurator.rb +190 -0
  18. data/lib/logsly/logging182/diagnostic_context.rb +332 -0
  19. data/lib/logsly/logging182/layout.rb +132 -0
  20. data/lib/logsly/logging182/layouts/basic.rb +38 -0
  21. data/lib/logsly/logging182/layouts/parseable.rb +256 -0
  22. data/lib/logsly/logging182/layouts/pattern.rb +568 -0
  23. data/lib/logsly/logging182/layouts.rb +9 -0
  24. data/lib/logsly/logging182/log_event.rb +44 -0
  25. data/lib/logsly/logging182/logger.rb +509 -0
  26. data/lib/logsly/logging182/proxy.rb +59 -0
  27. data/lib/logsly/logging182/rails_compat.rb +36 -0
  28. data/lib/logsly/logging182/repository.rb +231 -0
  29. data/lib/logsly/logging182/root_logger.rb +60 -0
  30. data/lib/logsly/logging182/stats.rb +277 -0
  31. data/lib/logsly/logging182/utils.rb +231 -0
  32. data/lib/logsly/logging182.rb +559 -0
  33. data/lib/logsly/outputs.rb +5 -5
  34. data/lib/logsly/version.rb +1 -1
  35. data/lib/logsly.rb +6 -6
  36. data/logsly.gemspec +4 -2
  37. data/test/unit/colors_tests.rb +3 -3
  38. data/test/unit/logsly_tests.rb +14 -14
  39. data/test/unit/outputs_tests.rb +34 -24
  40. metadata +45 -6
@@ -0,0 +1,398 @@
1
+
2
+ module Logsly::Logging182::Appenders
3
+
4
+ # The Buffering module is used to implement buffering of the log messages
5
+ # in a given appender. The size of the buffer can be specified, and the
6
+ # buffer can be configured to auto-flush at a given threshold. The
7
+ # threshold can be a single message or a very large number of messages.
8
+ #
9
+ # Log messages of a certain level can cause the buffer to be flushed
10
+ # immediately. If an error occurs, all previous messages and the error
11
+ # message will be written immediately to the logging destination if the
12
+ # buffer is configured to do so.
13
+ #
14
+ module Buffering
15
+
16
+ # Default buffer size
17
+ #
18
+ DEFAULT_BUFFER_SIZE = 500;
19
+
20
+ # The buffer holding the log messages
21
+ #
22
+ attr_reader :buffer
23
+
24
+ # The auto-flushing setting. When the buffer reaches this size, all
25
+ # messages will be be flushed automatically.
26
+ #
27
+ attr_reader :auto_flushing
28
+
29
+ # When set, the buffer will be flushed at regular intervals defined by the
30
+ # flush_period.
31
+ #
32
+ attr_reader :flush_period
33
+
34
+ # Setup the message buffer and other variables for automatically and
35
+ # periodically flushing the buffer.
36
+ #
37
+ def initialize( *args, &block )
38
+ @buffer = []
39
+ @immediate = []
40
+ @auto_flushing = 1
41
+ @flush_period = @periodic_flusher = nil
42
+
43
+ super(*args, &block)
44
+ end
45
+
46
+ # Close the message buffer by flushing all log events to the appender. If a
47
+ # periodic flusher thread is running, shut it down and allow it to exit.
48
+ #
49
+ def close( *args )
50
+ flush
51
+
52
+ if @periodic_flusher
53
+ @periodic_flusher.stop
54
+ @periodic_flusher = nil
55
+ Thread.pass
56
+ end
57
+
58
+ super(*args)
59
+ end
60
+
61
+ # Reopen the connection to the underlying logging destination. In addition
62
+ # if the appender is configured for periodic flushing, then the flushing
63
+ # thread will be stopped and restarted.
64
+ #
65
+ def reopen
66
+ _setup_periodic_flusher
67
+ super
68
+ end
69
+
70
+ # Call +flush+ to force an appender to write out any buffered log events.
71
+ # Similar to IO#flush, so use in a similar fashion.
72
+ #
73
+ def flush
74
+ return self if @buffer.empty?
75
+
76
+ str = nil
77
+ sync {
78
+ str = @buffer.join
79
+ @buffer.clear
80
+ }
81
+
82
+ canonical_write str unless str.empty?
83
+ self
84
+ end
85
+
86
+ # Configure the levels that will trigger an immediate flush of the
87
+ # logging buffer. When a log event of the given level is seen, the
88
+ # buffer will be flushed immediately. Only the levels explicitly given
89
+ # in this assignment will flush the buffer; if an "error" message is
90
+ # configured to immediately flush the buffer, a "fatal" message will not
91
+ # even though it is a higher level. Both must be explicitly passed to
92
+ # this assignment.
93
+ #
94
+ # You can pass in a single level name or number, an array of level
95
+ # names or numbers, or a string containing a comma separated list of level
96
+ # names or numbers.
97
+ #
98
+ # immediate_at = :error
99
+ # immediate_at = [:error, :fatal]
100
+ # immediate_at = "warn, error"
101
+ #
102
+ def immediate_at=( level )
103
+ @immediate.clear
104
+
105
+ # get the immediate levels -- no buffering occurs at these levels, and
106
+ # a log message is written to the logging destination immediately
107
+ immediate_at =
108
+ case level
109
+ when String; level.split(',').map {|x| x.strip}
110
+ when Array; level
111
+ else Array(level) end
112
+
113
+ immediate_at.each do |lvl|
114
+ num = ::Logsly::Logging182.level_num(lvl)
115
+ next if num.nil?
116
+ @immediate[num] = true
117
+ end
118
+ end
119
+
120
+ # Configure the auto-flushing threshold. Auto-flushing is used to flush
121
+ # the contents of the logging buffer to the logging destination
122
+ # automatically when the buffer reaches a certain threshold.
123
+ #
124
+ # By default, the auto-flushing will be configured to flush after each
125
+ # log message.
126
+ #
127
+ # The allowed settings are as follows:
128
+ #
129
+ # N : flush after every N messages (N is an integer)
130
+ # true : flush after each log message
131
+ # false OR
132
+ # nil OR
133
+ # 0 : only flush when the buffer is full (500 messages)
134
+ #
135
+ # If the default buffer size of 500 is too small, then you can manually
136
+ # configure it to be as large as you want. This will consume more memory.
137
+ #
138
+ # auto_flushing = 42_000
139
+ #
140
+ def auto_flushing=( period )
141
+ @auto_flushing =
142
+ case period
143
+ when true; 1
144
+ when false, nil, 0; DEFAULT_BUFFER_SIZE
145
+ when Integer; period
146
+ when String; Integer(period)
147
+ else
148
+ raise ArgumentError,
149
+ "unrecognized auto_flushing period: #{period.inspect}"
150
+ end
151
+
152
+ if @auto_flushing <= 0
153
+ raise ArgumentError,
154
+ "auto_flushing period must be greater than zero: #{period.inspect}"
155
+ end
156
+
157
+ @auto_flushing = DEFAULT_BUFFER_SIZE if @flush_period and @auto_flushing <= 1
158
+ end
159
+
160
+ # Configure periodic flushing of the message buffer. Periodic flushing is
161
+ # used to flush the contents of the logging buffer at some regular
162
+ # interval. Periodic flushing is disabled by default.
163
+ #
164
+ # When enabling periodic flushing the flush period should be set using one
165
+ # of the following formats: "HH:MM:SS" or seconds as an numeric or string.
166
+ #
167
+ # "01:00:00" : every hour
168
+ # "00:05:00" : every 5 minutes
169
+ # "00:00:30" : every 30 seconds
170
+ # 60 : every 60 seconds (1 minute)
171
+ # "120" : every 120 seconds (2 minutes)
172
+ #
173
+ # For the periodic flusher to work properly, the auto-flushing threshold
174
+ # will be set to the default value of 500. The auto-flushing threshold can
175
+ # be changed, but it must be greater than 1.
176
+ #
177
+ # To disable the periodic flusher simply set the flush period to +nil+.
178
+ # The auto-flushing threshold will not be changed; it must be disabled
179
+ # manually if so desired.
180
+ #
181
+ def flush_period=( period )
182
+ period =
183
+ case period
184
+ when Integer, Float, nil; period
185
+ when String;
186
+ num = _parse_hours_minutes_seconds(period) || _parse_numeric(period)
187
+ num = ArgumentError.new("unrecognized flush period: #{period.inspect}") if num.nil?
188
+ num
189
+ else ArgumentError.new("unrecognized flush period: #{period.inspect}") end
190
+
191
+ raise period if Exception === period
192
+ @flush_period = period
193
+
194
+ _setup_periodic_flusher
195
+ end
196
+
197
+ protected
198
+
199
+ # Configure the buffering using the arguments found in the give options
200
+ # hash. This method must be called in order to use the message buffer.
201
+ # The supported options are "immediate_at" and "auto_flushing". Please
202
+ # refer to the documentation for those methods to see the allowed
203
+ # options.
204
+ #
205
+ def configure_buffering( opts )
206
+ ::Logsly::Logging182.init unless ::Logsly::Logging182.initialized?
207
+
208
+ self.immediate_at = opts.getopt(:immediate_at, '')
209
+ self.auto_flushing = opts.getopt(:auto_flushing, true)
210
+ self.flush_period = opts.getopt(:flush_period, nil)
211
+ end
212
+
213
+ # Returns true if the _event_ level matches one of the configured
214
+ # immediate logging levels. Otherwise returns false.
215
+ #
216
+ def immediate?( event )
217
+ return false unless event.respond_to? :level
218
+ @immediate[event.level]
219
+ end
220
+
221
+
222
+ private
223
+
224
+ # call-seq:
225
+ # write( event )
226
+ #
227
+ # Writes the given _event_ to the logging destination. The _event_ can
228
+ # be either a LogEvent or a String. If a LogEvent, then it will be
229
+ # formatted using the layout given to the appender when it was created.
230
+ #
231
+ # The _event_ will be formatted and then buffered until the
232
+ # "auto_flushing" level has been reached. At this time the canonical_write
233
+ # method will be used to log all events stored in the buffer.
234
+ #
235
+ def write( event )
236
+ str = event.instance_of?(::Logsly::Logging182::LogEvent) ?
237
+ layout.format(event) : event.to_s
238
+ return if str.empty?
239
+
240
+ if @auto_flushing == 1
241
+ canonical_write(str)
242
+ else
243
+ sync {
244
+ str = str.force_encoding(encoding) if encoding and str.encoding != encoding
245
+ @buffer << str
246
+ }
247
+ @periodic_flusher.signal if @periodic_flusher
248
+ flush if @buffer.length >= @auto_flushing || immediate?(event)
249
+ end
250
+
251
+ self
252
+ end
253
+
254
+ # Attempt to parse an hours/minutes/seconds value from the string and return
255
+ # an integer number of seconds.
256
+ #
257
+ # _parse_hours_minutes_seconds("14:12:42") #=> 51162
258
+ # _parse_hours_minutes_seconds("foo") #=> nil
259
+ #
260
+ def _parse_hours_minutes_seconds( str )
261
+ m = %r/^\s*(\d{2,}):(\d{2}):(\d{2}(?:\.\d+)?)\s*$/.match(str)
262
+ return if m.nil?
263
+
264
+ (3600 * m[1].to_i) + (60 * m[2].to_i) + (m[3].to_f)
265
+ end
266
+
267
+ # Convert the string into a numeric value. If the string does not
268
+ # represent a valid Integer or Float then +nil+ is returned.
269
+ #
270
+ # _parse_numeric("10") #=> 10
271
+ # _parse_numeric("1.0") #=> 1.0
272
+ # _parse_numeric("foo") #=> nil
273
+ #
274
+ def _parse_numeric( str )
275
+ Integer(str) rescue (Float(str) rescue nil)
276
+ end
277
+
278
+ # Using the flush_period, create a new PeriodicFlusher attached to this
279
+ # appender. If the flush_period is nil, then no action will be taken. If a
280
+ # PeriodicFlusher already exists, it will be stopped and a new one will be
281
+ # created.
282
+ #
283
+ def _setup_periodic_flusher
284
+ # stop and remove any existing periodic flusher instance
285
+ if @periodic_flusher
286
+ @periodic_flusher.stop
287
+ @periodic_flusher = nil
288
+ Thread.pass
289
+ end
290
+
291
+ # create a new periodic flusher if we have a valid flush period
292
+ if @flush_period
293
+ @auto_flushing = DEFAULT_BUFFER_SIZE unless @auto_flushing > 1
294
+ @periodic_flusher = PeriodicFlusher.new(self, @flush_period)
295
+ @periodic_flusher.start
296
+ end
297
+ end
298
+
299
+ # :stopdoc:
300
+
301
+ # The PeriodicFlusher contains an internal run loop that will periodically
302
+ # wake up and flush any log events contained in the message buffer of the
303
+ # owning appender instance. The PeriodicFlusher relies on a _signal_ from
304
+ # the appender in order to wakeup and perform the flush on the appender.
305
+ #
306
+ class PeriodicFlusher
307
+
308
+ # Create a new PeriodicFlusher instance that will call the +flush+
309
+ # method on the given _appender_. The +flush+ method will be called
310
+ # every _period_ seconds, but only when the message buffer is non-empty.
311
+ #
312
+ def initialize( appender, period )
313
+ @appender = appender
314
+ @period = period
315
+
316
+ @mutex = Mutex.new
317
+ @cv = ConditionVariable.new
318
+ @thread = nil
319
+ @waiting = nil
320
+ @signaled = false
321
+ end
322
+
323
+ # Start the periodic flusher's internal run loop.
324
+ #
325
+ def start
326
+ return if @thread
327
+
328
+ @thread = Thread.new { loop {
329
+ begin
330
+ break if Thread.current[:stop]
331
+ _wait_for_signal
332
+ sleep @period unless Thread.current[:stop]
333
+ @appender.flush
334
+ rescue => err
335
+ ::Logsly::Logging182.log_internal {"PeriodicFlusher for appender #{@appender.inspect} encountered an error"}
336
+ ::Logsly::Logging182.log_internal(-2) {err}
337
+ end
338
+ }; @thread = nil }
339
+
340
+ self
341
+ end
342
+
343
+ # Stop the periodic flusher's internal run loop.
344
+ #
345
+ def stop
346
+ return if @thread.nil?
347
+ @thread[:stop] = true
348
+ signal if waiting?
349
+ self
350
+ end
351
+
352
+ # Signal the periodic flusher. This will wake up the run loop if it is
353
+ # currently waiting for something to do. If the signal method is never
354
+ # called, the periodic flusher will never perform the flush action on
355
+ # the appender.
356
+ #
357
+ def signal
358
+ return if Thread.current == @thread # don't signal ourselves
359
+ return if @signaled # don't need to signal again
360
+
361
+ @mutex.synchronize {
362
+ @signaled = true
363
+ @cv.signal
364
+ }
365
+ self
366
+ end
367
+
368
+ # Returns +true+ if the flusher is waiting for a signal. Returns +false+
369
+ # if the flusher is somewhere in the processing loop.
370
+ #
371
+ def waiting?
372
+ @waiting
373
+ end
374
+
375
+ private
376
+
377
+ def _wait_for_signal
378
+ @mutex.synchronize {
379
+ begin
380
+ # wait on the condition variable only if we have NOT been signaled
381
+ unless @signaled
382
+ @waiting = true
383
+ @cv.wait(@mutex)
384
+ @waiting = false
385
+ end
386
+ ensure
387
+ @signaled = false
388
+ end
389
+ }
390
+ ensure
391
+ @waiting = false
392
+ end
393
+ end # class PeriodicFlusher
394
+ # :startdoc:
395
+
396
+ end # Buffering
397
+ end # Logsly::Logging182::Appenders
398
+
@@ -0,0 +1,81 @@
1
+
2
+ module Logsly::Logging182::Appenders
3
+
4
+ # Accessor / Factory for the Stdout appender.
5
+ #
6
+ def self.stdout( *args )
7
+ if args.empty?
8
+ return self['stdout'] || ::Logsly::Logging182::Appenders::Stdout.new
9
+ end
10
+ ::Logsly::Logging182::Appenders::Stdout.new(*args)
11
+ end
12
+
13
+ # This class provides an Appender that can write to STDOUT.
14
+ #
15
+ class Stdout < ::Logsly::Logging182::Appenders::IO
16
+
17
+ # call-seq:
18
+ # Stdout.new( name = 'stdout' )
19
+ # Stdout.new( :layout => layout )
20
+ # Stdout.new( name = 'stdout', :level => 'info' )
21
+ #
22
+ # Creates a new Stdout Appender. The name 'stdout' will be used unless
23
+ # another is given. Optionally, a layout can be given for the appender
24
+ # to use (otherwise a basic appender will be created) and a log level
25
+ # can be specified.
26
+ #
27
+ # Options:
28
+ #
29
+ # :layout => the layout to use when formatting log events
30
+ # :level => the level at which to log
31
+ #
32
+ def initialize( *args )
33
+ opts = Hash === args.last ? args.pop : {}
34
+ name = args.empty? ? 'stdout' : args.shift
35
+
36
+ opts[:encoding] = STDOUT.external_encoding if STDOUT.respond_to? :external_encoding
37
+
38
+ super(name, STDOUT, opts)
39
+ end
40
+ end # Stdout
41
+
42
+
43
+ # Accessor / Factory for the Stderr appender.
44
+ #
45
+ def self.stderr( *args )
46
+ if args.empty?
47
+ return self['stderr'] || ::Logsly::Logging182::Appenders::Stderr.new
48
+ end
49
+ ::Logsly::Logging182::Appenders::Stderr.new(*args)
50
+ end
51
+
52
+ # This class provides an Appender that can write to STDERR.
53
+ #
54
+ class Stderr < ::Logsly::Logging182::Appenders::IO
55
+
56
+ # call-seq:
57
+ # Stderr.new( name = 'stderr' )
58
+ # Stderr.new( :layout => layout )
59
+ # Stderr.new( name = 'stderr', :level => 'warn' )
60
+ #
61
+ # Creates a new Stderr Appender. The name 'stderr' will be used unless
62
+ # another is given. Optionally, a layout can be given for the appender
63
+ # to use (otherwise a basic appender will be created) and a log level
64
+ # can be specified.
65
+ #
66
+ # Options:
67
+ #
68
+ # :layout => the layout to use when formatting log events
69
+ # :level => the level at which to log
70
+ #
71
+ def initialize( *args )
72
+ opts = Hash === args.last ? args.pop : {}
73
+ name = args.empty? ? 'stderr' : args.shift
74
+
75
+ opts[:encoding] = STDERR.external_encoding if STDERR.respond_to? :external_encoding
76
+
77
+ super(name, STDERR, opts)
78
+ end
79
+ end # Stderr
80
+ end # Logsly::Logging182::Appenders
81
+
@@ -0,0 +1,178 @@
1
+
2
+ require 'net/smtp'
3
+ require 'time' # get rfc822 time format
4
+
5
+ module Logsly::Logging182::Appenders
6
+
7
+ # Accessor / Factory for the Email appender.
8
+ #
9
+ def self.email( *args )
10
+ return ::Logsly::Logging182::Appenders::Email if args.empty?
11
+ ::Logsly::Logging182::Appenders::Email.new(*args)
12
+ end
13
+
14
+ # Provides an appender that can send log messages via email to a list of
15
+ # recipients.
16
+ #
17
+ class Email < ::Logsly::Logging182::Appender
18
+ include Buffering
19
+
20
+ attr_reader :authentication, :to, :port
21
+ attr_accessor :address, :domain, :from, :subject
22
+ attr_accessor :user_name, :password, :enable_starttls_auto
23
+
24
+ # call-seq:
25
+ # Email.new( name, :from => 'me@example.com', :to => 'you@example.com', :subject => 'Whoops!' )
26
+ #
27
+ # Create a new email appender that will buffer messages and then send them
28
+ # out in batches to the listed recipients. See the options below to
29
+ # configure how emails are sent through you mail server of choice. All the
30
+ # buffering options apply to the email appender.
31
+ #
32
+ # The following options are required:
33
+ #
34
+ # :from - The base filename to use when constructing new log
35
+ # filenames.
36
+ # :to - The list of email recipients either as an Array or a comma
37
+ # separated list.
38
+ #
39
+ # The following options are optional:
40
+ #
41
+ # :subject - The subject line for the email.
42
+ # :address - Allows you to use a remote mail server. Just change it
43
+ # from its default "localhost" setting.
44
+ # :port - On the off chance that your mail server doesn't run on
45
+ # port 25, you can change it.
46
+ # :domain - If you need to specify a HELO domain, you can do it here.
47
+ # :user_name - If your mail server requires authentication, set the user
48
+ # name in this setting.
49
+ # :password - If your mail server requires authentication, set the
50
+ # password in this setting.
51
+ # :authentication - If your mail server requires authentication, you need
52
+ # to specify the authentication type here. This is a
53
+ # symbol and one of :plain (will send the password in
54
+ # the clear), :login (will send password Base64
55
+ # encoded) or :cram_md5 (combines a Challenge/Response
56
+ # mechanism to exchange information and a cryptographic
57
+ # Message Digest 5 algorithm to hash important
58
+ # information)
59
+ # :enable_starttls_auto - When set to true, detects if STARTTLS is
60
+ # enabled in your SMTP server and starts to use it.
61
+ #
62
+ # Example:
63
+ #
64
+ # Setup an email appender that will buffer messages for up to 1 minute,
65
+ # and only send messages for ERROR and FATAL messages. This example uses
66
+ # Google's SMTP server with authentication to send out messages.
67
+ #
68
+ # Logger.appenders.email( 'email',
69
+ # :from => "server@example.com",
70
+ # :to => "developers@example.com",
71
+ # :subject => "Application Error [#{%x(uname -n).strip}]",
72
+ #
73
+ # :address => "smtp.google.com",
74
+ # :port => 443,
75
+ # :domain => "google.com",
76
+ # :user_name => "example",
77
+ # :password => "12345",
78
+ # :authentication => :plain,
79
+ # :enable_starttls_auto => true,
80
+ #
81
+ # :auto_flushing => 200, # send an email after 200 messages have been buffered
82
+ # :flush_period => 60, # send an email after one minute
83
+ # :level => :error # only process log events that are "error" or "fatal"
84
+ # )
85
+ #
86
+ def initialize( name, opts = {} )
87
+ opts[:header] = false
88
+ super(name, opts)
89
+
90
+ af = opts.getopt(:buffsize) ||
91
+ opts.getopt(:buffer_size) ||
92
+ 100
93
+ configure_buffering({:auto_flushing => af}.merge(opts))
94
+
95
+ # get the SMTP parameters
96
+ self.from = opts.getopt :from
97
+ raise ArgumentError, 'Must specify from address' if @from.nil?
98
+
99
+ self.to = opts.getopt :to
100
+ raise ArgumentError, 'Must specify recipients' if @to.empty?
101
+
102
+ self.subject = opts.getopt :subject, "Message from #{$0}"
103
+ self.address = opts.getopt(:server) || opts.getopt(:address) || 'localhost'
104
+ self.port = opts.getopt(:port, 25)
105
+ self.domain = opts.getopt(:domain, ENV['HOSTNAME']) || 'localhost.localdomain'
106
+ self.user_name = opts.getopt(:acct) || opts.getopt(:user_name)
107
+ self.password = opts.getopt(:passwd) || opts.getopt(:password)
108
+ self.enable_starttls_auto = opts.getopt(:enable_starttls_auto, false)
109
+ self.authentication = opts.getopt(:authtype) || opts.getopt(:authentication) || :plain
110
+ end
111
+
112
+ # Close the email appender. If the layout contains a foot, it will not be
113
+ # sent as an email.
114
+ #
115
+ def close( *args )
116
+ super(false)
117
+ end
118
+
119
+ # If your mail server requires authentication, you need to specify the
120
+ # authentication type here. This is a symbol and one of :plain (will send
121
+ # the password in the clear), :login (will send password Base64 encoded)
122
+ # or :cram_md5 (combines a Challenge/Response mechanism to exchange
123
+ # information and a cryptographic Message Digest 5 algorithm to hash
124
+ # important information)
125
+ #
126
+ def authentication=( val )
127
+ @authentication = val.to_s.to_sym
128
+ end
129
+
130
+ # On the off chance that your mail server doesn't run on port 25, you can
131
+ # change it.
132
+ #
133
+ def port=( val )
134
+ @port = Integer(val)
135
+ end
136
+
137
+ # The list of email recipients. This can either be an Array of recipients
138
+ # or a comma separated list. A single recipient is also valid.
139
+ #
140
+ # email.to = ['mike@example.com', 'tony@example.com']
141
+ # email.to = 'alicia@example.com'
142
+ # email.to = 'bob@example.com, andy@example.com, john@example.com'
143
+ #
144
+ def to=( val )
145
+ @to = val.respond_to?(:split) ? val.split(',') : Array(val)
146
+ end
147
+
148
+
149
+ private
150
+
151
+ # This method is called by the buffering code when messages need to be
152
+ # sent out as an email.
153
+ #
154
+ def canonical_write( str )
155
+ ### build a mail header for RFC 822
156
+ rfc822msg = "From: #{@from}\n"
157
+ rfc822msg << "To: #{@to.join(",")}\n"
158
+ rfc822msg << "Subject: #{@subject}\n"
159
+ rfc822msg << "Date: #{Time.new.rfc822}\n"
160
+ rfc822msg << "Message-Id: <#{"%.8f" % Time.now.to_f}@#{@domain}>\n\n"
161
+
162
+ rfc822msg = rfc822msg.force_encoding(encoding) if encoding and rfc822msg.encoding != encoding
163
+ rfc822msg << str
164
+
165
+ ### send email
166
+ smtp = Net::SMTP.new(@address, @port)
167
+ smtp.enable_starttls_auto if @enable_starttls_auto and smtp.respond_to? :enable_starttls_auto
168
+ smtp.start(@domain, @user_name, @password, @authentication) { |s| s.sendmail(rfc822msg, @from, @to) }
169
+ self
170
+ rescue StandardError, TimeoutError => err
171
+ self.level = :off
172
+ ::Logsly::Logging182.log_internal {'e-mail notifications have been disabled'}
173
+ ::Logsly::Logging182.log_internal(-2) {err}
174
+ end
175
+
176
+ end # Email
177
+ end # Logsly::Logging182::Appenders
178
+