PageRenderTime 21ms CodeModel.GetById 11ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 0ms

/deps/mochiweb/src/mochilogfile2.erl

http://github.com/zotonic/zotonic
Erlang | 140 lines | 108 code | 13 blank | 19 comment | 0 complexity | 89203b073ba5915f2e4c0818e0cbda34 MD5 | raw file
  1%% @author Bob Ippolito <bob@mochimedia.com>
  2%% @copyright 2010 Mochi Media, Inc.
  3
  4%% @doc Write newline delimited log files, ensuring that if a truncated
  5%%      entry is found on log open then it is fixed before writing. Uses
  6%%      delayed writes and raw files for performance.
  7-module(mochilogfile2).
  8-author('bob@mochimedia.com').
  9
 10-export([open/1, write/2, close/1, name/1]).
 11
 12%% @spec open(Name) -> Handle
 13%% @doc Open the log file Name, creating or appending as necessary. All data
 14%%      at the end of the file will be truncated until a newline is found, to
 15%%      ensure that all records are complete.
 16open(Name) ->
 17    {ok, FD} = file:open(Name, [raw, read, write, delayed_write, binary]),
 18    fix_log(FD),
 19    {?MODULE, Name, FD}.
 20
 21%% @spec name(Handle) -> string()
 22%% @doc Return the path of the log file.
 23name({?MODULE, Name, _FD}) ->
 24    Name.
 25
 26%% @spec write(Handle, IoData) -> ok
 27%% @doc Write IoData to the log file referenced by Handle.
 28write({?MODULE, _Name, FD}, IoData) ->
 29    ok = file:write(FD, [IoData, $\n]),
 30    ok.
 31
 32%% @spec close(Handle) -> ok
 33%% @doc Close the log file referenced by Handle.
 34close({?MODULE, _Name, FD}) ->
 35    ok = file:sync(FD),
 36    ok = file:close(FD),
 37    ok.
 38
 39fix_log(FD) ->
 40    {ok, Location} = file:position(FD, eof),
 41    Seek = find_last_newline(FD, Location),
 42    {ok, Seek} = file:position(FD, Seek),
 43    ok = file:truncate(FD),
 44    ok.
 45
 46%% Seek backwards to the last valid log entry
 47find_last_newline(_FD, N) when N =< 1 ->
 48    0;
 49find_last_newline(FD, Location) ->
 50    case file:pread(FD, Location - 1, 1) of
 51	{ok, <<$\n>>} ->
 52            Location;
 53	{ok, _} ->
 54	    find_last_newline(FD, Location - 1)
 55    end.
 56
 57%%
 58%% Tests
 59%%
 60-ifdef(TEST).
 61-include_lib("eunit/include/eunit.hrl").
 62name_test() ->
 63    D = mochitemp:mkdtemp(),
 64    FileName = filename:join(D, "open_close_test.log"),
 65    H = open(FileName),
 66    ?assertEqual(
 67       FileName,
 68       name(H)),
 69    close(H),
 70    file:delete(FileName),
 71    file:del_dir(D),
 72    ok.
 73
 74open_close_test() ->
 75    D = mochitemp:mkdtemp(),
 76    FileName = filename:join(D, "open_close_test.log"),
 77    OpenClose = fun () ->
 78                        H = open(FileName),
 79                        ?assertEqual(
 80                           true,
 81                           filelib:is_file(FileName)),
 82                        ok = close(H),
 83                        ?assertEqual(
 84                           {ok, <<>>},
 85                           file:read_file(FileName)),
 86                        ok
 87                end,
 88    OpenClose(),
 89    OpenClose(),
 90    file:delete(FileName),
 91    file:del_dir(D),
 92    ok.
 93
 94write_test() ->
 95    D = mochitemp:mkdtemp(),
 96    FileName = filename:join(D, "write_test.log"),
 97    F = fun () ->
 98                H = open(FileName),
 99                write(H, "test line"),
100                close(H),
101                ok
102        end,
103    F(),
104    ?assertEqual(
105       {ok, <<"test line\n">>},
106       file:read_file(FileName)),
107    F(),
108    ?assertEqual(
109       {ok, <<"test line\ntest line\n">>},
110       file:read_file(FileName)),
111    file:delete(FileName),
112    file:del_dir(D),
113    ok.
114
115fix_log_test() ->
116    D = mochitemp:mkdtemp(),
117    FileName = filename:join(D, "write_test.log"),
118    file:write_file(FileName, <<"first line good\nsecond line bad">>),
119    F = fun () ->
120                H = open(FileName),
121                write(H, "test line"),
122                close(H),
123                ok
124        end,
125    F(),
126    ?assertEqual(
127       {ok, <<"first line good\ntest line\n">>},
128       file:read_file(FileName)),
129    file:write_file(FileName, <<"first line bad">>),
130    F(),
131    ?assertEqual(
132       {ok, <<"test line\n">>},
133       file:read_file(FileName)),
134    F(),
135    ?assertEqual(
136       {ok, <<"test line\ntest line\n">>},
137       file:read_file(FileName)),
138    ok.
139
140-endif.