lumberjack 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/MIT_LICENSE +20 -0
  2. data/README.rdoc +86 -0
  3. data/Rakefile +56 -0
  4. data/VERSION +1 -0
  5. data/lib/lumberjack.rb +39 -0
  6. data/lib/lumberjack/device.rb +26 -0
  7. data/lib/lumberjack/device/date_rolling_log_file.rb +58 -0
  8. data/lib/lumberjack/device/log_file.rb +18 -0
  9. data/lib/lumberjack/device/null.rb +15 -0
  10. data/lib/lumberjack/device/rolling_log_file.rb +109 -0
  11. data/lib/lumberjack/device/size_rolling_log_file.rb +58 -0
  12. data/lib/lumberjack/device/writer.rb +119 -0
  13. data/lib/lumberjack/formatter.rb +76 -0
  14. data/lib/lumberjack/formatter/exception_formatter.rb +12 -0
  15. data/lib/lumberjack/formatter/inspect_formatter.rb +10 -0
  16. data/lib/lumberjack/formatter/pretty_print_formatter.rb +23 -0
  17. data/lib/lumberjack/formatter/string_formatter.rb +10 -0
  18. data/lib/lumberjack/log_entry.rb +36 -0
  19. data/lib/lumberjack/logger.rb +302 -0
  20. data/lib/lumberjack/rack.rb +5 -0
  21. data/lib/lumberjack/rack/unit_of_work.rb +15 -0
  22. data/lib/lumberjack/severity.rb +23 -0
  23. data/lib/lumberjack/template.rb +71 -0
  24. data/spec/device/date_rolling_log_file_spec.rb +66 -0
  25. data/spec/device/date_rolling_log_file_spec.rbc +2118 -0
  26. data/spec/device/log_file_spec.rb +26 -0
  27. data/spec/device/log_file_spec.rbc +727 -0
  28. data/spec/device/null_spec.rb +12 -0
  29. data/spec/device/null_spec.rbc +362 -0
  30. data/spec/device/rolling_log_file_spec.rb +117 -0
  31. data/spec/device/rolling_log_file_spec.rbc +2894 -0
  32. data/spec/device/size_rolling_log_file_spec.rb +54 -0
  33. data/spec/device/size_rolling_log_file_spec.rbc +1961 -0
  34. data/spec/device/stream_spec.rbc +3310 -0
  35. data/spec/device/writer_spec.rb +118 -0
  36. data/spec/entry_spec.rbc +2333 -0
  37. data/spec/formatter/exception_formatter_spec.rb +20 -0
  38. data/spec/formatter/exception_formatter_spec.rbc +620 -0
  39. data/spec/formatter/inspect_formatter_spec.rb +13 -0
  40. data/spec/formatter/inspect_formatter_spec.rbc +360 -0
  41. data/spec/formatter/pretty_print_formatter_spec.rb +14 -0
  42. data/spec/formatter/pretty_print_formatter_spec.rbc +380 -0
  43. data/spec/formatter/string_formatter_spec.rb +12 -0
  44. data/spec/formatter/string_formatter_spec.rbc +314 -0
  45. data/spec/formatter_spec.rb +45 -0
  46. data/spec/formatter_spec.rbc +1431 -0
  47. data/spec/log_entry_spec.rb +69 -0
  48. data/spec/logger_spec.rb +390 -0
  49. data/spec/logger_spec.rbc +10043 -0
  50. data/spec/lumberjack_spec.rb +22 -0
  51. data/spec/lumberjack_spec.rbc +523 -0
  52. data/spec/rack/unit_of_work_spec.rb +26 -0
  53. data/spec/rack/unit_of_work_spec.rbc +697 -0
  54. data/spec/severity_spec.rb +23 -0
  55. data/spec/spec_helper.rb +16 -0
  56. data/spec/spec_helper.rbc +391 -0
  57. data/spec/template_spec.rb +34 -0
  58. data/spec/template_spec.rbc +1563 -0
  59. data/spec/unique_identifier_spec.rbc +329 -0
  60. metadata +128 -0
