25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

417 lines
17 KiB

14 년 전
10 년 전
  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. %% @doc Lager's application module. Not a lot to see here.
  17. %% @private
  18. -module(lager_app).
  19. -behaviour(application).
  20. -include("lager.hrl").
  21. -ifdef(TEST).
  22. -compile([export_all]).
  23. -include_lib("eunit/include/eunit.hrl").
  24. -endif.
  25. -export([start/0,
  26. start/2,
  27. start_handler/3,
  28. configure_sink/2,
  29. stop/1,
  30. boot/1]).
  31. %% The `application:get_env/3` compatibility wrapper is useful
  32. %% for other modules
  33. -export([get_env/3]).
  34. -define(FILENAMES, '__lager_file_backend_filenames').
  35. -define(THROTTLE, lager_backend_throttle).
  36. -define(DEFAULT_HANDLER_CONF,
  37. [{lager_console_backend, info},
  38. {lager_file_backend,
  39. [{file, "log/error.log"}, {level, error},
  40. {size, 10485760}, {date, "$D0"}, {count, 5}]
  41. },
  42. {lager_file_backend,
  43. [{file, "log/console.log"}, {level, info},
  44. {size, 10485760}, {date, "$D0"}, {count, 5}]
  45. }
  46. ]).
  47. start() ->
  48. application:start(lager).
  49. start_throttle(Sink, Threshold, Window) ->
  50. _ = supervisor:start_child(lager_handler_watcher_sup,
  51. [Sink, ?THROTTLE, [Threshold, Window]]),
  52. ok.
  53. determine_async_behavior(_Sink, undefined, _Window) ->
  54. ok;
  55. determine_async_behavior(_Sink, Threshold, _Window) when not is_integer(Threshold) orelse Threshold < 0 ->
  56. error_logger:error_msg("Invalid value for 'async_threshold': ~p~n",
  57. [Threshold]),
  58. throw({error, bad_config});
  59. determine_async_behavior(Sink, Threshold, undefined) ->
  60. start_throttle(Sink, Threshold, erlang:trunc(Threshold * 0.2));
  61. determine_async_behavior(_Sink, Threshold, Window) when not is_integer(Window) orelse Window > Threshold orelse Window < 0 ->
  62. error_logger:error_msg(
  63. "Invalid value for 'async_threshold_window': ~p~n", [Window]),
  64. throw({error, bad_config});
  65. determine_async_behavior(Sink, Threshold, Window) ->
  66. start_throttle(Sink, Threshold, Window).
  67. start_handlers(_Sink, undefined) ->
  68. ok;
  69. start_handlers(_Sink, Handlers) when not is_list(Handlers) ->
  70. error_logger:error_msg(
  71. "Invalid value for 'handlers' (must be list): ~p~n", [Handlers]),
  72. throw({error, bad_config});
  73. start_handlers(Sink, Handlers) ->
  74. %% handlers failing to start are handled in the handler_watcher
  75. lager_config:global_set(handlers,
  76. lager_config:global_get(handlers, []) ++
  77. lists:map(fun({Module, Config}) ->
  78. check_handler_config(Module, Config),
  79. start_handler(Sink, Module, Config);
  80. (_) ->
  81. throw({error, bad_config})
  82. end,
  83. expand_handlers(Handlers))),
  84. ok.
  85. start_handler(Sink, Module, Config) ->
  86. {ok, Watcher} = supervisor:start_child(lager_handler_watcher_sup,
  87. [Sink, Module, Config]),
  88. {Module, Watcher, Sink}.
  89. check_handler_config({lager_file_backend, F}, Config) when is_list(Config); is_tuple(Config) ->
  90. Fs = case get(?FILENAMES) of
  91. undefined -> ordsets:new();
  92. X -> X
  93. end,
  94. case ordsets:is_element(F, Fs) of
  95. true ->
  96. error_logger:error_msg(
  97. "Cannot have same file (~p) in multiple file backends~n", [F]),
  98. throw({error, bad_config});
  99. false ->
  100. put(?FILENAMES,
  101. ordsets:add_element(F, Fs))
  102. end,
  103. ok;
  104. check_handler_config(_Handler, Config) when is_list(Config) orelse is_atom(Config) ->
  105. ok;
  106. check_handler_config(Handler, _BadConfig) ->
  107. throw({error, {bad_config, Handler}}).
  108. clean_up_config_checks() ->
  109. erase(?FILENAMES).
  110. interpret_hwm(undefined) ->
  111. undefined;
  112. interpret_hwm(HWM) when not is_integer(HWM) orelse HWM < 0 ->
  113. _ = lager:log(warning, self(), "Invalid error_logger high water mark: ~p, disabling", [HWM]),
  114. undefined;
  115. interpret_hwm(HWM) ->
  116. HWM.
  117. maybe_install_sink_killer(_Sink, undefined, _ReinstallTimer) -> ok;
  118. maybe_install_sink_killer(Sink, HWM, undefined) -> maybe_install_sink_killer(Sink, HWM, 5000);
  119. maybe_install_sink_killer(Sink, HWM, ReinstallTimer) when is_integer(HWM) andalso is_integer(ReinstallTimer)
  120. andalso HWM >= 0 andalso ReinstallTimer >= 0 ->
  121. _ = supervisor:start_child(lager_handler_watcher_sup, [Sink, lager_manager_killer,
  122. [HWM, ReinstallTimer]]);
  123. maybe_install_sink_killer(_Sink, HWM, ReinstallTimer) ->
  124. error_logger:error_msg("Invalid value for 'killer_hwm': ~p or 'killer_reinstall_after': ~p", [HWM, ReinstallTimer]),
  125. throw({error, bad_config}).
  126. -spec start_error_logger_handler(boolean(), pos_integer(), list()) -> list().
  127. start_error_logger_handler(false, _HWM, _Whitelist) ->
  128. [];
  129. start_error_logger_handler(true, HWM, WhiteList) ->
  130. GlStrategy = case application:get_env(lager, error_logger_groupleader_strategy) of
  131. undefined ->
  132. handle;
  133. {ok, GlStrategy0} when
  134. GlStrategy0 =:= handle;
  135. GlStrategy0 =:= ignore;
  136. GlStrategy0 =:= mirror ->
  137. GlStrategy0;
  138. {ok, BadGlStrategy} ->
  139. error_logger:error_msg(
  140. "Invalid value for 'error_logger_groupleader_strategy': ~p~n",
  141. [BadGlStrategy]),
  142. throw({error, bad_config})
  143. end,
  144. _ = case supervisor:start_child(lager_handler_watcher_sup, [error_logger, error_logger_lager_h, [HWM, GlStrategy]]) of
  145. {ok, _} ->
  146. [begin error_logger:delete_report_handler(X), X end ||
  147. X <- gen_event:which_handlers(error_logger) -- [error_logger_lager_h | WhiteList]];
  148. {error, _} ->
  149. []
  150. end,
  151. Handlers = case application:get_env(lager, handlers) of
  152. undefined ->
  153. [{lager_console_backend, info},
  154. {lager_file_backend, [{file, "log/error.log"}, {level, error}, {size, 10485760}, {date, "$D0"}, {count, 5}]},
  155. {lager_file_backend, [{file, "log/console.log"}, {level, info}, {size, 10485760}, {date, "$D0"}, {count, 5}]}];
  156. {ok, Val} ->
  157. Val
  158. end,
  159. Handlers.
  160. configure_sink(Sink, SinkDef) ->
  161. lager_config:new_sink(Sink),
  162. ChildId = lager_util:make_internal_sink_name(Sink),
  163. _ = supervisor:start_child(lager_sup,
  164. {ChildId,
  165. {gen_event, start_link,
  166. [{local, Sink}]},
  167. permanent, 5000, worker, dynamic}),
  168. determine_async_behavior(Sink, proplists:get_value(async_threshold, SinkDef),
  169. proplists:get_value(async_threshold_window, SinkDef)
  170. ),
  171. _ = maybe_install_sink_killer(Sink, proplists:get_value(killer_hwm, SinkDef),
  172. proplists:get_value(killer_reinstall_after, SinkDef)),
  173. start_handlers(Sink,
  174. proplists:get_value(handlers, SinkDef, [])),
  175. lager:update_loglevel_config(Sink).
  176. configure_extra_sinks(Sinks) ->
  177. lists:foreach(fun({Sink, Proplist}) -> configure_sink(Sink, Proplist) end,
  178. Sinks).
  179. -spec get_env(atom(), atom()) -> term().
  180. get_env(Application, Key) ->
  181. get_env(Application, Key, undefined).
  182. %% R15 doesn't know about application:get_env/3
  183. -spec get_env(atom(), atom(), term()) -> term().
  184. get_env(Application, Key, Default) ->
  185. get_env_default(application:get_env(Application, Key), Default).
  186. -spec get_env_default('undefined' | {'ok', term()}, term()) -> term().
  187. get_env_default(undefined, Default) ->
  188. Default;
  189. get_env_default({ok, Value}, _Default) ->
  190. Value.
  191. start(_StartType, _StartArgs) ->
  192. {ok, Pid} = lager_sup:start_link(),
  193. SavedHandlers = boot(),
  194. _ = boot('__all_extra'),
  195. _ = boot('__traces'),
  196. clean_up_config_checks(),
  197. {ok, Pid, SavedHandlers}.
  198. boot() ->
  199. %% Handle the default sink.
  200. determine_async_behavior(?DEFAULT_SINK,
  201. get_env(lager, async_threshold),
  202. get_env(lager, async_threshold_window)),
  203. _ = maybe_install_sink_killer(?DEFAULT_SINK, get_env(lager, killer_hwm),
  204. get_env(lager, killer_reinstall_after)),
  205. start_handlers(?DEFAULT_SINK,
  206. get_env(lager, handlers, ?DEFAULT_HANDLER_CONF)),
  207. lager:update_loglevel_config(?DEFAULT_SINK),
  208. SavedHandlers = start_error_logger_handler(
  209. get_env(lager, error_logger_redirect, true),
  210. interpret_hwm(get_env(lager, error_logger_hwm, 0)),
  211. get_env(lager, error_logger_whitelist, [])
  212. ),
  213. SavedHandlers.
  214. boot('__traces') ->
  215. _ = lager_util:trace_filter(none),
  216. ok = add_configured_traces();
  217. boot('__all_extra') ->
  218. configure_extra_sinks(get_env(lager, extra_sinks, []));
  219. boot(?DEFAULT_SINK) -> boot();
  220. boot(Sink) ->
  221. AllSinksDef = get_env(lager, extra_sinks, []),
  222. boot_sink(Sink, lists:keyfind(Sink, 1, AllSinksDef)).
  223. boot_sink(Sink, {Sink, Def}) ->
  224. configure_sink(Sink, Def);
  225. boot_sink(Sink, false) ->
  226. configure_sink(Sink, []).
  227. stop(Handlers) ->
  228. lists:foreach(fun(Handler) ->
  229. error_logger:add_report_handler(Handler)
  230. end, Handlers).
  231. expand_handlers([]) ->
  232. [];
  233. expand_handlers([{lager_file_backend, [{Key, _Value}|_]=Config}|T]) when is_atom(Key) ->
  234. %% this is definitely a new-style config, no expansion needed
  235. [maybe_make_handler_id(lager_file_backend, Config) | expand_handlers(T)];
  236. expand_handlers([{lager_file_backend, Configs}|T]) ->
  237. ?INT_LOG(notice, "Deprecated lager_file_backend config detected, please consider updating it", []),
  238. [ {lager_file_backend:config_to_id(Config), Config} || Config <- Configs] ++
  239. expand_handlers(T);
  240. expand_handlers([{Mod, Config}|T]) when is_atom(Mod) ->
  241. [maybe_make_handler_id(Mod, Config) | expand_handlers(T)];
  242. expand_handlers([H|T]) ->
  243. [H | expand_handlers(T)].
  244. add_configured_traces() ->
  245. Traces = case application:get_env(lager, traces) of
  246. undefined ->
  247. [];
  248. {ok, TraceVal} ->
  249. TraceVal
  250. end,
  251. lists:foreach(fun start_configured_trace/1, Traces),
  252. ok.
  253. start_configured_trace({Handler, Filter}) ->
  254. {ok, _} = lager:trace(Handler, Filter);
  255. start_configured_trace({Handler, Filter, Level}) when is_atom(Level) ->
  256. {ok, _} = lager:trace(Handler, Filter, Level).
  257. maybe_make_handler_id(Mod, Config) ->
  258. %% Allow the backend to generate a gen_event handler id, if it wants to.
  259. %% We don't use erlang:function_exported here because that requires the module
  260. %% already be loaded, which is unlikely at this phase of startup. Using code:load
  261. %% caused undesirable side-effects with generating code-coverage reports.
  262. try Mod:config_to_id(Config) of
  263. Id ->
  264. {Id, Config}
  265. catch
  266. error:undef ->
  267. {Mod, Config}
  268. end.
  269. -ifdef(TEST).
  270. application_config_mangling_test_() ->
  271. [
  272. {"Explode the file backend handlers",
  273. ?_assertMatch(
  274. [{lager_console_backend, info},
  275. {{lager_file_backend,"error.log"},{"error.log",error,10485760, "$D0",5}},
  276. {{lager_file_backend,"console.log"},{"console.log",info,10485760, "$D0",5}}
  277. ],
  278. expand_handlers([{lager_console_backend, info},
  279. {lager_file_backend, [
  280. {"error.log", error, 10485760, "$D0", 5},
  281. {"console.log", info, 10485760, "$D0", 5}
  282. ]}]
  283. ))
  284. },
  285. {"Explode the short form of backend file handlers",
  286. ?_assertMatch(
  287. [{lager_console_backend, info},
  288. {{lager_file_backend,"error.log"},{"error.log",error}},
  289. {{lager_file_backend,"console.log"},{"console.log",info}}
  290. ],
  291. expand_handlers([{lager_console_backend, info},
  292. {lager_file_backend, [
  293. {"error.log", error},
  294. {"console.log", info}
  295. ]}]
  296. ))
  297. },
  298. {"Explode with formatter info",
  299. ?_assertMatch(
  300. [{{lager_file_backend,"test.log"}, [{"test.log", debug, 10485760, "$D0", 5},{lager_default_formatter,["[",severity,"] ", message, "\n"]}]},
  301. {{lager_file_backend,"test2.log"}, [{"test2.log",debug, 10485760, "$D0", 5},{lager_default_formatter,["2>[",severity,"] ", message, "\n"]}]}],
  302. expand_handlers([{lager_file_backend, [
  303. [{"test.log", debug, 10485760, "$D0", 5},{lager_default_formatter,["[",severity,"] ", message, "\n"]}],
  304. [{"test2.log",debug, 10485760, "$D0", 5},{lager_default_formatter,["2>[",severity,"] ",message, "\n"]}]
  305. ]
  306. }])
  307. )
  308. },
  309. {"Explode short form with short formatter info",
  310. ?_assertMatch(
  311. [{{lager_file_backend,"test.log"}, [{"test.log", debug},{lager_default_formatter,["[",severity,"] ", message, "\n"]}]},
  312. {{lager_file_backend,"test2.log"}, [{"test2.log",debug},{lager_default_formatter}]}],
  313. expand_handlers([{lager_file_backend, [
  314. [{"test.log", debug},{lager_default_formatter,["[",severity,"] ", message, "\n"]}],
  315. [{"test2.log",debug},{lager_default_formatter}]
  316. ]
  317. }])
  318. )
  319. },
  320. {"New form needs no expansion",
  321. ?_assertMatch([
  322. {{lager_file_backend,"test.log"}, [{file, "test.log"}]},
  323. {{lager_file_backend,"test2.log"}, [{file, "test2.log"}, {level, info}, {sync_on, none}]},
  324. {{lager_file_backend,"test3.log"}, [{formatter, lager_default_formatter}, {file, "test3.log"}]}
  325. ],
  326. expand_handlers([
  327. {lager_file_backend, [{file, "test.log"}]},
  328. {lager_file_backend, [{file, "test2.log"}, {level, info}, {sync_on, none}]},
  329. {lager_file_backend, [{formatter, lager_default_formatter},{file, "test3.log"}]}
  330. ])
  331. )
  332. }
  333. ].
  334. check_handler_config_test_() ->
  335. Good = expand_handlers(?DEFAULT_HANDLER_CONF),
  336. Bad = expand_handlers([{lager_console_backend, info},
  337. {lager_file_backend, [{file, "same_file.log"}]},
  338. {lager_file_backend, [{file, "same_file.log"}, {level, info}]}]),
  339. AlsoBad = [{lager_logstash_backend,
  340. {level, info},
  341. {output, {udp, "localhost", 5000}},
  342. {format, json},
  343. {json_encoder, jiffy}}],
  344. BadToo = [{fail, {fail}}],
  345. OldSchoolLagerGood = expand_handlers([{lager_console_backend,info},
  346. {lager_file_backend, [
  347. {"./log/error.log",error,10485760,"$D0",5},
  348. {"./log/console.log",info,10485760,"$D0",5},
  349. {"./log/debug.log",debug,10485760,"$D0",5}
  350. ]}]),
  351. NewConfigMissingList = expand_handlers([{foo_backend, {file, "same_file.log"}}]),
  352. [
  353. {"lager_file_backend_good",
  354. ?_assertEqual([ok, ok, ok], [ check_handler_config(M,C) || {M,C} <- Good ])
  355. },
  356. {"lager_file_backend_bad",
  357. ?_assertThrow({error, bad_config}, [ check_handler_config(M,C) || {M,C} <- Bad ])
  358. },
  359. {"Invalid config dies",
  360. ?_assertThrow({error, bad_config}, start_handlers(foo, AlsoBad))
  361. },
  362. {"Invalid config dies",
  363. ?_assertThrow({error, {bad_config, _}}, start_handlers(foo, BadToo))
  364. },
  365. {"Old Lager config works",
  366. ?_assertEqual([ok, ok, ok, ok], [ check_handler_config(M, C) || {M, C} <- OldSchoolLagerGood])
  367. },
  368. {"New Config missing its list should fail",
  369. ?_assertThrow({error, {bad_config, foo_backend}}, [ check_handler_config(M, C) || {M, C} <- NewConfigMissingList])
  370. }
  371. ].
  372. -endif.