rewrite from lager
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

517 行
20 KiB

4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
  1. -module(rumBkdConsole).
  2. %% Configuration is a proplist with the following keys:
  3. %%`level' - log level to use
  4. %%`use_stderr' - either `true' or `false', defaults to false. If set to true, use standard error to output console log messages
  5. %%`formatter' - the module to use when formatting log messages. Defaults to `rumFormatter'
  6. %%`formatter_config' - the format configuration string. Defaults to `time [ severity ] message'
  7. -behaviour(gen_emm).
  8. -include("eRum.hrl").
  9. -ifdef(TEST).
  10. -include_lib("eunit/include/eunit.hrl").
  11. -compile([{parse_transform, rumTransform}]).
  12. -endif.
  13. -define(TERSE_FORMAT,[time, " ", color, "[", severity,"] ", message]).
  14. -define(DEFAULT_FORMAT_CONFIG, ?TERSE_FORMAT ++ [eol()]).
  15. -define(FORMAT_CONFIG_OFF, [{eol, eol()}]).
  16. -define(DefOpts, [{use_stderr, false}, {group_leader, false}, {id, ?MODULE}, {formatter, rumFormatter}, {formatter_config, ?DEFAULT_FORMAT_CONFIG}]).
  17. -export([
  18. init/1
  19. , handleCall/2
  20. , handleEvent/2
  21. , handleInfo/2
  22. , terminate/2
  23. , code_change/3
  24. ]).
  25. -record(state, {
  26. level :: {'mask', integer()},
  27. out = user :: user | standard_error | pid(),
  28. id :: atom() | {atom(), any()},
  29. formatter :: atom(),
  30. format_config :: any(),
  31. colors = [] :: list()
  32. }).
  33. init(Opts) ->
  34. case isNewStyleConsole() of
  35. false ->
  36. Msg = "Lager's console backend is incompatible with the 'old' shell, not enabling it",
  37. %% be as noisy as possible, log to every possible place
  38. try
  39. alarm_handler:set_alarm({?MODULE, "WARNING: " ++ Msg})
  40. catch
  41. _:_ ->
  42. error_logger:warning_msg(Msg ++ "~n")
  43. end,
  44. io:format("WARNING: " ++ Msg ++ "~n"),
  45. ?INT_LOG(warning, Msg, []),
  46. {error, {fatal, old_shell}};
  47. _ ->
  48. true = checkOpts(Opts),
  49. Colors = ?IIF(rumUtil:get_env(colored, false), rumUtil:get_env(colors, []), []),
  50. Level = rumUtil:get_opt(level, Opts, undefined),
  51. L = rumUtil:config_to_mask(Level),
  52. [UseErr, GroupLeader, ID, Formatter, Config] = [rumUtil:get_opt(Key, Opts, Def) || {Key, Def} <- ?DefOpts],
  53. Out = ?IIF(UseErr, standard_error, ?IIF(GroupLeader == false, user, begin erlang:monitor(process, GroupLeader), GroupLeader end)),
  54. {ok, #state{level = L, id = ID, out = Out, formatter = Formatter, format_config = Config, colors = Colors}}
  55. end.
  56. checkOpts([]) -> true;
  57. checkOpts([{level, L} | T]) when is_atom(L) ->
  58. ?IIF(lists:member(L, ?RumLevels) == false, throw({error, {fatal, {bad_level, L}}}), checkOpts(T));
  59. checkOpts([{use_stderr, true} | T]) ->
  60. checkOpts(T);
  61. checkOpts([{use_stderr, false} | T]) ->
  62. checkOpts(T);
  63. checkOpts([{formatter, M} | T]) when is_atom(M) ->
  64. checkOpts(T);
  65. checkOpts([{formatter_config, C} | T]) when is_list(C) ->
  66. checkOpts(T);
  67. checkOpts([{group_leader, L} | T]) when is_pid(L) ->
  68. checkOpts(T);
  69. checkOpts([{id, {?MODULE, _}} | T]) ->
  70. checkOpts(T);
  71. checkOpts([H | _]) ->
  72. throw({error, {fatal, {bad_console_config, H}}}).
  73. handleCall(mGetLogLevel, #state{level = Level} = State) ->
  74. {ok, Level, State};
  75. handleCall({mSetLogLevel, Level}, State) ->
  76. try rumUtil:config_to_mask(Level) of
  77. Levels ->
  78. {ok, ok, State#state{level = Levels}}
  79. catch
  80. _:_ ->
  81. {ok, {error, bad_log_level}, State}
  82. end;
  83. handleCall(_Request, State) ->
  84. {ok, ok, State}.
  85. %% @private
  86. handleEvent({mWriteLog, Message},
  87. #state{level = L, out = Out, formatter = Formatter, format_config = FormatConfig, colors = Colors, id = ID} = State) ->
  88. case rumUtil:is_loggable(Message, L, ID) of
  89. true ->
  90. io:put_chars(Out, Formatter:format(Message, FormatConfig, Colors)),
  91. {ok, State};
  92. false ->
  93. {ok, State}
  94. end;
  95. handleEvent(_Event, State) ->
  96. {ok, State}.
  97. handleInfo({'DOWN', _, process, Out, _}, #state{out = Out}) ->
  98. remove_handler;
  99. handleInfo(_Info, State) ->
  100. {ok, State}.
  101. terminate(remove_handler, _State = #state{id = ID}) ->
  102. %% have to do this asynchronously because we're in the event handlr
  103. spawn(fun() -> eRum:clear_trace_by_destination(ID) end),
  104. ok;
  105. terminate(_Reason, _State) ->
  106. ok.
  107. code_change(_OldVsn, State, _Extra) ->
  108. {ok, State}.
  109. eol() ->
  110. case application:get_env(lager, colored) of
  111. {ok, true} ->
  112. "\e[0m\r\n";
  113. _ ->
  114. "\r\n"
  115. end.
  116. isNewStyleConsole() ->
  117. %% Criteria:
  118. %% 1. If the user has specified '-noshell' on the command line,
  119. %% then we will pretend that the new-style console is available.
  120. %% If there is no shell at all, then we don't have to worry
  121. %% about log events being blocked by the old-style shell.
  122. %% 2. Windows doesn't support the new shell, so all windows users
  123. %% have is the oldshell.
  124. %% 3. If the user_drv process is registered, all is OK.
  125. %% 'user_drv' is a registered proc name used by the "new"
  126. %% console driver.
  127. init:get_argument(noshell) /= error orelse element(1, os:type()) /= win32 orelse is_pid(whereis(user_drv)).
  128. -ifdef(TEST).
  129. console_config_validation_test_() ->
  130. Good = [{level, info}, {use_stderr, true}],
  131. Bad1 = [{level, foo}, {use_stderr, flase}],
  132. Bad2 = [{level, info}, {use_stderr, flase}],
  133. AllGood = [{level, info}, {formatter, my_formatter},
  134. {formatter_config, ["blort", "garbage"]},
  135. {use_stderr, false}],
  136. [
  137. ?_assertEqual(true, checkOpts(Good)),
  138. ?_assertThrow({error, {fatal, {bad_level, foo}}}, checkOpts(Bad1)),
  139. ?_assertThrow({error, {fatal, {bad_console_config, {use_stderr, flase}}}}, checkOpts(Bad2)),
  140. ?_assertEqual(true, checkOpts(AllGood))
  141. ].
  142. console_log_test_() ->
  143. %% tiny recursive fun that pretends to be a group leader
  144. F = fun(Self) ->
  145. fun() ->
  146. YComb = fun(Fun) ->
  147. receive
  148. {io_request, From, ReplyAs, {put_chars, unicode, _Msg}} = Y ->
  149. From ! {io_reply, ReplyAs, ok},
  150. Self ! Y,
  151. Fun(Fun);
  152. Other ->
  153. ?debugFmt("unexpected message ~p~n", [Other]),
  154. Self ! Other
  155. end
  156. end,
  157. YComb(YComb)
  158. end
  159. end,
  160. {foreach,
  161. fun() ->
  162. error_logger:tty(false),
  163. application:load(lager),
  164. application:set_env(lager, handlers, []),
  165. application:set_env(lager, errLoggerRedirect, false),
  166. eRum:start(),
  167. whereis(user)
  168. end,
  169. fun(User) ->
  170. unregister(user),
  171. register(user, User),
  172. application:stop(lager),
  173. application:stop(goldrush),
  174. error_logger:tty(true)
  175. end,
  176. [
  177. {"regular console logging",
  178. fun() ->
  179. Pid = spawn(F(self())),
  180. unregister(user),
  181. register(user, Pid),
  182. erlang:group_leader(Pid, whereis(rumEvent)),
  183. gen_event:add_handler(rumEvent, lager_console_backend, [{level, info}]),
  184. rumConfig:set({rumEvent, loglevel}, {element(2, rumUtil:config_to_mask(info)), []}),
  185. eRum:log(info, self(), "Test message"),
  186. receive
  187. {io_request, From, ReplyAs, {put_chars, unicode, Msg}} ->
  188. From ! {io_reply, ReplyAs, ok},
  189. TestMsg = "Test message" ++ eol(),
  190. ?assertMatch([_, "[info]", TestMsg], re:split(Msg, " ", [{return, list}, {parts, 3}]))
  191. after
  192. 500 ->
  193. ?assert(false)
  194. end
  195. end
  196. },
  197. {"verbose console logging",
  198. fun() ->
  199. Pid = spawn(F(self())),
  200. unregister(user),
  201. register(user, Pid),
  202. erlang:group_leader(Pid, whereis(rumEvent)),
  203. gen_event:add_handler(rumEvent, lager_console_backend, [info, true]),
  204. rumConfig:set({rumEvent, loglevel}, {element(2, rumUtil:config_to_mask(info)), []}),
  205. eRum:info("Test message"),
  206. PidStr = pid_to_list(self()),
  207. receive
  208. {io_request, _, _, {put_chars, unicode, Msg}} ->
  209. TestMsg = "Test message" ++ eol(),
  210. ?assertMatch([_, _, "[info]", PidStr, _, TestMsg], re:split(Msg, "[ @]", [{return, list}, {parts, 6}]))
  211. after
  212. 500 ->
  213. ?assert(false)
  214. end
  215. end
  216. },
  217. {"custom format console logging",
  218. fun() ->
  219. Pid = spawn(F(self())),
  220. unregister(user),
  221. register(user, Pid),
  222. erlang:group_leader(Pid, whereis(rumEvent)),
  223. gen_event:add_handler(rumEvent, lager_console_backend,
  224. [{level, info}, {formatter, lager_default_formatter}, {formatter_config, [date, "#", time, "#", severity, "#", node, "#", pid, "#",
  225. module, "#", function, "#", file, "#", line, "#", message, "\r\n"]}]),
  226. rumConfig:set({rumEvent, loglevel}, {?INFO, []}),
  227. eRum:info("Test message"),
  228. PidStr = pid_to_list(self()),
  229. NodeStr = atom_to_list(node()),
  230. ModuleStr = atom_to_list(?MODULE),
  231. receive
  232. {io_request, _, _, {put_chars, unicode, Msg}} ->
  233. TestMsg = "Test message" ++ eol(),
  234. ?assertMatch([_, _, "info", NodeStr, PidStr, ModuleStr, _, _, _, TestMsg],
  235. re:split(Msg, "#", [{return, list}, {parts, 10}]))
  236. after
  237. 500 ->
  238. ?assert(false)
  239. end
  240. end
  241. },
  242. {"tracing should work",
  243. fun() ->
  244. Pid = spawn(F(self())),
  245. unregister(user),
  246. register(user, Pid),
  247. gen_event:add_handler(rumEvent, lager_console_backend, [{level, info}]),
  248. erlang:group_leader(Pid, whereis(rumEvent)),
  249. rumConfig:set({rumEvent, loglevel}, {element(2, rumUtil:config_to_mask(info)), []}),
  250. eRum:debug("Test message"),
  251. receive
  252. {io_request, From, ReplyAs, {put_chars, unicode, _Msg}} ->
  253. From ! {io_reply, ReplyAs, ok},
  254. ?assert(false)
  255. after
  256. 500 ->
  257. ?assert(true)
  258. end,
  259. {ok, _} = eRum:trace_console([{module, ?MODULE}]),
  260. eRum:debug("Test message"),
  261. receive
  262. {io_request, From1, ReplyAs1, {put_chars, unicode, Msg1}} ->
  263. From1 ! {io_reply, ReplyAs1, ok},
  264. TestMsg = "Test message" ++ eol(),
  265. ?assertMatch([_, "[debug]", TestMsg], re:split(Msg1, " ", [{return, list}, {parts, 3}]))
  266. after
  267. 500 ->
  268. ?assert(false)
  269. end
  270. end
  271. },
  272. {"tracing doesn't duplicate messages",
  273. fun() ->
  274. Pid = spawn(F(self())),
  275. unregister(user),
  276. register(user, Pid),
  277. gen_event:add_handler(rumEvent, lager_console_backend, [{level, info}]),
  278. rumConfig:set({rumEvent, loglevel}, {element(2, rumUtil:config_to_mask(info)), []}),
  279. erlang:group_leader(Pid, whereis(rumEvent)),
  280. eRum:debug("Test message"),
  281. receive
  282. {io_request, From, ReplyAs, {put_chars, unicode, _Msg}} ->
  283. From ! {io_reply, ReplyAs, ok},
  284. ?assert(false)
  285. after
  286. 500 ->
  287. ?assert(true)
  288. end,
  289. {ok, _} = eRum:trace_console([{module, ?MODULE}]),
  290. eRum:error("Test message"),
  291. receive
  292. {io_request, From1, ReplyAs1, {put_chars, unicode, Msg1}} ->
  293. From1 ! {io_reply, ReplyAs1, ok},
  294. TestMsg = "Test message" ++ eol(),
  295. ?assertMatch([_, "[error]", TestMsg], re:split(Msg1, " ", [{return, list}, {parts, 3}]))
  296. after
  297. 1000 ->
  298. ?assert(false)
  299. end,
  300. %% make sure this event wasn't duplicated
  301. receive
  302. {io_request, From2, ReplyAs2, {put_chars, unicode, _Msg2}} ->
  303. From2 ! {io_reply, ReplyAs2, ok},
  304. ?assert(false)
  305. after
  306. 500 ->
  307. ?assert(true)
  308. end
  309. end
  310. },
  311. {"blacklisting a loglevel works",
  312. fun() ->
  313. Pid = spawn(F(self())),
  314. unregister(user),
  315. register(user, Pid),
  316. gen_event:add_handler(rumEvent, lager_console_backend, [{level, info}]),
  317. rumConfig:set({rumEvent, loglevel}, {element(2, rumUtil:config_to_mask(info)), []}),
  318. eRum:set_loglevel(lager_console_backend, '!=info'),
  319. erlang:group_leader(Pid, whereis(rumEvent)),
  320. eRum:debug("Test message"),
  321. receive
  322. {io_request, From1, ReplyAs1, {put_chars, unicode, Msg1}} ->
  323. From1 ! {io_reply, ReplyAs1, ok},
  324. TestMsg = "Test message" ++ eol(),
  325. ?assertMatch([_, "[debug]", TestMsg], re:split(Msg1, " ", [{return, list}, {parts, 3}]))
  326. after
  327. 1000 ->
  328. ?assert(false)
  329. end,
  330. %% info is blacklisted
  331. eRum:info("Test message"),
  332. receive
  333. {io_request, From2, ReplyAs2, {put_chars, unicode, _Msg2}} ->
  334. From2 ! {io_reply, ReplyAs2, ok},
  335. ?assert(false)
  336. after
  337. 500 ->
  338. ?assert(true)
  339. end
  340. end
  341. },
  342. {"whitelisting a loglevel works",
  343. fun() ->
  344. Pid = spawn(F(self())),
  345. unregister(user),
  346. register(user, Pid),
  347. gen_event:add_handler(rumEvent, lager_console_backend, [{level, info}]),
  348. rumConfig:set({rumEvent, loglevel}, {element(2, rumUtil:config_to_mask(info)), []}),
  349. eRum:set_loglevel(lager_console_backend, '=debug'),
  350. erlang:group_leader(Pid, whereis(rumEvent)),
  351. eRum:debug("Test message"),
  352. receive
  353. {io_request, From1, ReplyAs1, {put_chars, unicode, Msg1}} ->
  354. From1 ! {io_reply, ReplyAs1, ok},
  355. TestMsg = "Test message" ++ eol(),
  356. ?assertMatch([_, "[debug]", TestMsg], re:split(Msg1, " ", [{return, list}, {parts, 3}]))
  357. after
  358. 1000 ->
  359. ?assert(false)
  360. end,
  361. %% info is blacklisted
  362. eRum:error("Test message"),
  363. receive
  364. {io_request, From2, ReplyAs2, {put_chars, unicode, _Msg2}} ->
  365. From2 ! {io_reply, ReplyAs2, ok},
  366. ?assert(false)
  367. after
  368. 500 ->
  369. ?assert(true)
  370. end
  371. end
  372. },
  373. {"console backend with custom group leader",
  374. fun() ->
  375. Pid = spawn(F(self())),
  376. gen_event:add_handler(rumEvent, lager_console_backend, [{level, info}, {group_leader, Pid}]),
  377. rumConfig:set({rumEvent, loglevel}, {element(2, rumUtil:config_to_mask(info)), []}),
  378. eRum:info("Test message"),
  379. ?assertNotEqual({group_leader, Pid}, erlang:process_info(whereis(rumEvent), group_leader)),
  380. receive
  381. {io_request, From1, ReplyAs1, {put_chars, unicode, Msg1}} ->
  382. From1 ! {io_reply, ReplyAs1, ok},
  383. TestMsg = "Test message" ++ eol(),
  384. ?assertMatch([_, "[info]", TestMsg], re:split(Msg1, " ", [{return, list}, {parts, 3}]))
  385. after
  386. 1000 ->
  387. ?assert(false)
  388. end,
  389. %% killing the pid should prevent any new log messages (to prove we haven't accidentally redirected
  390. %% the group leader some other way
  391. exit(Pid, kill),
  392. timer:sleep(100),
  393. %% additionally, check the lager backend has been removed (because the group leader process died)
  394. ?assertNot(lists:member(lager_console_backend, gen_event:which_handlers(rumEvent))),
  395. eRum:error("Test message"),
  396. receive
  397. {io_request, From2, ReplyAs2, {put_chars, unicode, _Msg2}} ->
  398. From2 ! {io_reply, ReplyAs2, ok},
  399. ?assert(false)
  400. after
  401. 500 ->
  402. ?assert(true)
  403. end
  404. end
  405. },
  406. {"console backend with custom group leader using a trace and an ID",
  407. fun() ->
  408. Pid = spawn(F(self())),
  409. ID = {?MODULE, trace_test},
  410. Handlers = rumConfig:global_get(handlers, []),
  411. HandlerInfo = lager_app:start_handler(rumEvent, ID,
  412. [{level, none}, {group_leader, Pid},
  413. {id, ID}]),
  414. rumConfig:global_set(handlers, [HandlerInfo | Handlers]),
  415. eRum:info("Test message"),
  416. ?assertNotEqual({group_leader, Pid}, erlang:process_info(whereis(rumEvent), group_leader)),
  417. receive
  418. {io_request, From, ReplyAs, {put_chars, unicode, _Msg}} ->
  419. From ! {io_reply, ReplyAs, ok},
  420. ?assert(false)
  421. after
  422. 500 ->
  423. ?assert(true)
  424. end,
  425. eRum:trace(ID, [{module, ?MODULE}], debug),
  426. eRum:info("Test message"),
  427. receive
  428. {io_request, From1, ReplyAs1, {put_chars, unicode, Msg1}} ->
  429. From1 ! {io_reply, ReplyAs1, ok},
  430. TestMsg = "Test message" ++ eol(),
  431. ?assertMatch([_, "[info]", TestMsg], re:split(Msg1, " ", [{return, list}, {parts, 3}]))
  432. after
  433. 500 ->
  434. ?assert(false)
  435. end,
  436. ?assertNotEqual({0, []}, rumConfig:get({rumEvent, loglevel})),
  437. %% killing the pid should prevent any new log messages (to prove we haven't accidentally redirected
  438. %% the group leader some other way
  439. exit(Pid, kill),
  440. timer:sleep(100),
  441. %% additionally, check the lager backend has been removed (because the group leader process died)
  442. ?assertNot(lists:member(lager_console_backend, gen_event:which_handlers(rumEvent))),
  443. %% finally, check the trace has been removed
  444. ?assertEqual({0, []}, rumConfig:get({rumEvent, loglevel})),
  445. eRum:error("Test message"),
  446. receive
  447. {io_request, From3, ReplyAs3, {put_chars, unicode, _Msg3}} ->
  448. From3 ! {io_reply, ReplyAs3, ok},
  449. ?assert(false)
  450. after
  451. 500 ->
  452. ?assert(true)
  453. end
  454. end
  455. }
  456. ]
  457. }.
  458. set_loglevel_test_() ->
  459. {foreach,
  460. fun() ->
  461. error_logger:tty(false),
  462. application:load(lager),
  463. application:set_env(lager, handlers, [{lager_console_backend, [{level, info}]}]),
  464. application:set_env(lager, errLoggerRedirect, false),
  465. eRum:start()
  466. end,
  467. fun(_) ->
  468. application:stop(lager),
  469. application:stop(goldrush),
  470. error_logger:tty(true)
  471. end,
  472. [
  473. {"Get/set loglevel test",
  474. fun() ->
  475. ?assertEqual(info, eRum:get_loglevel(lager_console_backend)),
  476. eRum:set_loglevel(lager_console_backend, debug),
  477. ?assertEqual(debug, eRum:get_loglevel(lager_console_backend)),
  478. eRum:set_loglevel(lager_console_backend, '!=debug'),
  479. ?assertEqual(info, eRum:get_loglevel(lager_console_backend)),
  480. eRum:set_loglevel(lager_console_backend, '!=info'),
  481. ?assertEqual(debug, eRum:get_loglevel(lager_console_backend)),
  482. ok
  483. end
  484. },
  485. {"Get/set invalid loglevel test",
  486. fun() ->
  487. ?assertEqual(info, eRum:get_loglevel(lager_console_backend)),
  488. ?assertEqual({error, bad_log_level},
  489. eRum:set_loglevel(lager_console_backend, fatfinger)),
  490. ?assertEqual(info, eRum:get_loglevel(lager_console_backend))
  491. end
  492. }
  493. ]
  494. }.
  495. -endif.