@@ -0,0 +1,119 @@
1
+ module Lumberjack
2
+ class Device
3
+ # This logging device writes log entries as strings to an IO stream.
4
+ class Writer < Device
5
+ DEFAULT_FIRST_LINE_TEMPLATE = "[:time :severity :progname(:pid) #:unit_of_work_id] :message".freeze
6
+ DEFAULT_ADDITIONAL_LINES_TEMPLATE = "#{Lumberjack::LINE_SEPARATOR}> [#:unit_of_work_id] :message".freeze
7
+
8
+ # The size of the internal buffer. Defaults to 32K.
9
+ attr_accessor :buffer_size
10
+
11
+ # Internal buffer to batch writes to the stream.
12
+ class Buffer # :nodoc:
13
+ attr_reader :size
14
+
15
+ def initialize
16
+ @values = []
17
+ @size = 0
18
+ end
19
+
20
+ def <<(string)
21
+ @values << string
22
+ @size += string.size
23
+ end
24
+
25
+ def empty?
26
+ @values.empty?
27
+ end
28
+
29
+ def join(delimiter)
30
+ @values.join(delimiter)
31
+ end
32
+
33
+ def clear
34
+ @values = []
35
+ @size = 0
36
+ end
37
+ end
38
+
39
+ # Create a new device to write log entries to a stream. Entries are converted to strings
40
+ # using a Template. The template can be specified using the <tt>:template</tt> option. This can
41
+ # either be a Proc or a string that will compile into a Template object.
42
+ #
43
+ # If the template is a Proc, it should accept an LogEntry as its only argument and output a string.
44
+ #
45
+ # If the template is a template string, it will be used to create a Template. The
46
+ # <tt>:additional_lines</tt> and <tt>:time_format</tt> options will be passed through to the
47
+ # Template constuctor.
48
+ #
49
+ # The default template is <tt>"[:time :severity :progname(:pid) #:unit_of_work_id] :message"</tt>
50
+ # with additional lines formatted as <tt>"\n [#:unit_of_work_id] :message"</tt>. The unit of
51
+ # work id will only appear if it is present.
52
+ def initialize(stream, options = {})
53
+ @lock = Mutex.new
54
+ @stream = stream
55
+ @stream.sync = true if @stream.respond_to?(:sync=)
56
+ @buffer = Buffer.new
57
+ @buffer_size = (options[:buffer_size] || 32 * 1024)
58
+ template = (options[:template] || DEFAULT_FIRST_LINE_TEMPLATE)
59
+ if template.respond_to?(:call)
60
+ @template = template
61
+ else
62
+ additional_lines = (options[:additional_lines] || DEFAULT_ADDITIONAL_LINES_TEMPLATE)
63
+ @template = Template.new(template, :additional_lines => additional_lines, :time_format => options[:time_format])
64
+ end
65
+ end
66
+
67
+ # Write an entry to the stream. The entry will be converted into a string using the defined template.
68
+ def write(entry)
69
+ string = @template.call(entry)
70
+ @lock.synchronize do
71
+ @buffer << string
72
+ end
73
+ flush if @buffer.size >= buffer_size
74
+ end
75
+
76
+ # Close the underlying stream.
77
+ def close
78
+ flush
79
+ stream.close
80
+ end
81
+
82
+ # Flush the underlying stream.
83
+ def flush
84
+ @lock.synchronize do
85
+ before_flush
86
+ unless @buffer.empty?
87
+ out = @buffer.join(Lumberjack::LINE_SEPARATOR) << Lumberjack::LINE_SEPARATOR
88
+ begin
89
+ stream.write(out)
90
+ stream.flush
91
+ rescue => e
92
+ $stderr.write("#{e.class.name}: #{e.message}#{' at ' + e.backtrace.first if e.backtrace}")
93
+ $stderr.write(out)
94
+ $stderr.flush
95
+ end
96
+ @buffer.clear
97
+ end
98
+ end
99
+ end
100
+
101
+ protected
102
+
103
+ # Callback method that will be executed before data is written to the stream. Subclasses
104
+ # can override this method if needed.
105
+ def before_flush
106
+ end
107
+
108
+ # Set the underlying stream.
109
+ def stream=(stream)
110
+ @stream = stream
111
+ end
112
+
113
+ # Get the underlying stream.
114
+ def stream
115
+ @stream
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,76 @@
1
+ module Lumberjack
2
+ # This class controls the conversion of log entry messages into strings. This allows you
3
+ # to log any object you want and have the logging system worry about converting it into a string.
4
+ #
5
+ # Formats are added to a Formatter by associating them with a class using the +add+ method. Formats
6
+ # are any object that responds to the +call+ method.
7
+ #
8
+ # By default, all object will be converted to strings using their inspect method except for Strings
9
+ # and Exceptions. Strings are not converted and Exceptions are converted using the ExceptionFormatter.
10
+ class Formatter
11
+ autoload :ExceptionFormatter, File.expand_path("../formatter/exception_formatter.rb", __FILE__)
12
+ autoload :InspectFormatter, File.expand_path("../formatter/inspect_formatter.rb", __FILE__)
13
+ autoload :PrettyPrintFormatter, File.expand_path("../formatter/pretty_print_formatter.rb", __FILE__)
14
+ autoload :StringFormatter, File.expand_path("../formatter/string_formatter.rb", __FILE__)
15
+
16
+ def initialize
17
+ @class_formatters = {}
18
+ @default_formatter = InspectFormatter.new
19
+ add(Object, @default_formatter)
20
+ add(String, :string)
21
+ add(Exception, :exception)
22
+ end
23
+
24
+ # Add a formatter for a class. The formatter can be specified as either an object
25
+ # that responds to the +call+ method or as a symbol representing one of the predefined
26
+ # formatters, or as a block to the method call.
27
+ #
28
+ # The predefined formatters are: <tt>:inspect</tt>, <tt>:string</tt>, <tt>:exception</tt>, and <tt>:pretty_print</tt>.
29
+ #
30
+ # === Examples
31
+ #
32
+ # # Use a predefined formatter
33
+ # formatter.add(MyClass, :pretty_print)
34
+ #
35
+ # # Pass in a formatter object
36
+ # formatter.add(MyClass, Lumberjack::Formatter::PrettyPrintFormatter.new)
37
+ #
38
+ # # Use a block
39
+ # formatter.add(MyClass){|obj| obj.humanize}
40
+ #
41
+ # # Add statements can be chained together
42
+ # formatter.add(MyClass, :pretty_print).add(YourClass){|obj| obj.humanize}
43
+ def add(klass, formatter = nil, &block)
44
+ formatter ||= block
45
+ if formatter.is_a?(Symbol)
46
+ formatter_class_name = "#{formatter.to_s.gsub(/(^|_)([a-z])/){|m| $~[2].upcase}}Formatter"
47
+ formatter = Formatter.const_get(formatter_class_name).new
48
+ end
49
+ @class_formatters[klass] = formatter
50
+ self
51
+ end
52
+
53
+ # Remove the formatter associated with a class. Remove statements can be chained together.
54
+ def remove(klass)
55
+ @class_formatters.delete(klass)
56
+ self
57
+ end
58
+
59
+ # Format a message object as a string.
60
+ def format(message)
61
+ formatter_for(message.class).call(message)
62
+ end
63
+
64
+ private
65
+
66
+ # Find the formatter for a class by looking it up using the class hierarchy.
67
+ def formatter_for(klass) #:nodoc:
68
+ while klass != nil do
69
+ formatter = @class_formatters[klass]
70
+ return formatter if formatter
71
+ klass = klass.superclass
72
+ end
73
+ @default_formatter
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,12 @@
1
+ module Lumberjack
2
+ class Formatter
3
+ # Format an exception including the backtrace.
4
+ class ExceptionFormatter
5
+ def call(exception)
6
+ message = "#{exception.class.name}: #{exception.message}"
7
+ message << "#{Lumberjack::LINE_SEPARATOR} #{exception.backtrace.join("#{Lumberjack::LINE_SEPARATOR} ")}" if exception.backtrace
8
+ message
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ module Lumberjack
2
+ class Formatter
3
+ # Format an object by calling +inspect+ on it.
4
+ class InspectFormatter
5
+ def call(obj)
6
+ obj.inspect
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,23 @@
1
+ require 'pp'
2
+ require 'stringio'
3
+
4
+ module Lumberjack
5
+ class Formatter
6
+ # Format an object with it's pretty print method.
7
+ class PrettyPrintFormatter
8
+ attr_accessor :width
9
+
10
+ # Create a new formatter. The maximum width of the message can be specified with the width
11
+ # parameter (defaults to 79 characters).
12
+ def initialize(width = 79)
13
+ @width = width
14
+ end
15
+
16
+ def call(obj)
17
+ s = StringIO.new
18
+ PP.pp(obj, s)
19
+ s.string.chomp
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,10 @@
1
+ module Lumberjack
2
+ class Formatter
3
+ # Format an object by calling +to_s+ on it.
4
+ class StringFormatter
5
+ def call(obj)
6
+ obj.to_s
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,36 @@
1
+ module Lumberjack
2
+ # An entry in a log is a data structure that captures the log message as well as
3
+ # information about the system that logged the message.
4
+ class LogEntry
5
+ attr_accessor :time, :message, :severity, :progname, :pid, :unit_of_work_id
6
+
7
+ TIME_FORMAT = "%Y-%m-%dT%H:%M:%S".freeze
8
+
9
+ def initialize(time, severity, message, progname, pid, unit_of_work_id)
10
+ @time = time
11
+ @severity = (severity.is_a?(Fixnum) ? severity : Severity.label_to_level(severity))
12
+ @message = message
13
+ @progname = progname
14
+ @pid = pid
15
+ @unit_of_work_id = unit_of_work_id
16
+ end
17
+
18
+ def severity_label
19
+ Severity.level_to_label(severity)
20
+ end
21
+
22
+ def to_s
23
+ buf = "[#{time.strftime(TIME_FORMAT)}.#{(time.usec / 1000.0).round.to_s.rjust(3, '0')} #{severity_label} #{progname}(#{pid})"
24
+ if unit_of_work_id
25
+ buf << " #"
26
+ buf << unit_of_work_id
27
+ end
28
+ buf << "] "
29
+ buf << message
30
+ end
31
+
32
+ def inspect
33
+ to_s
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,302 @@
1
+ module Lumberjack
2
+ # Logger is a thread safe logging object. It has a compatible API with the Ruby
3
+ # standard library Logger class, the Log4r gem, and ActiveSupport::BufferedLogger.
4
+ #
5
+ # === Example
6
+ #
7
+ # logger = Lumberjack::Logger.new
8
+ # logger.info("Starting processing")
9
+ # logger.debug("Processing options #{options.inspect}")
10
+ # logger.fatal("OMG the application is on fire!")
11
+ #
12
+ # Log entries are written to a logging Device if their severity meets or exceeds the log level.
13
+ #
14
+ # Devices may use buffers internally and the log entries are not guaranteed to be written until you call
15
+ # the +flush+ method. Sometimes this can result in problems when trying to track down extraordinarily
16
+ # long running sections of code since it is likely that none of the messages logged before the long
17
+ # running code will appear in the log until the entire process finishes. You can set the +:flush_seconds+
18
+ # option on the constructor to force the device to be flushed periodically. This will create a new
19
+ # monitoring thread, but its use is highly recommended.
20
+ #
21
+ # Each log entry records the log message and severity along with the time it was logged, the
22
+ # program name, process id, and unit of work id. The message will be converted to a string, but
23
+ # otherwise, it is up to the device how these values are recorded. Messages are converted to strings
24
+ # using a Formatter associated with the logger.
25
+ class Logger
26
+ include Severity
27
+
28
+ # The Formatter object used to convert messages into strings.
29
+ attr_reader :formatter
30
+
31
+ # The time that the device was last flushed.
32
+ attr_reader :last_flushed_at
33
+
34
+ # The name of the program associated with log messages.
35
+ attr_writer :progname
36
+
37
+ # The device being written to.
38
+ attr_reader :device
39
+
40
+ # Set +silencer+ to false to disable silencing the log.
41
+ attr_accessor :silencer
42
+
43
+ # Create a new logger to log to a Device.
44
+ #
45
+ # The +device+ argument can be in any one of several formats.
46
+ #
47
+ # If it is a Device object, that object will be used.
48
+ # If it has a +write+ method, it will be wrapped in a Device::Writer class.
49
+ # If it is <tt>:null</tt>, it will be a Null device that won't record any output.
50
+ # Otherwise, it will be assumed to be file path and wrapped in a Device::LogFile class.
51
+ #
52
+ # This method can take the following options:
53
+ #
54
+ # * <tt>:level</tt> - The logging level below which messages will be ignored.
55
+ # * <tt>:progname</tt> - The name of the program that will be recorded with each log entry.
56
+ # * <tt>:flush_seconds</tt> - The maximum number of seconds between flush calls.
57
+ # * <tt>:roll</tt> - If the log device is a file path, it will be a Device::DateRollingLogFile if this is set.
58
+ # * <tt>:max_size</tt> - If the log device is a file path, it will be a Device::SizeRollingLogFile if this is set.
59
+ #
60
+ # All other options are passed to the device constuctor.
61
+ def initialize(device = STDOUT, options = {})
62
+ @thread_settings = {}
63
+
64
+ options = options.dup
65
+ self.level = options.delete(:level) || INFO
66
+ self.progname = options.delete(:progname)
67
+ max_flush_seconds = options.delete(:flush_seconds).to_f
68
+
69
+ @device = open_device(device, options)
70
+ @formatter = Formatter.new
71
+ @lock = Mutex.new
72
+ @last_flushed_at = Time.now
73
+ @silencer = true
74
+
75
+ create_flusher_thread(max_flush_seconds) if max_flush_seconds > 0
76
+ end
77
+
78
+ # Get the level of severity of entries that are logged. Entries with a lower
79
+ # severity level will be ignored.
80
+ def level
81
+ thread_local_value(:lumberjack_logger_level) || @level
82
+ end
83
+
84
+ # Add a message to the log with a given severity. The message can be either
85
+ # passed in the +message+ argument or supplied with a block. This method
86
+ # is not normally called. Instead call one of the helper functions
87
+ # +fatal+, +error+, +warn+, +info+, or +debug+.
88
+ #
89
+ # The severity can be passed in either as one of the Severity constants,
90
+ # or as a Severity label.
91
+ #
92
+ # === Example
93
+ #
94
+ # logger.add(Lumberjack::Severity::ERROR, exception)
95
+ # logger.add(Lumberjack::Severity::INFO, "Request completed")
96
+ # logger.add(:warn, "Request took a long time")
97
+ # logger.add(Lumberjack::Severity::DEBUG){"Start processing with options #{options.inspect}"}
98
+ def add(severity, message = nil, progname = nil)
99
+ severity = Severity.label_to_level(severity) if severity.is_a?(String) || severity.is_a?(Symbol)
100
+ if severity && severity >= level
101
+ time = Time.now
102
+ message = yield if message.nil? && block_given?
103
+ message = @formatter.format(message)
104
+ entry = LogEntry.new(time, severity, message, progname || self.progname, $$, Lumberjack.unit_of_work_id)
105
+ begin
106
+ device.write(entry)
107
+ rescue => e
108
+ $stderr.puts("#{e.class.name}: #{e.message}#{' at ' + e.backtrace.first if e.backtrace}")
109
+ $stderr.puts(entry.to_s)
110
+ end
111
+ end
112
+ nil
113
+ end
114
+
115
+ alias_method :log, :add
116
+
117
+ # Flush the logging device.
118
+ def flush
119
+ device.flush
120
+ @last_flushed_at = Time.now
121
+ nil
122
+ end
123
+
124
+ # Close the logging device.
125
+ def close
126
+ flush
127
+ @device.close if @device.respond_to?(:close)
128
+ end
129
+
130
+ # Log a +FATAL+ message. The message can be passed in either the +message+ argument or in a block.
131
+ def fatal(message = nil, progname = nil, &block)
132
+ add(FATAL, message, progname, &block)
133
+ end
134
+
135
+ # Return +true+ if +FATAL+ messages are being logged.
136
+ def fatal?
137
+ level <= FATAL
138
+ end
139
+
140
+ # Log an +ERROR+ message. The message can be passed in either the +message+ argument or in a block.
141
+ def error(message = nil, progname = nil, &block)
142
+ add(ERROR, message, progname, &block)
143
+ end
144
+
145
+ # Return +true+ if +ERROR+ messages are being logged.
146
+ def error?
147
+ level <= ERROR
148
+ end
149
+
150
+ # Log a +WARN+ message. The message can be passed in either the +message+ argument or in a block.
151
+ def warn(message = nil, progname = nil, &block)
152
+ add(WARN, message, progname, &block)
153
+ end
154
+
155
+ # Return +true+ if +WARN+ messages are being logged.
156
+ def warn?
157
+ level <= WARN
158
+ end
159
+
160
+ # Log an +INFO+ message. The message can be passed in either the +message+ argument or in a block.
161
+ def info(message = nil, progname = nil, &block)
162
+ add(INFO, message, progname, &block)
163
+ end
164
+
165
+ # Return +true+ if +INFO+ messages are being logged.
166
+ def info?
167
+ level <= INFO
168
+ end
169
+
170
+ # Log a +DEBUG+ message. The message can be passed in either the +message+ argument or in a block.
171
+ def debug(message = nil, progname = nil, &block)
172
+ add(DEBUG, message, progname, &block)
173
+ end
174
+
175
+ # Return +true+ if +DEBUG+ messages are being logged.
176
+ def debug?
177
+ level <= DEBUG
178
+ end
179
+
180
+ # Log a message when the severity is not known. Unknown messages will always appear in the log.
181
+ # The message can be passed in either the +message+ argument or in a block.
182
+ def unknown(message = nil, progname = nil, &block)
183
+ add(UNKNOWN, message, progname, &block)
184
+ end
185
+
186
+ alias_method :<<, :unknown
187
+
188
+ # Set the minimum level of severity of messages to log.
189
+ def level=(severity)
190
+ if severity.is_a?(Fixnum)
191
+ @level = severity
192
+ else
193
+ @level = Severity.label_to_level(severity)
194
+ end
195
+ end
196
+
197
+ # Silence the logger by setting a new log level inside a block. By default, only +ERROR+ or +FATAL+
198
+ # messages will be logged.
199
+ #
200
+ # === Example
201
+ #
202
+ # logger.level = Lumberjack::Severity::INFO
203
+ # logger.silence do
204
+ # do_something # Log level inside the block is +ERROR+
205
+ # end
206
+ def silence(temporary_level = ERROR, &block)
207
+ if silencer
208
+ push_thread_local_value(:lumberjack_logger_level, temporary_level, &block)
209
+ else
210
+ yield
211
+ end
212
+ end
213
+
214
+ # Set the program name that is associated with log messages. If a block
215
+ # is given, the program name will be valid only within the block.
216
+ def set_progname(value, &block)
217
+ if block
218
+ push_thread_local_value(:lumberjack_logger_progname, value, &block)
219
+ else
220
+ self.progname = value
221
+ end
222
+ end
223
+
224
+ # Get the program name associated with log messages.
225
+ def progname
226
+ thread_local_value(:lumberjack_logger_progname) || @progname
227
+ end
228
+
229
+ private
230
+
231
+ # Set a local value for a thread tied to this object.
232
+ def set_thread_local_value(name, value) #:nodoc:
233
+ values = Thread.current[name]
234
+ unless values
235
+ values = {}
236
+ Thread.current[name] = values
237
+ end
238
+ if value.nil?
239
+ values.delete(self)
240
+ Thread.current[name] = nil if values.empty?
241
+ else
242
+ values[self] = value
243
+ end
244
+ end
245
+
246
+ # Get a local value for a thread tied to this object.
247
+ def thread_local_value(name) #:nodoc:
248
+ values = Thread.current[name]
249
+ values[self] if values
250
+ end
251
+
252
+ # Set a local value for a thread tied to this object within a block.
253
+ def push_thread_local_value(name, value) #:nodoc:
254
+ save_val = thread_local_value(name)
255
+ set_thread_local_value(name, value)
256
+ begin
257
+ yield
258
+ ensure
259
+ set_thread_local_value(name, save_val)
260
+ end
261
+ end
262
+
263
+ # Open a logging device.
264
+ def open_device(device, options) #:nodoc:
265
+ if device.is_a?(Device)
266
+ device
267
+ elsif device.respond_to?(:write)
268
+ Device::Writer.new(device, options)
269
+ elsif device == :null
270
+ Device::Null.new
271
+ else
272
+ device = device.to_s
273
+ if options[:roll]
274
+ Device::DateRollingLogFile.new(device, options)
275
+ elsif options[:max_size]
276
+ Device::SizeRollingLogFile.new(device, options)
277
+ else
278
+ Device::LogFile.new(device, options)
279
+ end
280
+ end
281
+ end
282
+
283
+ # Create a thread that will periodically call flush.
284
+ def create_flusher_thread(flush_seconds) #:nodoc:
285
+ if flush_seconds > 0
286
+ begin
287
+ logger = self
288
+ Thread.new do
289
+ loop do
290
+ begin
291
+ sleep(flush_seconds)
292
+ logger.flush if Time.now - logger.last_flushed_at >= flush_seconds
293
+ rescue => e
294
+ STDERR.puts("Error flushing log: #{e.inspect}")
295
+ end
296
+ end
297
+ end
298
+ end
299
+ end
300
+ end
301
+ end
302
+ end