diff --git a/src/couchdb/couch_log.erl b/src/couchdb/couch_log.erl
index 2676689..03e2fa7 100644
--- a/src/couchdb/couch_log.erl
+++ b/src/couchdb/couch_log.erl
@@ -27,10 +27,12 @@
 -define(LEVEL_INFO, 2).
 -define(LEVEL_DEBUG, 1).
 
+-define(MAX_BUFFER_LEN, 64 * 1024).
+
 -record(state, {
-    fd,
     level,
-    sasl
+    sasl,
+    writer
 }).
 
 debug(Format, Args) ->
@@ -81,17 +83,21 @@ init([]) ->
     Sasl = couch_config:get("log", "include_sasl", "true") =:= "true",
 
     case ets:info(?MODULE) of
-    undefined -> ets:new(?MODULE, [named_table]);
+    undefined -> ets:new(?MODULE, [named_table, {read_concurrency, true}]);
     _ -> ok
     end,
     ets:insert(?MODULE, {level, Level}),
 
-    case file:open(Filename, [append]) of
-    {ok, Fd} ->
-        {ok, #state{fd = Fd, level = Level, sasl = Sasl}};
-    {error, eacces} ->
+    process_flag(trap_exit, true),
+    Parent = self(),
+    Writer = spawn_link(fun() -> writer(Parent, Filename) end),
+
+    receive
+    {Writer, ok} ->
+        {ok, #state{writer = Writer, level = Level, sasl = Sasl}};
+    {Writer, {error, eacces}} ->
         {stop, {file_permission_error, Filename}};
-    Error ->
+    {Writer, Error} ->
         {stop, Error}
     end.
 
@@ -143,18 +149,21 @@ handle_call({set_level_integer, NewLevel}, State) ->
     ets:insert(?MODULE, {level, NewLevel}),
     {ok, ok, State#state{level = NewLevel}}.
 
+handle_info({'EXIT', Pid, Reason}, #state{writer = Pid}) ->
+    io:format("[error] log writer died: ~p~n", [Reason]),
+    exit({log_writer_died, Reason});
 handle_info(_Info, State) ->
     {ok, State}.
 
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-terminate(_Arg, #state{fd = Fd}) ->
-    file:close(Fd).
+terminate(_Arg, #state{writer = Writer}) ->
+    Writer ! stop,
+    receive {'EXIT', Writer, _} -> ok end.
 
-log(#state{fd = Fd}, ConsoleMsg, FileMsg) ->
-    ok = io:put_chars(ConsoleMsg),
-    ok = io:put_chars(Fd, FileMsg).
+log(#state{writer = Writer}, ConsoleMsg, FileMsg) ->
+    Writer ! {log, FileMsg, ConsoleMsg}.
 
 get_log_messages(Pid, Level, Format, Args) ->
     ConsoleMsg = unicode:characters_to_binary(io_lib:format(
@@ -175,3 +184,41 @@ read(Bytes, Offset) ->
     {ok, Chunk} = file:pread(Fd, Start, LogFileSize),
     ok = file:close(Fd),
     Chunk.
+
+
+writer(Parent, Filename) ->
+    case file:open(Filename, [append, raw, binary]) of
+    {ok, Fd} ->
+        Parent ! {self(), ok},
+        writer_loop(Fd);
+    Error ->
+        Parent ! {self(), Error}
+    end.
+
+writer_loop(Fd) ->
+    receive
+    {log, FileMsg, ConsoleMsg} ->
+        ok = io:put_chars(ConsoleMsg),
+        ok = file:write(Fd, FileMsg),
+        writer_loop(Fd);
+        % writer_loop_batch(Fd, [FileMsg], [ConsoleMsg], byte_size(FileMsg));
+    stop ->
+        ok = file:close(Fd)
+    end.
+
+writer_loop_batch(Fd, AccFile, AccConsole, Len) when Len >= ?MAX_BUFFER_LEN ->
+    ok = io:put_chars(lists:reverse(AccConsole)),
+    ok = file:write(Fd, lists:reverse(AccFile)),
+    writer_loop(Fd);
+
+writer_loop_batch(Fd, AccFile, AccConsole, Len) ->
+    receive
+    {log, FileMsg, ConsoleMsg} ->
+        writer_loop_batch(
+            Fd, [FileMsg | AccFile], [ConsoleMsg | AccConsole],
+            Len + byte_size(FileMsg))
+    after 0 ->
+        ok = io:put_chars(lists:reverse(AccConsole)),
+        ok = file:write(Fd, lists:reverse(AccFile)),
+        writer_loop(Fd)
+    end.
