| %% 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, 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) -> lager_msg:message(Msg); |
| 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. |