Current section
Files
Jump to
Current section
Files
src/eradius_metrics.erl
-module(eradius_metrics).
-include("eradius_metrics.hrl").
-include("eradius_lib.hrl").
-export([create_server/1,
create_nas/1,
create_client/1,
delete_server/1,
delete_nas/1,
delete_client/1]).
-export([update_server_request/3, update_server_response/2, update_server_time/2,
update_nas_request/3, update_nas_response/2, update_nas_time/2,
update_client_request/3, update_client_response/2, update_client_time/2]).
-export([make_addr_info/1, timestamp/1, update_uptime/2, update_since_last_request/2]).
%% -------------------------------------------------------
%% API for metric creation.
%% -------------------------------------------------------
-spec create_server(atom_address()) -> ok.
create_server(Address) ->
create(server, Address).
-spec delete_server(atom_address()) -> ok.
delete_server(Address) ->
delete(server, Address).
-spec create_nas(atom_address_pair()) -> ok.
create_nas({{SName, SIP, SPort}, {NID, NIP, _NPort}}) ->
create(nas, {SName, SIP, SPort, NID, NIP}).
-spec delete_nas(atom_address_pair()) -> ok.
delete_nas({{SName, SIP, SPort}, {NID, NIP, _NPort}}) ->
delete(nas, {SName, SIP, SPort, NID, NIP}).
-spec create_client(atom_address_pair()) -> ok.
create_client({{CName, CIP, undefined}, {SName, SIP, SPort}}) ->
create(client, {CName, CIP, SName, SIP, SPort}).
-spec delete_client(atom_address_pair()) -> ok.
delete_client({{CName, CIP, undefined}, {SName, SIP, SPort}}) ->
delete(client, {CName, CIP, SName, SIP, SPort}).
%% -------------------------------------------------------
%% API for metric updates.
%% -------------------------------------------------------
-spec update_server_request(atom(), atom_address(), integer()) -> any().
update_server_request(pending, Address, Pending) ->
update_request(server, pending, Address, Pending);
update_server_request(Type, Address, Ms) ->
[update_request(server, ReqType, Address, Ms) || ReqType <- [Type, total]],
update_server_time(last_request, Address).
-spec update_server_response(atom(), atom_address()) -> any().
update_server_response(Type, Address) ->
[update_response(server, ReqType, Address) || ReqType <- [Type, total]].
-spec update_server_time(last_reset | last_config_reset | last_request, atom_address()) -> any().
update_server_time(Type, Address) ->
update_time(server, Type, Address).
-spec update_nas_request(atom(), atom_address_pair(), integer()) -> any().
update_nas_request(pending, {ServerAddress = {SName, SIP, SPort}, {NID, NIP, _NPort}}, Pending) ->
update_server_request(pending, ServerAddress, Pending),
update_request(nas, pending, {SName, SIP, SPort, NID, NIP}, Pending);
update_nas_request(Type, MetricsInfo = {ServerAddress = {SName, SIP, SPort}, {NID, NIP, _NPort}}, Ms) ->
update_server_request(Type, ServerAddress, Ms),
[update_request(nas, ReqType, {SName, SIP, SPort, NID, NIP}, Ms) || ReqType <- [Type, total]],
update_nas_time(last_request, MetricsInfo).
-spec update_nas_response(atom(), atom_address_pair()) -> any().
update_nas_response(Type, {ServerAddress = {SName, SIP, SPort}, {NID, NIP, _NPort}}) ->
update_server_response(Type, ServerAddress),
[update_response(nas, ReqType, {SName, SIP, SPort, NID, NIP}) || ReqType <- [Type, total]].
-spec update_nas_time(last_request, atom_address_pair()) -> any().
update_nas_time(last_request, {{SName, SIP, SPort}, {NID, NIP, _NPort}}) ->
update_time(nas, last_request, {SName, SIP, SPort, NID, NIP}).
-spec update_client_request(atom(), atom_address_pair(), integer()) -> any().
update_client_request(pending, {{CName, CIP, undefined}, {SName, SIP, SPort}}, Pending) ->
update_request(client, pending, {CName, CIP, SName, SIP, SPort}, Pending);
update_client_request(retransmission, {{CName, CIP, undefined}, {SName, SIP, SPort}}, Ms) ->
update_request(client, retransmission, {CName, CIP, SName, SIP, SPort}, Ms);
update_client_request(Type, MetricsInfo = {{CName, CIP, undefined}, {SName, SIP, SPort}}, Ms) ->
[update_request(client, ReqType, {CName, CIP, SName, SIP, SPort}, Ms) || ReqType <- [Type, total]],
update_client_time(last_request, MetricsInfo).
-spec update_client_response(atom(), atom_address_pair()) -> any().
update_client_response(Type, {{CName, CIP, undefined}, {SName, SIP, SPort}}) ->
[update_response(client, ReqType, {CName, CIP, SName, SIP, SPort}) || ReqType <- [Type, total]].
-spec update_client_time(last_request, atom_address_pair()) -> any().
update_client_time(last_request, {{CName, CIP, undefined}, {SName, SIP, SPort}}) ->
update_time(client, last_request, {CName, CIP, SName, SIP, SPort}).
-spec make_addr_info({term(), {inet:ip_address(), integer()}}) -> atom_address().
make_addr_info({undefined, {IP, Port}}) ->
{socket_to_atom(IP, Port), ip_to_atom(IP), port_to_atom(Port)};
make_addr_info({Name, {IP, Port}}) ->
{to_atom(Name), ip_to_atom(IP), port_to_atom(Port)}.
-spec timestamp(erlang:time_unit()) -> integer().
timestamp(Unit) ->
erlang:system_time(Unit).
%% -----------------------------------------------------------------
%% Internal
%% -----------------------------------------------------------------
create(Service, Args) ->
metrics_action(create, Service, Args).
delete(Service, Args) ->
metrics_action(delete, Service, Args).
metrics_action(Action, Service, Args) ->
Metrics = proplists:get_value(Service, ?METRICS, undefined),
{ok, MetricsOpts} = application:get_env(eradius, metrics),
EnabledServices = proplists:get_value(enabled, MetricsOpts, []),
case lists:member(Service, EnabledServices) of
true ->
proceed_metrics_action(Action, Service, Args, Metrics);
false ->
ok
end.
%% this function traverses the datastructures in eradius_metrics.hrl
proceed_metrics_action(Action, Service, Args, Metrics) ->
lists:foreach(
fun({MetricName, MetricType, Units}) ->
lists:foreach(
fun({UnitType, {ExoType, ExoTypeOpts}}) ->
PartId = case Service of
server -> server_layout(Args);
nas -> nas_layout(Args);
client -> client_layout(Args)
end,
%% this is the final exometer id:
FinalId = lists:append([?DEFAULT_ENTRIES, [MetricName, MetricType], PartId, [UnitType]]),
case Action of
create ->
case ExoType of
{function,_,_,_,_,_} ->
% functions are tricky, they need further arguments and
% will not be initialized at start
try % exometer crashes if two identical ids are created
ExoType1 = setelement(4, ExoType, [Service, Args]),
exometer:new(FinalId, ExoType1, ExoTypeOpts)
catch
_:_ -> ok
end;
_ ->
exometer:update_or_create(FinalId, 0, ExoType, ExoTypeOpts)
end;
delete ->
exometer:delete(FinalId)
end
end, Units)
end, Metrics).
update_request(server, Type, Args, Ms) ->
Args1 = server_layout(Args),
update_exo_request(Type, Args1, Ms);
update_request(nas, Type, Args, Ms) ->
Args1 = nas_layout(Args),
update_exo_request(Type, Args1, Ms);
update_request(client, Type, Args, Ms) ->
Args1 = client_layout(Args),
update_exo_request(Type, Args1, Ms).
update_response(server, Type, Args) ->
Args1 = server_layout(Args),
update_exo_request(Type, Args1);
update_response(nas, Type, Args) ->
Args1 = nas_layout(Args),
update_exo_request(Type, Args1);
update_response(client, Type, Args) ->
Args1 = client_layout(Args),
update_exo_request(Type, Args1).
update_time(server, Type, Args) ->
Sec = timestamp(milli_seconds),
Args1 = server_layout(Args),
update_exo_time(Type, Args1, Sec);
update_time(nas, Type, Args) ->
Sec = timestamp(milli_seconds),
Args1 = nas_layout(Args),
update_exo_time(Type, Args1, Sec);
update_time(client, Type, Args) ->
Sec = timestamp(milli_seconds),
Args1 = client_layout(Args),
update_exo_time(Type, Args1, Sec).
update_exo_request(pending, Args, Value) ->
PartId = lists:append([?DEFAULT_ENTRIES, [request, pending], Args]),
exometer:update(PartId ++ [gauge], Value);
update_exo_request(Type, Args, Ms) ->
PartId = lists:append([?DEFAULT_ENTRIES, [request, Type], Args]),
exometer:update(PartId ++ [counter], 1),
exometer:update(PartId ++ [gauge], Ms).
update_exo_request(Type, Args) ->
PartId = lists:append([?DEFAULT_ENTRIES, [response, Type], Args]),
exometer:update(PartId ++ [counter], 1).
update_exo_time(Type, Args, Sec) ->
PartId = lists:append([?DEFAULT_ENTRIES, [time, Type], Args]),
exometer:update(PartId ++ [ticks], Sec).
%% exometer id layouts for generic parts of the exometer id
server_layout({ServerName, ServerIP, ServerPort}) ->
[server, ServerName, ServerIP, ServerPort, total, undefined, undefined].
nas_layout({ServerName, ServerIP, ServerPort, NasId, NasIP}) ->
[server, ServerName, ServerIP, ServerPort, NasId, NasIP, undefined].
client_layout({ClientName, ClientIP, ServerName, ServerIP, ServerPort}) ->
[client, ClientName, ClientIP, undefined, ServerName, ServerIP, ServerPort].
to_atom(Value) when is_atom(Value) -> Value;
to_atom(Value) when is_binary(Value) -> binary_to_atom(Value, latin1);
to_atom(Value) when is_list(Value) -> list_to_atom(Value).
socket_to_atom(IP, undefined) ->
ip_to_atom(IP);
socket_to_atom(IP, Port) when is_tuple(IP) ->
list_to_atom(inet:ntoa(IP) ++ ":" ++ integer_to_list(Port));
socket_to_atom(IP, Port) when is_binary(IP) ->
binary_to_atom(erlang:iolist_to_binary([IP, <<":">>, Port]), latin1);
socket_to_atom(IP, Port) when is_atom(IP) ->
binary_to_atom(erlang:iolist_to_binary([atom_to_binary(IP, latin1), <<":">>, Port]), latin1).
ip_to_atom(IP) when is_atom(IP) -> IP;
ip_to_atom(IP) -> list_to_atom(inet:ntoa(IP)).
port_to_atom(undefined) -> undefined;
port_to_atom(Port) when is_atom(Port) -> Port;
port_to_atom(Port) -> list_to_atom(integer_to_list(Port)).
update_uptime(server, ServerAddress) ->
Args = server_layout(ServerAddress),
LastResetId = lists:append([?DEFAULT_ENTRIES, [time, last_reset], Args, [ticks]]),
{ok, [{ms_since_reset, Uptime}]} = exometer:get_value(LastResetId, ms_since_reset),
[{value, round(Uptime)}].
update_since_last_request(Service, Address) ->
Args = case Service of
server -> server_layout(Address);
nas -> nas_layout(Address);
client -> client_layout(Address)
end,
LastRequestId = lists:append([?DEFAULT_ENTRIES, [time, last_request], Args, [ticks]]),
{ok, [{value, LastRequestTs}]} = exometer:get_value(LastRequestId, value),
[{value, round(timestamp(milli_seconds) - LastRequestTs)}].