1% Licensed under the Apache License, Version 2.0 (the "License"); you may not
2% use this file except in compliance with the License. You may obtain a copy of
3% the License at
4%
5%   http://www.apache.org/licenses/LICENSE-2.0
6%
7% Unless required by applicable law or agreed to in writing, software
8% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10% License for the specific language governing permissions and limitations under
11% the License.
12
13-module(couch_io_logger).
14
15-export([
16    start/1,
17    log_output/1,
18    log_input/1,
19    stop_noerror/0,
20    stop_error/1
21]).
22
23
24start(undefined) ->
25    ok;
26start(Dir) ->
27    case filelib:is_dir(Dir) of
28        true ->
29            Name = log_name(),
30            Path = Dir ++ "/" ++ Name,
31            OPath = Path ++ ".out.log_",
32            IPath = Path ++ ".in.log_",
33            {ok, OFd} = file:open(OPath, [read, write, raw]),
34            {ok, IFd} = file:open(IPath, [read, write, raw]),
35            ok = file:delete(OPath),
36            ok = file:delete(IPath),
37            put(logger_path, Path),
38            put(logger_out_fd, OFd),
39            put(logger_in_fd, IFd),
40            ok;
41        false ->
42            ok
43    end.
44
45
46stop_noerror() ->
47    case get(logger_path) of
48        undefined ->
49            ok;
50        _Path ->
51            close_logs()
52    end.
53
54
55stop_error(Err) ->
56    case get(logger_path) of
57        undefined ->
58            ok;
59        Path ->
60            save_error_logs(Path, Err),
61            close_logs()
62    end.
63
64
65log_output(Data) ->
66    log(get(logger_out_fd), Data).
67
68
69log_input(Data) ->
70    log(get(logger_in_fd), Data).
71
72
73unix_time() ->
74    {Mega, Sec, USec} = os:timestamp(),
75    UnixTs = (Mega * 1000000 + Sec) * 1000000 + USec,
76    integer_to_list(UnixTs).
77
78
79log_name() ->
80    Ts = unix_time(),
81    Pid0 = erlang:pid_to_list(self()),
82    Pid1 = string:strip(Pid0, left, $<),
83    Pid2 = string:strip(Pid1, right, $>),
84    lists:flatten(io_lib:format("~s_~s", [Ts, Pid2])).
85
86
87close_logs() ->
88    file:close(get(logger_out_fd)),
89    file:close(get(logger_in_fd)).
90
91
92save_error_logs(Path, Err) ->
93    Otp = erlang:system_info(otp_release),
94    Msg = io_lib:format("Error: ~p~nNode: ~p~nOTP: ~p~n", [Err, node(), Otp]),
95    file:write_file(Path ++ ".meta", Msg),
96    IFd = get(logger_out_fd),
97    OFd = get(logger_in_fd),
98    file:position(IFd, 0),
99    file:position(OFd, 0),
100    file:copy(IFd, Path ++  ".out.log"),
101    file:copy(OFd, Path ++ ".in.log").
102
103
104log(undefined, _Data) ->
105    ok;
106log(Fd, Data) ->
107    ok = file:write(Fd, [Data, io_lib:nl()]).
108