/home/runner/work/eturnal/eturnal/_build/test/cover/aggregate/eturnal_logger.html

1 %%% eturnal STUN/TURN server.
2 %%%
3 %%% Copyright (c) 2020-2022 Holger Weiss <holger@zedat.fu-berlin.de>.
4 %%% Copyright (c) 2020-2022 ProcessOne, SARL.
5 %%% All rights reserved.
6 %%%
7 %%% Licensed under the Apache License, Version 2.0 (the "License");
8 %%% you may not use this file except in compliance with the License.
9 %%% You may obtain a copy of the License at
10 %%%
11 %%% http://www.apache.org/licenses/LICENSE-2.0
12 %%%
13 %%% Unless required by applicable law or agreed to in writing, software
14 %%% distributed under the License is distributed on an "AS IS" BASIS,
15 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 %%% See the License for the specific language governing permissions and
17 %%% limitations under the License.
18
19 -module(eturnal_logger).
20 -export([start/0,
21 stop/0,
22 progress_filter/2,
23 reconfigure/0,
24 is_valid_level/1,
25 get_level/0,
26 set_level/1,
27 flush/0]).
28 -export_type([level/0]).
29
30 -include_lib("kernel/include/logger.hrl").
31 -define(ETURNAL_HANDLER, eturnal_log).
32 -define(LOG_FILE_NAME, "eturnal.log").
33 -define(VALID_LEVELS, [critical, error, warning, notice, info, debug]).
34
35 -type logger_config() :: #{file => file:filename(),
36 file_check => non_neg_integer(),
37 max_no_bytes => pos_integer() | infinity,
38 max_no_files => non_neg_integer(),
39 flush_qlen => pos_integer(),
40 sync_mode_qlen => non_neg_integer(),
41 drop_mode_qlen => pos_integer()}.
42
43 % Subset of logger:level():
44 -type level() :: critical | error | warning | notice | info | debug.
45
46 % Currently not exported by logger_formatter:
47 -type metakey() :: atom() | [atom()].
48 -type template() :: [metakey() | {metakey(), template(), template()} |
49 string()].
50
51 %% API.
52
53 -spec start() -> ok.
54 start() ->
55 1 ok = init(get_config()),
56 1 ok = configure_default_handler().
57
58 -spec stop() -> ok.
59 stop() ->
60 1 ok = terminate().
61
62 -spec progress_filter(logger:log_event(), any()) -> logger:filter_return().
63 progress_filter(#{level := info,
64 msg := {report, #{label := {_, progress}}}} = Event,
65 _Extra) ->
66 25 case get_level() of
67 debug ->
68 25 logger_filters:progress(Event#{level => debug}, log);
69 _ ->
70
:-(
stop
71 end;
72 progress_filter(_Event, _Extra) ->
73 287 ignore.
74
75 -spec reconfigure() -> ok.
76 reconfigure() ->
77
:-(
Config = get_config(),
78
:-(
case logger:get_handler_config(?ETURNAL_HANDLER) of
79 {ok, _OldConfig} ->
80
:-(
case logger:set_handler_config(?ETURNAL_HANDLER, config, Config) of
81 ok ->
82
:-(
ok;
83 {error, {illegal_config_change, _, _, _}} ->
84
:-(
?LOG_ERROR("New logging settings require restart")
85 end,
86
:-(
ok = set_level();
87 {error, {not_found, _}} ->
88
:-(
ok = init(Config)
89 end,
90
:-(
ok = configure_default_handler().
91
92 -spec is_valid_level(atom()) -> boolean().
93 is_valid_level(Level) ->
94 2 lists:member(Level, ?VALID_LEVELS).
95
96 -spec get_level() -> logger:level() | all | none.
97 get_level() ->
98 30 #{level := Level} = logger:get_primary_config(),
99 30 Level.
100
101 -spec set_level(level()) -> ok.
102 set_level(Level) ->
103 2 ok = logger:set_primary_config(level, Level),
104 2 ok = logger:update_formatter_config(
105 ?ETURNAL_HANDLER, template, format_template()).
106
107 %% Internal functions.
108
109 -spec init(logger_config()) -> ok.
110 init(Config) ->
111 1 FmtConfig = #{time_designator => $\s,
112 max_size => 100 * 1024,
113 single_line => false},
114 1 case logger:add_primary_filter(progress_report,
115 {fun ?MODULE:progress_filter/2, stop}) of
116 ok ->
117 1 ok;
118 {error, {already_exist, _}} ->
119
:-(
ok
120 end,
121 1 case logger:add_handler(?ETURNAL_HANDLER, logger_std_h,
122 #{level => all,
123 config => Config,
124 formatter => {logger_formatter, FmtConfig}}) of
125 ok ->
126 1 ok;
127 {error, {already_exist, _}} ->
128
:-(
ok
129 end,
130 1 set_level().
131
132 -spec get_config() -> logger_config().
133 -ifdef(old_logger). % Erlang/OTP < 21.3.
134 get_config() ->
135 Config = #{sync_mode_qlen => 1000,
136 drop_mode_qlen => 1000, % Never switch to synchronous mode.
137 flush_qlen => 5000},
138 case get_log_file() of
139 LogFile when is_list(LogFile) ->
140 case eturnal:get_opt(log_rotate_size) of
141 Size when is_integer(Size) ->
142 ?LOG_WARNING("Log rotation requires newer Erlang/OTP "
143 "version, ignoring 'log_rotate_*' options");
144 infinity ->
145 ok
146 end,
147 Config#{type => {file, LogFile}};
148 stdout ->
149 Config
150 end.
151 -else.
152 get_config() ->
153 1 Config = #{sync_mode_qlen => 1000,
154 drop_mode_qlen => 1000, % Never switch to synchronous mode.
155 flush_qlen => 5000},
156 1 case get_log_file() of
157 LogFile when is_list(LogFile) ->
158 1 Config#{file => LogFile,
159 file_check => 1000,
160 max_no_bytes => eturnal:get_opt(log_rotate_size),
161 max_no_files => eturnal:get_opt(log_rotate_count)};
162 stdout ->
163
:-(
Config
164 end.
165 -endif.
166
167 -spec get_log_file() -> file:filename() | stdout.
168 get_log_file() ->
169 1 case eturnal:get_opt(log_dir) of
170 LogDir when is_binary(LogDir) ->
171 1 LogFile = filename:join(LogDir, <<?LOG_FILE_NAME>>),
172 1 unicode:characters_to_list(LogFile);
173 stdout ->
174
:-(
stdout
175 end.
176
177 -spec set_level() -> ok.
178 set_level() ->
179 1 ok = set_level(eturnal:get_opt(log_level)),
180 1 ok = logger:update_formatter_config(
181 ?ETURNAL_HANDLER, template, format_template()).
182
183 -spec logging_to_journal() -> boolean().
184 logging_to_journal() ->
185 3 (eturnal:get_opt(log_dir) =:= stdout) and
186 (os:getenv("JOURNAL_STREAM") =/= false).
187
188 -spec format_template() -> template().
189 format_template() ->
190 3 format_prefix() ++ format_message() ++ format_suffix().
191
192 -spec format_prefix() -> template().
193 format_prefix() ->
194 3 case logging_to_journal() of
195 true ->
196
:-(
["[", level, "] "];
197 false ->
198 3 [time, " [", level, "] "]
199 end.
200
201 -spec format_message() -> template().
202 format_message() ->
203 % For progress reports:
204 3 [{logger_formatter, [[logger_formatter, title], ":", io_lib:nl()], []},
205 % The actual log message:
206 msg].
207
208 -spec format_suffix() -> template().
209 format_suffix() ->
210 3 case get_level() of
211 debug ->
212 % Append (Module:Function/Arity and maybe :Line), if available:
213 3 [{mfa, [" (", mfa, {line, [":", line], []}, ")"], []},
214 io_lib:nl()];
215 _Level ->
216
:-(
[io_lib:nl()]
217 end.
218
219 -spec configure_default_handler() -> ok.
220 configure_default_handler() ->
221 1 case eturnal:get_opt(log_dir) of
222 LogDir when is_binary(LogDir) ->
223 1 ok = logger:set_handler_config(default, level, warning);
224 stdout ->
225
:-(
ok = logger:set_handler_config(default, level, none)
226 end.
227
228 -spec flush() -> ok.
229 flush() ->
230 1 lists:foreach(
231 fun(#{id := HandlerID, module := logger_std_h}) ->
232 3 logger_std_h:filesync(HandlerID);
233 (_) ->
234 2 ok
235 end, logger:get_handler_config()).
236
237 -spec terminate() -> ok.
238 terminate() ->
239 1 ok = flush(),
240 1 ok = logger:set_handler_config(default, level, notice),
241 1 ok = logger:remove_primary_filter(progress_report),
242 1 ok = logger:remove_handler(?ETURNAL_HANDLER).
Line Hits Source