blob: 874789c97cdc9345157b96a3eb2f446859c24ac6 [file] [log] [blame]
% Licensed 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(twig_event_handler).
-behaviour(gen_event).
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2,
code_change/3]).
-record(state, {
socket,
host,
port,
hostname,
os_pid,
appid,
facility,
level
}).
-include("twig_int.hrl").
init([]) ->
{ok, Socket} = gen_udp:open(0),
{ok, ok, State} = handle_call(load_config, #state{socket=Socket}),
{ok, State}.
handle_event({twig, Level, Msg}, State) ->
write(Level, Msg, State),
{ok, State};
% OTP standard events
handle_event({Class, _GL, {Pid, Format, Args}}, #state{level=Max} = State) ->
case otp_event_level(Class, Format) of
undefined ->
{ok, State};
Level when Level > Max ->
{ok, State};
Level ->
write(Level, message(Pid, Format, Args), State),
{ok, State}
end;
handle_event(_Event, State) ->
{ok, State}.
handle_call({set_level, Level}, State) ->
{ok, ok, State#state{level = Level}};
handle_call(load_config, State) ->
Host = case inet:getaddr(get_env(host, undefined), inet) of
{ok, Address} ->
Address;
{error, _} ->
undefined
end,
NewState = State#state{
host = Host,
port = get_env(port, 514),
hostname = net_adm:localhost(),
os_pid = os:getpid(),
appid = get_env(appid, "bigcouch"),
facility = twig_util:facility(get_env(facility, local2)),
level = twig_util:level(get_env(level, info))
},
{ok, ok, NewState};
handle_call(_Call, State) ->
{ok, ignored, State}.
handle_info(_Info, State) ->
{ok, State}.
terminate(_Reason, State) ->
gen_udp:close(State#state.socket).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
get_env(Key, Default) ->
case application:get_env(twig, Key) of
{ok, Value} ->
Value;
undefined ->
Default
end.
write(_, _, #state{host=undefined}) ->
ok;
write(Level, Msg, State) when is_list(Msg); is_binary(Msg) ->
#state{facility=Facil, appid=App, hostname=Hostname, host=Host, port=Port,
socket=Socket, os_pid=OsPid} = State,
Pre = io_lib:format("<~B>~B ~s ~s ~s ~s - - ", [Facil bor Level,
?SYSLOG_VERSION, twig_util:iso8601_timestamp(), Hostname, App, OsPid]),
%% TODO truncate large messages
gen_udp:send(Socket, Host, Port, [Pre, Msg, $\n]);
write(Level, {Format0, Args0}, State) ->
#state{facility=Facil, appid=App, hostname=Hostname, host=Host, port=Port,
socket=Socket, os_pid=OsPid} = State,
Format = "<~B>~B ~s ~s ~s ~s - - " ++ Format0 ++ "\n",
Args = [Facil bor Level, ?SYSLOG_VERSION, twig_util:iso8601_timestamp(),
Hostname, App, OsPid | Args0],
%% TODO truncate large messages
Packet = io_lib:format(Format, Args),
gen_udp:send(Socket, Host, Port, Packet).
message(_Pid, crash_report, Report) ->
proc_lib:format(Report);
message(Pid, supervisor_report, Report) ->
Name = lists:keyfind(supervisor, 1, Report),
Error = lists:keyfind(errorContext, Report),
Reason = lists:keyfind(reason, 1, Report),
Offender = lists:keyfind(offender, 1, Report),
ChildPid = lists:keyfind(pid, 1, Offender),
ChildName = lists:keyfind(name, 1, Offender),
{M,F,_} = lists:keyfind(mfa, 1, Offender),
{"[~p] SUPERVISOR REPORT ~p ~p (~p) child: ~p [~p] ~p:~p",
[Pid, Name, Error, Reason, ChildName, ChildPid, M, F]};
message(Pid, progress_report, Report) ->
{"[~p] PROGRESS REPORT~n~p", [Pid, Report]};
message(Pid, Type, Report) when Type == std_error;
Type == std_info;
Type == std_warning ->
{"[~p] ~p: ~p", [Pid, Type, Report]};
message(Pid, Format, Args) ->
{"[~p] " ++ Format, [Pid|Args]}.
otp_event_level(error, _) -> ?LEVEL_ERR;
otp_event_level(warning_msg, _) -> ?LEVEL_WARN;
otp_event_level(info_msg, _) -> ?LEVEL_INFO;
otp_event_level(_, {_, crash_report, _}) -> ?LEVEL_CRIT;
otp_event_level(_, {_, supervisor_report, _}) -> ?LEVEL_WARN;
otp_event_level(_, {_, progress_report, _}) -> ?LEVEL_DEBUG;
otp_event_level(error_report, _) -> ?LEVEL_ERR;
otp_event_level(warning_report, _) -> ?LEVEL_WARN;
otp_event_level(info_report, _) -> ?LEVEL_INFO;
otp_event_level(_, _) -> undefined.