Current section
Files
Jump to
Current section
Files
src/lager_hipchat_backend.erl
%% ----------------------------------------------------------------------------
%%
%% lager_hipchat: HipChat backend for Lager
%%
%% Copyright (c) 2013 Synlay Technologies
%%
%% Permission is hereby granted, free of charge, to any person obtaining a
%% copy of this software and associated documentation files (the "Software"),
%% to deal in the Software without restriction, including without limitation
%% the rights to use, copy, modify, merge, publish, distribute, sublicense,
%% and/or sell copies of the Software, and to permit persons to whom the
%% Software is furnished to do so, subject to the following conditions:
%%
%% The above copyright notice and this permission notice shall be included in
%% all copies or substantial portions of the Software.
%%
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
%% DEALINGS IN THE SOFTWARE.
%%
%% ----------------------------------------------------------------------------
-module(lager_hipchat_backend).
-behaviour(gen_event).
-export([
init/1
,handle_call/2
,handle_event/2
,handle_info/2
,terminate/2
,code_change/3
]).
%%% this is only exported for the spawn call
-export([send_log/3]).
-record(state, {
auth_token :: string()
,room_id :: string()
,sender :: string()
,color :: string()
,mentions :: [string() | atom()]
,notify :: integer()
,level :: integer()
,formatter :: atom()
,format_config
,retry_interval :: integer()
,retry_times :: integer()
}).
-include_lib("lager/include/lager.hrl").
-define (HIPCHAT_API_URL, "https://api.hipchat.com/v1/rooms/message").
-define (HIPCHAT_API_REQUEST (Data), {?HIPCHAT_API_URL, [], "application/x-www-form-urlencoded", Data}).
-define(DEFAULT_FORMAT, ["[", severity, "] ",
{pid, ""},
{module, [
{pid, ["@"], ""},
module,
{function, [":", function], ""},
{line, [":",line], ""}], ""},
" ", message]).
init([AuthToken, RoomId, Sender, Color, Mentions, Notify, Level, RetryTimes, RetryInterval]) ->
init([AuthToken, RoomId, Sender, Color, Mentions, Notify, Level, {lager_default_formatter, ?DEFAULT_FORMAT}, RetryTimes, RetryInterval]);
init([AuthToken, RoomId, Sender, Color, Mentions, Notify, Level, {Formatter, FormatterConfig}, RetryTimes, RetryInterval])
when is_list(RoomId), is_list(Sender), is_list(Mentions), is_boolean(Notify), is_atom(Formatter) ->
State = #state{
auth_token = AuthToken
,room_id = RoomId
,sender = Sender
,color = any_to_list(Color)
,mentions = any_to_list(Mentions)
,notify = any_to_list(Notify)
,level = lager_util:level_to_num(Level)
,formatter = Formatter
,format_config = FormatterConfig
,retry_interval = RetryInterval
,retry_times = RetryTimes
},
{ok, State}.
handle_call(get_loglevel, #state{ level = Level } = State) ->
{ok, Level, State};
handle_call({set_loglevel, Level}, State) ->
{ok, ok, State#state{ level = lager_util:level_to_num(Level) }};
handle_call(_Request, State) ->
{ok, ok, State}.
handle_event({log, Message}, #state{ auth_token = AuthToken, color = Color, mentions = Mentions,
room_id = RoomId, sender = Sender, notify = Notify, level = Level,
formatter = Formatter, format_config = FormatterConfig,
retry_times = RetryTimes, retry_interval = RetryInterval} = State) ->
case lager_util:is_loggable(Message, Level, ?MODULE) of
true ->
Data = [{"format", "json"}, {"auth_token", AuthToken},
{"room_id", RoomId}, {"color", Color},
{"message_format", "text"}, {"message", convert_lager_message(Message, Formatter, FormatterConfig, Mentions)},
{"notify", Notify}, {"from", Sender}],
spawn(?MODULE, send_log, [?HIPCHAT_API_REQUEST(url_encode(Data)), RetryTimes, RetryInterval]),
{ok, State};
false ->
{ok, State}
end;
handle_event(_Event, State) ->
{ok, State}.
handle_info(_Info, State) ->
{ok, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%% Private
-spec(url_encode([{string(), string()}]) -> string()).
url_encode(Attributes) ->
url_encode(Attributes, "").
url_encode([], Acc) ->
Acc;
url_encode([{Key, Value} | Rest], "") when is_list(Key),
is_list(Value) ->
url_encode(Rest, edoc_lib:escape_uri(Key) ++ "=" ++ edoc_lib:escape_uri(Value));
url_encode([{Key, Value} | Rest], Acc) when is_list(Key),
is_list(Value) ->
url_encode(Rest, Acc ++ "&" ++ edoc_lib:escape_uri(Key) ++ "=" ++ edoc_lib:escape_uri(Value)).
-spec(send_log(Request :: term(), Retries :: integer(), Interval :: integer()) -> ok).
send_log(_Request, 0, _) ->
ok;
send_log(Request, Retries, Interval) ->
case httpc:request(post, Request, [], [{sync, true}]) of
{ok, {{_, 200, _}, _Headers, _Body}} -> ok;
_Response ->
timer:sleep(Interval * 1000),
send_log(Request, Retries - 1, Interval)
end.
-spec(convert_lager_message(Message::lager_msg:lager_msg(), Formatter::atom(), FormatterConfig::list(), Mentions::[string()]) -> string()).
convert_lager_message(Message, Formatter, FormatterConfig, Mentions) ->
convert_lager_message(Message, Formatter, FormatterConfig, Mentions, "").
convert_lager_message(Message, Formatter, FormatterConfig, [UserToMention | Rest], MentionsAcc) ->
convert_lager_message(Message, Formatter, FormatterConfig, Rest, MentionsAcc ++ " @" ++ UserToMention);
convert_lager_message(Message, Formatter, FormatterConfig, [], MentionsAcc) ->
binary_to_list(iolist_to_binary(Formatter:format(Message, FormatterConfig))) ++ MentionsAcc.
any_to_list([], Acc) -> Acc;
any_to_list([Value | Rest], Acc) when is_list(Value) -> any_to_list(Rest, [Value | Acc]);
any_to_list([Value | Rest], Acc) -> any_to_list(Rest, [any_to_list(Value) | Acc]).
any_to_list(List=[_Value | _Rest]) -> any_to_list(List, []);
any_to_list(true) -> "1";
any_to_list(false) -> "0";
any_to_list(Value) when is_atom(Value) -> atom_to_list(Value);
any_to_list(Value) -> Value.