logsly 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
+