Revision 353336333637 () - Diff

Link to this snippet: https://friendpaste.com/X9tX1LCb9Mn6SiTueFlXY
Embed:
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
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.