From 7742eb3046a417e88cf09e2b8fd8a91102bd6a7f Mon Sep 17 00:00:00 2001 From: anlicheng Date: Thu, 7 Sep 2023 19:29:37 +0800 Subject: [PATCH] add logger --- apps/iot/src/iot_logger.erl | 146 ++++++++++++++++++++++++++ apps/iot/src/postman/mqtt_postman.erl | 5 +- 2 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 apps/iot/src/iot_logger.erl diff --git a/apps/iot/src/iot_logger.erl b/apps/iot/src/iot_logger.erl new file mode 100644 index 0000000..feed296 --- /dev/null +++ b/apps/iot/src/iot_logger.erl @@ -0,0 +1,146 @@ +%%%------------------------------------------------------------------- +%%% @author aresei +%%% @copyright (C) 2023, +%%% @doc +%%% +%%% @end +%%% Created : 07. 9月 2023 17:07 +%%%------------------------------------------------------------------- +-module(iot_logger). +-author("aresei"). + +-behaviour(gen_server). + +%% API +-export([start_link/0, write/1]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). + +-define(SERVER, ?MODULE). +-define(LOG_FILE, "north_data"). + +-record(state, { + root_dir :: string(), + file_path :: string(), + file +}). + +%%%=================================================================== +%%% API +%%%=================================================================== + +write(Data) -> + gen_server:cast(?MODULE, {write, Data}). + +%% @doc Spawns the server and registers the local name (unique) +-spec(start_link() -> + {ok, Pid :: pid()} | ignore | {error, Reason :: term()}). +start_link() -> + gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +%% @private +%% @doc Initializes the server +-spec(init(Args :: term()) -> + {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} | + {stop, Reason :: term()} | ignore). +init([]) -> + RealFile = make_file(), + RootDir = code:root_dir() ++ "/log/", + FilePath = RootDir ++ RealFile, + lager:debug("root_dir: ~p", [FilePath]), + case filelib:is_dir(RootDir) of + false -> + file:make_dir(RootDir); + true -> + ok + end, + {ok, File} = file:open(FilePath, [append, binary]), + + {ok, #state{file = File, root_dir = RootDir, file_path = FilePath}}. + +%% @private +%% @doc Handling call messages +-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()}, + State :: #state{}) -> + {reply, Reply :: term(), NewState :: #state{}} | + {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} | + {noreply, NewState :: #state{}} | + {noreply, NewState :: #state{}, timeout() | hibernate} | + {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} | + {stop, Reason :: term(), NewState :: #state{}}). +handle_call(_Request, _From, State = #state{}) -> + {reply, ok, State}. + +%% @private +%% @doc Handling cast messages +-spec(handle_cast(Request :: term(), State :: #state{}) -> + {noreply, NewState :: #state{}} | + {noreply, NewState :: #state{}, timeout() | hibernate} | + {stop, Reason :: term(), NewState :: #state{}}). +handle_cast({write, Data}, State = #state{root_dir = RootDir, file_path = OldFilePath, file = OldFile}) -> + Line = format(Data), + FilePath = RootDir ++ make_file(), + case FilePath =:= OldFilePath of + true -> + ok = file:write(OldFile, Line), + {noreply, State}; + false -> + file:close(OldFile), + {ok, File} = file:open(FilePath, [append, binary]), + {noreply, State#state{file = File}} + end. + +%% @private +%% @doc Handling all non call/cast messages +-spec(handle_info(Info :: timeout() | term(), State :: #state{}) -> + {noreply, NewState :: #state{}} | + {noreply, NewState :: #state{}, timeout() | hibernate} | + {stop, Reason :: term(), NewState :: #state{}}). +handle_info(_Info, State = #state{}) -> + {noreply, State}. + +%% @private +%% @doc This function is called by a gen_server when it is about to +%% terminate. It should be the opposite of Module:init/1 and do any +%% necessary cleaning up. When it returns, the gen_server terminates +%% with Reason. The return value is ignored. +-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()), + State :: #state{}) -> term()). +terminate(_Reason, _State = #state{}) -> + ok. + +%% @private +%% @doc Convert process state when code is changed +-spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{}, + Extra :: term()) -> + {ok, NewState :: #state{}} | {error, Reason :: term()}). +code_change(_OldVsn, State = #state{}, _Extra) -> + {ok, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== + +format(Data) when is_binary(Data) -> + TimePrefix = time_prefix(), + NData = iolist_to_binary(Data), + <>; +format(Items) when is_list(Items) -> + TimePrefix = time_prefix(), + Line = iolist_to_binary(lists:join(<<"\t">>, Items)), + <>. + +time_prefix() -> + {{Y, M, D}, {H, I, S}} = calendar:local_time(), + iolist_to_binary(io_lib:format("[~b-~2..0b-~2..0b ~2..0b:~2..0b:~2..0b]", [Y, M, D, H, I, S])). + +-spec make_file() -> string(). +make_file() -> + {Year, Month, Day} = erlang:date(), + Prefix = io_lib:format("~b~2..0b~2..0b-", [Year, Month, Day]), + lists:flatten(Prefix ++ ?LOG_FILE). \ No newline at end of file diff --git a/apps/iot/src/postman/mqtt_postman.erl b/apps/iot/src/postman/mqtt_postman.erl index e9d68fd..3d69002 100644 --- a/apps/iot/src/postman/mqtt_postman.erl +++ b/apps/iot/src/postman/mqtt_postman.erl @@ -89,7 +89,8 @@ handle_info({publish, Message = #{packet_id := _PacketId, payload := Payload}}, {noreply, State}; handle_info({puback, #{packet_id := PacketId}}, State = #state{inflight = Inflight}) -> case maps:take(PacketId, Inflight) of - {{Id, ReceiverPid}, RestInflight} -> + {{Id, ReceiverPid, AssocMessage}, RestInflight} -> + iot_logger:write(AssocMessage), ReceiverPid ! {ack, Id}, {noreply, State#state{inflight = RestInflight}}; error -> @@ -105,7 +106,7 @@ handle_info({post, ReceiverPid, #post_data{id = Id, location_code = LocationCode ReceiverPid ! {ack, Id}, {noreply, State}; {ok, PacketId} -> - {noreply, State#state{inflight = maps:put(PacketId, {Id, ReceiverPid}, InFlight)}}; + {noreply, State#state{inflight = maps:put(PacketId, {Id, ReceiverPid, Message}, InFlight)}}; {error, Reason} -> lager:warning("[mqtt_postman] send message to topic: ~p, get error: ~p", [Topic, Reason]), {stop, Reason, State}