%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. %% %% 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. -module(lager). -behaviour(gen_server). %% API -export([start_link/0, start/0, log/7, log/8, log/3, log/4, get_loglevel/1, set_loglevel/2, set_loglevel/3]). %% callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -record(state, {event_pid, handler_loglevels, error_logger_handlers}). %% API start_link() -> %application:start(sasl), %application:set_env(riak_err, print_fun, {lager, sasl_log}), %application:start(riak_err), Handlers = case application:get_env(lager, handlers) of undefined -> [{lager_console_backend, [info]}, {lager_file_backend, [{"error.log", error}, {"console.log", info}]}]; {ok, Val} -> Val end, gen_server:start_link({local, ?MODULE}, ?MODULE, [Handlers], []). start() -> Handlers = case application:get_env(lager, handlers) of undefined -> [{lager_console_backend, [info]}, {lager_file_backend, [{"error.log", error}, {"console.log", info}]}]; {ok, Val} -> Val end, gen_server:start({local, ?MODULE}, ?MODULE, [Handlers], []). log(Level, Module, Function, Line, Pid, {{Y, M, D}, {H, Mi, S}}, Message) -> Time = io_lib:format("~b-~b-~b ~b:~b:~b", [Y, M, D, H, Mi, S]), Msg = io_lib:format("[~p] ~p@~p:~p:~p ~s", [Level, Pid, Module, Function, Line, Message]), gen_event:sync_notify(lager_event, {log, lager_util:level_to_num(Level), Time, Msg}). log(Level, Module, Function, Line, Pid, {{Y, M, D}, {H, Mi, S}}, Format, Args) -> Time = io_lib:format("~b-~b-~b ~b:~b:~b", [Y, M, D, H, Mi, S]), Msg = io_lib:format("[~p] ~p@~p:~p:~p ~s", [Level, Pid, Module, Function, Line, io_lib:format(Format, Args)]), gen_event:sync_notify(lager_event, {log, lager_util:level_to_num(Level), Time, Msg}). log(Level, Pid, Message) -> {{Y, M, D}, {H, Mi, S}} = lager_stdlib:maybe_utc(erlang:localtime()), Time = io_lib:format("~b-~b-~b ~b:~b:~b", [Y, M, D, H, Mi, S]), Msg = io_lib:format("[~p] ~p ~s", [Level, Pid, Message]), gen_event:sync_notify(lager_event, {log, lager_util:level_to_num(Level), Time, Msg}). log(Level, Pid, Format, Args) -> {{Y, M, D}, {H, Mi, S}} = lager_stdlib:maybe_utc(erlang:localtime()), Time = io_lib:format("~b-~b-~b ~b:~b:~b", [Y, M, D, H, Mi, S]), Msg = io_lib:format("[~p] ~p ~s", [Level, Pid, io_lib:format(Format, Args)]), gen_event:sync_notify(lager_event, {log, lager_util:level_to_num(Level), Time, Msg}). set_loglevel(Handler, Level) when is_atom(Level) -> gen_server:call(?MODULE, {set_loglevel, Handler, Level}). set_loglevel(Handler, Ident, Level) when is_atom(Level) -> gen_server:call(?MODULE, {set_loglevel, Handler, Ident, Level}). get_loglevel(Handler) -> case gen_server:call(?MODULE, {get_loglevel, Handler}) of X when is_integer(X) -> lager_util:num_to_level(X); Y -> Y end. %% gen_server callbacks init([Handlers]) -> %% start a gen_event linked to this process gen_event:start_link({local, lager_event}), %% spin up all the defined handlers [gen_event:add_sup_handler(lager_event, Module, Args) || {Module, Args} <- Handlers], MinLog = minimum_log_level(get_log_levels()), lager_mochiglobal:put(loglevel, MinLog), case application:get_env(lager, error_logger_redirect) of {ok, true} -> gen_event:add_sup_handler(error_logger, error_logger_lager_h, []), %% TODO allow user to whitelist handlers to not be removed [gen_event:delete_handler(error_logger, X, {stop_please, ?MODULE}) || X <- gen_event:which_handlers(error_logger) -- [error_logger_lager_h]], {ok, #state{}}; _ -> {ok, #state{}} end. handle_call({set_loglevel, Handler, Level}, _From, State) -> Reply = gen_event:call(lager_event, Handler, {set_loglevel, Level}), %% recalculate min log level MinLog = minimum_log_level(get_log_levels()), lager_mochiglobal:put(loglevel, MinLog), {reply, Reply, State}; handle_call({set_loglevel, Handler, Ident, Level}, _From, State) -> Reply = gen_event:call(lager_event, Handler, {set_loglevel, Ident, Level}), %% recalculate min log level MinLog = minimum_log_level(get_log_levels()), lager_mochiglobal:put(loglevel, MinLog), {reply, Reply, State}; handle_call({get_loglevel, Handler}, _From, State) -> Reply = gen_event:call(lager_event, Handler, get_loglevel), {reply, Reply, State}; handle_call(_Request, _From, State) -> {reply, ok, State}. handle_cast(_Request, State) -> {noreply, State}. handle_info({gen_event_EXIT, error_logger_lager_h, {'EXIT', Reason}}, State) -> lager:log(error, self(), ["Restarting lager error handler after it exited with ", error_logger_lager_h:format_reason(Reason)]), gen_event:add_sup_handler(error_logger, error_logger_lager_h, []), {noreply, State}; handle_info(Info, State) -> io:format("got info ~p~n", [Info]), {noreply, State}. terminate(_Reason, _State) -> gen_event:stop(lager_event), ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %% internal functions get_log_levels() -> [gen_event:call(lager_event, Handler, get_loglevel) || Handler <- gen_event:which_handlers(lager_event)]. minimum_log_level([]) -> 9; %% higher than any log level, logging off minimum_log_level(Levels) -> erlang:hd(lists:sort(Levels)).