lumberjack_aziz_light 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/MIT_LICENSE +20 -0
  3. data/README.rdoc +100 -0
  4. data/Rakefile +40 -0
  5. data/VERSION +1 -0
  6. data/lib/lumberjack/device/date_rolling_log_file.rb +58 -0
  7. data/lib/lumberjack/device/log_file.rb +18 -0
  8. data/lib/lumberjack/device/null.rb +15 -0
  9. data/lib/lumberjack/device/rolling_log_file.rb +110 -0
  10. data/lib/lumberjack/device/size_rolling_log_file.rb +60 -0
  11. data/lib/lumberjack/device/writer.rb +129 -0
  12. data/lib/lumberjack/device.rb +26 -0
  13. data/lib/lumberjack/formatter/exception_formatter.rb +12 -0
  14. data/lib/lumberjack/formatter/inspect_formatter.rb +10 -0
  15. data/lib/lumberjack/formatter/pretty_print_formatter.rb +23 -0
  16. data/lib/lumberjack/formatter/string_formatter.rb +10 -0
  17. data/lib/lumberjack/formatter.rb +76 -0
  18. data/lib/lumberjack/log_entry.rb +36 -0
  19. data/lib/lumberjack/logger.rb +302 -0
  20. data/lib/lumberjack/rack/unit_of_work.rb +15 -0
  21. data/lib/lumberjack/rack.rb +5 -0
  22. data/lib/lumberjack/severity.rb +23 -0
  23. data/lib/lumberjack/template.rb +71 -0
  24. data/lib/lumberjack.rb +42 -0
  25. data/spec/device/date_rolling_log_file_spec.rb +66 -0
  26. data/spec/device/log_file_spec.rb +26 -0
  27. data/spec/device/null_spec.rb +12 -0
  28. data/spec/device/rolling_log_file_spec.rb +129 -0
  29. data/spec/device/size_rolling_log_file_spec.rb +54 -0
  30. data/spec/device/writer_spec.rb +118 -0
  31. data/spec/formatter/exception_formatter_spec.rb +20 -0
  32. data/spec/formatter/inspect_formatter_spec.rb +13 -0
  33. data/spec/formatter/pretty_print_formatter_spec.rb +14 -0
  34. data/spec/formatter/string_formatter_spec.rb +12 -0
  35. data/spec/formatter_spec.rb +45 -0
  36. data/spec/log_entry_spec.rb +69 -0
  37. data/spec/logger_spec.rb +390 -0
  38. data/spec/lumberjack_spec.rb +29 -0
  39. data/spec/rack/unit_of_work_spec.rb +26 -0
  40. data/spec/severity_spec.rb +23 -0
  41. data/spec/spec_helper.rb +16 -0
  42. data/spec/template_spec.rb +34 -0
  43. metadata +92 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3ae9171da4a378479133de30af3e891550667e9a
