1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2017-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(logger_simple_h). 21 22-export([adding_handler/1, removing_handler/1, log/2]). 23 24%% This module implements a simple handler for logger. It is the 25%% default used during system start. 26 27%%%----------------------------------------------------------------- 28%%% Logger callback 29 30adding_handler(#{id:=simple}=Config) -> 31 Me = self(), 32 case whereis(?MODULE) of 33 undefined -> 34 {Pid,Ref} = spawn_opt(fun() -> init(Me) end, 35 [link,monitor,{message_queue_data,off_heap}]), 36 receive 37 {'DOWN',Ref,process,Pid,Reason} -> 38 {error,Reason}; 39 {Pid,started} -> 40 erlang:demonitor(Ref), 41 {ok,Config} 42 end; 43 _ -> 44 {error,{handler_process_name_already_exists,?MODULE}} 45 end. 46 47removing_handler(#{id:=simple}) -> 48 case whereis(?MODULE) of 49 undefined -> 50 ok; 51 Pid -> 52 Ref = erlang:monitor(process,Pid), 53 Pid ! stop, 54 receive {'DOWN',Ref,process,Pid,_} -> 55 ok 56 end 57 end. 58 59log(#{meta:=#{error_logger:=#{tag:=info_report,type:=Type}}},_Config) 60 when Type=/=std_info -> 61 %% Skip info reports that are not 'std_info' (ref simple logger in 62 %% error_logger) 63 ok; 64log(#{msg:=_,meta:=#{time:=_}}=Log,_Config) -> 65 _ = case whereis(?MODULE) of 66 undefined -> 67 %% Is the node on the way down? Real emergency? 68 %% Log directly from client just to get it out 69 do_log( 70 #{level=>error, 71 msg=>{report,{error,simple_handler_process_dead}}, 72 meta=>#{time=>logger:timestamp()}}), 73 do_log(Log); 74 _ -> 75 ?MODULE ! {log,Log} 76 end, 77 ok; 78log(_,_) -> 79 %% Unexpected log. 80 %% We don't want to crash the simple logger, so ignore this. 81 ok. 82 83%%%----------------------------------------------------------------- 84%%% Process 85init(Starter) -> 86 register(?MODULE,self()), 87 Starter ! {self(),started}, 88 loop(#{buffer_size=>10,dropped=>0,buffer=>[]}). 89 90loop(Buffer) -> 91 receive 92 stop -> 93 %% We replay the logger messages of there is 94 %% a default handler when the simple handler 95 %% is removed. 96 case logger:get_handler_config(default) of 97 {ok, _} -> 98 replay_buffer(Buffer); 99 _ -> 100 ok 101 end, 102 %% Before stopping, we unlink the logger process to avoid 103 %% an unexpected EXIT message 104 unlink(whereis(logger)), 105 ok; 106 {log,#{msg:=_,meta:=#{time:=_}}=Log} -> 107 do_log(Log), 108 loop(update_buffer(Buffer,Log)); 109 _ -> 110 %% Unexpected message - flush it! 111 loop(Buffer) 112 end. 113 114update_buffer(#{buffer_size:=0,dropped:=D}=Buffer,_Log) -> 115 Buffer#{dropped=>D+1}; 116update_buffer(#{buffer_size:=S,buffer:=B}=Buffer,Log) -> 117 Buffer#{buffer_size=>S-1,buffer=>[Log|B]}. 118 119replay_buffer(#{ dropped := D, buffer := Buffer }) -> 120 lists:foreach( 121 fun F(#{msg := {Tag, Msg}} = L) when Tag =:= string; Tag =:= report -> 122 F(L#{ msg := Msg }); 123 F(#{ level := Level, msg := Msg, meta := MD}) -> 124 logger:log(Level, Msg, MD) 125 end, lists:reverse(Buffer, drop_msg(D))). 126 127drop_msg(0) -> 128 []; 129drop_msg(N) -> 130 [#{level=>info, 131 msg=>{"Simple handler buffer full, dropped ~w messages",[N]}, 132 meta=>#{time=>logger:timestamp()}}]. 133 134%%%----------------------------------------------------------------- 135%%% Internal 136 137%% Can't do io_lib:format 138 139do_log(#{msg:={report,Report}, 140 meta:=#{time:=T,error_logger:=#{type:=Type}}}) -> 141 display_date(T), 142 display_report(Type,Report); 143do_log(#{msg:=Msg,meta:=#{time:=T}}) -> 144 display_date(T), 145 display(Msg). 146 147display_date(Timestamp) when is_integer(Timestamp) -> 148 Micro = Timestamp rem 1000000, 149 Sec = Timestamp div 1000000, 150 {{Y,Mo,D},{H,Mi,S}} = erlang:universaltime_to_localtime( 151 erlang:posixtime_to_universaltime(Sec)), 152 erlang:display_string( 153 integer_to_list(Y) ++ "-" ++ 154 pad(Mo,2) ++ "-" ++ 155 pad(D,2) ++ " " ++ 156 pad(H,2) ++ ":" ++ 157 pad(Mi,2) ++ ":" ++ 158 pad(S,2) ++ "." ++ 159 pad(Micro,6) ++ " "). 160 161pad(Int,Size) when is_integer(Int) -> 162 pad(integer_to_list(Int),Size); 163pad(Str,Size) when length(Str)==Size -> 164 Str; 165pad(Str,Size) -> 166 pad([$0|Str],Size). 167 168display({string,Chardata}) -> 169 try unicode:characters_to_list(Chardata) of 170 String -> erlang:display_string(String), erlang:display_string("\n") 171 catch _:_ -> erlang:display(Chardata) 172 end; 173display({report,Report}) when is_map(Report) -> 174 display_report(maps:to_list(Report)); 175display({report,Report}) -> 176 display_report(Report); 177display({F, A}) when is_list(F), is_list(A) -> 178 erlang:display_string(F ++ "\n"), 179 [begin 180 erlang:display_string("\t"), 181 erlang:display(Arg) 182 end || Arg <- A], 183 ok. 184 185display_report(Atom, A) when is_atom(Atom) -> 186 %% The widest atom seems to be 'supervisor_report' at 17. 187 ColumnWidth = 20, 188 AtomString = atom_to_list(Atom), 189 AtomLength = length(AtomString), 190 Padding = lists:duplicate(ColumnWidth - AtomLength, $\s), 191 erlang:display_string(AtomString ++ Padding), 192 display_report(A); 193display_report(F, A) -> 194 erlang:display({F, A}). 195 196display_report([A, []]) -> 197 %% Special case for crash reports when process has no links 198 display_report(A); 199display_report(A = [_|_]) -> 200 case lists:all(fun({Key,_Value}) -> is_atom(Key); (_) -> false end, A) of 201 true -> 202 erlang:display_string("\n"), 203 lists:foreach( 204 fun({Key, Value}) -> 205 erlang:display_string( 206 " " ++ 207 atom_to_list(Key) ++ 208 ": "), 209 erlang:display(Value) 210 end, A); 211 false -> 212 erlang:display(A) 213 end; 214display_report(A) -> 215 erlang:display(A). 216