diff options
author | Guido Günther <agx@sigxcpu.org> | 2017-02-16 08:07:41 +0100 |
---|---|---|
committer | Guido Günther <agx@sigxcpu.org> | 2017-02-16 08:07:41 +0100 |
commit | 4b9ed81166b40e3c2690f7505348bfc889eaf272 (patch) | |
tree | f9b4217a17e9d4df99cf4860fc1f12cccda68953 /src/ldapsp_error_log_handler.erl | |
parent | eefbd57f8be7cd5263c1e4f4a16976beafc9dcba (diff) |
Add our own error log handler
This handler is logrotate friendly as it does not add any datetime into
the filename and the log file is recreated if moved.
Diffstat (limited to 'src/ldapsp_error_log_handler.erl')
-rw-r--r-- | src/ldapsp_error_log_handler.erl | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/src/ldapsp_error_log_handler.erl b/src/ldapsp_error_log_handler.erl new file mode 100644 index 0000000..f7825ea --- /dev/null +++ b/src/ldapsp_error_log_handler.erl @@ -0,0 +1,160 @@ +%%----------------------------------------------------------------------- +%% Copyright (c) 2017 Guido Günther +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% based on error_log_handler.erl +%% Copyright (c) 2011-2014 Basho Technologies, Inc. All Rights Reserved. +%%----------------------------------------------------------------------- + +%% @doc Log handler for ldapsp + +-module(ldapsp_error_log_handler). + +-behaviour(gen_event). + +%% gen_event callbacks +-export([init/1, + handle_call/2, + handle_event/2, + handle_info/2, + terminate/2, + code_change/3]). + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). +-endif. + +-record(state, {filename, handle}). + +-define(FILENAME, "ldapsp_error.log"). + +%% =================================================================== +%% gen_event callbacks +%% =================================================================== + +%% @private +init([BaseDir]) -> + {ok,_} = webmachine_log:defer_refresh(?MODULE), + FileName = filename:join(BaseDir, ?FILENAME), + Handle = log_open(FileName), + {ok, #state{filename=FileName, handle=Handle}}. + +%% @private +handle_call({_Label, MRef, get_modules}, State) -> + {ok, {MRef, [?MODULE]}, State}; +handle_call({refresh, _Time}, State) -> + NewHandle = maybe_reopen(?MODULE, + State#state.filename, + State#state.handle), + {ok, ok, State#state{handle=NewHandle}}; +handle_call(_Request, State) -> + {ok, ok, State}. + +%% @private +handle_event({log_error, Msg}, State) -> + NewHandle = maybe_reopen(?MODULE, + State#state.filename, + State#state.handle), + NewState = State#state{handle=NewHandle}, + FormattedMsg = format_req(error, undefined, undefined, Msg), + _ = webmachine_log:log_write(State#state.handle, FormattedMsg), + {ok, NewState}; +handle_event({log_error, Code, _Req, _Reason}, State) when Code < 500 -> + {ok, State}; +handle_event({log_error, Code, Req, Reason}, State) -> + NewHandle = maybe_reopen(?MODULE, + State#state.filename, + State#state.handle), + NewState = State#state{handle=NewHandle}, + Msg = format_req(error, Code, Req, Reason), + _ = webmachine_log:log_write(State#state.handle, Msg), + {ok, NewState}; +handle_event({log_info, Msg}, State) -> + NewHandle = maybe_reopen(?MODULE, + State#state.filename, + State#state.handle), + NewState = State#state{handle=NewHandle}, + FormattedMsg = format_req(info, undefined, undefined, Msg), + _ = webmachine_log:log_write(State#state.handle, FormattedMsg), + {ok, NewState}; +handle_event(_Event, State) -> + {ok, State}. + +%% @private +handle_info(_Info, State) -> + {ok, State}. + +%% @private +terminate(_Reason, _State) -> + ok. + +%% @private +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%% =================================================================== +%% Internal functions +%% =================================================================== + + +format_req(Level, Code, Req, Msg) -> + Time = webmachine_log:fmtnow(), + format_req(Level, Time, Code, Req, Msg). + +format_req(info, Time, undefined, _, Msg) -> + [Time, " [info] ", Msg]; +format_req(error, Time, undefined, _, Msg) -> + [Time, " [error] ", Msg]; +format_req(error, Time, 501, Req, _) -> + {Path, _} = Req:path(), + {Method, _} = Req:method(), + Reason = "Webmachine does not support method ", + [Time, " [error] ", Reason, Method, ": path=", Path, $\n]; +format_req(error, Time, 503, Req, _) -> + {Path, _} = Req:path(), + Reason = "Webmachine cannot fulfill the request at this time", + [Time, " [error] ", Reason, ": path=", Path, $\n]; +format_req(error, Time, _Code, Req, Reason) -> + {Path, _} = Req:path(), + Str = io_lib:format("~p", [Reason]), + [Time, " [error] ", "path=", Path, $\x20, Str, $\n]. + +%% @doc Open a new log file for writing +-spec log_open(string()) -> file:io_device(). +log_open(LogName) -> + error_logger:info_msg("opening log file: ~p~n", [LogName]), + ok = filelib:ensure_dir(LogName), + {ok, FD} = file:open(LogName, [read, write, raw]), + {ok, Location} = file:position(FD, eof), + webmachine_log:fix_log(FD, Location), + ok = file:truncate(FD), + FD. + +%% @doc Rotate a log file if the hour it represents +%% has passed. +-spec maybe_reopen(atom(), string(), file:io_device()) -> file:io_device(). +maybe_reopen(Mod, FileName, Handle) -> + Reopen = not filelib:is_regular(FileName), + maybe_reopen(Mod, FileName, Handle, Reopen). + +-spec maybe_reopen(atom(), string(), file:io_device(), boolean()) -> + file:io_device(). +maybe_reopen(_Mod, _FileName, Handle, false) -> + Handle; +maybe_reopen(Mod, FileName, Handle, true) -> + ok = webmachine_log:log_close(Mod, FileName, Handle), + NewHandle = log_open(FileName), + NewHandle. |