<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,6 +1,7 @@
 === 0.0.5 / 2009-11-08
 
 * API CHANGE: Reworked how string encoding is specified. Default encoding for both reading and writing is now ASCII-8BIT unless specified in the API or within the file as a parser directive.
+* Handle encoding errors more gracefully by replacing characters rather than raising exceptions.
 * Added a parser directive to set logfile encoding.
 * Versionomy is no longer a hard dependency-- it is now used only if available.
 </diff>
      <filename>History.rdoc</filename>
    </modified>
    <modified>
      <diff>@@ -267,6 +267,8 @@ module Sawmill
     #   Specify an encoding for file data. (Ruby 1.9 only.)
     #   You may specify an encoding name or an encoding object.
     #   If not specified, reads raw bytes (e.g. defaults to 'ASCII-8BIT').
+    #   Note that the encoding may also be modified by the file itself,
+    #   if an appropriate parser directive is encountered.
     # &lt;tt&gt;:internal_encoding&lt;/tt&gt;::
     #   Specify an encoding to transcode to. (Ruby 1.9 only.)
     #   You may specify an encoding name or an encoding object.
@@ -298,6 +300,8 @@ module Sawmill
     #   Specify an encoding for file data. (Ruby 1.9 only.)
     #   You may specify an encoding name or an encoding object.
     #   If not specified, reads raw bytes (e.g. defaults to 'ASCII-8BIT').
+    #   Note that the encoding may also be modified by the file itself,
+    #   if an appropriate parser directive is encountered.
     # &lt;tt&gt;:internal_encoding&lt;/tt&gt;::
     #   Specify an encoding to transcode to. (Ruby 1.9 only.)
     #   You may specify an encoding name or an encoding object.
@@ -332,6 +336,8 @@ module Sawmill
     #   Specify an encoding for file data. (Ruby 1.9 only.)
     #   You may specify an encoding name or an encoding object.
     #   If not specified, reads raw bytes (e.g. defaults to 'ASCII-8BIT').
+    #   Note that the encoding may also be modified by the file itself,
+    #   if an appropriate parser directive is encountered.
     # &lt;tt&gt;:internal_encoding&lt;/tt&gt;::
     #   Specify an encoding to transcode to. (Ruby 1.9 only.)
     #   You may specify an encoding name or an encoding object.
@@ -353,10 +359,7 @@ module Sawmill
         end
         if encoding_
           mode_ &lt;&lt; &quot;:#{encoding_.name}&quot;
-        elsif internal_encoding_
-          mode_ &lt;&lt; &quot;:#{::Encoding.default_external.name}&quot;
         end
-        mode_ &lt;&lt; &quot;:#{internal_encoding_.name}&quot; if internal_encoding_
       else
         encoding_ = nil
         internal_encoding_ = nil
@@ -376,7 +379,7 @@ module Sawmill
             else
               io_array_ &lt;&lt; ::File.open(path_, mode_)
               encoding_array_ &lt;&lt; nil
-              internal_encoding_array_ &lt;&lt; nil
+              internal_encoding_array_ &lt;&lt; internal_encoding_
             end
           end
         end</diff>
      <filename>lib/sawmill/interface.rb</filename>
    </modified>
    <modified>
      <diff>@@ -47,6 +47,7 @@ module Sawmill
     DIRECTIVE_REGEXP = /^#\s+sawmill_format:\s+(\w+)=(.*)$/
     ATTRIBUTE_REGEXP = /^([[:graph:]]+)\s([=+\/-])\s/
     SUPPORTS_ENCODING = defined?(::Encoding)
+    ENCODING_OPTS = {:invalid =&gt; :replace, :undef =&gt; :replace}
     # :startdoc:
     
     
@@ -197,7 +198,7 @@ module Sawmill
       str_ = @io.gets
       if str_ &amp;&amp; SUPPORTS_ENCODING
         str_.force_encoding(@encoding) if @encoding
-        str_.encode!(@internal_encoding) if @internal_encoding
+        str_.encode!(@internal_encoding, ENCODING_OPTS) if @internal_encoding
       end
       str_
     end</diff>
      <filename>lib/sawmill/parser.rb</filename>
    </modified>
    <modified>
      <diff>@@ -59,6 +59,11 @@ module Sawmill
   
   class Rotater
     
+    # :stopdoc:
+    SUPPORTS_ENCODING = defined?(::Encoding)
+    ENCODING_OPTS = {:invalid =&gt; :replace, :undef =&gt; :replace}
+    # :startdoc:
+    
     
     # Create a rotater using the given rotation strategy.
     # See Sawmill::Rotater::DateBasedLogFile and
@@ -70,8 +75,26 @@ module Sawmill
     # 
     # &lt;tt&gt;:omit_directives&lt;/tt&gt;::
     #   If true, omit standard logfile directives. Default is false.
+    # &lt;tt&gt;:concurrent_writes&lt;/tt&gt;::
+    #   Set this to true if you expect multiple processes to attempt to
+    #   write to the same log file simultaneously. This option causes the
+    #   rotater to surround writes with an acquisition of the cooperative
+    #   filesystem lock (if available) for the logfile, in an attempt to
+    #   prevent lines from interleaving in one another. Default is false.
+    # &lt;tt&gt;:encoding&lt;/tt&gt;::
+    #   Specify an encoding for file data. (Ruby 1.9 only).
+    #   You may pass either an encoding object or an encoding name.
+    #   If not specified, writes raw bytes (e.g. defaults to ASCII-8BIT).
     
     def initialize(io_manager_, opts_={})
