1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1996-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20-module(error_logger). 21 22-include("logger_internal.hrl"). 23 24-export([start/0,start_link/0,stop/0, 25 format/2,error_msg/1,error_msg/2,error_report/1, 26 error_report/2,info_report/1,info_report/2,warning_report/1, 27 warning_report/2,error_info/1, 28 info_msg/1,info_msg/2,warning_msg/1,warning_msg/2, 29 logfile/1,tty/1, 30 add_report_handler/1,add_report_handler/2, 31 delete_report_handler/1, 32 which_report_handlers/0]). 33 34%% logger callbacks 35-export([adding_handler/1, removing_handler/1, log/2]). 36 37-export([get_format_depth/0, limit_term/1]). 38 39%%----------------------------------------------------------------- 40%% Types used in this file 41%%----------------------------------------------------------------- 42 43-type msg_tag() :: 'error' | 'error_report' 44 | 'info' | 'info_msg' | 'info_report' 45 | 'warning_msg' | 'warning_report'. 46 47%%% BIF 48 49-export([warning_map/0]). 50 51-spec warning_map() -> Tag when 52 Tag :: error | warning | info. 53 54warning_map() -> 55 erlang:nif_error(undef). 56 57%%% End of BIF 58 59%%----------------------------------------------------------------- 60 61%%%----------------------------------------------------------------- 62%%% Start the event manager process under logger_sup, which is part of 63%%% the kernel application's supervision tree. 64-spec start() -> 'ok' | {'error', any()}. 65 66start() -> 67 case whereis(?MODULE) of 68 undefined -> 69 ErrorLogger = 70 #{id => ?MODULE, 71 start => {?MODULE, start_link, []}, 72 restart => transient, 73 shutdown => 2000, 74 type => worker, 75 modules => dynamic}, 76 case supervisor:start_child(logger_sup, ErrorLogger) of 77 {ok,Pid} -> 78 ok = logger_handler_watcher:register_handler(?MODULE,Pid); 79 Error -> 80 Error 81 end; 82 _ -> 83 ok 84 end. 85 86%%%----------------------------------------------------------------- 87%%% Start callback specified in child specification to supervisor, see start/0 88-spec start_link() -> {'ok', pid()} | {'error', any()}. 89 90start_link() -> 91 gen_event:start_link({local, ?MODULE}, 92 [{spawn_opt,[{message_queue_data, off_heap}]}]). 93 94%%%----------------------------------------------------------------- 95%%% Stop the event manager 96-spec stop() -> ok. 97stop() -> 98 case whereis(?MODULE) of 99 undefined -> 100 ok; 101 _Pid -> 102 _ = gen_event:stop(?MODULE,{shutdown,stopped},infinity), 103 _ = supervisor:delete_child(logger_sup,?MODULE), 104 ok 105 end. 106 107%%%----------------------------------------------------------------- 108%%% Callbacks for logger 109-spec adding_handler(logger:handler_config()) -> 110 {ok,logger:handler_config()} | {error,term()}. 111adding_handler(#{id:=?MODULE}=Config) -> 112 case start() of 113 ok -> 114 {ok,Config}; 115 Error -> 116 Error 117 end. 118 119-spec removing_handler(logger:handler_config()) -> ok. 120removing_handler(#{id:=?MODULE}) -> 121 stop(), 122 ok. 123 124-spec log(logger:log_event(),logger:handler_config()) -> ok. 125log(#{level:=Level,msg:=Msg,meta:=Meta},_Config) -> 126 do_log(Level,Msg,Meta). 127 128do_log(Level,{report,Msg},#{?MODULE:=#{tag:=Tag,type:=Type}}=Meta) -> 129 %% From error_logger:*_report/1,2, or logger call which added 130 %% error_logger data to obtain backwards compatibility with 131 %% error_logger:*_report/1,2 132 Report = 133 case Msg of 134 #{label:=_,report:=R} -> R; 135 _ -> Msg 136 end, 137 notify(Level,Tag,Type,Report,Meta); 138do_log(Level,{report,Msg},#{?MODULE:=#{tag:=Tag}}=Meta) -> 139 {Format,Args} = 140 case Msg of 141 #{label:=_,format:=F,args:=A} -> 142 %% From error_logger:*_msg/1,2. 143 %% In order to be backwards compatible with handling 144 %% of faulty parameters to error_logger:*_msg/1,2, 145 %% don't use report_cb here. 146 {F,A}; 147 _ -> 148 %% From logger call which added error_logger data to 149 %% obtain backwards compatibility with error_logger:*_msg/1,2 150 case maps:get(report_cb,Meta,fun logger:format_report/1) of 151 RCBFun when is_function(RCBFun,1) -> 152 try RCBFun(Msg) of 153 {F,A} when is_list(F), is_list(A) -> 154 {F,A}; 155 Other -> 156 {"REPORT_CB ERROR: ~tp; Returned: ~tp",[Msg,Other]} 157 catch C:R -> 158 {"REPORT_CB CRASH: ~tp; Reason: ~tp",[Msg,{C,R}]} 159 end; 160 RCBFun when is_function(RCBFun,2) -> 161 try RCBFun(Msg,#{depth=>get_format_depth(), 162 chars_limit=>unlimited, 163 single_line=>false}) of 164 Chardata when ?IS_STRING(Chardata) -> 165 {"~ts",[Chardata]}; 166 Other -> 167 {"REPORT_CB ERROR: ~tp; Returned: ~tp",[Msg,Other]} 168 catch C:R -> 169 {"REPORT_CB CRASH: ~tp; Reason: ~tp",[Msg,{C,R}]} 170 end 171 end 172 end, 173 notify(Level,Tag,Format,Args,Meta); 174do_log(Level,{Format,Args},#{?MODULE:=#{tag:=Tag}}=Meta) 175 when is_list(Format), is_list(Args) -> 176 %% From logger call which added error_logger data to obtain 177 %% backwards compatibility with error_logger:*_msg/1,2 178 notify(Level,Tag,Format,Args,Meta); 179do_log(_Level,_Msg,_Meta) -> 180 %% Ignore the rest - i.e. to get backwards compatibility with 181 %% error_logger, you must use the error_logger API for logging. 182 %% Some modules within OTP go around this by adding an 183 %% error_logger field to its metadata. This is done only to allow 184 %% complete backwards compatibility for log events originating 185 %% from within OTP, while still using the new logger interface. 186 ok. 187 188-spec notify(logger:level(), msg_tag(), any(), any(), map()) -> 'ok'. 189notify(Level,Tag0,FormatOrType0,ArgsOrReport,#{pid:=Pid0,gl:=GL,?MODULE:=My}) -> 190 {Tag,FormatOrType} = maybe_map_warnings(Level,Tag0,FormatOrType0), 191 Pid = case maps:get(emulator,My,false) of 192 true -> emulator; 193 _ -> Pid0 194 end, 195 gen_event:notify(?MODULE,{Tag,GL,{Pid,FormatOrType,ArgsOrReport}}). 196 197%% For backwards compatibility with really old even handlers, check 198%% the warning map and update tag and type. 199maybe_map_warnings(warning,Tag,FormatOrType) -> 200 case error_logger:warning_map() of 201 warning -> 202 {Tag,FormatOrType}; 203 Level -> 204 {fix_warning_tag(Level,Tag),fix_warning_type(Level,FormatOrType)} 205 end; 206maybe_map_warnings(_,Tag,FormatOrType) -> 207 {Tag,FormatOrType}. 208 209fix_warning_tag(error,warning_msg) -> error; 210fix_warning_tag(error,warning_report) -> error_report; 211fix_warning_tag(info,warning_msg) -> info_msg; 212fix_warning_tag(info,warning_report) -> info_report; 213fix_warning_tag(_,Tag) -> Tag. 214 215fix_warning_type(error,std_warning) -> std_error; 216fix_warning_type(info,std_warning) -> std_info; 217fix_warning_type(_,Type) -> Type. 218 219%%----------------------------------------------------------------- 220%% These two simple old functions generate events tagged 'error' 221%% Used for simple messages; error or information. 222%%----------------------------------------------------------------- 223 224-spec error_msg(Format) -> 'ok' when 225 Format :: string(). 226 227error_msg(Format) -> 228 error_msg(Format,[]). 229 230-spec error_msg(Format, Data) -> 'ok' when 231 Format :: string(), 232 Data :: list(). 233 234error_msg(Format, Args) -> 235 logger:log(error, 236 #{label=>{?MODULE,error_msg}, 237 format=>Format, 238 args=>Args}, 239 meta(error)). 240 241-spec format(Format, Data) -> 'ok' when 242 Format :: string(), 243 Data :: list(). 244 245format(Format, Args) -> 246 error_msg(Format, Args). 247 248%%----------------------------------------------------------------- 249%% This functions should be used for error reports. Events 250%% are tagged 'error_report'. 251%% The 'std_error' error_report type can always be used. 252%%----------------------------------------------------------------- 253 254-type report() :: 255 [{Tag :: term(), Data :: term()} | term()] | string() | term(). 256 257-spec error_report(Report) -> 'ok' when 258 Report :: report(). 259 260error_report(Report) -> 261 error_report(std_error, Report). 262 263-spec error_report(Type, Report) -> 'ok' when 264 Type :: term(), 265 Report :: report(). 266 267error_report(Type, Report) -> 268 logger:log(error, 269 #{label=>{?MODULE,error_report}, 270 report=>Report}, 271 meta(error_report,Type)). 272 273%%----------------------------------------------------------------- 274%% This function should be used for warning reports. 275%% These might be mapped to error reports or info reports, 276%% depending on emulator flags. Events that ore not mapped 277%% are tagged 'info_report'. 278%% The 'std_warning' info_report type can always be used and is 279%% mapped to std_info or std_error accordingly. 280%%----------------------------------------------------------------- 281 282-spec warning_report(Report) -> 'ok' when 283 Report :: report(). 284 285warning_report(Report) -> 286 warning_report(std_warning, Report). 287 288-spec warning_report(Type, Report) -> 'ok' when 289 Type :: any(), 290 Report :: report(). 291 292warning_report(Type, Report) -> 293 logger:log(warning, 294 #{label=>{?MODULE,warning_report}, 295 report=>Report}, 296 meta(warning_report,Type)). 297 298%%----------------------------------------------------------------- 299%% This function provides similar functions as error_msg for 300%% warning messages, like warning report it might get mapped to 301%% other types of reports. 302%%----------------------------------------------------------------- 303 304-spec warning_msg(Format) -> 'ok' when 305 Format :: string(). 306 307warning_msg(Format) -> 308 warning_msg(Format,[]). 309 310-spec warning_msg(Format, Data) -> 'ok' when 311 Format :: string(), 312 Data :: list(). 313 314warning_msg(Format, Args) -> 315 logger:log(warning, 316 #{label=>{?MODULE,warning_msg}, 317 format=>Format, 318 args=>Args}, 319 meta(warning_msg)). 320 321%%----------------------------------------------------------------- 322%% This function should be used for information reports. Events 323%% are tagged 'info_report'. 324%% The 'std_info' info_report type can always be used. 325%%----------------------------------------------------------------- 326 327-spec info_report(Report) -> 'ok' when 328 Report :: report(). 329 330info_report(Report) -> 331 info_report(std_info, Report). 332 333-spec info_report(Type, Report) -> 'ok' when 334 Type :: any(), 335 Report :: report(). 336 337info_report(Type, Report) -> 338 logger:log(notice, 339 #{label=>{?MODULE,info_report}, 340 report=>Report}, 341 meta(info_report,Type)). 342 343%%----------------------------------------------------------------- 344%% This function provides similar functions as error_msg for 345%% information messages. 346%%----------------------------------------------------------------- 347 348-spec info_msg(Format) -> 'ok' when 349 Format :: string(). 350 351info_msg(Format) -> 352 info_msg(Format,[]). 353 354-spec info_msg(Format, Data) -> 'ok' when 355 Format :: string(), 356 Data :: list(). 357 358info_msg(Format, Args) -> 359 logger:log(notice, 360 #{label=>{?MODULE,info_msg}, 361 format=>Format, 362 args=>Args}, 363 meta(info_msg)). 364 365%%----------------------------------------------------------------- 366%% Used by the init process. Events are tagged 'info'. 367%%----------------------------------------------------------------- 368 369-spec error_info(Error :: any()) -> 'ok'. 370 371%% unused? 372error_info(Error) -> 373 {Format,Args} = 374 case string_p(Error) of 375 true -> {Error,[]}; 376 false -> {"~p",[Error]} 377 end, 378 MyMeta = #{tag=>info,type=>Error}, 379 logger:log(notice, Format, Args, #{?MODULE=>MyMeta,domain=>[Error]}). 380 381%%----------------------------------------------------------------- 382%% Create metadata 383meta(Tag) -> 384 meta(Tag,undefined). 385meta(Tag,Type) -> 386 meta(Tag,Type,#{report_cb=>fun report_to_format/1}). 387meta(Tag,undefined,Meta0) -> 388 Meta0#{?MODULE=>#{tag=>Tag}}; 389meta(Tag,Type,Meta0) -> 390 maybe_add_domain(Tag,Type,Meta0#{?MODULE=>#{tag=>Tag,type=>Type}}). 391 392%% This is to prevent events of non standard type from being printed 393%% with the standard logger. Similar to how error_logger_tty_h 394%% discards events of non standard type. 395maybe_add_domain(error_report,std_error,Meta) -> Meta; 396maybe_add_domain(info_report,std_info,Meta) -> Meta; 397maybe_add_domain(warning_report,std_warning,Meta) -> Meta; 398maybe_add_domain(_,Type,Meta) -> Meta#{domain=>[Type]}. 399 400%% ----------------------------------------------------------------- 401%% Report formatting - i.e. Term => {Format,Args} 402%% This was earlier done in the event handler (error_logger_tty_h, etc) 403%% ----------------------------------------------------------------- 404report_to_format(#{label:={?MODULE,_}, 405 report:=Report}) when is_map(Report) -> 406 %% logger:format_otp_report does maps:to_list, and for backwards 407 %% compatibility reasons we don't want that. 408 {"~tp\n",[Report]}; 409report_to_format(#{label:={?MODULE,_}, 410 format:=Format, 411 args:=Args}) -> 412 %% This is not efficient, but needed for backwards compatibility 413 %% in giving faulty arguments to the *_msg functions. 414 try io_lib:scan_format(Format,Args) of 415 _ -> {Format,Args} 416 catch _:_ -> 417 {"ERROR: ~tp - ~tp",[Format,Args]} 418 end; 419report_to_format(Term) -> 420 logger:format_otp_report(Term). 421 422string_p(List) when is_list(List) -> 423 string_p1(lists:flatten(List)); 424string_p(_) -> 425 false. 426 427string_p1([]) -> 428 false; 429string_p1(FlatList) -> 430 io_lib:printable_list(FlatList). 431 432%% ----------------------------------------------------------------- 433%% Stuff directly related to the event manager 434%% ----------------------------------------------------------------- 435-spec add_report_handler(Handler) -> any() when 436 Handler :: module(). 437 438add_report_handler(Module) when is_atom(Module) -> 439 add_report_handler(Module, []). 440 441-spec add_report_handler(Handler, Args) -> Result when 442 Handler :: module(), 443 Args :: gen_event:handler_args(), 444 Result :: gen_event:add_handler_ret(). 445 446add_report_handler(Module, Args) when is_atom(Module) -> 447 _ = logger:add_handler(?MODULE,?MODULE,#{level=>info,filter_default=>log}), 448 gen_event:add_handler(?MODULE, Module, Args). 449 450-spec delete_report_handler(Handler) -> Result when 451 Handler :: module(), 452 Result :: gen_event:del_handler_ret(). 453 454delete_report_handler(Module) when is_atom(Module) -> 455 case whereis(?MODULE) of 456 Pid when is_pid(Pid) -> 457 Return = gen_event:delete_handler(?MODULE, Module, []), 458 case gen_event:which_handlers(?MODULE) of 459 [] -> 460 %% Don't want a lot of logs here if it's not needed 461 _ = logger:remove_handler(?MODULE), 462 ok; 463 _ -> 464 ok 465 end, 466 Return; 467 _ -> 468 ok 469 end. 470 471which_report_handlers() -> 472 case whereis(?MODULE) of 473 Pid when is_pid(Pid) -> 474 gen_event:which_handlers(?MODULE); 475 undefined -> 476 [] 477 end. 478 479%% Log all errors to File for all eternity 480 481-type open_error() :: file:posix() | badarg | system_limit. 482 483-spec logfile(Request :: {open, Filename}) -> ok | {error, OpenReason} when 484 Filename ::file:name(), 485 OpenReason :: allready_have_logfile | open_error() 486 ; (Request :: close) -> ok | {error, CloseReason} when 487 CloseReason :: module_not_found 488 ; (Request :: filename) -> Filename | {error, FilenameReason} when 489 Filename :: file:name(), 490 FilenameReason :: no_log_file. 491 492logfile({open, File}) -> 493 case lists:member(error_logger_file_h,which_report_handlers()) of 494 true -> 495 {error, allready_have_logfile}; 496 _ -> 497 add_report_handler(error_logger_file_h, File) 498 end; 499logfile(close) -> 500 case whereis(?MODULE) of 501 Pid when is_pid(Pid) -> 502 case gen_event:delete_handler(?MODULE, error_logger_file_h, normal) of 503 {error,Reason} -> 504 {error,Reason}; 505 _ -> 506 ok 507 end; 508 _ -> 509 {error,module_not_found} 510 end; 511logfile(filename) -> 512 case whereis(?MODULE) of 513 Pid when is_pid(Pid) -> 514 case gen_event:call(?MODULE, error_logger_file_h, filename) of 515 {error,_} -> 516 {error, no_log_file}; 517 Val -> 518 Val 519 end; 520 _ -> 521 {error, no_log_file} 522 end. 523 524%% Possibly turn off all tty printouts, maybe we only want the errors 525%% to go to a file 526 527-spec tty(Flag) -> 'ok' when 528 Flag :: boolean(). 529 530tty(true) -> 531 _ = case lists:member(error_logger_tty_h, which_report_handlers()) of 532 false -> 533 case logger:get_handler_config(default) of 534 {ok,#{module:=logger_std_h,config:=#{type:=standard_io}}} -> 535 logger:remove_handler_filter(default, 536 error_logger_tty_false); 537 _ -> 538 logger:add_handler(error_logger_tty_true,logger_std_h, 539 #{filter_default=>stop, 540 filters=>?DEFAULT_HANDLER_FILTERS( 541 [otp]), 542 formatter=>{?DEFAULT_FORMATTER, 543 ?DEFAULT_FORMAT_CONFIG}, 544 config=>#{type=>standard_io}}) 545 end; 546 true -> 547 ok 548 end, 549 ok; 550tty(false) -> 551 delete_report_handler(error_logger_tty_h), 552 _ = logger:remove_handler(error_logger_tty_true), 553 _ = case logger:get_handler_config(default) of 554 {ok,#{module:=logger_std_h,config:=#{type:=standard_io}}} -> 555 logger:add_handler_filter(default,error_logger_tty_false, 556 {fun(_,_) -> stop end, ok}); 557 _ -> 558 ok 559 end, 560 ok. 561 562%%%----------------------------------------------------------------- 563-spec limit_term(term()) -> term(). 564 565limit_term(Term) -> 566 case get_format_depth() of 567 unlimited -> Term; 568 D -> io_lib:limit_term(Term, D) 569 end. 570 571-spec get_format_depth() -> 'unlimited' | pos_integer(). 572 573get_format_depth() -> 574 case application:get_env(kernel, error_logger_format_depth) of 575 {ok, Depth} when is_integer(Depth) -> 576 max(10, Depth); 577 {ok, unlimited} -> 578 unlimited; 579 undefined -> 580 unlimited 581 end. 582