/elib/ernie_access_logger.erl

https://github.com/brynary/ernie · Erlang · 170 lines · 114 code · 23 blank · 33 comment · 0 complexity · e209426725d03c1a44851e07af558cca MD5 · raw file

  1. -module(ernie_access_logger).
  2. -behaviour(gen_server).
  3. %% api
  4. -export([start_link/1, start/1, acc/1, err/3, reopen/0]).
  5. %% gen_server callbacks
  6. -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
  7. terminate/2, code_change/3]).
  8. -include_lib("ernie.hrl").
  9. -record(lstate, {access_file_name = undefined,
  10. access_file = undefined}).
  11. %%====================================================================
  12. %% API
  13. %%====================================================================
  14. start_link(Args) ->
  15. gen_server:start_link({local, ?MODULE}, ?MODULE, Args, []).
  16. start(Args) ->
  17. gen_server:start({global, ?MODULE}, ?MODULE, Args, []).
  18. acc(Request) ->
  19. gen_server:cast(?MODULE, {acc, Request}).
  20. err(Request, Msg, Args) ->
  21. gen_server:cast(?MODULE, {err, Request, Msg, Args}).
  22. reopen() ->
  23. gen_server:cast(?MODULE, reopen).
  24. %%====================================================================
  25. %% gen_server callbacks
  26. %%====================================================================
  27. %%--------------------------------------------------------------------
  28. %% Function: init(Args) -> {ok, State} |
  29. %% {ok, State, Timeout} |
  30. %% ignore |
  31. %% {stop, Reason}
  32. %% Description: Initiates the server
  33. %%--------------------------------------------------------------------
  34. init([undefined]) ->
  35. error_logger:info_msg("~p starting~n", [?MODULE]),
  36. {ok, #lstate{}};
  37. init([AccessFileName]) ->
  38. error_logger:info_msg("~p starting~n", [?MODULE]),
  39. case file:open(AccessFileName, [append]) of
  40. {ok, AccessFile} ->
  41. {ok, _T} = timer:apply_interval(10000, ernie_access_logger, reopen, []),
  42. {ok, #lstate{access_file_name = AccessFileName,
  43. access_file = AccessFile}};
  44. {error, Error} ->
  45. error_logger:error_msg("Error opening access log ~p: ~p.~n", [AccessFileName, Error]),
  46. {ok, #lstate{}}
  47. end.
  48. %%--------------------------------------------------------------------
  49. %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
  50. %% {reply, Reply, State, Timeout} |
  51. %% {noreply, State} |
  52. %% {noreply, State, Timeout} |
  53. %% {stop, Reason, Reply, State} |
  54. %% {stop, Reason, State}
  55. %% Description: Handling call messages
  56. %%--------------------------------------------------------------------
  57. handle_call(_Request, _From, State) ->
  58. {reply, ok, State}.
  59. %%--------------------------------------------------------------------
  60. %% Function: handle_cast(Msg, State) -> {noreply, State} |
  61. %% {noreply, State, Timeout} |
  62. %% {stop, Reason, State}
  63. %% Description: Handling cast messages
  64. %%--------------------------------------------------------------------
  65. handle_cast({acc, Request}, State) ->
  66. case State#lstate.access_file_name of
  67. undefined -> ok;
  68. _AccessFilename -> acc(Request, State)
  69. end,
  70. {noreply, State};
  71. handle_cast({err, Request, Msg, Args}, State) ->
  72. case State#lstate.access_file_name of
  73. undefined -> ok;
  74. _AccessFilename -> err(Request, Msg, Args, State)
  75. end,
  76. {noreply, State};
  77. handle_cast(reopen, State) ->
  78. case State#lstate.access_file_name of
  79. undefined ->
  80. {noreply, State};
  81. AccessFileName ->
  82. case file:read_file_info(AccessFileName) of
  83. {ok, _FileInfo} ->
  84. {noreply, State};
  85. {error, enoent} ->
  86. ok = file:close(State#lstate.access_file),
  87. {ok, AccessFile} = file:open(AccessFileName, [append]),
  88. {noreply, State#lstate{access_file = AccessFile}};
  89. _OtherError ->
  90. {noreply, #lstate{}}
  91. end
  92. end;
  93. handle_cast(_Msg, State) ->
  94. {noreply, State}.
  95. handle_info(Msg, State) ->
  96. error_logger:error_msg("Unexpected message: ~p~n", [Msg]),
  97. {noreply, State}.
  98. terminate(_Reason, _State) -> ok.
  99. code_change(_OldVersion, State, _Extra) -> {ok, State}.
  100. %%====================================================================
  101. %% Internal
  102. %%====================================================================
  103. acc(Request, State) ->
  104. StatString = stat_string(Request),
  105. ActionString = action_string(Request),
  106. Line = io_lib:fwrite("ACC ~s - ~s~n", [StatString, ActionString]),
  107. file:write(State#lstate.access_file, Line).
  108. err(Request, Msg, Args, State) ->
  109. StatString = stat_string(Request),
  110. ActionString = action_string(Request),
  111. ErrString = io_lib:fwrite(Msg, Args),
  112. Line = io_lib:fwrite("ERR ~s - ~s : ~s~n", [StatString, ErrString, ActionString]),
  113. file:write(State#lstate.access_file, Line).
  114. stat_string(Request) ->
  115. Log = Request#request.log,
  116. TAccept = time_tuple_to_iso_8601_date(Log#log.taccept),
  117. D1 = time_difference_in_seconds(Log#log.taccept, Log#log.tprocess),
  118. D2 = time_difference_in_seconds(Log#log.tprocess, Log#log.tdone),
  119. Type = Log#log.type,
  120. HQ = Log#log.hq,
  121. LQ = Log#log.lq,
  122. Prio = Request#request.priority,
  123. Args = [TAccept, D1, D2, HQ, LQ, Type, Prio],
  124. io_lib:fwrite("[~s] ~f ~f - ~B ~B ~3s ~p", Args).
  125. action_string(Request) ->
  126. TermAction = binary_to_term(Request#request.action),
  127. RawAction = lists:flatten(io_lib:fwrite("~1000000000.0.0p", [TermAction])),
  128. case string:len(RawAction) > 150 of
  129. true ->
  130. Action = re:replace(RawAction, "\n", "", [global, {return, list}]),
  131. [string:sub_string(Action, 1, 150), "..."];
  132. false ->
  133. RawAction
  134. end.
  135. time_tuple_to_iso_8601_date(TimeTuple) ->
  136. {{YY, MM, DD}, {H, M, S}} = calendar:now_to_local_time(TimeTuple),
  137. {_MegaSecs, _Secs, MicroSecs} = TimeTuple,
  138. Args = [YY, MM, DD, H, M, S, MicroSecs],
  139. io_lib:fwrite("~4B-~2.10.0B-~2.10.0BT~2.10.0B:~2.10.0B:~2.10.0B.~-6.10.0B", Args).
  140. time_difference_in_seconds(T1, T2) ->
  141. {_, _, MS1} = T1,
  142. {_, _, MS2} = T2,
  143. S1 = calendar:datetime_to_gregorian_seconds(calendar:now_to_local_time(T1)),
  144. S2 = calendar:datetime_to_gregorian_seconds(calendar:now_to_local_time(T2)),
  145. F1 = S1 + (MS1 / 1000000),
  146. F2 = S2 + (MS2 / 1000000),
  147. F2 - F1.