X9tX1LCb9Mn6SiTueFlXY changeset

Changeset353336333637 (b)
ParentNone (a)
ab
0+diff --git a/src/couchdb/couch_log.erl b/src/couchdb/couch_log.erl
0+index 2676689..03e2fa7 100644
0+--- a/src/couchdb/couch_log.erl
0++++ b/src/couchdb/couch_log.erl
0+@@ -27,10 +27,12 @@
0+ -define(LEVEL_INFO, 2).
0+ -define(LEVEL_DEBUG, 1).
0+ 
0++-define(MAX_BUFFER_LEN, 64 * 1024).
0++
0+ -record(state, {
0+-    fd,
0+     level,
0+-    sasl
0++    sasl,
0++    writer
0+ }).
0+ 
0+ debug(Format, Args) ->
0+@@ -81,17 +83,21 @@ init([]) ->
0+     Sasl = couch_config:get("log", "include_sasl", "true") =:= "true",
0+ 
0+     case ets:info(?MODULE) of
0+-    undefined -> ets:new(?MODULE, [named_table]);
0++    undefined -> ets:new(?MODULE, [named_table, {read_concurrency, true}]);
0+     _ -> ok
0+     end,
0+     ets:insert(?MODULE, {level, Level}),
0+ 
0+-    case file:open(Filename, [append]) of
0+-    {ok, Fd} ->
0+-        {ok, #state{fd = Fd, level = Level, sasl = Sasl}};
0+-    {error, eacces} ->
0++    process_flag(trap_exit, true),
0++    Parent = self(),
0++    Writer = spawn_link(fun() -> writer(Parent, Filename) end),
0++
0++    receive
0++    {Writer, ok} ->
0++        {ok, #state{writer = Writer, level = Level, sasl = Sasl}};
0++    {Writer, {error, eacces}} ->
0+         {stop, {file_permission_error, Filename}};
0+-    Error ->
0++    {Writer, Error} ->
0+         {stop, Error}
0+     end.
0+ 
0+@@ -143,18 +149,21 @@ handle_call({set_level_integer, NewLevel}, State) ->
0+     ets:insert(?MODULE, {level, NewLevel}),
0+     {ok, ok, State#state{level = NewLevel}}.
0+ 
0++handle_info({'EXIT', Pid, Reason}, #state{writer = Pid}) ->
0++    io:format("[error] log writer died: ~p~n", [Reason]),
0++    exit({log_writer_died, Reason});
0+ handle_info(_Info, State) ->
0+     {ok, State}.
0+ 
0+ code_change(_OldVsn, State, _Extra) ->
0+     {ok, State}.
0+ 
0+-terminate(_Arg, #state{fd = Fd}) ->
0+-    file:close(Fd).
0++terminate(_Arg, #state{writer = Writer}) ->
0++    Writer ! stop,
0++    receive {'EXIT', Writer, _} -> ok end.
0+ 
0+-log(#state{fd = Fd}, ConsoleMsg, FileMsg) ->
0+-    ok = io:put_chars(ConsoleMsg),
0+-    ok = io:put_chars(Fd, FileMsg).
0++log(#state{writer = Writer}, ConsoleMsg, FileMsg) ->
0++    Writer ! {log, FileMsg, ConsoleMsg}.
0+ 
0+ get_log_messages(Pid, Level, Format, Args) ->
0+     ConsoleMsg = unicode:characters_to_binary(io_lib:format(
0+@@ -175,3 +184,41 @@ read(Bytes, Offset) ->
0+     {ok, Chunk} = file:pread(Fd, Start, LogFileSize),
0+     ok = file:close(Fd),
0+     Chunk.
0++
0++
0++writer(Parent, Filename) ->
0++    case file:open(Filename, [append, raw, binary]) of
0++    {ok, Fd} ->
0++        Parent ! {self(), ok},
0++        writer_loop(Fd);
0++    Error ->
0++        Parent ! {self(), Error}
0++    end.
0++
0++writer_loop(Fd) ->
0++    receive
0++    {log, FileMsg, ConsoleMsg} ->
0++        ok = io:put_chars(ConsoleMsg),
0++        ok = file:write(Fd, FileMsg),
0++        writer_loop(Fd);
0++        % writer_loop_batch(Fd, [FileMsg], [ConsoleMsg], byte_size(FileMsg));
0++    stop ->
0++        ok = file:close(Fd)
0++    end.
0++
0++writer_loop_batch(Fd, AccFile, AccConsole, Len) when Len >= ?MAX_BUFFER_LEN ->
0++    ok = io:put_chars(lists:reverse(AccConsole)),
0++    ok = file:write(Fd, lists:reverse(AccFile)),
0++    writer_loop(Fd);
0++
0++writer_loop_batch(Fd, AccFile, AccConsole, Len) ->
0++    receive
0++    {log, FileMsg, ConsoleMsg} ->
0++        writer_loop_batch(
0++            Fd, [FileMsg | AccFile], [ConsoleMsg | AccConsole],
0++            Len + byte_size(FileMsg))
0++    after 0 ->
0++        ok = io:put_chars(lists:reverse(AccConsole)),
0++        ok = file:write(Fd, lists:reverse(AccFile)),
0++        writer_loop(Fd)
0++    end.
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
--- 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.