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.

298 line
11 KiB

14 年之前
14 年之前
14 年之前
14 年之前
14 年之前
14 年之前
14 年之前
14 年之前
14 年之前
14 年之前
14 年之前
14 年之前
14 年之前
14 年之前
14 年之前
14 年之前
14 年之前
14 年之前
14 年之前
14 年之前
  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}]).