You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
6.3 KiB

13 years ago
  1. %% Copyright (c) 2011-2012 Basho Technologies, Inc. All Rights Reserved.
  2. %%
  3. %% This file is provided to you under the Apache License,
  4. %% Version 2.0 (the "License"); you may not use this file
  5. %% except in compliance with the License. You may obtain
  6. %% a copy of the License at
  7. %%
  8. %% http://www.apache.org/licenses/LICENSE-2.0
  9. %%
  10. %% Unless required by applicable law or agreed to in writing,
  11. %% software distributed under the License is distributed on an
  12. %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  13. %% KIND, either express or implied. See the License for the
  14. %% specific language governing permissions and limitations
  15. %% under the License.
  16. -module(lager_default_formatter).
  17. %%
  18. %% Include files
  19. %%
  20. -include_lib("lager/include/lager.hrl").
  21. -ifdef(TEST).
  22. -include_lib("eunit/include/eunit.hrl").
  23. -endif.
  24. %%
  25. %% Exported Functions
  26. %%
  27. -export([format/2]).
  28. %%
  29. %% API Functions
  30. %%
  31. %% @doc Provides a generic, default formatting for log messages using a semi-iolist as configuration. Any iolist allowed
  32. %% elements in the configuration are printed verbatim. Atoms in the configuration are treated as metadata properties
  33. %% and extracted from the log message. Optionally, a tuple of {atom(),semi-iolist()} can be used. The atom will look
  34. %% up the property, but if not found it will use the semi-iolist() instead. These fallbacks can be similarly nested
  35. %% or refer to other properties, if desired. You can also use a {atom, semi-iolist(), semi-iolist()} formatter, which
  36. %% acts like a ternary operator's true/false branches.
  37. %%
  38. %% The metadata properties date,time, message, and severity will always exist.
  39. %% The properties pid, file, line, module, and function will always exist if the parser transform is used.
  40. %%
  41. %% Example:
  42. %%
  43. %% `["Foo"]' -> "Foo", regardless of message content.
  44. %%
  45. %% `[message]' -> The content of the logged message, alone.
  46. %%
  47. %% `[{pid,"Unknown Pid"}]' -> "?.?.?" if pid is in the metadata, "Unknown Pid" if not.
  48. %%
  49. %% `[{pid, ["My pid is ", pid], "Unknown Pid"}]' -> if pid is in the metada print "My pid is ?.?.?", otherwise print "Unknown Pid"
  50. %% @end
  51. -spec format(lager_msg:lager_msg(),list()) -> any().
  52. format(Msg,[]) ->
  53. format(Msg, [{eol, "\n"}]);
  54. format(Msg,[{eol, EOL}]) ->
  55. format(Msg,
  56. [date, " ", time, " [", severity, "] ",
  57. {pid, ""},
  58. {module, [
  59. {pid, ["@"], ""},
  60. module,
  61. {function, [":", function], ""},
  62. {line, [":",line], ""}], ""},
  63. " ", message, EOL]);
  64. format(Message,Config) ->
  65. [ output(V,Message) || V <- Config ].
  66. -spec output(term(),lager_msg:lager_msg()) -> iolist().
  67. output(message,Msg) -> lager_msg:message(Msg);
  68. output(date,Msg) ->
  69. {D, _T} = lager_msg:timestamp(Msg),
  70. D;
  71. output(time,Msg) ->
  72. {_D, T} = lager_msg:timestamp(Msg),
  73. T;
  74. output(severity,Msg) ->
  75. atom_to_list(lager_msg:severity(Msg));
  76. output(Prop,Msg) when is_atom(Prop) ->
  77. Metadata = lager_msg:metadata(Msg),
  78. make_printable(get_metadata(Prop,Metadata,<<"Undefined">>));
  79. output({Prop,Default},Msg) when is_atom(Prop) ->
  80. Metadata = lager_msg:metadata(Msg),
  81. make_printable(get_metadata(Prop,Metadata,output(Default,Msg)));
  82. output({Prop, Present, Absent}, Msg) when is_atom(Prop) ->
  83. %% sort of like a poor man's ternary operator
  84. Metadata = lager_msg:metadata(Msg),
  85. case get_metadata(Prop, Metadata) of
  86. undefined ->
  87. [ output(V, Msg) || V <- Absent];
  88. _ ->
  89. [ output(V, Msg) || V <- Present]
  90. end;
  91. output(Other,_) -> make_printable(Other).
  92. -spec make_printable(any()) -> iolist().
  93. make_printable(A) when is_atom(A) -> atom_to_list(A);
  94. make_printable(P) when is_pid(P) -> pid_to_list(P);
  95. make_printable(L) when is_list(L) orelse is_binary(L) -> L;
  96. make_printable(Other) -> io_lib:format("~p",[Other]).
  97. get_metadata(Key, Metadata) ->
  98. get_metadata(Key, Metadata, undefined).
  99. get_metadata(Key, Metadata, Default) ->
  100. case lists:keyfind(Key, 1, Metadata) of
  101. false ->
  102. Default;
  103. {Key, Value} ->
  104. Value
  105. end.
  106. -ifdef(TEST).
  107. basic_test_() ->
  108. [{"Default formatting test",
  109. ?_assertEqual(iolist_to_binary([<<"Day Time [error] ">>, pid_to_list(self()), <<" Message\n">>]),
  110. iolist_to_binary(format(lager_msg:new("Message",
  111. {"Day", "Time"},
  112. error,
  113. [{pid, self()}],
  114. []),
  115. [])))
  116. },
  117. {"Basic Formatting",
  118. ?_assertEqual(<<"Simplist Format">>,
  119. iolist_to_binary(format(lager_msg:new("Message",
  120. {"Day", "Time"},
  121. error,
  122. [{pid, self()}],
  123. []),
  124. ["Simplist Format"])))
  125. },
  126. {"Default equivalent formatting test",
  127. ?_assertEqual(iolist_to_binary([<<"Day Time [error] ">>, pid_to_list(self()), <<" Message\n">>]),
  128. iolist_to_binary(format(lager_msg:new("Message",
  129. {"Day", "Time"},
  130. error,
  131. [{pid, self()}],
  132. []),
  133. [date, " ", time," [",severity,"] ",pid, " ", message, "\n"]
  134. )))
  135. },
  136. {"Non existant metadata can default to string",
  137. ?_assertEqual(iolist_to_binary([<<"Day Time [error] Fallback Message\n">>]),
  138. iolist_to_binary(format(lager_msg:new("Message",
  139. {"Day", "Time"},
  140. error,
  141. [{pid, self()}],
  142. []),
  143. [date, " ", time," [",severity,"] ",{does_not_exist,"Fallback"}, " ", message, "\n"]
  144. )))
  145. },
  146. {"Non existant metadata can default to other metadata",
  147. ?_assertEqual(iolist_to_binary([<<"Day Time [error] Fallback Message\n">>]),
  148. iolist_to_binary(format(lager_msg:new("Message",
  149. {"Day", "Time"},
  150. error,
  151. [{pid, "Fallback"}],
  152. []),
  153. [date, " ", time," [",severity,"] ",{does_not_exist,pid}, " ", message, "\n"]
  154. )))
  155. }
  156. ].
  157. -endif.