diff --git a/config/sys-dev.config b/config/sys-dev.config index 64f3f9f..cdc0657 100644 --- a/config/sys-dev.config +++ b/config/sys-dev.config @@ -34,6 +34,12 @@ ]} ]}, + {endpoint_log, [ + {path, "${endpoint_root}/endpoint_log/unmatched_publish.log"}, + {max_bytes, 10485760}, + {max_files, 10} + ]}, + %% 目标服务器地址 {emqx_server, [ {host, {39, 98, 184, 67}}, diff --git a/config/sys-prod.config b/config/sys-prod.config index 63d9332..11cd3ce 100644 --- a/config/sys-prod.config +++ b/config/sys-prod.config @@ -28,6 +28,12 @@ ]} ]}, + {endpoint_log, [ + {path, "${endpoint_root}/endpoint_log/unmatched_publish.log"}, + {max_bytes, 10485760}, + {max_files, 10} + ]}, + {udp_server, [ {port, 18080} ]}, diff --git a/src/endpoint/endpoint_log.erl b/src/endpoint/endpoint_log.erl index 7151701..e8952d9 100644 --- a/src/endpoint/endpoint_log.erl +++ b/src/endpoint/endpoint_log.erl @@ -16,9 +16,8 @@ -define(SERVER, ?MODULE). -define(LOG_NAME, endpoint_unmatched_publish_log). --define(LOG_DIR, "endpoint_log"). --define(LOG_FILE, "unmatched_publish.log"). --define(DEFAULT_ROOT_DIR, "log"). +-define(DEFAULT_PATH_TEMPLATE, "${endpoint_root}/endpoint_log/unmatched_publish.log"). +-define(DEFAULT_ENDPOINT_ROOT_DIR, "log"). -define(DEFAULT_MAX_BYTES, 10485760). -define(DEFAULT_MAX_FILES, 10). -define(MAX_LOG_CONTENT_BYTES, 32 * 1024). @@ -104,14 +103,16 @@ code_change(_OldVsn, State = #state{}, _Extra) -> -spec open_log() -> ok | {error, term()}. open_log() -> - Path = log_path(), + Config = log_config(), + Path = log_path(Config), ok = filelib:ensure_dir(Path), case disk_log:open([ {name, ?LOG_NAME}, {file, Path}, {type, wrap}, {format, external}, - {size, {?DEFAULT_MAX_BYTES, ?DEFAULT_MAX_FILES}}, + {size, {config_pos_integer(max_bytes, Config, ?DEFAULT_MAX_BYTES), + config_pos_integer(max_files, Config, ?DEFAULT_MAX_FILES)}}, {linkto, self()}, {repair, true} ]) of @@ -123,19 +124,65 @@ open_log() -> {error, Reason} end. --spec log_path() -> file:filename_all(). -log_path() -> - filename:join([root_dir(), ?LOG_DIR, ?LOG_FILE]). +-spec log_config() -> proplists:proplist(). +log_config() -> + case application:get_env(iot, endpoint_log) of + {ok, Config} when is_list(Config) -> + Config; + _ -> + [] + end. --spec root_dir() -> file:filename_all(). -root_dir() -> +-spec log_path(proplists:proplist()) -> file:filename_all(). +log_path(Config) -> + Template = proplists:get_value(path, Config, ?DEFAULT_PATH_TEMPLATE), + expand_path_template(Template). + +-spec endpoint_root_dir() -> file:filename_all(). +endpoint_root_dir() -> case application:get_env(iot, endpoints) of {ok, Endpoints} -> - proplists:get_value(root_dir, Endpoints, ?DEFAULT_ROOT_DIR); + proplists:get_value(root_dir, Endpoints, ?DEFAULT_ENDPOINT_ROOT_DIR); undefined -> - ?DEFAULT_ROOT_DIR + ?DEFAULT_ENDPOINT_ROOT_DIR end. +-spec config_pos_integer(atom(), proplists:proplist(), pos_integer()) -> pos_integer(). +config_pos_integer(Key, Config, Default) -> + case proplists:get_value(Key, Config, Default) of + Value when is_integer(Value), Value > 0 -> + Value; + _ -> + Default + end. + +-spec expand_path_template(file:filename_all()) -> file:filename_all(). +expand_path_template(Template) -> + TemplateBin = iolist_to_binary(Template), + Vars = path_vars(), + Expanded = lists:foldl(fun({Name, Value}, Acc) -> + replace_path_var(Name, Value, Acc) + end, TemplateBin, Vars), + binary_to_list(Expanded). + +-spec path_vars() -> [{binary(), binary()}]. +path_vars() -> + {{Year, Month, Day}, {Hour, _Minute, _Second}} = calendar:local_time(), + [ + {<<"endpoint_root">>, iolist_to_binary(endpoint_root_dir())}, + {<<"date">>, format_date(Year, Month, Day)}, + {<<"year">>, integer_to_binary(Year)}, + {<<"month">>, two_digits(Month)}, + {<<"day">>, two_digits(Day)}, + {<<"hour">>, two_digits(Hour)}, + {<<"node">>, atom_to_binary(node(), utf8)} + ]. + +-spec replace_path_var(binary(), binary(), binary()) -> binary(). +replace_path_var(Name, Value, Template) -> + Template0 = binary:replace(Template, <<"${", Name/binary, "}">>, Value, [global]), + binary:replace(Template0, <<"{", Name/binary, "}">>, Value, [global]). + -spec encode_unmatched_publish(binary(), binary()) -> binary(). encode_unmatched_publish(RouteKey, Content) -> {LoggedContent, Truncated} = maybe_truncate(Content), @@ -162,6 +209,14 @@ format_local_time() -> [Year, Month, Day, Hour, Minute, Second] )). +-spec format_date(integer(), integer(), integer()) -> binary(). +format_date(Year, Month, Day) -> + iolist_to_binary(io_lib:format("~4..0B-~2..0B-~2..0B", [Year, Month, Day])). + +-spec two_digits(integer()) -> binary(). +two_digits(Value) -> + iolist_to_binary(io_lib:format("~2..0B", [Value])). + -spec maybe_truncate(binary()) -> {binary(), boolean()}. maybe_truncate(Content) when byte_size(Content) =< ?MAX_LOG_CONTENT_BYTES -> {Content, false};