您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

298 行
11 KiB

  1. %% Copyright (c) 2011 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. %% @doc The lager logging framework.
  17. -module(lager).
  18. -include("lager.hrl").
  19. %% API
  20. -export([start/0,
  21. log/8, log_dest/9, log/3, log/4,
  22. trace_file/2, trace_file/3, trace_console/1, trace_console/2,
  23. clear_all_traces/0, stop_trace/1, status/0,
  24. get_loglevel/1, set_loglevel/2, set_loglevel/3, get_loglevels/0,
  25. minimum_loglevel/1, posix_error/1,
  26. safe_format/3, safe_format_chop/3,dispatch_log/8]).
  27. -type log_level() :: debug | info | notice | warning | error | critical | alert | emergency.
  28. -type log_level_number() :: 0..7.
  29. -export_type([log_level/0, log_level_number/0]).
  30. %% API
  31. %% @doc Start the application. Mainly useful for using `-s lager' as a command
  32. %% line switch to the VM to make lager start on boot.
  33. start() -> start(lager).
  34. start(App) ->
  35. start_ok(App, application:start(App, permanent)).
  36. start_ok(_App, ok) -> ok;
  37. start_ok(_App, {error, {already_started, _App}}) -> ok;
  38. start_ok(App, {error, {not_started, Dep}}) ->
  39. ok = start(Dep),
  40. start(App);
  41. start_ok(App, {error, Reason}) ->
  42. erlang:error({app_start_failed, App, Reason}).
  43. -spec dispatch_log(log_level(), atom(), atom(), pos_integer(), pid(), list(), string(), list()) ->
  44. ok | {error, lager_not_running}.
  45. dispatch_log(Severity, Module, Function, Line, Pid, Traces, Format, Args) ->
  46. {LevelThreshold,TraceFilters} = lager_mochiglobal:get(loglevel,{?LOG_NONE,[]}),
  47. Result=
  48. case LevelThreshold >= lager_util:level_to_num(Severity) of
  49. true -> lager:log(Severity,Module,Function,Line,Pid,
  50. lager_util:maybe_utc(lager_util:localtime_ms()),
  51. Format,Args);
  52. _ -> ok
  53. end,
  54. case TraceFilters of
  55. [] -> Result;
  56. Match when is_list(Match) ->
  57. lager:log_dest(Severity,Module,Function,Line,Pid,
  58. lager_util:maybe_utc(lager_util:localtime_ms()),
  59. lager_util:check_traces(Traces,
  60. lager_util:level_to_num(Severity),
  61. TraceFilters,
  62. []),
  63. Format,Args);
  64. _ -> ok
  65. end.
  66. %% @private
  67. -spec log(log_level(), atom(), atom(), pos_integer(), pid(), tuple(), string(), list()) ->
  68. ok | {error, lager_not_running}.
  69. log(Level, Module, Function, Line, Pid, Time, Format, Args) ->
  70. Timestamp = lager_util:format_time(Time),
  71. Msg = [["[", atom_to_list(Level), "] "],
  72. io_lib:format("~p@~p:~p:~p ", [Pid, Module, Function, Line]),
  73. safe_format_chop(Format, Args, 4096)],
  74. safe_notify({log, lager_util:level_to_num(Level), Timestamp, Msg}).
  75. %% @private
  76. -spec log_dest(log_level(), atom(), atom(), pos_integer(), pid(), tuple(), list(), string(), list()) ->
  77. ok | {error, lager_not_running}.
  78. log_dest(_Level, _Module, _Function, _Line, _Pid, _Time, [], _Format, _Args) ->
  79. ok;
  80. log_dest(Level, Module, Function, Line, Pid, Time, Dest, Format, Args) ->
  81. Timestamp = lager_util:format_time(Time),
  82. Msg = [["[", atom_to_list(Level), "] "],
  83. io_lib:format("~p@~p:~p:~p ", [Pid, Module, Function, Line]),
  84. safe_format_chop(Format, Args, 4096)],
  85. safe_notify({log, Dest, lager_util:level_to_num(Level), Timestamp, Msg}).
  86. %% @doc Manually log a message into lager without using the parse transform.
  87. -spec log(log_level(), pid(), list()) -> ok | {error, lager_not_running}.
  88. log(Level, Pid, Message) ->
  89. Timestamp = lager_util:format_time(),
  90. Msg = [["[", atom_to_list(Level), "] "], io_lib:format("~p ", [Pid]),
  91. safe_format_chop("~s", [Message], 4096)],
  92. safe_notify({log, lager_util:level_to_num(Level), Timestamp, Msg}).
  93. %% @doc Manually log a message into lager without using the parse transform.
  94. -spec log(log_level(), pid(), string(), list()) -> ok | {error, lager_not_running}.
  95. log(Level, Pid, Format, Args) ->
  96. Timestamp = lager_util:format_time(),
  97. Msg = [["[", atom_to_list(Level), "] "], io_lib:format("~p ", [Pid]),
  98. safe_format_chop(Format, Args, 4096)],
  99. safe_notify({log, lager_util:level_to_num(Level), Timestamp, Msg}).
  100. trace_file(File, Filter) ->
  101. trace_file(File, Filter, debug).
  102. trace_file(File, Filter, Level) ->
  103. Trace0 = {Filter, Level, {lager_file_backend, File}},
  104. case lager_util:validate_trace(Trace0) of
  105. {ok, Trace} ->
  106. Handlers = gen_event:which_handlers(lager_event),
  107. %% check if this file backend is already installed
  108. Res = case lists:member({lager_file_backend, File}, Handlers) of
  109. false ->
  110. %% install the handler
  111. supervisor:start_child(lager_handler_watcher_sup,
  112. [lager_event, {lager_file_backend, File}, {File, none}]);
  113. _ ->
  114. {ok, exists}
  115. end,
  116. case Res of
  117. {ok, _} ->
  118. %% install the trace.
  119. {MinLevel, Traces} = lager_mochiglobal:get(loglevel),
  120. case lists:member(Trace, Traces) of
  121. false ->
  122. lager_mochiglobal:put(loglevel, {MinLevel, [Trace|Traces]});
  123. _ ->
  124. ok
  125. end,
  126. {ok, Trace};
  127. {error, _} = E ->
  128. E
  129. end;
  130. Error ->
  131. Error
  132. end.
  133. trace_console(Filter) ->
  134. trace_console(Filter, debug).
  135. trace_console(Filter, Level) ->
  136. Trace0 = {Filter, Level, lager_console_backend},
  137. case lager_util:validate_trace(Trace0) of
  138. {ok, Trace} ->
  139. {MinLevel, Traces} = lager_mochiglobal:get(loglevel),
  140. case lists:member(Trace, Traces) of
  141. false ->
  142. lager_mochiglobal:put(loglevel, {MinLevel, [Trace|Traces]});
  143. _ -> ok
  144. end,
  145. {ok, Trace};
  146. Error ->
  147. Error
  148. end.
  149. stop_trace({_Filter, _Level, Target} = Trace) ->
  150. {MinLevel, Traces} = lager_mochiglobal:get(loglevel),
  151. NewTraces = lists:delete(Trace, Traces),
  152. lager_mochiglobal:put(loglevel, {MinLevel, NewTraces}),
  153. case get_loglevel(Target) of
  154. none ->
  155. %% check no other traces point here
  156. case lists:keyfind(Target, 3, NewTraces) of
  157. false ->
  158. gen_event:delete_handler(lager_event, Target, []);
  159. _ ->
  160. ok
  161. end;
  162. _ ->
  163. ok
  164. end,
  165. ok.
  166. clear_all_traces() ->
  167. {MinLevel, _Traces} = lager_mochiglobal:get(loglevel),
  168. lager_mochiglobal:put(loglevel, {MinLevel, []}),
  169. lists:foreach(fun(Handler) ->
  170. case get_loglevel(Handler) of
  171. none ->
  172. gen_event:delete_handler(lager_event, Handler, []);
  173. _ ->
  174. ok
  175. end
  176. end, gen_event:which_handlers(lager_event)).
  177. status() ->
  178. Handlers = gen_event:which_handlers(lager_event),
  179. Status = ["Lager status:\n",
  180. [begin
  181. Level = get_loglevel(Handler),
  182. case Handler of
  183. {lager_file_backend, File} ->
  184. io_lib:format("File ~s at level ~p\n", [File, Level]);
  185. lager_console_backend ->
  186. io_lib:format("Console at level ~p\n", [Level]);
  187. _ ->
  188. []
  189. end
  190. end || Handler <- Handlers],
  191. "Active Traces:\n",
  192. [begin
  193. io_lib:format("Tracing messages matching ~p at level ~p to ~p\n",
  194. [Filter, lager_util:num_to_level(Level), Destination])
  195. end || {Filter, Level, Destination} <- element(2, lager_mochiglobal:get(loglevel))]],
  196. io:put_chars(Status).
  197. %% @doc Set the loglevel for a particular backend.
  198. set_loglevel(Handler, Level) when is_atom(Level) ->
  199. Reply = gen_event:call(lager_event, Handler, {set_loglevel, Level}, infinity),
  200. %% recalculate min log level
  201. MinLog = minimum_loglevel(get_loglevels()),
  202. {_, Traces} = lager_mochiglobal:get(loglevel),
  203. lager_mochiglobal:put(loglevel, {MinLog, Traces}),
  204. Reply.
  205. %% @doc Set the loglevel for a particular backend that has multiple identifiers
  206. %% (eg. the file backend).
  207. set_loglevel(Handler, Ident, Level) when is_atom(Level) ->
  208. io:format("handler: ~p~n", [{Handler, Ident}]),
  209. Reply = gen_event:call(lager_event, {Handler, Ident}, {set_loglevel, Level}, infinity),
  210. %% recalculate min log level
  211. MinLog = minimum_loglevel(get_loglevels()),
  212. {_, Traces} = lager_mochiglobal:get(loglevel),
  213. lager_mochiglobal:put(loglevel, {MinLog, Traces}),
  214. Reply.
  215. %% @doc Get the loglevel for a particular backend. In the case that the backend
  216. %% has multiple identifiers, the lowest is returned
  217. get_loglevel(Handler) ->
  218. case gen_event:call(lager_event, Handler, get_loglevel, infinity) of
  219. X when is_integer(X) ->
  220. lager_util:num_to_level(X);
  221. Y -> Y
  222. end.
  223. %% @doc Try to convert an atom to a posix error, but fall back on printing the
  224. %% term if its not a valid posix error code.
  225. posix_error(Error) when is_atom(Error) ->
  226. case erl_posix_msg:message(Error) of
  227. "unknown POSIX error" -> atom_to_list(Error);
  228. Message -> Message
  229. end;
  230. posix_error(Error) ->
  231. safe_format_chop("~p", [Error], 4096).
  232. %% @private
  233. get_loglevels() ->
  234. [gen_event:call(lager_event, Handler, get_loglevel, infinity) ||
  235. Handler <- gen_event:which_handlers(lager_event)].
  236. %% @private
  237. minimum_loglevel([]) ->
  238. -1; %% lower than any log level, logging off
  239. minimum_loglevel(Levels) ->
  240. erlang:hd(lists:reverse(lists:sort(Levels))).
  241. safe_notify(Event) ->
  242. case whereis(lager_event) of
  243. undefined ->
  244. %% lager isn't running
  245. {error, lager_not_running};
  246. Pid ->
  247. gen_event:sync_notify(Pid, Event)
  248. end.
  249. %% @doc Print the format string `Fmt' with `Args' safely with a size
  250. %% limit of `Limit'. If the format string is invalid, or not enough
  251. %% arguments are supplied 'FORMAT ERROR' is printed with the offending
  252. %% arguments. The caller is NOT crashed.
  253. safe_format(Fmt, Args, Limit) ->
  254. safe_format(Fmt, Args, Limit, []).
  255. safe_format(Fmt, Args, Limit, Options) ->
  256. try lager_trunc_io:format(Fmt, Args, Limit, Options)
  257. catch
  258. _:_ -> lager_trunc_io:format("FORMAT ERROR: ~p ~p", [Fmt, Args], Limit)
  259. end.
  260. %% @private
  261. safe_format_chop(Fmt, Args, Limit) ->
  262. safe_format(Fmt, Args, Limit, [{chomp, true}]).