Skip to content

Commit

Permalink
Merge pull request #3 from wk8/wk8/lager_handler
Browse files Browse the repository at this point in the history
Adding a lager handler to report lager messages above a certain level to Busgnag
  • Loading branch information
aeden committed Apr 6, 2016
2 parents 7cbd5d0 + 9217d02 commit 8fd35bc
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 15 deletions.
11 changes: 11 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ application:start(bugsnag)

Or add the application to your .app configuration.

### Lager handler

We also provide a [lager](https://github.com/basho/lager) to report anything
above a certain level (by default, `error`) to Bugsnag.

For example, simply add
```
{bugsnag_lager_handler, critical}
```
to your lager handler config.

## Thanks

Thank you to Ken Pratt: his library https://github.com/kenpratt/erlbrake provided a lot of code for this library.
48 changes: 33 additions & 15 deletions src/bugsnag.erl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ start_link(ApiKey, ReleaseStage) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [ApiKey, ReleaseStage], []).

notify(Type, Reason, Message, Module, Line) ->
notify(Type, Reason, Message, Module, Line, undefined, undefined).
notify(Type, Reason, Message, Module, Line, generate_trace(), undefined).
notify(Type, Reason, Message, Module, Line, Trace, Request) ->
gen_server:cast(?MODULE, {exception, Type, Reason, Message, Module, Line, Trace, Request}).

Expand Down Expand Up @@ -65,24 +65,29 @@ code_change(_OldVsn, State, _Extra) ->
{ok, State}.

% Internal API
% See https://bugsnag.com/docs/notifier-api
send_exception(_Type, Reason, Message, _Module, _Line, Trace, _Request, State) ->
{ok, Hostname} = inet:gethostname(),
Payload = [
{apiKey, list_to_binary(State#state.api_key)},
{apiKey, to_bin(State#state.api_key)},
{notifier, [
{name, ?NOTIFIER_NAME},
{version, ?NOTIFIER_VERSION},
{url, ?NOTIFIER_URL}
]},
{events, [
[
{payloadVersion, "2"},
{context, list_to_binary(Hostname)},
{releaseStage, list_to_binary(State#state.release_stage)},
{payloadVersion, <<"2">>},
{device, [
{hostname, to_bin(Hostname)}
]},
{app, [
{releaseStage, to_bin(State#state.release_stage)}
]},
{exceptions, [
[
{errorClass, list_to_binary(io_lib:format("~p", [Reason]))},
{message, list_to_binary(io_lib:format("~p", [Message]))},
{errorClass, to_bin(Reason)},
{message, to_bin(Message)},
{stacktrace, process_trace(Trace)}
]
]}
Expand All @@ -91,8 +96,6 @@ send_exception(_Type, Reason, Message, _Module, _Line, Trace, _Request, State) -
],
deliver_payload(jsx:encode(Payload)).


process_trace(undefined) -> [];
process_trace(Trace) ->
lager:info("Processing trace ~p", [Trace]),
process_trace(Trace, []).
Expand All @@ -102,28 +105,43 @@ process_trace([Current|Rest], ProcessedTrace) ->
StackTraceLine = case Current of
{_, F, _, [{file, File}, {line, Line}]} ->
[
{file, list_to_binary(File)},
{file, to_bin(File)},
{lineNumber, Line},
{method, list_to_binary(io_lib:format("~p", [F]))}
{method, to_bin(F)}
];
{_, F, _} ->
[
{method, list_to_binary(io_lib:format("~p", [F]))}
{method, to_bin(F)}
];
_ ->
lager:error("Discarding stack trace line: ~p", [Current]),
lager:warning("Discarding stack trace line: ~p", [Current]),
[]
end,
process_trace(Rest, ProcessedTrace ++ [StackTraceLine]).


deliver_payload(Payload) ->
lager:info("Sending exception: ~p", [Payload]),
case httpc:request(post, {?NOTIFY_ENDPOINT, [], "application/json", Payload}, [{timeout, 5000}], []) of
{ok, {{_Version, 200, _ReasonPhrase}, _Headers, Body}} ->
lager:info("Error sent. Response: ~p", [Body]);
{_, {{_Version, Status, ReasonPhrase}, _Headers, _Body}} ->
lager:error("Failed to send error to bugsnag (~p : ~p)", [Status, ReasonPhrase])
lager:warning("Failed to send error to bugsnag (~p : ~p)", [Status, ReasonPhrase])
end,

ok.

to_bin(Atom) when erlang:is_atom(Atom) ->
erlang:atom_to_binary(Atom, utf8);
to_bin(Bin) when erlang:is_binary(Bin) ->
Bin;
to_bin(Int) when erlang:is_integer(Int) ->
erlang:integer_to_binary(Int);
to_bin(List) when erlang:is_list(List) ->
erlang:iolist_to_binary(List).

generate_trace() ->
lager:info("Generating trace"),
try
throw(bugsnag_gen_trace)
catch bugsnag_gen_trace -> erlang:get_stacktrace()
end.
76 changes: 76 additions & 0 deletions src/bugsnag_lager_handler.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
%% A lager handler to report messages above a certain level to bugsnag
-module(bugsnag_lager_handler).
-behaviour(gen_event).

-export([
init/1,
handle_call/2,
handle_event/2,
handle_info/2,
terminate/2,
code_change/3
]).

-record(state, {
level :: {'mask', integer()}
}).

-define(DEFAULT_LEVEL, error).

init(Level) when erlang:is_atom(Level) ->
init([{level, Level}]);
init(Options) ->
Level = proplists:get_value(level, Options, ?DEFAULT_LEVEL),

{ok, #state{level = lager_util:config_to_mask(Level)}}.

handle_call(get_loglevel, #state{level = Level} = State) ->
{ok, Level, State};
handle_call({set_loglevel, Level}, State) ->
{ok, ok, State#state{level = lager_util:config_to_mask(Level)}};
handle_call(_Request, State) ->
{ok, ok, State}.

handle_event({log, Message}, #state{level = Level} = State) ->
case lager_util:is_loggable(Message, Level, ?MODULE) of
true ->
ok = notify(Message),
{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}.

notify(Message) ->
ok = bugsnag:notify(lager_msg, reason(Message), message(Message),
module(Message), line(Message)).

reason(Message) ->
Severity = lager_msg:severity(Message),
"lager:" ++ erlang:atom_to_list(Severity).

message(Message) ->
lager_msg:message(Message).

module(Message) ->
get_metadata(module, Message).

line(Message) ->
get_metadata(line, Message).

get_metadata(Key, Message) ->
Metadata = lager_msg:metadata(Message),
case lists:keyfind(Key, 1, Metadata) of
false -> undefined;
{Key, Value} -> Value
end.

0 comments on commit 8fd35bc

Please sign in to comment.