Merge pull request #12172 from zhongwencool/audit-max-size-microsecond

fix: use microsecond precision as the primary key for audit logs
This commit is contained in:
zhongwencool 2023-12-15 10:52:21 +08:00 committed by GitHub
commit a49750049f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 66 additions and 12 deletions

View File

@ -516,7 +516,7 @@ do_t_session_expiration(_Config, Opts) ->
t_session_gc(Config) ->
GCInterval = ?config(gc_interval, Config),
[Node1, Node2, Node3] = Nodes = ?config(nodes, Config),
[Node1, Node2, _Node3] = Nodes = ?config(nodes, Config),
CoreNodes = [Node1, Node2],
[
Port1,

View File

@ -119,7 +119,7 @@ log_to_db(Log) ->
Audit0 = to_audit(Log),
Audit = Audit0#?AUDIT{
node = node(),
created_at = erlang:system_time(millisecond)
created_at = erlang:system_time(microsecond)
},
mria:dirty_write(?AUDIT, Audit).

View File

@ -32,7 +32,7 @@
{<<"http_method">>, atom},
{<<"gte_created_at">>, timestamp},
{<<"lte_created_at">>, timestamp},
{<<"gte_duration_ms">>, timestamp},
{<<"gte_duration_ms">>, integer},
{<<"lte_duration_ms">>, integer}
]).
-define(DISABLE_MSG, <<"Audit is disabled">>).
@ -130,14 +130,14 @@ schema("/audit") ->
desc => ?DESC(filter_lte_duration_ms)
})},
{gte_created_at,
?HOCON(emqx_utils_calendar:epoch_millisecond(), #{
?HOCON(emqx_utils_calendar:epoch_microsecond(), #{
in => query,
required => false,
example => <<"2023-10-15T00:00:00.820384+08:00">>,
desc => ?DESC(filter_gte_created_at)
})},
{lte_created_at,
?HOCON(emqx_utils_calendar:epoch_millisecond(), #{
?HOCON(emqx_utils_calendar:epoch_microsecond(), #{
in => query,
example => <<"2023-10-16T00:00:00.820384+08:00">>,
required => false,
@ -170,7 +170,7 @@ fields(audit) ->
[
{created_at,
?HOCON(
emqx_utils_calendar:epoch_millisecond(),
emqx_utils_calendar:epoch_microsecond(),
#{
desc => "The time when the log is created"
}

View File

@ -140,9 +140,9 @@ t_disabled(_) ->
t_cli(_Config) ->
Size = mnesia:table_info(emqx_audit, size),
TimeInt = erlang:system_time(millisecond) - 10,
TimeInt = erlang:system_time(microsecond) - 1000,
Time = integer_to_list(TimeInt),
DateStr = calendar:system_time_to_rfc3339(TimeInt, [{unit, millisecond}]),
DateStr = calendar:system_time_to_rfc3339(TimeInt, [{unit, microsecond}]),
Date = emqx_http_lib:uri_encode(DateStr),
ok = emqx_ctl:run_command(["conf", "show", "log"]),
AuditPath = emqx_mgmt_api_test_util:api_path(["audit"]),
@ -164,7 +164,11 @@ t_cli(_Config) ->
],
Data
),
%% check create at is valid
[#{<<"created_at">> := CreateAtRaw}] = Data,
CreateAt = calendar:rfc3339_to_system_time(binary_to_list(CreateAtRaw), [{unit, microsecond}]),
?assert(CreateAt > TimeInt, CreateAtRaw),
?assert(CreateAt < TimeInt + 5000000, CreateAtRaw),
%% check cli filter
{ok, Res1} = emqx_mgmt_api_test_util:request_api(get, AuditPath, "from=cli", AuthHeader),
#{<<"data">> := Data1} = emqx_utils_json:decode(Res1, [return_maps]),
@ -174,25 +178,41 @@ t_cli(_Config) ->
),
?assertMatch(#{<<"data">> := []}, emqx_utils_json:decode(Res2, [return_maps])),
%% check created_at filter
%% check created_at filter microsecond
{ok, Res3} = emqx_mgmt_api_test_util:request_api(
get, AuditPath, "gte_created_at=" ++ Time, AuthHeader
),
#{<<"data">> := Data3} = emqx_utils_json:decode(Res3, [return_maps]),
?assertEqual(1, erlang:length(Data3)),
%% check created_at filter rfc3339
{ok, Res31} = emqx_mgmt_api_test_util:request_api(
get, AuditPath, "gte_created_at=" ++ Date, AuthHeader
),
?assertEqual(Res3, Res31),
%% check created_at filter millisecond
TimeMs = integer_to_list(TimeInt div 1000),
{ok, Res32} = emqx_mgmt_api_test_util:request_api(
get, AuditPath, "gte_created_at=" ++ TimeMs, AuthHeader
),
?assertEqual(Res3, Res32),
%% check created_at filter microsecond
{ok, Res4} = emqx_mgmt_api_test_util:request_api(
get, AuditPath, "lte_created_at=" ++ Time, AuthHeader
),
#{<<"data">> := Data4} = emqx_utils_json:decode(Res4, [return_maps]),
?assertEqual(Size, erlang:length(Data4)),
%% check created_at filter rfc3339
{ok, Res41} = emqx_mgmt_api_test_util:request_api(
get, AuditPath, "lte_created_at=" ++ Date, AuthHeader
),
?assertEqual(Res4, Res41),
%% check created_at filter millisecond
{ok, Res42} = emqx_mgmt_api_test_util:request_api(
get, AuditPath, "lte_created_at=" ++ TimeMs, AuthHeader
),
?assertEqual(Res4, Res42),
%% check duration_ms filter
{ok, Res5} = emqx_mgmt_api_test_util:request_api(
@ -224,7 +244,7 @@ t_max_size(_Config) ->
fun(_) ->
ok = emqx_ctl:run_command(["conf", "show", "log"])
end,
lists:duplicate(110, 1)
lists:duplicate(100, 1)
),
_ = mnesia:dump_log(),
LogCount = wait_for_dirty_write_log_done(1500),

View File

@ -143,6 +143,24 @@ readable("epoch_millisecond()") ->
]
}
};
readable("epoch_microsecond()") ->
%% only for swagger
#{
swagger => #{
<<"oneOf">> => [
#{
type => integer,
example => 1640995200000000,
description => <<"epoch-microsecond">>
},
#{
type => string,
example => <<"2022-01-01T00:00:00.000000Z">>,
format => <<"date-time">>
}
]
}
};
readable("duration()") ->
#{
swagger => #{type => string, example => <<"12m">>},