+      @omit_directives = opts_.delete(:omit_directives)
+      @concurrent_writes = opts_.delete(:concurrent_writes)
+      if SUPPORTS_ENCODING
+        @encoding = opts_.delete(:encoding)
+        if @encoding &amp;&amp; !@encoding.respond_to?(:name)
+          @encoding = ::Encoding.find(@encoding)
+        end
+      end
       if io_manager_.kind_of?(::Class)
         @io_manager = io_manager_.new(opts_)
       else
@@ -98,6 +121,23 @@ module Sawmill
     end
     
     
+    def _write_to_stream(io_, str_)
+      if SUPPORTS_ENCODING &amp;&amp; @encoding
+        str_ = str_.encode(@encoding, ENCODING_OPTS)
+      end
+      if @concurrent_writes
+        begin
+          io_.flock(::File::LOCK_EX)
+          io_.write(str_)
+        ensure
+          io_.flock(::File::LOCK_UN)
+        end
+      else
+        io_.write(str_)
+      end
+    end
+    
+    
     def _obtain_handle  # :nodoc:
       handle_ = @io_manager.preferred_handle
       if @handles.include?(handle_)
@@ -105,11 +145,11 @@ module Sawmill
       else
         io_ = @io_manager.open_handle(handle_)
         unless @omit_directives
-          io_.write(&quot;# sawmill_format: version=1\n&quot;)
+          _write_to_stream(io_, &quot;# sawmill_format: version=1\n&quot;)
           if defined?(::Encoding)
             encoding_ = io_.encoding
             if encoding_
-              io_.write(&quot;# sawmill_format: encoding=#{encoding_.name}\n&quot;)
+              _write_to_stream(io_, &quot;# sawmill_format: encoding=#{encoding_.name}\n&quot;)
             end
           end
         end
@@ -154,7 +194,7 @@ module Sawmill
           handle_ = _check_rotate_handle(handle_)
         end
         info_ = @handles[handle_]
-        info_[1].write(str_)
+        _write_to_stream(info_[1], str_)
         handle_
       end
     end</diff>
      <filename>lib/sawmill/rotater.rb</filename>
    </modified>
    <modified>
      <diff>@@ -79,9 +79,6 @@ module Sawmill
       # &lt;tt&gt;:local_datestamps&lt;/tt&gt;::
       #   If true, use the local timezone to create datestamps.
       #   The default is to use UTC.
-      # &lt;tt&gt;:encoding&lt;/tt&gt;::
-      #   Specify an encoding name for file data. (Ruby 1.9 only).
-      #   If not specified, writes raw bytes (e.g. defaults to ASCII-8BIT).
       
       def initialize(options_)
         @turnover_frequency = options_[:turnover_frequency] || :none
@@ -97,10 +94,6 @@ module Sawmill
           when :hourly then &quot;%Y-%m-%d-%H&quot;
           else nil
           end
-        @mode = 'a'
-        if defined?(::Encoding) &amp;&amp; (encoding_ = options_[:encoding])
-          @mode &lt;&lt; &quot;:#{encoding_}&quot;
-        end
       end
       
       
@@ -125,7 +118,7 @@ module Sawmill
         else
           path_ = @prefix+@suffix
         end
-        file_ = ::File.open(path_, @mode)
+        file_ = ::File.open(path_, 'a')
         file_.sync = true
         file_
       end</diff>
      <filename>lib/sawmill/rotater/date_based_log_file.rb</filename>
    </modified>
    <modified>
      <diff>@@ -76,9 +76,6 @@ module Sawmill
       #   The maximum number of old logfiles (files with indexes) to
       #   keep. Files beyond this history size will be automatically
       #   deleted. Default is 1. This value must be at least 1.
-      # &lt;tt&gt;:encoding&lt;/tt&gt;::
-      #   Specify an encoding name for file data. (Ruby 1.9 only).
-      #   If not specified, writes raw bytes (e.g. defaults to ASCII-8BIT).
       
       def initialize(options_)
         @max_logfile_size = options_[:max_file_size] || options_[:max_logfile_size]
@@ -100,10 +97,6 @@ module Sawmill
         @preferred_handle = 0
         @open_handles = {}
         @last_shift = ::Time.now
-        @mode = 'a'
-        if defined?(::Encoding) &amp;&amp; (encoding_ = options_[:encoding])
-          @mode &lt;&lt; &quot;:#{encoding_}&quot;
-        end
       end
       
       
@@ -122,7 +115,7 @@ module Sawmill
         else
           path_ = &quot;#{@normal_path}.#{@preferred_handle-handle_-1}&quot;
         end
-        file_ = ::File.open(path_, @mode)
+        file_ = ::File.open(path_, 'a')
         file_.sync = true
         @open_handles[handle_] = true
         file_</diff>
      <filename>lib/sawmill/rotater/shifting_log_file.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>906581908884d55f504890a2b8cdb47fc786abc5</id>
    </parent>
  </parents>
  <author>
    <name>Daniel Azuma</name>
    <email>dazuma@gmail.com</email>
  </author>
  <url>http://github.com/dazuma/sawmill/commit/89b213963dc831bf72d4065d1cd9ec5a72172edf</url>
  <id>89b213963dc831bf72d4065d1cd9ec5a72172edf</id>
  <committed-date>2009-11-08T17:18:48-08:00</committed-date>
  <authored-date>2009-11-08T17:18:48-08:00</authored-date>
  <message>Some more encoding work</message>
  <tree>66022200f875dacf4d4f825c28fabb0666a346c5</tree>
  <committer>
    <name>Daniel Azuma</name>
    <email>dazuma@gmail.com</email>
  </committer>
</commit>
