%% Copyright (c) 2011-2012 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_default_formatter). %% %% Include files %% -include("lager.hrl"). -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). -endif. %% %% Exported Functions %% -export([format/2]). %% %% 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()) -> any(). format(Msg,[]) -> format(Msg, [{eol, "\n"}]); format(Msg,[{eol, EOL}]) -> format(Msg, [date, " ", time, " [", severity, "] ", {pid, ""}, {module, [ {pid, ["@"], ""}, module, {function, [":", function], ""}, {line, [":",line], ""}], ""}, " ", message, EOL]); format(Message,Config) -> [ output(V,Message) || V <- Config ]. -spec output(term(),lager_msg:lager_msg()) -> iolist(). output(message,Msg) -> lager_msg:message(Msg); output(date,Msg) -> {D, _T} = lager_msg:timestamp(Msg), D; output(time,Msg) -> {_D, T} = lager_msg:timestamp(Msg), T; output(node,Msg) -> atom_to_list(node()); 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). -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). basic_test_() -> [{"Default formatting test", ?_assertEqual(iolist_to_binary([<<"Day Time [error] ">>, pid_to_list(self()), <<" Message\n">>]), iolist_to_binary(format(lager_msg:new("Message", {"Day", "Time"}, error, [{pid, self()}], []), []))) }, {"Basic Formatting", ?_assertEqual(<<"Simplist Format">>, iolist_to_binary(format(lager_msg:new("Message", {"Day", "Time"}, error, [{pid, self()}], []), ["Simplist Format"]))) }, {"Default equivalent formatting test", ?_assertEqual(iolist_to_binary([<<"Day Time [error] ">>, pid_to_list(self()), <<" Message\n">>]), iolist_to_binary(format(lager_msg:new("Message", {"Day", "Time"}, error, [{pid, self()}], []), [date, " ", time," [",severity,"] ",pid, " ", message, "\n"] ))) }, {"Non existant metadata can default to string", ?_assertEqual(iolist_to_binary([<<"Day Time [error] Fallback Message\n">>]), iolist_to_binary(format(lager_msg:new("Message", {"Day", "Time"}, error, [{pid, self()}], []), [date, " ", time," [",severity,"] ",{does_not_exist,"Fallback"}, " ", message, "\n"] ))) }, {"Non existant metadata can default to other metadata", ?_assertEqual(iolist_to_binary([<<"Day Time [error] Fallback Message\n">>]), iolist_to_binary(format(lager_msg:new("Message", {"Day", "Time"}, error, [{pid, "Fallback"}], []), [date, " ", time," [",severity,"] ",{does_not_exist,pid}, " ", message, "\n"] ))) } ]. -endif.