View File

@ -29,6 +29,7 @@
%% API
-export([
to_epoch_millisecond/1,
to_epoch_microsecond/1,
to_epoch_second/1,
human_readable_duration_string/1
]).
@ -54,6 +55,7 @@
%% so the maximum date can reach 9999-12-31 which is ample.
-define(MAXIMUM_EPOCH, 253402214400).
-define(MAXIMUM_EPOCH_MILLI, 253402214400_000).
-define(MAXIMUM_EPOCH_MICROS, 253402214400_000_000).
-define(DATE_PART, [
year,
@ -75,13 +77,16 @@
-reflect_type([
epoch_millisecond/0,
epoch_second/0
epoch_second/0,
epoch_microsecond/0
]).
-type epoch_second() :: non_neg_integer().
-type epoch_millisecond() :: non_neg_integer().
-type epoch_microsecond() :: non_neg_integer().
-typerefl_from_string({epoch_second/0, ?MODULE, to_epoch_second}).
-typerefl_from_string({epoch_millisecond/0, ?MODULE, to_epoch_millisecond}).
-typerefl_from_string({epoch_microsecond/0, ?MODULE, to_epoch_microsecond}).
%%--------------------------------------------------------------------
%% Epoch <-> RFC 3339
@ -93,6 +98,9 @@ to_epoch_second(DateTime) ->
to_epoch_millisecond(DateTime) ->
to_epoch(DateTime, millisecond).
to_epoch_microsecond(DateTime) ->
to_epoch(DateTime, microsecond).
to_epoch(DateTime, Unit) ->
try
case string:to_integer(DateTime) of
@ -131,6 +139,14 @@ validate_epoch(Epoch, second) when Epoch =< ?MAXIMUM_EPOCH ->
{ok, Epoch};
validate_epoch(Epoch, millisecond) when Epoch =< ?MAXIMUM_EPOCH_MILLI ->
{ok, Epoch};
%% http api use millisecond but we should transform to microsecond
validate_epoch(Epoch, microsecond) when
Epoch >= ?MAXIMUM_EPOCH andalso
Epoch =< ?MAXIMUM_EPOCH_MILLI
->
{ok, Epoch * 1000};
validate_epoch(Epoch, microsecond) when Epoch =< ?MAXIMUM_EPOCH_MICROS ->
{ok, Epoch};
validate_epoch(_Epoch, _Unit) ->
{error, bad_epoch}.