rewrite from lager
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.

986 lines
37 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. -module(rumUtil).
  2. -include("eRum.hrl").
  3. -include_lib("kernel/include/file.hrl").
  4. -export([
  5. levels/0
  6. , levelToNum/1
  7. , levelToChr/1
  8. , numToLevel/1
  9. , validateLogLevel/1
  10. , configToMask/1
  11. , atomCfgToLevels/1
  12. , maskToLevels/1
  13. , nowMs/0
  14. , msToBinStr/0
  15. , msToBinStr/1
  16. , parseRotateSpec/1
  17. , calcNextRotateMs/1
  18. , calcNextRotateMs/2
  19. , calcNextRotateDt/2
  20. , validate_trace/1
  21. , check_traces/4
  22. , isLoggAble/3
  23. , trace_filter/1
  24. , trace_filter/2
  25. , parsePath/1
  26. , find_file/2
  27. , checkHwm/1
  28. , checkHwm/2
  29. , makeInnerSinkName/1
  30. , maybeFlush/2
  31. , isFileChanged/3
  32. , get_env/2
  33. , get_opt/3
  34. ]).
  35. -ifdef(TEST).
  36. -export([
  37. create_test_dir/0
  38. , get_test_dir/0
  39. , delete_test_dir/0
  40. , set_dir_permissions/2
  41. , safe_application_load/1
  42. , safe_write_file/2
  43. ]).
  44. -include_lib("eunit/include/eunit.hrl").
  45. -endif.
  46. levels() ->
  47. [debug, info, notice, warning, error, critical, alert, emergency, none].
  48. levelToNum(debug) -> ?DEBUG;
  49. levelToNum(info) -> ?INFO;
  50. levelToNum(notice) -> ?NOTICE;
  51. levelToNum(warning) -> ?WARNING;
  52. levelToNum(error) -> ?ERROR;
  53. levelToNum(critical) -> ?CRITICAL;
  54. levelToNum(alert) -> ?ALERT;
  55. levelToNum(emergency) -> ?EMERGENCY;
  56. levelToNum(none) -> ?LOG_NONE.
  57. numToLevel(?DEBUG) -> debug;
  58. numToLevel(?INFO) -> info;
  59. numToLevel(?NOTICE) -> notice;
  60. numToLevel(?WARNING) -> warning;
  61. numToLevel(?ERROR) -> error;
  62. numToLevel(?CRITICAL) -> critical;
  63. numToLevel(?ALERT) -> alert;
  64. numToLevel(?EMERGENCY) -> emergency;
  65. numToLevel(?LOG_NONE) -> none.
  66. levelToChr(debug) -> $D;
  67. levelToChr(info) -> $I;
  68. levelToChr(notice) -> $N;
  69. levelToChr(warning) -> $W;
  70. levelToChr(error) -> $E;
  71. levelToChr(critical) -> $C;
  72. levelToChr(alert) -> $A;
  73. levelToChr(emergency) -> $M;
  74. levelToChr(none) -> $ .
  75. -spec validateLogLevel(atom()|string()) -> false | rumMaskLevel().
  76. validateLogLevel(Level) ->
  77. try rumUtil:configToMask(Level) of
  78. Levels ->
  79. Levels
  80. catch
  81. _:_ ->
  82. false
  83. end.
  84. -spec configToMask(atom()|string()) -> rumMaskLevel().
  85. configToMask(Conf) ->
  86. Levels = atomCfgToLevels(Conf),
  87. levelsToMask(Levels, 0).
  88. -spec levelsToMask([rumAtomLevel()], rumMaskLevel()) -> rumMaskLevel().
  89. levelsToMask([], Acc) ->
  90. Acc;
  91. levelsToMask([Level | Left], Acc) ->
  92. levelsToMask(Left, levelToNum(Level) bor Acc).
  93. -spec maskToLevels(rumMaskLevel()) -> [rumAtomLevel()].
  94. maskToLevels(Mask) ->
  95. maskToLevels(?RumLevels, Mask, []).
  96. maskToLevels([], _Mask, Acc) ->
  97. lists:reverse(Acc);
  98. maskToLevels([Level | Levels], Mask, Acc) ->
  99. case (levelToNum(Level) band Mask) /= 0 of
  100. true ->
  101. maskToLevels(Levels, Mask, [Level | Acc]);
  102. _ ->
  103. maskToLevels(Levels, Mask, Acc)
  104. end.
  105. -spec atomCfgToLevels(atom()) -> [rumAtomLevel()].
  106. atomCfgToLevels(Cfg) ->
  107. binCfgToLevels(atom_to_binary(Cfg, utf8)).
  108. binCfgToLevels(<<"!", Rest/binary>>) ->
  109. ?RumLevels -- binCfgToLevels(Rest);
  110. binCfgToLevels(<<"=<", Rest/binary>>) ->
  111. riseInWhile(?RumLevels, levelBinToAtom(Rest), []);
  112. binCfgToLevels(<<"<=", Rest/binary>>) ->
  113. riseInWhile(?RumLevels, levelBinToAtom(Rest), []);
  114. binCfgToLevels(<<">=", Rest/binary>>) ->
  115. dropInWhile(?RumLevels, levelBinToAtom(Rest));
  116. binCfgToLevels(<<"=>", Rest/binary>>) ->
  117. dropInWhile(?RumLevels, levelBinToAtom(Rest));
  118. binCfgToLevels(<<"=", Rest/binary>>) ->
  119. [levelBinToAtom(Rest)];
  120. binCfgToLevels(<<"<", Rest/binary>>) ->
  121. riseOutWhile(?RumLevels, levelBinToAtom(Rest), []);
  122. binCfgToLevels(<<">", Rest/binary>>) ->
  123. dropOutWhile(?RumLevels, levelBinToAtom(Rest));
  124. binCfgToLevels(Rest) ->
  125. [levelBinToAtom(Rest)].
  126. dropInWhile([], _Level) ->
  127. [];
  128. dropInWhile([CurLevel | Left] = Rest, Level) ->
  129. case CurLevel == Level of
  130. true ->
  131. Rest;
  132. _ ->
  133. dropInWhile(Left, Level)
  134. end.
  135. dropOutWhile([], _Level) ->
  136. [];
  137. dropOutWhile([CurLevel | Left], Level) ->
  138. case CurLevel == Level of
  139. true ->
  140. Left;
  141. _ ->
  142. dropOutWhile(Left, Level)
  143. end.
  144. riseInWhile([], _Level, Acc) ->
  145. Acc;
  146. riseInWhile([CurLevel | Left], Level, Acc) ->
  147. case CurLevel == Level of
  148. true ->
  149. [CurLevel | Acc];
  150. _ ->
  151. riseInWhile(Left, Level, [CurLevel | Acc])
  152. end.
  153. riseOutWhile([], _Level, Acc) ->
  154. Acc;
  155. riseOutWhile([CurLevel | Left], Level, Acc) ->
  156. case CurLevel == Level of
  157. true ->
  158. Acc;
  159. _ ->
  160. riseOutWhile(Left, Level, [CurLevel | Acc])
  161. end.
  162. levelBinToAtom(BinStr) ->
  163. AtomLevel = binary_to_atom(BinStr, utf8),
  164. case lists:member(AtomLevel, ?RumLevels) of
  165. true ->
  166. AtomLevel;
  167. _ ->
  168. erlang:error(badarg)
  169. end.
  170. nowMs() ->
  171. erlang:system_time(millisecond).
  172. msToBinStr() ->
  173. msToBinStr(nowMs()).
  174. msToBinStr(MsTick) ->
  175. ThisSec = MsTick div 1000,
  176. ThisMs = MsTick rem 1000,
  177. {{Y, M, D}, {H, Mi, S}} = erlang:universaltime_to_localtime(erlang:posixtime_to_universaltime(ThisSec)),
  178. <<(integer_to_binary(Y))/binary, "-", (i2b(M))/binary, "-", (i2b(D))/binary, " ", (i2b(H))/binary, ":", (i2b(Mi))/binary, ":", (i2b(S))/binary, ".", (i3b(ThisMs))/binary>>.
  179. i2b(Num) ->
  180. if
  181. Num < 10 ->
  182. <<"0", (integer_to_binary(Num))/binary>>;
  183. true ->
  184. integer_to_binary(Num)
  185. end.
  186. i3b(Num) ->
  187. if
  188. Num < 10 ->
  189. <<"00", (integer_to_binary(Num))/binary>>;
  190. Num < 100 ->
  191. <<"0", (integer_to_binary(Num))/binary>>;
  192. true ->
  193. integer_to_binary(Num)
  194. end.
  195. %% last parse hour
  196. parseRotateHourSpec([], DayOrMonthF, Hour, Minute, DayOrMonthV) ->
  197. {DayOrMonthF, Hour, Minute, DayOrMonthV};
  198. parseRotateHourSpec([$H, M1, M2], DayOrMonthF, Hour, _Minute, DayOrMonthV) when M1 >= $0, M1 =< $9, M2 >= $0, M2 =< $9 ->
  199. Min = list_to_integer([M1, M2]),
  200. ?IIF(Min >= 0 andalso Min =< 59, {DayOrMonthF, Hour, Min, DayOrMonthV}, {error, invalid_date_spec});
  201. parseRotateHourSpec([$H, M], DayOrMonthF, Hour, _Minute, DayOrMonthV) when M >= $0, M =< $9 ->
  202. {DayOrMonthF, Hour, M - $0, DayOrMonthV};
  203. parseRotateHourSpec(_, _DayOrMonth, _Hour, _Minute, _DayOrMonthV) ->
  204. {error, invalid_date_spec}.
  205. %% second parse day Default to 00:00:00 rotation
  206. parseRotateDaySpec([], DayOrMonthF, Hour, Minute, DayOrMonthV) ->
  207. {DayOrMonthF, Hour, Minute, DayOrMonthV};
  208. parseRotateDaySpec([$D, D1, D2 | T], DayOrMonthF, _Hour, _Minute, DayOrMonthV) when D1 > $0, D1 < $9, D2 > $0, D2 < $9 ->
  209. Day = list_to_integer([D1, D2]),
  210. ?IIF(Day >= 0 andalso Day =< 23, parseRotateHourSpec(T, DayOrMonthF, Day, 0, DayOrMonthV), {error, invalid_date_spec});
  211. parseRotateDaySpec([$D, D | T], DayOrMonthF, _Hour, _Minute, DayOrMonthV) when D >= $0, D =< $9 ->
  212. parseRotateHourSpec(T, DayOrMonthF, D - $0, 0, DayOrMonthV);
  213. parseRotateDaySpec(T, DayOrMonth, Hour, Minute, DayOrMonthV) ->
  214. parseRotateHourSpec(T, DayOrMonth, Hour, Minute, DayOrMonthV).
  215. %% first parse date or week
  216. parseRotateDateSpec([$$, $W, W | T], _DayOrMonthF, _Hour, _Minute, _DayOrMonthV) when W >= $1, W =< $7 ->
  217. parseRotateDaySpec(T, day, 0, 0, W - $0);
  218. parseRotateDateSpec([$$, $M, L | T], _DayOrMonthF, _Hour, _Minute, DayOrMonthV) when L == $L; L == $l ->
  219. parseRotateDaySpec(T, last, 0, 0, DayOrMonthV);
  220. parseRotateDateSpec([$$, $M, M1, M2 | T], _DayOrMonthF, _Hour, _Minute, _DayOrMonthV) when M1 >= $0, M1 =< $9, M2 >= $0, M2 =< $9 ->
  221. Date = list_to_integer([M1, M2]),
  222. ?IIF(Date >= 1 andalso Date =< 31, parseRotateDaySpec(T, date, 0, 0, Date), {error, invalid_date_spec});
  223. parseRotateDateSpec([$$, $M, M | T], _DayOrMonthF, _Hour, _Minute, _DayOrMonthV) when M >= $1, M =< $9 ->
  224. parseRotateDaySpec(T, date, 0, 0, M - $0);
  225. parseRotateDateSpec([$$ | T], DayOrMonthF, Hour, Minute, DayOrMonthV) ->
  226. parseRotateDaySpec(T, DayOrMonthF, Hour, Minute, DayOrMonthV);
  227. parseRotateDateSpec(_, _DayOrMonthF, _Hour, _Minute, _DayOrMonthV) ->
  228. {error, invalid_date_spec}.
  229. parseRotateSpec(Spec) ->
  230. case parseRotateDateSpec(Spec, undefined, undefined, undefined, undefined) of
  231. {error, _} = ErrRet ->
  232. ErrRet;
  233. {undefined, undefined, undefined, _} ->
  234. {error, invalid_date_spec};
  235. STuple ->
  236. {ok, STuple}
  237. end.
  238. calcNextRotateMs(Spec) ->
  239. {Date, Time} = NowDataTime = erlang:localtime(),
  240. NextTime = calcNextRotate(Spec, Date, Time),
  241. (rumTime:lDateTimeToSec(NextTime) - rumTime:lDateTimeToSec(NowDataTime)) * 1000.
  242. calcNextRotateMs(Spec, NowDataTime) ->
  243. {Date, Time} = NowDataTime,
  244. NextTime = calcNextRotate(Spec, Date, Time),
  245. (rumTime:lDateTimeToSec(NextTime) - rumTime:lDateTimeToSec(NowDataTime)) * 1000.
  246. calcNextRotateDt(Spec, NowDataTime) ->
  247. {Date, Time} = NowDataTime,
  248. calcNextRotate(Spec, Date, Time).
  249. calcNextRotate({undefined, SHour, SMinute, _SMonthV}, CurDate, CurTime) ->
  250. case SHour of
  251. undefined ->
  252. {CurHour, CurMinute, _} = CurTime,
  253. case CurMinute < SMinute of
  254. true ->
  255. %% rotation is this hour
  256. {CurDate, {CurHour, SMinute, 0}};
  257. _ ->
  258. %% rotation is next hour
  259. NexSec = rumTime:lDateTimeToSec({CurDate, {CurHour, SMinute, 0}}) + 3600,
  260. rumTime:secToLDateTime(NexSec)
  261. end;
  262. _ ->
  263. case CurTime < {SHour, SMinute, 0} of
  264. true ->
  265. %% rotation is this day
  266. {CurDate, {SHour, SMinute, 0}};
  267. _ ->
  268. %% rotation is next day
  269. NexSec = rumTime:lDateTimeToSec({CurDate, {SHour, SMinute, 0}}) + 86400,
  270. rumTime:secToLDateTime(NexSec)
  271. end
  272. end;
  273. calcNextRotate({day, SHour, SMinute, SDay}, CurDate, CurTime) ->
  274. CurWeekDay = rumTime:weekDay(CurDate),
  275. if
  276. CurWeekDay < SDay ->
  277. %% rotation is this week
  278. DiffDays = SDay - CurWeekDay,
  279. NexSec = rumTime:lDateTimeToSec({CurDate, {SHour, SMinute, 0}}) + (86400 * DiffDays),
  280. rumTime:secToLDateTime(NexSec);
  281. CurWeekDay > SDay ->
  282. %% rotation is next week
  283. DiffDays = ((7 - CurWeekDay) + SDay),
  284. NexSec = rumTime:lDateTimeToSec({CurDate, {SHour, SMinute, 0}}) + (86400 * DiffDays),
  285. rumTime:secToLDateTime(NexSec);
  286. true ->
  287. case CurTime < {SHour, SMinute, 0} of
  288. true ->
  289. %% rotation is this week
  290. {CurDate, {SHour, SMinute, 0}};
  291. _ ->
  292. %% rotation is next week
  293. NexSec = rumTime:lDateTimeToSec({CurDate, {SHour, SMinute, 0}}) + (86400 * 7),
  294. rumTime:secToLDateTime(NexSec)
  295. end
  296. end;
  297. calcNextRotate({last, SHour, SMinute, _SMonthV}, CurDate, CurTime) ->
  298. {CurYear, CurMonth, CurDay} = CurDate,
  299. CurMonthDay = rumTime:monthDay(CurYear, CurMonth),
  300. case CurMonthDay == CurDay of
  301. true ->
  302. case CurTime < {SHour, SMinute, 0} of
  303. true ->
  304. %% rotation is this last month day
  305. {CurDate, {SHour, SMinute, 0}};
  306. _ ->
  307. %% rotation is next last month day
  308. NexSec = rumTime:lDateTimeToSec({CurDate, {23, 59, 59}}) + 1, %% 下个月1号凌晨
  309. {NewNDate, _NewNTime} = rumTime:secToLDateTime(NexSec),
  310. {NewNYear, NewNMonth, _} = NewNDate,
  311. NewMonthDay = rumTime:monthDay(NewNYear, NewNMonth),
  312. {{NewNYear, NewNMonth, NewMonthDay}, {SHour, SMinute, 0}}
  313. end;
  314. _ ->
  315. %% rotation is this last month day
  316. {{CurYear, CurMonth, CurMonthDay}, {SHour, SMinute, 0}}
  317. end;
  318. calcNextRotate({date, SHour, SMinute, SDate}, CurDate, CurTime) ->
  319. {CurYear, CurMonth, CurDay} = CurDate,
  320. if
  321. CurDay < SDate ->
  322. %% rotation is this month day
  323. {{CurYear, CurMonth, SDate}, {SHour, SMinute, 0}};
  324. CurDay > SDate ->
  325. %% rotation is next month day
  326. CurMonthDay = rumTime:monthDay(CurYear, CurMonth),
  327. NexSec = rumTime:lDateTimeToSec({{CurYear, CurMonth, CurMonthDay}, {23, 59, 59}}) + 1,
  328. {NewNDate, _NewNTime} = rumTime:secToLDateTime(NexSec),
  329. {NewNYear, NewNMonth, _} = NewNDate,
  330. {{NewNYear, NewNMonth, SDate}, {SHour, SMinute, 0}};
  331. true ->
  332. case CurTime < {SHour, SMinute, 0} of
  333. true ->
  334. %% rotation is this month day
  335. {CurDate, {SHour, SMinute, 0}};
  336. _ ->
  337. %% rotation is next month day
  338. CurMonthDay = rumTime:monthDay(CurYear, CurMonth),
  339. NexSec = rumTime:lDateTimeToSec({{CurYear, CurMonth, CurMonthDay}, {23, 59, 59}}) + 1,
  340. {NewNDate, _NewNTime} = rumTime:secToLDateTime(NexSec),
  341. {NewNYear, NewNMonth, _} = NewNDate,
  342. {{NewNYear, NewNMonth, SDate}, {SHour, SMinute, 0}}
  343. end
  344. end.
  345. -spec trace_filter(Query :: 'none' | [tuple()]) -> {ok, any()}.
  346. trace_filter(Query) ->
  347. trace_filter(?RumDefTracer, Query).
  348. %% TODO: Support multiple trace modules
  349. %-spec trace_filter(Module :: atom(), Query :: 'none' | [tuple()]) -> {ok, any()}.
  350. trace_filter(Module, Query) when Query == none; Query == [] ->
  351. {ok, _} = glc:compile(Module, glc:null(false));
  352. trace_filter(Module, Query) when is_list(Query) ->
  353. {ok, _} = glc:compile(Module, glc_lib:reduce(trace_any(Query))).
  354. validate_trace({Filter, Level, {Destination, ID}}) when is_tuple(Filter); is_list(Filter), is_atom(Level), is_atom(Destination) ->
  355. case validate_trace({Filter, Level, Destination}) of
  356. {ok, {F, L, D}} ->
  357. {ok, {F, L, {D, ID}}};
  358. Error ->
  359. Error
  360. end;
  361. validate_trace({Filter, Level, Destination}) when is_tuple(Filter); is_list(Filter), is_atom(Level), is_atom(Destination) ->
  362. ValidFilter = validate_trace_filter(Filter),
  363. try configToMask(Level) of
  364. _ when not ValidFilter ->
  365. {error, invalid_trace};
  366. L when is_list(Filter) ->
  367. {ok, {trace_all(Filter), L, Destination}};
  368. L ->
  369. {ok, {Filter, L, Destination}}
  370. catch
  371. _:_ ->
  372. {error, invalid_level}
  373. end;
  374. validate_trace(_) ->
  375. {error, invalid_trace}.
  376. validate_trace_filter(Filter) when is_tuple(Filter), is_atom(element(1, Filter)) =:= false ->
  377. false;
  378. validate_trace_filter(Filter) when is_list(Filter) ->
  379. lists:all(fun validate_trace_filter/1, Filter);
  380. validate_trace_filter({Key, '*'}) when is_atom(Key) -> true;
  381. validate_trace_filter({any, L}) when is_list(L) -> lists:all(fun validate_trace_filter/1, L);
  382. validate_trace_filter({all, L}) when is_list(L) -> lists:all(fun validate_trace_filter/1, L);
  383. validate_trace_filter({null, Bool}) when is_boolean(Bool) -> true;
  384. validate_trace_filter({Key, _Value}) when is_atom(Key) -> true;
  385. validate_trace_filter({Key, '=', _Value}) when is_atom(Key) -> true;
  386. validate_trace_filter({Key, '!=', _Value}) when is_atom(Key) -> true;
  387. validate_trace_filter({Key, '<', _Value}) when is_atom(Key) -> true;
  388. validate_trace_filter({Key, '=<', _Value}) when is_atom(Key) -> true;
  389. validate_trace_filter({Key, '>', _Value}) when is_atom(Key) -> true;
  390. validate_trace_filter({Key, '>=', _Value}) when is_atom(Key) -> true;
  391. validate_trace_filter(_) -> false.
  392. trace_all(Query) ->
  393. glc:all(trace_acc(Query)).
  394. trace_any(Query) ->
  395. glc:any(Query).
  396. trace_acc(Query) ->
  397. trace_acc(Query, []).
  398. trace_acc([], Acc) ->
  399. lists:reverse(Acc);
  400. trace_acc([{any, L} | T], Acc) ->
  401. trace_acc(T, [glc:any(L) | Acc]);
  402. trace_acc([{all, L} | T], Acc) ->
  403. trace_acc(T, [glc:all(L) | Acc]);
  404. trace_acc([{null, Bool} | T], Acc) ->
  405. trace_acc(T, [glc:null(Bool) | Acc]);
  406. trace_acc([{Key, '*'} | T], Acc) ->
  407. trace_acc(T, [glc:wc(Key) | Acc]);
  408. trace_acc([{Key, '!'} | T], Acc) ->
  409. trace_acc(T, [glc:nf(Key) | Acc]);
  410. trace_acc([{Key, Val} | T], Acc) ->
  411. trace_acc(T, [glc:eq(Key, Val) | Acc]);
  412. trace_acc([{Key, '=', Val} | T], Acc) ->
  413. trace_acc(T, [glc:eq(Key, Val) | Acc]);
  414. trace_acc([{Key, '!=', Val} | T], Acc) ->
  415. trace_acc(T, [glc:neq(Key, Val) | Acc]);
  416. trace_acc([{Key, '>', Val} | T], Acc) ->
  417. trace_acc(T, [glc:gt(Key, Val) | Acc]);
  418. trace_acc([{Key, '>=', Val} | T], Acc) ->
  419. trace_acc(T, [glc:gte(Key, Val) | Acc]);
  420. trace_acc([{Key, '=<', Val} | T], Acc) ->
  421. trace_acc(T, [glc:lte(Key, Val) | Acc]);
  422. trace_acc([{Key, '<', Val} | T], Acc) ->
  423. trace_acc(T, [glc:lt(Key, Val) | Acc]).
  424. check_traces(_, _, [], Acc) ->
  425. lists:flatten(Acc);
  426. check_traces(Attrs, Level, [{_, FilterLevel, _} | Flows], Acc) when (Level band FilterLevel) == 0 ->
  427. check_traces(Attrs, Level, Flows, Acc);
  428. check_traces(Attrs, Level, [{Filter, _, _} | Flows], Acc) when length(Attrs) < length(Filter) ->
  429. check_traces(Attrs, Level, Flows, Acc);
  430. check_traces(Attrs, Level, [Flow | Flows], Acc) ->
  431. check_traces(Attrs, Level, Flows, [check_trace(Attrs, Flow) | Acc]).
  432. check_trace(Attrs, {Filter, _Level, Dest}) when is_list(Filter) ->
  433. check_trace(Attrs, {trace_all(Filter), _Level, Dest});
  434. check_trace(Attrs, {Filter, _Level, Dest}) when is_tuple(Filter) ->
  435. Made = gre:make(Attrs, [list]),
  436. glc:handle(?RumDefTracer, Made),
  437. Match = glc_lib:matches(Filter, Made),
  438. case Match of
  439. true ->
  440. Dest;
  441. false ->
  442. []
  443. end.
  444. -spec isLoggAble(rumMsg:rumMsg(), rumMaskLevel(), term()) -> boolean().
  445. isLoggAble(Msg, Mask, MyName) ->
  446. (rumMsg:severity_as_int(Msg) band Mask) /= 0 orelse lists:member(MyName, rumMsg:destinations(Msg)).
  447. parsePath(RelPath) ->
  448. NewRelPath =
  449. case rumUtil:get_env(logRoot, undefined) of
  450. undefined ->
  451. RelPath;
  452. LogRoot ->
  453. case filename:dirname(RelPath) of
  454. "." ->
  455. filename:join(LogRoot, RelPath);
  456. false ->
  457. RelPath
  458. end
  459. end,
  460. filename:absname(NewRelPath).
  461. %% Find a file among the already installed handlers.
  462. %%
  463. %% The file is already expanded (i.e. lager_util:expand_path already added the
  464. %% "logRoot"), but the file paths inside Handlers are not.
  465. find_file(_File1, _Handlers = []) ->
  466. false;
  467. find_file(File1, [{{lager_file_backend, File2}, _Handler, _Sink} = HandlerInfo | Handlers]) ->
  468. File1Abs = File1,
  469. File2Abs = lager_util:expand_path(File2),
  470. case File1Abs =:= File2Abs of
  471. true ->
  472. % The file inside HandlerInfo is the same as the file we are looking
  473. % for, so we are done.
  474. HandlerInfo;
  475. false ->
  476. find_file(File1, Handlers)
  477. end;
  478. find_file(File1, [_HandlerInfo | Handlers]) ->
  479. find_file(File1, Handlers).
  480. %% conditionally check the HWM if the event would not have been filtered
  481. checkHwm(Shaper = #rumShaper{filter = Filter}, Event) ->
  482. case Filter(Event) of
  483. true ->
  484. {true, 0, Shaper};
  485. _ ->
  486. checkHwm(Shaper)
  487. end.
  488. %% 日志速率限制S i.e. 即传入消息的高水位标记
  489. checkHwm(#rumShaper{id = Id, hwm = Hwm, mps = Mps, lastTime = LastTime, dropped = Drop, flushQueue = FlushQueue, flushThreshold = FlushThreshold, timer = Timer, filter = Filter} = Shaper) ->
  490. if
  491. Hwm == undefined ->
  492. {true, 0, Shaper};
  493. Mps < Hwm ->
  494. NowTime = rumTime:now(),
  495. case LastTime == NowTime of
  496. true ->
  497. {true, 0, Shaper#rumShaper{mps = Mps + 1}};
  498. _ ->
  499. %different second - reset mps
  500. {true, 0, Shaper#rumShaper{dropped = 0, mps = 1, lastTime = NowTime}}
  501. end;
  502. true ->
  503. %% are we still in the same second?
  504. NowTimeMs = rumTime:nowMs(),
  505. NowTime = NowTimeMs div 1000,
  506. PastMs = NowTimeMs rem 1000,
  507. case LastTime == NowTime of
  508. true ->
  509. %% still in same second, but have exceeded the high water mark
  510. NewDrops = ?IIF(isNeedFlush(FlushQueue, FlushThreshold), dropMsg(NowTime, Filter, 0), 0),
  511. NewTimer = ?IIF(erlang:read_timer(Timer) =/= false, Timer, erlang:send_after(1000 - PastMs, self(), {mShaperExpired, Id})),
  512. {false, 0, Shaper#rumShaper{dropped = Drop + NewDrops + 1, timer = NewTimer}};
  513. _ ->
  514. _ = erlang:cancel_timer(Shaper#rumShaper.timer),
  515. %% different second, reset all counters and allow it
  516. {drop, Drop, Shaper#rumShaper{dropped = 0, mps = 1, lastTime = NowTime}}
  517. end
  518. end.
  519. isNeedFlush(true, FlushThreshold) ->
  520. case FlushThreshold of
  521. 0 ->
  522. true;
  523. _ ->
  524. PInfo = process_info(self(), message_queue_len),
  525. element(2, PInfo) > FlushThreshold
  526. end;
  527. isNeedFlush(_FlushQueue, _FlushThreshold) ->
  528. false.
  529. dropMsg(LastTime, Filter, Count) ->
  530. CurTime = rumUtil:now(),
  531. case CurTime == LastTime of
  532. true ->
  533. receive
  534. %% we only discard gen_event notifications, because
  535. %% otherwise we might discard gen_event internal
  536. %% messages, such as trapped EXITs
  537. {'$gen_info', Event} ->
  538. NewCount = ?IIF(Filter(Event), Count, Count + 1),
  539. dropMsg(LastTime, Filter, NewCount)
  540. after 0 ->
  541. Count
  542. end;
  543. _ ->
  544. Count
  545. end.
  546. %% @private Build an atom for the gen_event process based on a sink name.
  547. %% For historical reasons, the default gen_event process for lager itself is named
  548. %% `lager_event'. For all other sinks, it is SinkName++`_lager_event'
  549. makeInnerSinkName(Sink) ->
  550. binary_to_atom(<<(atom_to_binary(Sink, utf8))/binary, "Event">>).
  551. maybeFlush(undefined, #rumShaper{} = S) ->
  552. S;
  553. maybeFlush(Flag, #rumShaper{} = S) ->
  554. S#rumShaper{flushQueue = Flag}.
  555. -spec isFileChanged(FileName :: file:name_all(), Inode :: pos_integer(), Ctime :: file:date_time()) -> {boolean(), file:file_info() | undefined}.
  556. isFileChanged(FileName, Inode, Ctime) ->
  557. case file:read_file_info(FileName, [raw]) of
  558. {ok, FileInfo} ->
  559. case os:type() of
  560. {win32, _} ->
  561. % Note: on win32, Inode is always zero So check the file's ctime to see if it needs to be re-opened
  562. {Ctime =/= FileInfo#file_info.ctime, FileInfo};
  563. _ ->
  564. {Inode =/= FileInfo#file_info.inode, FileInfo}
  565. end;
  566. _ ->
  567. {true, undefined}
  568. end.
  569. -spec get_env(Par :: atom(), Def :: term()) -> Val :: term().
  570. get_env(Key, Def) ->
  571. case application:get_env(?RumAppName, Key) of
  572. {ok, Val} ->
  573. Val;
  574. _ ->
  575. Def
  576. end.
  577. get_opt(Key, Opts, Def) ->
  578. case lists:keyfind(Key, 1, Opts) of
  579. false ->
  580. Def;
  581. V ->
  582. element(2, V)
  583. end.
  584. -ifdef(TEST).
  585. parse_test() ->
  586. ?assertEqual({ok, {undefined, undefined, 0, undefined}}, rumUtil:parseRotateSpec("$H0")),
  587. ?assertEqual({ok, {undefined, undefined, 59, undefined}}, rumUtil:parseRotateSpec("$H59")),
  588. ?assertEqual({ok, {undefined, 0, 0, undefined}}, rumUtil:parseRotateSpec("$D0")),
  589. ?assertEqual({ok, {undefined, 23, 0, undefined}}, rumUtil:parseRotateSpec("$D23")),
  590. ?assertEqual({ok, {day, 23, 0, 7}}, rumUtil:parseRotateSpec("$W7D23")),
  591. ?assertEqual({ok, {day, 16, 0, 5}}, rumUtil:parseRotateSpec("$W5D16")),
  592. ?assertEqual({ok, {day, 12, 30, 7}}, rumUtil:parseRotateSpec("$W7D12H30")),
  593. ?assertEqual({ok, {date, 0, 0, 1}}, rumUtil:parseRotateSpec("$M1D0")),
  594. ?assertEqual({ok, {date, 6, 0, 5}}, rumUtil:parseRotateSpec("$M5D6")),
  595. ?assertEqual({ok, {date, 0, 0, 5}}, rumUtil:parseRotateSpec("$M5")),
  596. ?assertEqual({ok, {date, 0, 0, 31}}, rumUtil:parseRotateSpec("$M31")),
  597. ?assertEqual({ok, {date, 1, 0, 31}}, rumUtil:parseRotateSpec("$M31D1")),
  598. ?assertEqual({ok, {last, 0, 0, undefined}}, rumUtil:parseRotateSpec("$ML")),
  599. ?assertEqual({ok, {last, 0, 0, undefined}}, rumUtil:parseRotateSpec("$Ml")),
  600. ?assertEqual({ok, {day, 0, 0, 5}}, rumUtil:parseRotateSpec("$W5")),
  601. ?assertEqual({ok, {date, 12, 36, 5}}, rumUtil:parseRotateSpec("$M5D12H36")),
  602. ok.
  603. parse_fail_test() ->
  604. ?assertEqual({error, invalid_date_spec}, rumUtil:parseRotateSpec("$H")),
  605. ?assertEqual({error, invalid_date_spec}, rumUtil:parseRotateSpec("$H60")),
  606. ?assertEqual({error, invalid_date_spec}, rumUtil:parseRotateSpec("$D")),
  607. ?assertEqual({error, invalid_date_spec}, rumUtil:parseRotateSpec("$D24")),
  608. ?assertEqual({error, invalid_date_spec}, rumUtil:parseRotateSpec("$W0")),
  609. ?assertEqual({error, invalid_date_spec}, rumUtil:parseRotateSpec("$W0D1")),
  610. ?assertEqual({error, invalid_date_spec}, rumUtil:parseRotateSpec("$M32")),
  611. ?assertEqual({error, invalid_date_spec}, rumUtil:parseRotateSpec("$M32D1")),
  612. ?assertEqual({error, invalid_date_spec}, rumUtil:parseRotateSpec("$")),
  613. ?assertEqual({error, invalid_date_spec}, rumUtil:parseRotateSpec("")),
  614. ?assertEqual({error, invalid_date_spec}, rumUtil:parseRotateSpec("$D15M5")),
  615. ?assertEqual({error, invalid_date_spec}, rumUtil:parseRotateSpec("$M5W5")),
  616. ok.
  617. rotation_calculation_test() ->
  618. ?assertMatch({{2000, 1, 1}, {13, 0, 0}},
  619. rumUtil:calcNextRotateDt({undefined, undefined, 0, 0}, {{2000, 1, 1}, {12, 34, 43}})),
  620. ?assertMatch({{2000, 1, 1}, {12, 45, 0}},
  621. rumUtil:calcNextRotateDt({undefined, undefined, 45, 0}, {{2000, 1, 1}, {12, 34, 43}})),
  622. ?assertMatch({{2000, 1, 2}, {0, 0, 0}},
  623. rumUtil:calcNextRotateDt({undefined, undefined, 0, 0}, {{2000, 1, 1}, {23, 45, 43}})),
  624. ?assertMatch({{2000, 1, 2}, {0, 0, 0}},
  625. rumUtil:calcNextRotateDt({undefined, 0, 0, 0}, {{2000, 1, 1}, {12, 34, 43}})),
  626. ?assertMatch({{2000, 1, 1}, {16, 0, 0}},
  627. rumUtil:calcNextRotateDt({undefined, 16, 0, 0}, {{2000, 1, 1}, {12, 34, 43}})),
  628. ?assertMatch({{2000, 1, 2}, {12, 0, 0}},
  629. rumUtil:calcNextRotateDt({undefined, 12, 0, 0}, {{2000, 1, 1}, {12, 34, 43}})),
  630. ?assertMatch({{2000, 2, 1}, {12, 0, 0}},
  631. rumUtil:calcNextRotateDt({date, 12, 0, 1}, {{2000, 1, 1}, {12, 34, 43}})),
  632. ?assertMatch({{2000, 2, 1}, {12, 0, 0}},
  633. rumUtil:calcNextRotateDt({date, 12, 0, 1}, {{2000, 1, 15}, {12, 34, 43}})),
  634. ?assertMatch({{2000, 2, 1}, {12, 0, 0}},
  635. rumUtil:calcNextRotateDt({date, 12, 0, 1}, {{2000, 1, 2}, {12, 34, 43}})),
  636. ?assertMatch({{2000, 2, 1}, {12, 0, 0}},
  637. rumUtil:calcNextRotateDt({date, 12, 0, 1}, {{2000, 1, 31}, {12, 34, 43}})),
  638. ?assertMatch({{2000, 1, 1}, {16, 0, 0}},
  639. rumUtil:calcNextRotateDt({date, 16, 0, 1}, {{2000, 1, 1}, {12, 34, 43}})),
  640. ?assertMatch({{2000, 1, 15}, {16, 0, 0}},
  641. rumUtil:calcNextRotateDt({date, 16, 0, 15}, {{2000, 1, 1}, {12, 34, 43}})),
  642. ?assertMatch({{2000, 1, 31}, {16, 0, 0}},
  643. rumUtil:calcNextRotateDt({last, 16, 0, 0}, {{2000, 1, 1}, {12, 34, 43}})),
  644. ?assertMatch({{2000, 1, 31}, {16, 0, 0}},
  645. rumUtil:calcNextRotateDt({last, 16, 0, 0}, {{2000, 1, 31}, {12, 34, 43}})),
  646. ?assertMatch({{2000, 2, 29}, {16, 0, 0}},
  647. rumUtil:calcNextRotateDt({last, 16, 0, 0}, {{2000, 1, 31}, {17, 34, 43}})),
  648. ?assertMatch({{2001, 2, 28}, {16, 0, 0}},
  649. rumUtil:calcNextRotateDt({last, 16, 0, 0}, {{2001, 1, 31}, {17, 34, 43}})),
  650. ?assertMatch({{2000, 1, 1}, {16, 0, 0}},
  651. rumUtil:calcNextRotateDt({day, 16, 0, 6}, {{2000, 1, 1}, {12, 34, 43}})),
  652. ?assertMatch({{2000, 1, 8}, {16, 0, 0}},
  653. rumUtil:calcNextRotateDt({day, 16, 0, 6}, {{2000, 1, 1}, {17, 34, 43}})),
  654. ?assertMatch({{2000, 1, 7}, {16, 0, 0}},
  655. rumUtil:calcNextRotateDt({day, 16, 0, 5}, {{2000, 1, 1}, {17, 34, 43}})),
  656. ?assertMatch({{2000, 1, 3}, {16, 0, 0}},
  657. rumUtil:calcNextRotateDt({day, 16, 0, 1}, {{2000, 1, 1}, {17, 34, 43}})),
  658. ?assertMatch({{2000, 1, 2}, {16, 0, 0}},
  659. rumUtil:calcNextRotateDt({day, 16, 0, 7}, {{2000, 1, 1}, {17, 34, 43}})),
  660. ?assertMatch({{2000, 1, 9}, {16, 0, 0}},
  661. rumUtil:calcNextRotateDt({day, 16, 0, 7}, {{2000, 1, 2}, {17, 34, 43}})),
  662. ?assertMatch({{2000, 2, 3}, {16, 0, 0}},
  663. rumUtil:calcNextRotateDt({day, 16, 0, 4}, {{2000, 1, 29}, {17, 34, 43}})),
  664. ?assertMatch({{2000, 1, 7}, {16, 0, 0}},
  665. rumUtil:calcNextRotateDt({day, 16, 0, 5}, {{2000, 1, 3}, {17, 34, 43}})),
  666. ?assertMatch({{2000, 1, 3}, {16, 0, 0}},
  667. rumUtil:calcNextRotateDt({day, 16, 0, 1}, {{1999, 12, 28}, {17, 34, 43}})),
  668. ok.
  669. check_trace_test() ->
  670. eRum:start(),
  671. trace_filter(none),
  672. %% match by module
  673. ?assertEqual([foo], check_traces([{module, ?MODULE}], ?EMERGENCY, [
  674. {[{module, ?MODULE}], configToMask(emergency), foo},
  675. {[{module, test}], configToMask(emergency), bar}], [])),
  676. %% match by module, but other unsatisfyable attribute
  677. ?assertEqual([], check_traces([{module, ?MODULE}], ?EMERGENCY, [
  678. {[{module, ?MODULE}, {foo, bar}], configToMask(emergency), foo},
  679. {[{module, test}], configToMask(emergency), bar}], [])),
  680. %% match by wildcard module
  681. ?assertEqual([bar], check_traces([{module, ?MODULE}], ?EMERGENCY, [
  682. {[{module, ?MODULE}, {foo, bar}], configToMask(emergency), foo},
  683. {[{module, '*'}], configToMask(emergency), bar}], [])),
  684. %% wildcard module, one trace with unsatisfyable attribute
  685. ?assertEqual([bar], check_traces([{module, ?MODULE}], ?EMERGENCY, [
  686. {[{module, '*'}, {foo, bar}], configToMask(emergency), foo},
  687. {[{module, '*'}], configToMask(emergency), bar}], [])),
  688. %% wildcard but not present custom trace attribute
  689. ?assertEqual([bar], check_traces([{module, ?MODULE}], ?EMERGENCY, [
  690. {[{module, '*'}, {foo, '*'}], configToMask(emergency), foo},
  691. {[{module, '*'}], configToMask(emergency), bar}], [])),
  692. %% wildcarding a custom attribute works when it is present
  693. ?assertEqual([bar, foo], check_traces([{module, ?MODULE}, {foo, bar}], ?EMERGENCY, [
  694. {[{module, '*'}, {foo, '*'}], configToMask(emergency), foo},
  695. {[{module, '*'}], configToMask(emergency), bar}], [])),
  696. %% denied by level
  697. ?assertEqual([], check_traces([{module, ?MODULE}, {foo, bar}], ?INFO, [
  698. {[{module, '*'}, {foo, '*'}], configToMask(emergency), foo},
  699. {[{module, '*'}], configToMask(emergency), bar}], [])),
  700. %% allowed by level
  701. ?assertEqual([foo], check_traces([{module, ?MODULE}, {foo, bar}], ?INFO, [
  702. {[{module, '*'}, {foo, '*'}], configToMask(debug), foo},
  703. {[{module, '*'}], configToMask(emergency), bar}], [])),
  704. ?assertEqual([anythingbutnotice, infoandbelow, infoonly], check_traces([{module, ?MODULE}], ?INFO, [
  705. {[{module, '*'}], configToMask('=debug'), debugonly},
  706. {[{module, '*'}], configToMask('=info'), infoonly},
  707. {[{module, '*'}], configToMask('<=info'), infoandbelow},
  708. {[{module, '*'}], configToMask('!=info'), anythingbutinfo},
  709. {[{module, '*'}], configToMask('!=notice'), anythingbutnotice}
  710. ], [])),
  711. application:stop(lager),
  712. application:stop(goldrush),
  713. ok.
  714. is_loggable_test_() ->
  715. [
  716. {"Loggable by severity only", ?_assert(isLoggAble(rumMsg:new("", alert, [], []), 2, me))},
  717. {"Not loggable by severity only", ?_assertNot(isLoggAble(rumMsg:new("", critical, [], []), 1, me))},
  718. {"Loggable by severity with destination", ?_assert(isLoggAble(rumMsg:new("", alert, [], [you]), 2, me))},
  719. {"Not loggable by severity with destination", ?_assertNot(isLoggAble(rumMsg:new("", critical, [], [you]), 1, me))},
  720. {"Loggable by destination overriding severity", ?_assert(isLoggAble(rumMsg:new("", critical, [], [me]), 1, me))}
  721. ].
  722. format_time_test_() ->
  723. [
  724. ?_assertEqual("2012-10-04 11:16:23.002",
  725. begin
  726. {D, T} = msToBinStr({{2012, 10, 04}, {11, 16, 23, 2}}),
  727. lists:flatten([D, $ , T])
  728. end),
  729. ?_assertEqual("2012-10-04 11:16:23.999",
  730. begin
  731. {D, T} = msToBinStr({{2012, 10, 04}, {11, 16, 23, 999}}),
  732. lists:flatten([D, $ , T])
  733. end),
  734. ?_assertEqual("2012-10-04 11:16:23",
  735. begin
  736. {D, T} = msToBinStr({{2012, 10, 04}, {11, 16, 23}}),
  737. lists:flatten([D, $ , T])
  738. end),
  739. ?_assertEqual("2012-10-04 00:16:23.092 UTC",
  740. begin
  741. {D, T} = msToBinStr({utc, {{2012, 10, 04}, {0, 16, 23, 92}}}),
  742. lists:flatten([D, $ , T])
  743. end),
  744. ?_assertEqual("2012-10-04 11:16:23 UTC",
  745. begin
  746. {D, T} = msToBinStr({utc, {{2012, 10, 04}, {11, 16, 23}}}),
  747. lists:flatten([D, $ , T])
  748. end)
  749. ].
  750. config_to_levels_test() ->
  751. ?assertEqual([none], atomCfgToLevels('none')),
  752. ?assertEqual(0, configToMask('none')),
  753. ?assertEqual([debug], atomCfgToLevels('=debug')),
  754. ?assertEqual([debug], atomCfgToLevels('<info')),
  755. ?assertEqual(levels() -- [debug], atomCfgToLevels('!=debug')),
  756. ?assertEqual(levels() -- [debug], atomCfgToLevels('>debug')),
  757. ?assertEqual(levels() -- [debug], atomCfgToLevels('>=info')),
  758. ?assertEqual(levels() -- [debug], atomCfgToLevels('=>info')),
  759. ?assertEqual([debug, info, notice], atomCfgToLevels('<=notice')),
  760. ?assertEqual([debug, info, notice], atomCfgToLevels('=<notice')),
  761. ?assertEqual([debug], atomCfgToLevels('<info')),
  762. ?assertEqual([debug], atomCfgToLevels('!info')),
  763. ?assertError(badarg, atomCfgToLevels(ok)),
  764. ?assertError(badarg, atomCfgToLevels('<=>info')),
  765. ?assertError(badarg, atomCfgToLevels('=<=info')),
  766. ?assertError(badarg, atomCfgToLevels('<==>=<=>info')),
  767. %% double negatives DO work, however
  768. ?assertEqual([debug], atomCfgToLevels('!!=debug')),
  769. ?assertEqual(levels() -- [debug], atomCfgToLevels('!!!=debug')),
  770. ok.
  771. config_to_mask_test() ->
  772. ?assertEqual(0, configToMask('none')),
  773. ?assertEqual(?DEBUG bor ?INFO bor ?NOTICE bor ?WARNING bor ?ERROR bor ?CRITICAL bor ?ALERT bor ?EMERGENCY, configToMask('debug')),
  774. ?assertEqual(?WARNING bor ?ERROR bor ?CRITICAL bor ?ALERT bor ?EMERGENCY, configToMask('warning')),
  775. ?assertEqual(?DEBUG bor ?NOTICE bor ?WARNING bor ?ERROR bor ?CRITICAL bor ?ALERT bor ?EMERGENCY, configToMask('!=info')),
  776. ok.
  777. mask_to_levels_test() ->
  778. ?assertEqual([], maskToLevels(0)),
  779. ?assertEqual([debug], maskToLevels(2#10000000)),
  780. ?assertEqual([debug, info], maskToLevels(2#11000000)),
  781. ?assertEqual([debug, info, emergency], maskToLevels(2#11000001)),
  782. ?assertEqual([debug, notice, error], maskToLevels(?DEBUG bor ?NOTICE bor ?ERROR)),
  783. ok.
  784. expand_path_test() ->
  785. OldRootVal = application:get_env(lager, logRoot),
  786. ok = application:unset_env(lager, logRoot),
  787. ?assertEqual(filename:absname("/foo/bar"), parsePath("/foo/bar")),
  788. ?assertEqual(filename:absname("foo/bar"), parsePath("foo/bar")),
  789. ok = application:set_env(lager, logRoot, "log/dir"),
  790. ?assertEqual(filename:absname("/foo/bar"), parsePath("/foo/bar")), % Absolute path should not be changed
  791. ?assertEqual(filename:absname("log/dir/foo/bar"), parsePath("foo/bar")),
  792. ?assertEqual(filename:absname("log/dir/foo/bar"), parsePath("log/dir/foo/bar")), %% gh #304
  793. case OldRootVal of
  794. undefined -> application:unset_env(lager, logRoot);
  795. {ok, Root} -> application:set_env(lager, logRoot, Root)
  796. end,
  797. ok.
  798. sink_name_test_() ->
  799. [
  800. ?_assertEqual(rumEvent, makeInnerSinkName(lager)),
  801. ?_assertEqual(audit_lager_event, makeInnerSinkName(audit))
  802. ].
  803. create_test_dir() ->
  804. {ok, Tmp} = get_temp_dir(),
  805. Dir = filename:join([Tmp, "lager_test",
  806. erlang:integer_to_list(erlang:phash2(os:timestamp()))]),
  807. ?assertEqual(ok, filelib:ensure_dir(Dir)),
  808. TestDir = case file:make_dir(Dir) of
  809. ok ->
  810. Dir;
  811. Err ->
  812. ?assertEqual({error, eexist}, Err),
  813. create_test_dir()
  814. end,
  815. ok = application:set_env(lager, test_dir, TestDir),
  816. {ok, TestDir}.
  817. get_test_dir() ->
  818. case application:get_env(lager, test_dir) of
  819. undefined ->
  820. create_test_dir();
  821. {ok, _} = Res ->
  822. Res
  823. end.
  824. get_temp_dir() ->
  825. Tmp = case os:getenv("TEMP") of
  826. false ->
  827. case os:getenv("TMP") of
  828. false -> "/tmp";
  829. Dir1 -> Dir1
  830. end;
  831. Dir0 -> Dir0
  832. end,
  833. ?assertEqual(true, filelib:is_dir(Tmp)),
  834. {ok, Tmp}.
  835. delete_test_dir() ->
  836. {ok, TestDir} = get_test_dir(),
  837. ok = delete_test_dir(TestDir).
  838. delete_test_dir(TestDir) ->
  839. ok = application:unset_env(lager, test_dir),
  840. ok =
  841. case os:type() of
  842. {win32, _} ->
  843. application:stop(lager),
  844. do_delete_test_dir(TestDir);
  845. {_, _} ->
  846. do_delete_test_dir(TestDir)
  847. end.
  848. do_delete_test_dir(Dir) ->
  849. ListRet = file:list_dir_all(Dir),
  850. ?assertMatch({ok, _}, ListRet),
  851. {_, Entries} = ListRet,
  852. lists:foreach(
  853. fun(Entry) ->
  854. FsElem = filename:join(Dir, Entry),
  855. case filelib:is_dir(FsElem) of
  856. true ->
  857. delete_test_dir(FsElem);
  858. _ ->
  859. case file:delete(FsElem) of
  860. ok -> ok;
  861. Error ->
  862. io:format(standard_error, "[ERROR]: error deleting file ~p~n", [FsElem]),
  863. ?assertEqual(ok, Error)
  864. end
  865. end
  866. end, Entries),
  867. ?assertEqual(ok, file:del_dir(Dir)).
  868. do_delete_file(_FsElem, 0) ->
  869. ?assert(false);
  870. do_delete_file(FsElem, Attempts) ->
  871. case file:delete(FsElem) of
  872. ok -> ok;
  873. _Error ->
  874. do_delete_file(FsElem, Attempts - 1)
  875. end.
  876. set_dir_permissions(Perms, Dir) ->
  877. do_set_dir_permissions(os:type(), Perms, Dir).
  878. do_set_dir_permissions({win32, _}, _Perms, _Dir) ->
  879. ok;
  880. do_set_dir_permissions({unix, _}, Perms, Dir) ->
  881. os:cmd("chmod -R " ++ Perms ++ " " ++ Dir),
  882. ok.
  883. safe_application_load(App) ->
  884. case application:load(App) of
  885. ok ->
  886. ok;
  887. {error, {already_loaded, App}} ->
  888. ok;
  889. Error ->
  890. ?assertEqual(ok, Error)
  891. end.
  892. safe_write_file(File, Content) ->
  893. % Note: ensures that the new creation time is at least one second
  894. % in the future
  895. ?assertEqual(ok, file:write_file(File, Content)),
  896. Ctime0 = calendar:local_time(),
  897. Ctime0Sec = calendar:datetime_to_gregorian_seconds(Ctime0),
  898. Ctime1Sec = Ctime0Sec + 1,
  899. Ctime1 = calendar:gregorian_seconds_to_datetime(Ctime1Sec),
  900. {ok, FInfo0} = file:read_file_info(File, [raw]),
  901. FInfo1 = FInfo0#file_info{ctime = Ctime1},
  902. ?assertEqual(ok, file:write_file_info(File, FInfo1, [raw])).
  903. -endif.