Created
March 25, 2015 20:00
-
-
Save angrycub/97319a1a58d3b3a8e95b to your computer and use it in GitHub Desktop.
Modifying lager_default_formatter to make a filtering formatter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% Copyright (c) 2015 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_sensitive_formatter). | |
%% | |
%% Include files | |
%% | |
-include("lager.hrl"). | |
-ifdef(TEST). | |
-include_lib("eunit/include/eunit.hrl"). | |
-endif. | |
%% | |
%% Exported Functions | |
%% | |
-export([format/2, format/3]). | |
%% | |
%% API Functions | |
%% | |
%% @doc Provides a generic, default formatting for log messages using a semi-iolist as configuration. Any iolist allowed | |
%% elements in the configuration are printed verbatim. Atoms in the configuration are treated as metadata properties | |
%% and extracted from the log message. Optionally, a tuple of {atom(),semi-iolist()} can be used. The atom will look | |
%% up the property, but if not found it will use the semi-iolist() instead. These fallbacks can be similarly nested | |
%% or refer to other properties, if desired. You can also use a {atom, semi-iolist(), semi-iolist()} formatter, which | |
%% acts like a ternary operator's true/false branches. | |
%% | |
%% The metadata properties date,time, message, and severity will always exist. | |
%% The properties pid, file, line, module, and function will always exist if the parser transform is used. | |
%% | |
%% Example: | |
%% | |
%% `["Foo"]' -> "Foo", regardless of message content. | |
%% | |
%% `[message]' -> The content of the logged message, alone. | |
%% | |
%% `[{pid,"Unknown Pid"}]' -> "?.?.?" if pid is in the metadata, "Unknown Pid" if not. | |
%% | |
%% `[{pid, ["My pid is ", pid], "Unknown Pid"}]' -> if pid is in the metada print "My pid is ?.?.?", otherwise print "Unknown Pid" | |
%% @end | |
-spec format(lager_msg:lager_msg(),list(),list()) -> any(). | |
format(Msg,[], Colors) -> | |
format(Msg, [{eol, "\n"}], Colors); | |
format(Msg,[{eol, EOL}], Colors) -> | |
format(Msg, | |
[date, " ", time, " ", color, "[", severity, "] ", | |
{pid, ""}, | |
{module, [ | |
{pid, ["@"], ""}, | |
module, | |
{function, [":", function], ""}, | |
{line, [":",line], ""}], ""}, | |
" ", message, EOL], Colors); | |
format(Message,Config,Colors) -> | |
[ case V of | |
color -> output_color(Message,Colors); | |
_ -> output(V,Message) | |
end || V <- Config ]. | |
-spec format(lager_msg:lager_msg(),list()) -> any(). | |
format(Msg, Config) -> | |
format(Msg, Config, []). | |
-spec output(term(),lager_msg:lager_msg()) -> iolist(). | |
output(message,Msg) -> | |
MessageIn = lager_msg:message(Msg), | |
%% This ip filter is overly simplified for the sake of clarity. It doesn't | |
%% check the validity of the item as an IP list looks for IP-like numbers. | |
%% Four components of up to 3 digits seperated by periods | |
Message2 = re:replace(MessageIn, "(?:\\d{1,3}\\.){3}\\d{1,3}", "«redacted_ip»",[global]), | |
%% This looks for node atoms to be printed out and removes the tailing | |
%% host information | |
Message1 = re:replace(Message2, "'(.*@).*'", "'\\1«redacted_host»'",[global]), | |
%% Suppress binaries from the logstream | |
Message = re:replace(Message1, "<<.*?>>","«redacted_binary»",[global]), | |
%% Emit cleaned message | |
Message; | |
output(date,Msg) -> | |
{D, _T} = lager_msg:datetime(Msg), | |
D; | |
output(time,Msg) -> | |
{_D, T} = lager_msg:datetime(Msg), | |
T; | |
output(severity,Msg) -> | |
atom_to_list(lager_msg:severity(Msg)); | |
output(Prop,Msg) when is_atom(Prop) -> | |
Metadata = lager_msg:metadata(Msg), | |
make_printable(get_metadata(Prop,Metadata,<<"Undefined">>)); | |
output({Prop,Default},Msg) when is_atom(Prop) -> | |
Metadata = lager_msg:metadata(Msg), | |
make_printable(get_metadata(Prop,Metadata,output(Default,Msg))); | |
output({Prop, Present, Absent}, Msg) when is_atom(Prop) -> | |
%% sort of like a poor man's ternary operator | |
Metadata = lager_msg:metadata(Msg), | |
case get_metadata(Prop, Metadata) of | |
undefined -> | |
[ output(V, Msg) || V <- Absent]; | |
_ -> | |
[ output(V, Msg) || V <- Present] | |
end; | |
output(Other,_) -> make_printable(Other). | |
output_color(_Msg,[]) -> []; | |
output_color(Msg,Colors) -> | |
Level = lager_msg:severity(Msg), | |
case lists:keyfind(Level, 1, Colors) of | |
{_, Color} -> Color; | |
_ -> [] | |
end. | |
-spec make_printable(any()) -> iolist(). | |
make_printable(A) when is_atom(A) -> atom_to_list(A); | |
make_printable(P) when is_pid(P) -> pid_to_list(P); | |
make_printable(L) when is_list(L) orelse is_binary(L) -> L; | |
make_printable(Other) -> io_lib:format("~p",[Other]). | |
get_metadata(Key, Metadata) -> | |
get_metadata(Key, Metadata, undefined). | |
get_metadata(Key, Metadata, Default) -> | |
case lists:keyfind(Key, 1, Metadata) of | |
false -> | |
Default; | |
{Key, Value} -> | |
Value | |
end. | |
-ifdef(TEST). | |
date_time_now() -> | |
Now = os:timestamp(), | |
{Date, Time} = lager_util:format_time(lager_util:maybe_utc(lager_util:localtime_ms(Now))), | |
{Date, Time, Now}. | |
basic_test_() -> | |
{Date, Time, Now} = date_time_now(), | |
[{"Default formatting test", | |
?_assertEqual(iolist_to_binary([Date, " ", Time, " [error] ", pid_to_list(self()), " Message\n"]), | |
iolist_to_binary(format(lager_msg:new("Message", | |
Now, | |
error, | |
[{pid, self()}], | |
[]), | |
[]))) | |
}, | |
{"Basic Formatting", | |
?_assertEqual(<<"Simplist Format">>, | |
iolist_to_binary(format(lager_msg:new("Message", | |
Now, | |
error, | |
[{pid, self()}], | |
[]), | |
["Simplist Format"]))) | |
}, | |
{"Default equivalent formatting test", | |
?_assertEqual(iolist_to_binary([Date, " ", Time, " [error] ", pid_to_list(self()), " Message\n"]), | |
iolist_to_binary(format(lager_msg:new("Message", | |
Now, | |
error, | |
[{pid, self()}], | |
[]), | |
[date, " ", time," [",severity,"] ",pid, " ", message, "\n"] | |
))) | |
}, | |
{"Non existant metadata can default to string", | |
?_assertEqual(iolist_to_binary([Date, " ", Time, " [error] Fallback Message\n"]), | |
iolist_to_binary(format(lager_msg:new("Message", | |
Now, | |
error, | |
[{pid, self()}], | |
[]), | |
[date, " ", time," [",severity,"] ",{does_not_exist,"Fallback"}, " ", message, "\n"] | |
))) | |
}, | |
{"Non existant metadata can default to other metadata", | |
?_assertEqual(iolist_to_binary([Date, " ", Time, " [error] Fallback Message\n"]), | |
iolist_to_binary(format(lager_msg:new("Message", | |
Now, | |
error, | |
[{pid, "Fallback"}], | |
[]), | |
[date, " ", time," [",severity,"] ",{does_not_exist,pid}, " ", message, "\n"] | |
))) | |
} | |
]. | |
-endif. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi Charlie,
I have seen that you have some experience working with Riak hooks. Could give some help with this error?
https://gist.github.com/4b42251f54557f1525e9
Thanks in advance,
Humberto