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

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