4
+ data.tar.gz: aa5e35929402d35ecff363c776c8ff2fc1620ab9
5
+ SHA512:
6
+ metadata.gz: c92d8d0fb6d685146e00c3bffa0f44abffd7d7834d155409fb82a13645dac65f32d3507d7e71c5a18cced4b4ea3a9b41be17f4f0a0de2c098a6dd00b032c9807
7
+ data.tar.gz: 65d396ab9d6e02007c0976ed95fc5c942112f7d40dd635a2fcb9bc2b988ff918b4ed8981307db214630ee4b2f2584c280bd6dfb8f9f99e6e6f0d4ad8adac1c5f
data/MIT_LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Brian Durand
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,100 @@
1
+ = Lumberjack
2
+
3
+ Lumberjack is a simple, powerful, and fast logging implementation in Ruby. It uses nearly the same API as the Logger class in the Ruby standard library and as ActiveSupport::BufferedLogger in Rails.
4
+
5
+ == Usage
6
+
7
+ This code aims to be extremely simple to use. The core interface it the Lumberjack::Logger which is used to log messages (which can be any object) with a specified Severity. Each logger has a level associated with it and messages are only written if their severity is greater than or equal to the level.
8
+
9
+ logger = Lumberjack::Logger.new("logs/application.log") # Open a new log file with INFO level
10
+ logger.info("Begin request")
11
+ logger.debug(request.params) # Message not written unless the level is set to DEBUG
12
+ begin
13
+ # do something
14
+ rescue => exception
15
+ logger.error(exception)
16
+ raise
17
+ end
18
+ logger.info("End request")
19
+
20
+ This is all you need to know to log messages.
21
+
22
+ == Features
23
+
24
+ === Meta data
25
+
26
+ When messages are added to the log, additional data about the message is kept in a Lumberjack::LogEntry. This means you don't need to worry about adding the time or process id to your log messages as they will be automatically recorded.
27
+
28
+ The following information is recorded for each message:
29
+
30
+ * severity - The severity recorded for the message.
31
+ * time - The time at which the message was recorded.
32
+ * program name - The name of the program logging the message. This can be either set for all messages or customized with each message.
33
+ * process id - The process id (pid) of the process that logged the message.
34
+ * unit of work id - The unique 12 byte hexadecimal number generated for a unit of work.
35
+
36
+ === Units Of Work
37
+
38
+ A unit of work can be used to tie together all log messages within a block. This can be very useful to isolate a group of messages that represent one path through the system. For instance, in a web application, a single request represents a natural unit of work, and when you are looking through a log file, it is useful to see the entire set of log entries as a unit instead of interspersed with messages from other concurrent requests.
39
+
40
+ # All log entries in this block will get a common unit of work id.
41
+ Lumberjack.unit_of_work do
42
+ logger.info("Begin request")
43
+ yield
44
+ logger.info("End request")
45
+ end
46
+
47
+ === Pluggable Devices
48
+
49
+ When a Logger logs a LogEntry, it sends it to a Lumberjack::Device. Lumberjack comes with a variety of devices for logging to IO streams or files.
50
+
51
+ * Lumberjack::Device::Writer - Writes log entries to an IO stream.
52
+ * Lumberjack::Device::LogFile - Writes log entries to a file.
53
+ * Lumberjack::Device::DateRollingLogFile - Writes log entries to a file that will automatically roll itself based on date.
54
+ * Lumberjack::Device::SizeRollingLogFile - Writes log entries to a file that will automatically roll itself based on size.
55
+ * Lumberjack::Device::Null - This device produces no output and is intended for testing environments.
56
+
57
+ If you'd like to send you log to a different kind of output, you just need to extend the Device class and implement the +write+ method. Or check out these plugins:
58
+
59
+ * lumberjack_syslog_device[https://github.com/bdurand/lumberjack_syslog_device] - send your log messages to the system wide syslog service
60
+ * lumberjack_mongo_device[https://github.com/bdurand/lumberjack_mongo_device] - store your log messages to a MongoDB[http://www.mongodb.org/] NoSQL data store
61
+ * lumberjack-couchdb-driver[https://github.com/narkisr/lumberjack-couchdb-driver] - store your log messages to a CouchDB[http://couchdb.apache.org/] NoSQL data store
62
+
63
+ === Customize Formatting
64
+
65
+ When a message is logged, it is first converted into a string. You can customize how it is converted by adding mappings to a Formatter.
66
+
67
+ logger.formatter.add(Hash, :pretty_print) # use the Formatter::PrettyPrintFormatter for all Hashes
68
+ logger.formatter.add(MyClass){|obj| "#{obj.class}@#{obj.id}"} # use a block to provide a custom format
69
+
70
+ If you use the built in devices, you can also customize the Template used to format the LogEntry.
71
+
72
+ # Change the format of the time in the log
73
+ Lumberjack::Logger.new("application.log", :time_format => "%m/%d/%Y %H:%M:%S")
74
+
75
+ # Use a simple template that only includes the time and the message
76
+ Lumberjack::Logger.new("application.log", :template => ":time - :message")
77
+
78
+ # Use a custom template as a block that only includes the first character of the severity
79
+ template = lambda{|e| "#{e.severity_label[0, 1]} #{e.time} - #{e.message}"}
80
+ Lumberjack::Logger.new("application.log", :template => template)
81
+
82
+ === Buffered Performance
83
+
84
+ The logger has hooks for devices that support buffering to increase performance by batching physical writes. Log entries are not guaranteed to be written until the Lumberjack::Logger#flush method is called.
85
+
86
+ You can use the <tt>:flush_seconds</tt> option on the logger to periodically flush the log. This is usually a good idea so you can more easily debug hung processes. Without periodic flushing, a process that hangs may never write anything to the log because the messages are sitting in a buffer. By turning on periodic flushing, the logged messages will be written which can greatly aid in debugging the problem.
87
+
88
+ The built in stream based logging devices use an internal buffer. The size of the buffer (in bytes) can be set with the <tt>:buffer_size</tt> options when initializing a logger. The default behavior is to not to buffer.
89
+
90
+ # Set buffer to flush after 8K has been written to the log.
91
+ logger = Lumberjack::Logger.new("application.log", :buffer_size => 8192)
92
+
93
+ # Turn off buffering so entries are immediately written to disk.
94
+ logger = Lumberjack::Logger.new("application.log", :buffer_size => 0)
95
+
96
+ === Automatic Log Rolling
97
+
98
+ The built in devices include two that can automatically roll log files based either on date or on file size. When a log file is rolled, it will be renamed with a suffix and a new file will be created to receive new log entries. This can keep your log files from growing to unusable sizes and removes the need to schedule an external process to roll the files.
99
+
100
+ There is a similar feature in the standard library Logger class, but the implementation here is safe to use with multiple processes writing to the same log file.
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rubygems/package_task'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'RVM likes to call it tests'
9
+ task :tests => :test
10
+
11
+ begin
12
+ require 'rspec'
13
+ require 'rspec/core/rake_task'
14
+ desc 'Run the unit tests'
15
+ RSpec::Core::RakeTask.new(:test)
16
+ rescue LoadError
17
+ task :test do
18
+ STDERR.puts "You must have rspec 2.0 installed to run the tests"
19
+ end
20
+ end
21
+
22
+ namespace :rbx do
23
+ desc "Cleanup *.rbc files in lib directory"
24
+ task :delete_rbc_files do
25
+ FileList["**/*.rbc"].each do |rbc_file|
26
+ File.delete(rbc_file)
27
+ end
28
+ nil
29
+ end
30
+ end
31
+
32
+ spec_file = File.expand_path('../lumberjack.gemspec', __FILE__)
33
+ if File.exist?(spec_file)
34
+ spec = eval(File.read(spec_file))
35
+
36
+ Gem::PackageTask.new(spec) do |p|
37
+ p.gem_spec = spec
38
+ end
39
+ Rake.application["package"].prerequisites.unshift("rbx:delete_rbc_files")
40
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.5
@@ -0,0 +1,58 @@
1
+ require 'date'
2
+
3
+ module Lumberjack
4
+ class Device
5
+ # This log device will append entries to a file and roll the file periodically by date. Files
6
+ # are rolled at midnight and can be rolled daily, weekly, or monthly. Archive file names will
7
+ # have the date appended to them in the format ".YYYY-MM-DD" for daily, ".week-of-YYYY-MM-DD" for weekly
8
+ # and ".YYYY-MM" for monthly. It is not guaranteed that log messages will break exactly on the
9
+ # roll period as buffered entries will always be written to the same file.
10
+ class DateRollingLogFile < RollingLogFile
11
+ # Create a new logging device to the specified file. The period to roll the file is specified
12
+ # with the <tt>:roll</tt> option which may contain a value of <tt>:daily</tt>, <tt>:weekly</tt>,
13
+ # or <tt>:monthly</tt>.
14
+ def initialize(path, options = {})
15
+ @file_date = Date.today
16
+ if options[:roll] && options[:roll].to_s.match(/(daily)|(weekly)|(monthly)/i)
17
+ @roll_period = $~[0].downcase.to_sym
18
+ options.delete(:roll)
19
+ else
20
+ raise ArgumentError.new("illegal value for :roll (#{options[:roll].inspect})")
21
+ end
22
+ super
23
+ end
24
+
25
+ def archive_file_suffix
26
+ case @roll_period
27
+ when :weekly
28
+ "#{@file_date.strftime('week-of-%Y-%m-%d')}"
29
+ when :monthly
30
+ "#{@file_date.strftime('%Y-%m')}"
31
+ else
32
+ "#{@file_date.strftime('%Y-%m-%d')}"
33
+ end
34
+ end
35
+
36
+ def roll_file?
37
+ date = Date.today
38
+ if date.year > @file_date.year
39
+ true
40
+ elsif @roll_period == :daily && date.yday > @file_date.yday
41
+ true
42
+ elsif @roll_period == :weekly && date.cweek > @file_date.cweek
43
+ true
44
+ elsif @roll_period == :monthly && date.month > @file_date.month
45
+ true
46
+ else
47
+ false
48
+ end
49
+ end
50
+
51
+ protected
52
+
53
+ def after_roll
54
+ @file_date = Date.today
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,18 @@
1
+ require 'fileutils'
2
+
3
+ module Lumberjack
4
+ class Device
5
+ # This is a logging device that appends log entries to a file.
6
+ class LogFile < Writer
7
+ # The absolute path of the file being logged to.
8
+ attr_reader :path
9
+
10
+ # Create a logger to the file at +path+. Options are passed through to the Writer constructor.
11
+ def initialize(path, options = {})
12
+ @path = File.expand_path(path)
13
+ FileUtils.mkdir_p(File.dirname(@path))
14
+ super(File.new(@path, 'a', :encoding => "ascii-8bit"), options)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ require 'date'
2
+
3
+ module Lumberjack
4
+ class Device
5
+ # This is a logging device that produces no output. It can be useful in
6
+ # testing environments when log file output is not useful.
7
+ class Null < Device
8
+ def initialize(*args)
9
+ end
10
+
11
+ def write(entry)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,110 @@
1
+ module Lumberjack
2
+ class Device
3
+ # This is an abstract class for a device that appends entries to a file and periodically archives
4
+ # the existing file and starts a one. Subclasses must implement the roll_file? and archive_file_suffix
5
+ # methods.
6
+ #
7
+ # The <tt>:keep</tt> option can be used to specify a maximum number of rolled log files to keep.
8
+ # Older files will be deleted based on the time they were created. The default is to keep all files.
9
+ class RollingLogFile < LogFile
10
+ attr_reader :path
11
+ attr_accessor :keep
12
+
13
+ def initialize(path, options = {})
14
+ @path = File.expand_path(path)
15
+ @keep = options[:keep]
16
+ super(path, options)
17
+ @file_inode = stream.lstat.ino rescue nil
18
+ @@rolls = []
19
+ end
20
+
21
+ # Returns a suffix that will be appended to the file name when it is archived.. The suffix should
22
+ # change after it is time to roll the file. The log file will be renamed when it is rolled.
23
+ def archive_file_suffix
24
+ raise NotImplementedError
25
+ end
26
+
27
+ # Return +true+ if the file should be rolled.
28
+ def roll_file?
29
+ raise NotImplementedError
30
+ end
31
+
32
+ # Roll the log file by renaming it to the archive file name and then re-opening a stream to the log
33
+ # file path. Rolling a file is safe in multi-threaded or multi-process environments.
34
+ def roll_file! #:nodoc:
35
+ do_once(stream) do
36
+ archive_file = "#{path}.#{archive_file_suffix}"
37
+ stream.flush
38
+ current_inode = File.stat(path).ino rescue nil
39
+ if @file_inode && current_inode == @file_inode && !File.exist?(archive_file) && File.exist?(path)
40
+ begin
41
+ File.rename(path, archive_file)
42
+ after_roll
43
+ cleanup_files!
44
+ rescue SystemCallError
45
+ # Ignore rename errors since it indicates the file was already rolled
46
+ end
47
+ end
48
+ reopen_file
49
+ end
50
+ rescue => e
51
+ STDERR.write("Failed to roll file #{path}: #{e.inspect}\n#{e.backtrace.join("\n")}\n")
52
+ end
53
+
54
+ protected
55
+
56
+ # This method will be called after a file has been rolled. Subclasses can
57
+ # implement code to reset the state of the device. This method is thread safe.
58
+ def after_roll
59
+ end
60
+
61
+ # Handle rolling the file before flushing.
62
+ def before_flush # :nodoc:
63
+ path_inode = File.lstat(path).ino rescue nil
64
+ if path_inode != @file_inode
65
+ @file_inode = path_inode
66
+ reopen_file
67
+ else
68
+ roll_file! if roll_file?
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def reopen_file
75
+ old_stream = stream
76
+ self.stream = File.open(path, 'a')
77
+ stream.sync = true
78
+ @file_inode = stream.lstat.ino rescue nil
79
+ old_stream.close
80
+ end
81
+ end
82
+
83
+ def cleanup_files!
84
+ if keep
85
+ files = Dir.glob("#{path}.*").collect{|f| [f, File.ctime(f)]}.sort{|a,b| b.last <=> a.last}.collect{|a| a.first}
86
+ if files.size > keep
87
+ files[keep, files.length].each do |f|
88
+ File.delete(f)
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ def do_once(file)
95
+ begin
96
+ file.flock(File::LOCK_EX)
97
+ rescue SystemCallError
98
+ # Most likely can't lock file because the stream is closed
99
+ return
100
+ end
101
+ begin
102
+ verify = file.lstat rescue nil
103
+ # Execute only if the file we locked is still the same one that needed to be rolled
104
+ yield if verify && verify.ino == @file_inode && verify.size > 0
105
+ ensure
106
+ file.flock(File::LOCK_UN) rescue nil
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,60 @@
1
+ module Lumberjack
2
+ class Device
3
+ # This is a log device that appends entries to a file and rolls the file when it reaches a specified
4
+ # size threshold. When a file is rolled, it will have an number extension appended to the file name.
5
+ # For example, if the log file is named production.log, the first time it is rolled it will be renamed
6
+ # production.log.1, then production.log.2, etc.
7
+ class SizeRollingLogFile < RollingLogFile
8
+ attr_reader :max_size
9
+
10
+ # Create an new log device to the specified file. The maximum size of the log file is specified with
11
+ # the <tt>:max_size</tt> option. The unit can also be specified: "32K", "100M", "2G" are all valid.
12
+ def initialize(path, options = {})
13
+ @max_size = options[:max_size]
14
+ if @max_size.is_a?(String)
15
+ if @max_size.match(/^(\d+(\.\d+)?)([KMG])?$/i)
16
+ @max_size = $~[1].to_f
17
+ units = $~[3].to_s.upcase
18
+ case units
19
+ when "K"
20
+ @max_size *= 1024
21
+ when "M"
22
+ @max_size *= 1024 ** 2
23
+ when "G"
24
+ @max_size *= 1024 ** 3
25
+ end
26
+ @max_size = @max_size.round
27
+ else
28
+ raise ArgumentError.new("illegal value for :max_size (#{@max_size})")
29
+ end
30
+ end
31
+
32
+ super
33
+ end
34
+
35
+ def archive_file_suffix
36
+ next_archive_number.to_s
37
+ end
38
+
39
+ def roll_file?
40
+ stream.stat.size > @max_size
41
+ rescue SystemCallError => e
42
+ false
43
+ end
44
+
45
+ protected
46
+
47
+ # Calculate the next archive file name extension.
48
+ def next_archive_number # :nodoc:
49
+ max = 0
50
+ Dir.glob("#{path}.*").each do |filename|
51
+ if filename.match(/\.\d+$/)
52
+ suffix = filename.split('.').last.to_i
53
+ max = suffix if suffix > max
54
+ end
55
+ end
56
+ max + 1
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,129 @@
1
+ module Lumberjack
2
+ class Device
3
+ # This logging device writes log entries as strings to an IO stream. By default, messages will be buffered
4
+ # and written to the stream in a batch when the buffer is full or when +flush+ is called.
5
+ class Writer < Device
6
+ DEFAULT_FIRST_LINE_TEMPLATE = "[:time :severity :progname(:pid) #:unit_of_work_id] :message".freeze
7
+ DEFAULT_ADDITIONAL_LINES_TEMPLATE = "#{Lumberjack::LINE_SEPARATOR}> [#:unit_of_work_id] :message".freeze
8
+
9
+ # The size of the internal buffer. Defaults to 32K.
10
+ attr_reader :buffer_size
11
+
12
+ # Internal buffer to batch writes to the stream.
13
+ class Buffer # :nodoc:
14
+ attr_reader :size
15
+
16
+ def initialize
17
+ @values = []
18
+ @size = 0
19
+ end
20
+
21
+ def <<(string)
22
+ @values << string
23
+ @size += string.size
24
+ end
25
+
26
+ def empty?
27
+ @values.empty?
28
+ end
29
+
30
+ def join(delimiter)
31
+ @values.join(delimiter)
32
+ end
33
+
34
+ def clear
35
+ @values = []
36
+ @size = 0
37
+ end
38
+ end
39
+
40
+ # Create a new device to write log entries to a stream. Entries are converted to strings
41
+ # using a Template. The template can be specified using the <tt>:template</tt> option. This can
42
+ # either be a Proc or a string that will compile into a Template object.
43
+ #
44
+ # If the template is a Proc, it should accept an LogEntry as its only argument and output a string.
45
+ #
46
+ # If the template is a template string, it will be used to create a Template. The
47
+ # <tt>:additional_lines</tt> and <tt>:time_format</tt> options will be passed through to the
48
+ # Template constuctor.
49
+ #
50
+ # The default template is <tt>"[:time :severity :progname(:pid) #:unit_of_work_id] :message"</tt>
51
+ # with additional lines formatted as <tt>"\n [#:unit_of_work_id] :message"</tt>. The unit of
52
+ # work id will only appear if it is present.
53
+ #
54
+ # The size of the internal buffer in bytes can be set by providing <tt>:buffer_size</tt> (defaults to 32K).
55
+ def initialize(stream, options = {})
56
+ @lock = Mutex.new
57
+ @stream = stream
58
+ @stream.sync = true if @stream.respond_to?(:sync=)
59
+ @buffer = Buffer.new
60
+ @buffer_size = (options[:buffer_size] || 0)
61
+ template = (options[:template] || DEFAULT_FIRST_LINE_TEMPLATE)
62
+ if template.respond_to?(:call)
63
+ @template = template
64
+ else
65
+ additional_lines = (options[:additional_lines] || DEFAULT_ADDITIONAL_LINES_TEMPLATE)
66
+ @template = Template.new(template, :additional_lines => additional_lines, :time_format => options[:time_format])
67
+ end
68
+ end
69
+
70
+ # Set the buffer size in bytes. The device will only be physically written to when the buffer size
71
+ # is exceeded.
72
+ def buffer_size=(value)
73
+ @buffer_size = value
74
+ flush
75
+ end
76
+
77
+ # Write an entry to the stream. The entry will be converted into a string using the defined template.
78
+ def write(entry)
79
+ string = @template.call(entry)
80
+ @lock.synchronize do
81
+ @buffer << string
82
+ end
83
+ flush if @buffer.size >= buffer_size
84
+ end
85
+
86
+ # Close the underlying stream.
87
+ def close
88
+ flush
89
+ stream.close
90
+ end
91
+
92
+ # Flush the underlying stream.
93
+ def flush
94
+ @lock.synchronize do
95
+ before_flush
96
+ unless @buffer.empty?
97
+ out = @buffer.join(Lumberjack::LINE_SEPARATOR) << Lumberjack::LINE_SEPARATOR
98
+ begin
99
+ stream.write(out)
100
+ stream.flush
101
+ rescue => e
102
+ $stderr.write("#{e.class.name}: #{e.message}#{' at ' + e.backtrace.first if e.backtrace}")
103
+ $stderr.write(out)
104
+ $stderr.flush
105
+ end
106
+ @buffer.clear
107
+ end
108
+ end
109
+ end
110
+
111
+ protected
112
+
113
+ # Callback method that will be executed before data is written to the stream. Subclasses
114
+ # can override this method if needed.
115
+ def before_flush
116
+ end
117
+
118
+ # Set the underlying stream.
119
+ def stream=(stream)
120
+ @stream = stream
121
+ end
122
+
123
+ # Get the underlying stream.
124
+ def stream
125
+ @stream
126
+ end
127
+ end
128
+ end
129
+ end