--- Revision None +++ Revision 353336333637 @@ -0,0 +1,116 @@ +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.