1 |
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2 |
|
%% File : mongoose_deprecations.erl |
3 |
|
%% Author : Dominik Stanaszek <dominik.stanaszek@erlang-solutions.com> |
4 |
|
%% Purpose : More generic deprecation handling |
5 |
|
%% Created : 10 Oct 2017 |
6 |
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
7 |
|
%% @doc This module is responsible for initialising |
8 |
|
%% and stopping stuff needed to handle different deprecation |
9 |
|
%% warnings as well as for exposing API for logging these |
10 |
|
%% deprecations. |
11 |
|
%% It checks whether a specific deprecation warning is not exceeding |
12 |
|
%% given frequency of logging. |
13 |
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
14 |
|
|
15 |
|
-module(mongoose_deprecations). |
16 |
|
-author("dominik.stanaszek@erlang-solutions.com"). |
17 |
|
|
18 |
|
-export([start/0, stop/0, log/2, log/3]). |
19 |
|
|
20 |
|
%% Test API |
21 |
|
-export([log_with_lvl/2]). |
22 |
|
|
23 |
|
-ignore_xref([log/2]). |
24 |
|
|
25 |
|
-include("mongoose.hrl"). |
26 |
|
|
27 |
|
-define(DEPRECATION_TAB, deprecations). % ETS table name |
28 |
|
-define(DEFAULT_COOLDOWN_HOURS, 6). % default cooldown time |
29 |
|
|
30 |
|
-type deprecation_tag() :: any(). % Specifies the deprecation |
31 |
|
-type log_level() :: warning | error. |
32 |
|
-type unix_timestamp() :: mod_mam:unix_timestamp(). |
33 |
|
-type log_map() :: map(). |
34 |
|
|
35 |
|
|
36 |
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
37 |
|
%%% Public API |
38 |
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
39 |
|
|
40 |
|
%% @doc Should be called before using the module. Sets everything |
41 |
|
%% needed up |
42 |
|
-spec start() -> ok. |
43 |
|
start() -> |
44 |
104 |
prepare_ets(), |
45 |
104 |
ok. |
46 |
|
|
47 |
|
%% @doc Used after using the module, when we won't log deprecation |
48 |
|
%% messages again. |
49 |
|
-spec stop() -> ok. |
50 |
|
stop() -> |
51 |
105 |
destroy_ets(), |
52 |
105 |
ok. |
53 |
|
|
54 |
|
%% @doc Should be used to log deprecation messages. It logs |
55 |
|
%% keeping proper frequency. Opts can be: |
56 |
|
%% * cooldown - the minimal interval (in milliseconds) |
57 |
|
%% to be held between logs. Default: 6 hours |
58 |
|
%% It is internally represented in microseconds |
59 |
|
%% but API requires milliseconds. |
60 |
|
%% * log_level - 'warning' or 'error' |
61 |
|
-spec log(deprecation_tag(), log_map(), proplists:proplist()) -> ok. |
62 |
|
log(Tag, Msg, Opts) -> |
63 |
8 |
Ms = proplists:get_value(cooldown, Opts, default_cooldown()), |
64 |
8 |
Cooldown = milliseconds_to_microseconds(Ms), |
65 |
8 |
LogLvl = proplists:get_value(log_level, Opts, default_log_lvl()), |
66 |
8 |
maybe_log(Tag, Msg, LogLvl, Cooldown). |
67 |
|
|
68 |
|
-spec log(deprecation_tag(), log_map()) -> ok. |
69 |
|
log(Tag, Msg) -> |
70 |
:-( |
log(Tag, Msg, []). |
71 |
|
|
72 |
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
73 |
|
%%% Private functions |
74 |
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
75 |
|
|
76 |
|
|
77 |
|
%% @doc Deprecation table will hold pairs in form: |
78 |
|
%% {deprecation_tag(), unix_timestamp()} |
79 |
|
%% and will indicate at what (unix) time last deprecation |
80 |
|
%% warning was logged concerning a deprecation connected to |
81 |
|
%% the deprecation tag specified in a key |
82 |
|
-spec prepare_ets() -> ok. |
83 |
|
prepare_ets() -> |
84 |
104 |
ets:new(?DEPRECATION_TAB, [{read_concurrency, true}, named_table, public]), |
85 |
104 |
ok. |
86 |
|
|
87 |
|
-spec destroy_ets() -> ok. |
88 |
|
destroy_ets() -> |
89 |
105 |
ets:delete(?DEPRECATION_TAB), |
90 |
105 |
ok. |
91 |
|
|
92 |
|
-spec maybe_log(deprecation_tag(), log_map(), log_level(), unix_timestamp()) -> ok. |
93 |
|
maybe_log(Tag, Msg, Lvl, Cooldown) -> |
94 |
8 |
Timestamp = case ets:lookup(?DEPRECATION_TAB, Tag) of |
95 |
|
[] -> |
96 |
4 |
not_logged; |
97 |
|
[{Tag, LastLogged}] -> |
98 |
4 |
LastLogged |
99 |
|
end, |
100 |
8 |
case did_cooldown_elapse(Timestamp, Cooldown) of |
101 |
|
true -> |
102 |
4 |
?MODULE:log_with_lvl(Msg, Lvl), % ?MODULE lets meck mock it |
103 |
4 |
ets:insert(?DEPRECATION_TAB, {Tag, os:timestamp()}), |
104 |
4 |
ok; |
105 |
|
false -> |
106 |
4 |
ok |
107 |
|
end. |
108 |
|
|
109 |
|
-spec did_cooldown_elapse(unix_timestamp() | 'not_logged', unix_timestamp()) |
110 |
|
-> boolean(). |
111 |
4 |
did_cooldown_elapse(not_logged, _) -> true; |
112 |
|
did_cooldown_elapse(LastLogged, Cooldown) -> |
113 |
4 |
Now = os:timestamp(), |
114 |
4 |
timer:now_diff(Now, LastLogged) > Cooldown. |
115 |
|
|
116 |
|
-spec default_cooldown() -> unix_timestamp(). |
117 |
8 |
default_cooldown() -> ?DEFAULT_COOLDOWN_HOURS * 3600000000. |
118 |
|
|
119 |
|
-spec default_log_lvl() -> log_level(). |
120 |
8 |
default_log_lvl() -> error. |
121 |
|
|
122 |
|
-spec log_with_lvl(log_map(), log_level()) -> ok. |
123 |
|
log_with_lvl(Msg, error) -> |
124 |
:-( |
?LOG_ERROR(Msg); |
125 |
|
log_with_lvl(Msg, warning) -> |
126 |
4 |
?LOG_WARNING(Msg). |
127 |
|
|
128 |
|
-spec milliseconds_to_microseconds(Milliseconds :: integer()) |
129 |
|
-> unix_timestamp(). |
130 |
8 |
milliseconds_to_microseconds(N) -> N * 1000. |