Skip to content

Instantly share code, notes, and snippets.

@maxlapshin
Created February 7, 2014 09:03
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maxlapshin/8859384 to your computer and use it in GitHub Desktop.
Save maxlapshin/8859384 to your computer and use it in GitHub Desktop.
-module(web_lager_handler).
-behaviour(gen_event).
-export([subscribe/1, unsubscribe/1]).
-export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,
code_change/3]).
-export([remover_loop/0]).
-record(state, {
pid,
media,
trace
}).
name(Media,Pid) ->
{web_lager_handler,{Media,Pid}}.
subscribe(Media) when is_binary(Media) ->
unsubscribe(Media),
Self = self(),
gen_event:add_sup_handler(lager_event, name(Media,Self), [Self,Media]),
ok.
unsubscribe(Media) when is_binary(Media) ->
Trace0 = {[{media,Media}], debug, name(Media,self())},
{ok, Trace} = lager_util:validate_trace(Trace0),
lager:stop_trace(Trace),
gen_event:delete_handler(lager_event, name(Media,self()), []),
ok.
init([Pid, Media]) ->
{ok, Trace} = lager:trace(name(Media,Pid), [{media,Media}], debug),
start_trace_remover(),
{ok, #state{pid = Pid, media = Media, trace = Trace}}.
handle_call(stop, #state{trace = Trace}) ->
lager:stop_trace(Trace),
{remove_handler, ok};
handle_call(_, State) ->
{ok, undefined, State}.
handle_info(_Info, State) ->
{ok, State}.
handle_event({log, Msg}, #state{pid = Pid, media = Media} = State) ->
Format = [time, " ",
{media, ["[", media, "] "], ""},
{module, [module, ":", line, " "], ""},
message, "\n"
],
case lists:keyfind(media, 1, lager_msg:metadata(Msg)) of
{media,Media} ->
Text = lager_default_formatter:format(Msg, Format),
Pid ! {media_log, Media, iolist_to_binary(Text)},
ok;
_ ->
ok
end,
{ok, State}.
terminate(_,#state{media = _Media, pid = _Pid, trace = Trace}) ->
whereis(trace_remover) ! {remove, Trace},
ok.
code_change(_,State,_) -> {ok, State}.
% We need to have this stupid singleton process, because lager
% has race condition in trace remover and
% it is also impossible to remove trace in terminate callback
% because it will lead to deadlock
start_trace_remover() ->
case whereis(trace_remover) of
undefined ->
proc_lib:spawn(fun() ->
erlang:register(trace_remover, self()),
remover_loop()
end);
_ ->
ok
end.
remover_loop() ->
Msg = receive M -> M end,
case Msg of
{remove, Trace} ->
lager:stop_trace(Trace);
_ ->
ok
end,
?MODULE:remover_loop().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment