From 582b819e9faee7b1b5a0eb8417bbe79712d3ae3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E7=A4=BC=E6=88=90?= Date: Thu, 20 Apr 2023 20:00:47 +0800 Subject: [PATCH] add log support --- apps/iot/include/iot.hrl | 6 +- .../iot/src/http_handler/http_log_handler.erl | 41 +++++++++++++ apps/iot/src/http_handler/http_protocol.erl | 2 +- apps/iot/src/iot_app.erl | 5 +- apps/iot/src/iot_mock.erl | 14 ++++- apps/iot/src/model/log_model.erl | 57 ++++++++++++++++++- 6 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 apps/iot/src/http_handler/http_log_handler.erl diff --git a/apps/iot/include/iot.hrl b/apps/iot/include/iot.hrl index ce9a57c..da85104 100644 --- a/apps/iot/include/iot.hrl +++ b/apps/iot/include/iot.hrl @@ -246,13 +246,17 @@ status = 0 }). +%% 设备分类 +-define(DEVICE_HOST, 1). +-define(DEVICE_TERMINAL, 2). + %% 操作日志表 -record(log, { log_id :: integer(), %% 操作名称名称 action_name = <<>>, %% 设备分类名称 - assoc_name = <<>>, + device_type :: integer(), %% 关联ID assoc_id :: term(), %% 创建时间 diff --git a/apps/iot/src/http_handler/http_log_handler.erl b/apps/iot/src/http_handler/http_log_handler.erl new file mode 100644 index 0000000..8c501a8 --- /dev/null +++ b/apps/iot/src/http_handler/http_log_handler.erl @@ -0,0 +1,41 @@ +%%%------------------------------------------------------------------- +%%% @author licheng5 +%%% @copyright (C) 2020, +%%% @doc +%%% +%%% @end +%%% Created : 26. 4月 2020 3:36 下午 +%%%------------------------------------------------------------------- +-module(http_log_handler). +-author("licheng5"). +-include("iot.hrl"). + +%% API +-export([handle_request/4]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% helper methods +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +handle_request(_, "/log/list", GetParams, _PostParams) -> + Size0 = maps:get(<<"size">>, GetParams, <<"10">>), + Size = binary_to_integer(Size0), + true = Size > 0, + + case log_model:get_last_logs(Size) of + {ok, Logs} -> + LogInfos = lists:map(fun log_model:to_map/1, Logs), + + {ok, 200, iot_util:json_data(LogInfos)}; + {error, Reason} -> + lager:warning("[host_handler] get a error: ~p", [Reason]), + {ok, 200, iot_util:json_error(-1, <<"database error">>)} + end; + +handle_request(_, Path, _, _) -> + Path1 = list_to_binary(Path), + {ok, 200, iot_util:json_error(-1, <<"url: ", Path1/binary, " not found">>)}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% helper methods +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file diff --git a/apps/iot/src/http_handler/http_protocol.erl b/apps/iot/src/http_handler/http_protocol.erl index 29e1740..4b1d927 100644 --- a/apps/iot/src/http_handler/http_protocol.erl +++ b/apps/iot/src/http_handler/http_protocol.erl @@ -64,7 +64,7 @@ supported_gzip(AcceptEncoding) when is_binary(AcceptEncoding) -> parse_body(Req0) -> ContentType = cowboy_req:header(<<"content-type">>, Req0), case ContentType of - <<"application/json">> -> + <<"application/json", _/binary>> -> {ok, Body, Req1} = read_body(Req0), {ok, catch jiffy:decode(Body, [return_maps]), Req1}; <<"application/x-www-form-urlencoded">> -> diff --git a/apps/iot/src/iot_app.erl b/apps/iot/src/iot_app.erl index 1a7afab..ee1f23c 100644 --- a/apps/iot/src/iot_app.erl +++ b/apps/iot/src/iot_app.erl @@ -42,7 +42,10 @@ start_http_server() -> Dispatcher = cowboy_router:compile([ {'_', [ {"/host/[...]", http_protocol, [http_host_handler]}, - {"/api/[...]", http_protocol, [http_api_handler]}, + {"/terminal/[...]", http_protocol, [http_terminal_handler]}, + {"/scenario/[...]", http_protocol, [http_scenario_handler]}, + {"/log/[...]", http_protocol, [http_log_handler]}, + {"/iot/[...]", http_protocol, [http_iot_handler]}, {"/router/[...]", http_protocol, [http_router_handler]} ]} ]), diff --git a/apps/iot/src/iot_mock.erl b/apps/iot/src/iot_mock.erl index 2357106..6555bd9 100644 --- a/apps/iot/src/iot_mock.erl +++ b/apps/iot/src/iot_mock.erl @@ -11,7 +11,7 @@ -include("iot.hrl"). %% API --export([insert_hosts/0, insert_services/1, insert_terminals/1, insert_routers/0]). +-export([insert_hosts/0, insert_services/1, insert_terminals/1, insert_routers/0, insert_logs/0]). -export([start_router/1]). -export([rsa_encode/1]). -export([start_issue/0]). @@ -43,6 +43,18 @@ insert_hosts() -> host_model:add_host(Host) end, lists:seq(1, 1)). +insert_logs() -> + lists:foreach(fun(Id0) -> + Log = #log{ + log_id = Id0, + device_type = ?DEVICE_HOST, + action_name = <<"主机上线"/utf8>>, + assoc_id = <<"1">>, + create_ts = Id0 + 123456 + }, + log_model:add_log(Log) + end, lists:seq(1, 500000)). + insert_services(HostId) -> lists:foreach(fun(Id0) -> Q0 = queue:new(), diff --git a/apps/iot/src/model/log_model.erl b/apps/iot/src/model/log_model.erl index 758b1b6..92a74bb 100644 --- a/apps/iot/src/model/log_model.erl +++ b/apps/iot/src/model/log_model.erl @@ -14,10 +14,10 @@ -define(TAB_NAME, log). %% API --export([get_logs/1, add_log/1, delete/1, table_size/0]). +-export([get_logs/1, add_log/1, delete/1, table_size/0, get_last_logs/1]). -export([to_map/1]). -%% 获取app信息 +%% 获取app信息, 该函数的效率较低 -spec get_logs(Limit :: integer()) -> {ok, Logs :: list()} | {error, Reason :: any()}. get_logs(Limit) when is_integer(Limit), Limit > 0 -> Fun = fun() -> @@ -36,6 +36,20 @@ get_logs(Limit) when is_integer(Limit), Limit > 0 -> {error, Error} end. +%% 获取最新的n条记录 +-spec get_last_logs(N :: integer()) -> {ok, Logs :: [#log{}]} | {error, Reason :: any()}. +get_last_logs(N) when N > 0 -> + Fun = fun() -> + Keys = read_last_keys(N), + lists:flatmap(fun(Key) -> mnesia:read(?TAB_NAME, Key, read) end, lists:reverse(Keys)) + end, + case mnesia:transaction(Fun) of + {atomic, Logs} when is_list(Logs) -> + {ok, Logs}; + {aborted, Error} -> + {error, Error} + end. + -spec add_log(Log :: #log{}) -> ok | {error, Reason :: binary()}. add_log(Log = #log{}) -> case mnesia:transaction(fun() -> mnesia:write(?TAB_NAME, Log, write) end) of @@ -54,6 +68,24 @@ delete(LogId) when is_binary(LogId) -> {error, Reason} end. +%% 从后向前变量表 +read_last_keys(N) when N >= 1 -> + case mnesia:last(?TAB_NAME) of + '$end_of_table' -> + []; + LastKey -> + read_last_keys0(N - 1, LastKey, [LastKey]) + end. +read_last_keys0(0, _, Keys) -> + Keys; +read_last_keys0(N, Key, Keys) -> + case mnesia:prev(?TAB_NAME, Key) of + '$end_of_table' -> + Keys; + PrevKey -> + read_last_keys0(N - 1, PrevKey, [PrevKey|Keys]) + end. + %% 获取app表的数据大小 table_size() -> mnesia:table_info(?TAB_NAME, size). @@ -67,9 +99,28 @@ sort(Logs) when is_list(Logs) -> lists:sort(fun(#log{create_ts = Ts0}, #log{create_ts = Ts1}) -> Ts0 > Ts1 end, Logs). %% 将数据转换成hash -to_map(#log{log_id = LogId, action_name = ActionName, assoc_name = AssocName, assoc_id = AssocId, create_ts = CreateTs}) -> +to_map(#log{log_id = LogId, action_name = ActionName, device_type = DeviceType, assoc_id = AssocId, create_ts = CreateTs}) -> + DeviceInfo = case DeviceType of + ?DEVICE_HOST -> + case host_model:get_host(AssocId) of + {ok, Host} -> + host_model:to_map(Host); + _ -> + #{} + end; + ?DEVICE_TERMINAL -> + case terminal_model:get_terminal(AssocId) of + {ok, Terminal} -> + terminal_model:to_map(Terminal); + _ -> + #{} + end + end, + #{ <<"log_id">> => LogId, <<"action_name">> => ActionName, + <<"device_type">> => DeviceType, + <<"device_info">> => DeviceInfo, <<"create_ts">> => CreateTs }. \ No newline at end of file