%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
%%----------------------------------------------------------------------
%% Purpose : Main API for Megaco/H.248 protocol stack
%%----------------------------------------------------------------------
-module(megaco).
%%-----------------------------------------------------------------
%% Public interface
%%-----------------------------------------------------------------
-export([
start/0,
stop/0,
start_user/2,
stop_user/1,
info/0,
user_info/1, user_info/2,
update_user_info/3,
conn_info/1, conn_info/2,
update_conn_info/3,
system_info/0, system_info/1,
connect/4, connect/5,
disconnect/2,
call/3,
cast/3,
cancel/2,
process_received_message/4, process_received_message/5,
receive_message/4, receive_message/5,
encode_actions/3,
token_tag2string/1, token_tag2string/2, token_tag2string/3,
parse_digit_map/1,
eval_digit_map/1,
eval_digit_map/2,
report_digit_event/2,
test_digit_event/2,
encode_binary_term_id/2,
decode_binary_term_id/2,
encode_sdp/1,
decode_sdp/1,
versions1/0, versions2/0,
print_version_info/0, print_version_info/1,
ms/0, nc/0, nc/1, ni/0, ni/1,
enable_trace/2, disable_trace/0, set_trace/1,
report_event/4, report_event/5,
test_request/5,
test_reply/5
]).
-export([
get_stats/0, get_stats/1, get_stats/2,
reset_stats/0, reset_stats/1
]).
%% Deprecated
-export([format_versions/1]).
%% Internal
-export([format_timestamp/1]).
%% This is for XREF
-deprecated([{format_versions, 1, eventually}]).
-include("megaco_internal.hrl").
%%-----------------------------------------------------------------
%% Starts the Megaco application
%%-----------------------------------------------------------------
start() ->
application:start(?APPLICATION).
%%-----------------------------------------------------------------
%% Stops the Megaco application
%%-----------------------------------------------------------------
stop() ->
application:stop(?APPLICATION).
%%-----------------------------------------------------------------
%% Initial configuration of a user
%%-----------------------------------------------------------------
start_user(UserMid, Config) ->
megaco_config:start_user(UserMid, Config).
%%-----------------------------------------------------------------
%% Delete the configuration of a user
%%-----------------------------------------------------------------
stop_user(UserMid) ->
megaco_config:stop_user(UserMid).
%%-----------------------------------------------------------------
%% Lookup user information
%%-----------------------------------------------------------------
user_info(UserMid) ->
[{requests, user_info(UserMid, requests)},
{replies, user_info(UserMid, replies)} | user_info(UserMid, all)].
user_info(UserMid, requests) ->
megaco_messenger:which_requests(UserMid);
user_info(UserMid, replies) ->
megaco_messenger:which_replies(UserMid);
user_info(UserMid, Item) ->
megaco_config:user_info(UserMid, Item).
%%-----------------------------------------------------------------
%% Update information about a user
%%-----------------------------------------------------------------
update_user_info(UserMid, Item, Value) ->
megaco_config:update_user_info(UserMid, Item, Value).
%%-----------------------------------------------------------------
%% Lookup information about an active connection
%%-----------------------------------------------------------------
conn_info(ConnHandle) ->
[{requests, conn_info(ConnHandle, requests)},
{replies, conn_info(ConnHandle, replies)} | conn_info(ConnHandle, all)].
conn_info(ConnHandle, requests) ->
megaco_messenger:which_requests(ConnHandle);
conn_info(ConnHandle, replies) ->
megaco_messenger:which_replies(ConnHandle);
conn_info(ConnHandle, Item) ->
megaco_config:conn_info(ConnHandle, Item).
%%-----------------------------------------------------------------
%% Update information about an active connection
%%-----------------------------------------------------------------
update_conn_info(ConnHandle, Item, Value) ->
megaco_config:update_conn_info(ConnHandle, Item, Value).
%%-----------------------------------------------------------------
%% All information for the application
%%-----------------------------------------------------------------
info() ->
Stats =
case get_stats() of
{ok, Statistics} ->
Statistics;
_ ->
[]
end,
SysInfo = system_info(),
[{statistics, Stats} | info(SysInfo)].
info(SysInfo) ->
info(SysInfo, []).
info([], Acc) ->
lists:reverse(Acc);
info([{connections, Conns} | SysInfo], Acc) ->
Conns2 = extend_conns_info(Conns),
info(SysInfo, [{connections, Conns2} | Acc]);
info([{users, Users} | SysInfo], Acc) ->
Users2 = extend_users_info(Users),
info(SysInfo, [{users, Users2} | Acc]);
info([Info | SysInfo], Acc) ->
info(SysInfo, [Info | Acc]).
extend_conns_info(Conns) ->
extend_conns_info(Conns, []).
extend_conns_info([], Acc) ->
lists:reverse(Acc);
extend_conns_info([Conn | Conns], Acc) ->
ConnInfo = conn_info(Conn),
extend_conns_info(Conns, [{Conn, ConnInfo} | Acc]).
extend_users_info(Users) ->
extend_users_info(Users, []).
extend_users_info([], Acc) ->
lists:reverse(Acc);
extend_users_info([User | Users], Acc) ->
UserInfo = user_info(User),
extend_users_info(Users, [{User, UserInfo} | Acc]).
%%-----------------------------------------------------------------
%% Lookup system information
%%-----------------------------------------------------------------
system_info_items() ->
[
text_config,
connections,
users,
n_active_requests,
n_active_replies,
n_active_connections,
pending_counters
].
system_info() ->
[{Item, system_info(Item)} || Item <- system_info_items()].
system_info(Item) ->
megaco_config:system_info(Item).
%%-----------------------------------------------------------------
%% Establish a "virtual" connection
%%-----------------------------------------------------------------
connect(ReceiveHandle, RemoteMid, SendHandle, ControlPid) ->
megaco_messenger:connect(ReceiveHandle, RemoteMid, SendHandle, ControlPid).
connect(ReceiveHandle, RemoteMid, SendHandle, ControlPid, Extra)
when (Extra =/= ?default_user_callback_extra) ->
megaco_messenger:connect(ReceiveHandle, RemoteMid, SendHandle,
ControlPid, Extra).
%%-----------------------------------------------------------------
%% Tear down a "virtual" connection
%%-----------------------------------------------------------------
disconnect(ConnHandle, Reason) ->
megaco_messenger:disconnect(ConnHandle, {user_disconnect, Reason}).
%%-----------------------------------------------------------------
%% Sends a transaction request and waits for a reply
%%-----------------------------------------------------------------
call(ConnHandle, ActionRequests, Options) ->
megaco_messenger:call(ConnHandle, ActionRequests, Options).
%%-----------------------------------------------------------------
%% Sends a transaction request but does NOT wait for a reply
%%-----------------------------------------------------------------
cast(ConnHandle, ActionRequests, Options) ->
megaco_messenger:cast(ConnHandle, ActionRequests, Options).
%%-----------------------------------------------------------------
%% Test the validity of the actions
%%-----------------------------------------------------------------
test_request(ConnHandle, Version, EncodingMod, EncodingConfig,
ActionRequests) ->
megaco_messenger:test_request(ConnHandle, ActionRequests,
Version, EncodingMod, EncodingConfig).
%% This tests the actual_reply() type of return from the
%% handle_trans_request function.
%%
test_reply(ConnHandle, Version, EncodingMod, EncodingConfig,
Reply) ->
megaco_messenger:test_reply(ConnHandle, Version,
EncodingMod, EncodingConfig, Reply).
%%-----------------------------------------------------------------
%% Func: get_stats/0, get_stats/1, get_stats/2
%% Description: Retreive statistics (counters) for TCP
%%-----------------------------------------------------------------
get_stats() ->
megaco_messenger:get_stats().
get_stats(SendHandle) ->
megaco_messenger:get_stats(SendHandle).
get_stats(SendHandle, Counter) ->
megaco_messenger:get_stats(SendHandle, Counter).
%%-----------------------------------------------------------------
%% Func: reset_stats/0, reaet_stats/1
%% Description: Reset statistics (counters) for TCP
%%-----------------------------------------------------------------
reset_stats() ->
megaco_messenger:reset_stats().
reset_stats(SendHandle) ->
megaco_messenger:reset_stats(SendHandle).
%%-----------------------------------------------------------------
%% Cancel all outstanding messages for this connection
%%-----------------------------------------------------------------
cancel(ConnHandle, Reason) ->
megaco_messenger:cancel(ConnHandle, {user_cancel, Reason}).
%%-----------------------------------------------------------------
%% Process a received message
%%-----------------------------------------------------------------
process_received_message(ReceiveHandle, ControlPid, SendHandle, BinMsg) ->
megaco_messenger:process_received_message(ReceiveHandle, ControlPid,
SendHandle, BinMsg).
process_received_message(ReceiveHandle, ControlPid, SendHandle, BinMsg, Extra) ->
megaco_messenger:process_received_message(ReceiveHandle, ControlPid,
SendHandle, BinMsg,
Extra).
receive_message(ReceiveHandle, ControlPid, SendHandle, BinMsg) ->
megaco_messenger:receive_message(ReceiveHandle, ControlPid,
SendHandle, BinMsg).
receive_message(ReceiveHandle, ControlPid, SendHandle, BinMsg, Extra) ->
megaco_messenger:receive_message(ReceiveHandle, ControlPid,
SendHandle, BinMsg,
Extra).
%%-----------------------------------------------------------------
%% Encode the actions list for one or more transactions.
%%-----------------------------------------------------------------
encode_actions(ConnHandle, ActionRequests, Options) ->
megaco_messenger:encode_actions(ConnHandle, ActionRequests, Options).
%%-----------------------------------------------------------------
%% Convert the (token) tags found in a decoded message into a
%% printable string.
%%-----------------------------------------------------------------
token_tag2string(Tag) ->
token_tag2string(Tag, pretty).
token_tag2string(Tag, pretty) ->
token_tag2string(Tag, megaco_pretty_text_encoder);
token_tag2string(Tag, compact) ->
token_tag2string(Tag, megaco_compact_text_encoder);
token_tag2string(Tag, Mod) when is_atom(Tag) and is_atom(Mod) ->
Mod:token_tag2string(Tag).
token_tag2string(Tag, pretty, V) ->
token_tag2string(Tag, megaco_pretty_text_encoder, V);
token_tag2string(Tag, compact, V) ->
token_tag2string(Tag, megaco_compact_text_encoder, V);
token_tag2string(Tag, Mod, V) when is_atom(Tag) and is_atom(Mod) ->
Mod:token_tag2string(Tag, V).
%%-----------------------------------------------------------------
%% Parses a digit map body
%%-----------------------------------------------------------------
parse_digit_map(DigitMapBody) ->
megaco_digit_map:parse(DigitMapBody).
%%-----------------------------------------------------------------
%% Collect digit map letters according to the digit map
%%-----------------------------------------------------------------
eval_digit_map(DigitMap) ->
megaco_digit_map:eval(DigitMap).
eval_digit_map(DigitMap, Timers) ->
megaco_digit_map:eval(DigitMap, Timers).
%%-----------------------------------------------------------------
%% Send one or more events to event collector process
%%-----------------------------------------------------------------
report_digit_event(DigitMapEvalPid, Event) ->
megaco_digit_map:report(DigitMapEvalPid, Event).
%%-----------------------------------------------------------------
%% Feed digit map collector with events and return the result
%%-----------------------------------------------------------------
test_digit_event(DigitMap, Events) ->
megaco_digit_map:test(DigitMap, Events).
%%-----------------------------------------------------------------
%% encode_binary_term_id(Config, MegacoTermId) ->
%%
%% {ok, TerminationId} | {error, Reason}
%%
%% Encode the Megaco internal form of a termination id (a
%% megaco_term_id record) into ASN.1'1 internal form of a termination
%% id (a 'TerminationId' record).
%% %%-----------------------------------------------------------------
encode_binary_term_id(Config, TermId) ->
megaco_binary_term_id:encode(Config, TermId).
%%-----------------------------------------------------------------
%% decode_binary_term_id(Config, TerminationId) ->
%%
%% {ok, MegacoTermId} | {error, Reason}
%%
%% Decode ASN.1's internal form of a termination id (a 'TerminationId'
%% record) into the Megaco internal form of a termination id (a
%% megaco_term_id record).
%%-----------------------------------------------------------------
decode_binary_term_id(Config, TermId) ->
megaco_binary_term_id:decode(Config, TermId).
%%-----------------------------------------------------------------
%% encode_sdp(SDP) ->
%%
%% {ok, PP} | {error, Reason}
%%
%% Encode a SDP construct into a property parm construct
%%-----------------------------------------------------------------
encode_sdp(SDP) ->
megaco_sdp:encode(SDP).
%%-----------------------------------------------------------------
%% decode_sdp(PP) ->
%%
%% {ok, SDP} | {error, Reason}
%%
%% Decode a property parm construct into a SDP construct
%%-----------------------------------------------------------------
decode_sdp(PP) ->
megaco_sdp:decode(PP).
%%-----------------------------------------------------------------
%% {ok, Vs} = megaco:versions1(), megaco:format_versions(Vs).
print_version_info() ->
{ok, Versions} = megaco:versions1(),
print_version_info(Versions).
print_version_info(Versions) when is_list(Versions) ->
print_sys_info(Versions),
print_os_info(Versions),
print_mods_info(Versions);
print_version_info(BadVersions) ->
{error, {bad_versions, BadVersions}}.
format_versions(Versions) ->
print_version_info(Versions).
print_sys_info(Versions) ->
case key1search(sys_info, Versions) of
{value, SysInfo} when is_list(SysInfo) ->
{value, Arch} = key1search(arch, SysInfo, "Not found"),
{value, Ver} = key1search(ver, SysInfo, "Not found"),
io:format("System info: "
"~n Arch: ~s"
"~n Ver: ~s"
"~n", [Arch, Ver]),
ok;
_ ->
io:format("System info: Not found~n", []),
not_found
end.
print_os_info(Versions) ->
case key1search(os_info, Versions) of
{value, OsInfo} when is_list(OsInfo) ->
Fam =
case key1search(fam, OsInfo, "Not found") of
{value, F} when is_atom(F) ->
atom_to_list(F);
{value, LF} when is_list(LF) ->
LF;
{value, XF} ->
lists:flatten(io_lib:format("~p", [XF]))
end,
Name =
case key1search(name, OsInfo) of
{value, N} when is_atom(N) ->
"[" ++ atom_to_list(N) ++ "]";
{value, LN} when is_list(LN) ->
"[" ++ LN ++ "]";
not_found ->
""
end,
Ver =
case key1search(ver, OsInfo, "Not found") of
{value, T} when is_tuple(T) ->
tversion(T);
{value, LV} when is_list(LV) ->
LV;
{value, XV} ->
lists:flatten(io_lib:format("~p", [XV]))
end,
io:format("OS info: "
"~n Family: ~s ~s"
"~n Ver: ~s"
"~n", [Fam, Name, Ver]),
ok;
_ ->
io:format("OS info: Not found~n", []),
not_found
end.
%% tversion({A, B, C}) ->
%% lists:flatten(io_lib:format("~w.~w.~w", [A, B, C]));
tversion(T) ->
L = tuple_to_list(T),
lversion(L).
lversion([]) ->
"";
lversion([A]) ->
integer_to_list(A);
lversion([A|R]) ->
integer_to_list(A) ++ "." ++ lversion(R).
print_mods_info(Versions) ->
case key1search(mod_info, Versions) of
{value, ModsInfo} when is_list(ModsInfo) ->
io:format("Module info: ~n", []),
lists:foreach(fun print_mod_info/1, ModsInfo);
_ ->
io:format("Module info: Not found~n", []),
not_found
end.
print_mod_info({Module, Info}) ->
% Maybe a asn1 generated module
Asn1Vsn =
case (catch Module:info()) of
AI when is_list(AI) ->
case (catch key1search(vsn, AI)) of
{value, V} when is_atom(V) ->
atom_to_list(V);
_ ->
"-"
end;
_ ->
"-"
end,
Vsn =
case key1search(vsn, Info) of
{value, I} when is_integer(I) ->
integer_to_list(I);
_ ->
"Not found"
end,
AppVsn =
case key1search(app_vsn, Info) of
{value, S1} when is_list(S1) ->
S1;
_ ->
"Not found"
end,
CompVer =
case key1search(compiler_version, Info) of
{value, S2} when is_list(S2) ->
S2;
_ ->
"Not found"
end,
CompDate =
case key1search(compile_time, Info) of
{value, {Year, Month, Day, Hour, Min, Sec}} ->
lists:flatten(
io_lib:format("~w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w",
[Year, Month, Day, Hour, Min, Sec]));
_ ->
"Not found"
end,
io:format(" ~w:~n"
" Vsn: ~s~n"
" App vsn: ~s~n"
" ASN.1 vsn: ~s~n"
" Compiler ver: ~s~n"
" Compile time: ~s~n",
[Module, Vsn, AppVsn, Asn1Vsn, CompVer, CompDate]),
ok.
key1search(Key, Vals) ->
case lists:keysearch(Key, 1, Vals) of
{value, {Key, Val}} ->
{value, Val};
false ->
not_found
end.
key1search(Key, Vals, Def) ->
case key1search(Key, Vals) of
not_found ->
{value, Def};
Value ->
Value
end.
%%-----------------------------------------------------------------
versions1() ->
case ms1() of
{ok, Mods} ->
{ok, version_info(Mods)};
Error ->
Error
end.
versions2() ->
case ms2() of
{ok, Mods} ->
{ok, version_info(Mods)};
Error ->
Error
end.
version_info(Mods) ->
SysInfo = sys_info(),
OsInfo = os_info(),
ModInfo = [mod_version_info(Mod) || Mod <- Mods],
[{sys_info, SysInfo}, {os_info, OsInfo}, {mod_info, ModInfo}].
mod_version_info(Mod) ->
Info = Mod:module_info(),
{value, {attributes, Attr}} = lists:keysearch(attributes, 1, Info),
{value, {vsn, [Vsn]}} = lists:keysearch(vsn, 1, Attr),
{value, {app_vsn, AppVsn}} = lists:keysearch(app_vsn, 1, Attr),
{value, {compile, Comp}} = lists:keysearch(compile, 1, Info),
{value, {version, Ver}} = lists:keysearch(version, 1, Comp),
{value, {time, Time}} = lists:keysearch(time, 1, Comp),
{Mod, [{vsn, Vsn},
{app_vsn, AppVsn},
{compiler_version, Ver},
{compile_time, Time}]}.
sys_info() ->
SysArch = string:strip(erlang:system_info(system_architecture),right,$\n),
SysVer = string:strip(erlang:system_info(system_version),right,$\n),
[{arch, SysArch}, {ver, SysVer}].
os_info() ->
V = os:version(),
case os:type() of
{OsFam, OsName} ->
[{fam, OsFam}, {name, OsName}, {ver, V}];
OsFam ->
[{fam, OsFam}, {ver, V}]
end.
ms() ->
ms1().
ms1() ->
App = ?APPLICATION,
LibDir = code:lib_dir(App),
File = filename:join([LibDir, "ebin", atom_to_list(App) ++ ".app"]),
case file:consult(File) of
{ok, [{application, App, AppFile}]} ->
case lists:keysearch(modules, 1, AppFile) of
{value, {modules, Mods}} ->
{ok, Mods};
_ ->
{error, {invalid_format, modules}}
end;
Error ->
{error, {invalid_format, Error}}
end.
ms2() ->
application:get_key(?APPLICATION, modules).
nc() ->
{ok, Mods} = ms(),
nc(Mods).
nc(all) ->
application:load(?APPLICATION),
case application:get_key(?APPLICATION, modules) of
{ok, Mods} ->
application:unload(?APPLICATION),
nc(Mods);
_ ->
{error, not_found}
end;
nc(Mods) when is_list(Mods) ->
[Mod || Mod <- Mods, ok /= load(Mod, compile)].
ni() ->
case ms() of
{ok, Mods} ->
ni(Mods);
Error ->
Error
end.
ni(all) ->
application:load(?APPLICATION),
case application:get_key(?APPLICATION, modules) of
{ok, Mods} ->
application:unload(?APPLICATION),
ni(Mods);
_ ->
{error, not_found}
end;
ni(Mods) when is_list(Mods) ->
[Mod || Mod <- Mods, ok /= load(Mod, interpret)].
load(Mod, How) when is_atom(Mod) ->
case try_load(Mod, How) of
ok ->
ok;
_ ->
io:format( "~n RETRY ~p FROM: ", [Mod]),
ModString = atom_to_list(Mod) ++ ".erl",
LibDir = code:lib_dir(?APPLICATION),
case find_file([LibDir], ModString) of
{ok, Abs} ->
load(Abs, How);
{error, Reason} ->
io:format( " *** ERROR *** ~p~n", [Reason]),
{error, Reason}
end
end;
load(Abs, How) ->
case try_load(Abs, How) of
ok ->
ok;
{error, Reason} ->
io:format( " *** ERROR *** ~p~n", [Reason]),
{error, Reason}
end.
try_load(Mod, How) ->
io:format( " ~p ", [Mod]),
Flags = [{d, debug}],
case How of
compile ->
case catch c:nc(Mod, Flags) of
{ok, _} -> ok;
Other -> {error, Other}
end;
interpret ->
case catch int:ni(Mod, Flags) of
{module, _} -> ok;
Other -> {error, Other}
end
end.
find_file([Dir | Dirs], File) ->
case file:list_dir(Dir) of
{ok, List} ->
case lists:member(File, List) of
true ->
{ok, filename:join([Dir, File])};
false ->
SubDirs = [filename:join([Dir, Sub]) || Sub <- List],
case find_file(SubDirs, File) of
{ok, Abs} ->
{ok, Abs};
{error, _Reason} ->
find_file(Dirs, File)
end
end;
{error, _Reason} ->
find_file(Dirs, File)
end;
find_file([], File) ->
{error, {no_such_file, File}}.
%%-----------------------------------------------------------------
%% -----------------------------
%% These functions can be used instead of the et tool for
%% managing trace of the megaco application.
%%-----------------------------------------------------------------
%% enable_trace(Level, Destination) -> void()
%%
%% Parameters:
%% Level -> max | min | integer()
%% Destination -> File | Port | io | {io, Verbosity} | HandlerSpec
%% File -> string()
%% Port -> integer()
%% Verbosity -> true | false
%% HandlerSpec = {function(), Data}
%% Data = term()
%%
%% Description:
%% This function is used to start tracing at level Level and send
%% the result either to the file File or the port Port. Note that
%% it starts a tracer server.
%% When Destination is the atom io (or the tuple {io, Verbosity}),
%% all (printable) megaco trace events (trace_ts events which has
%% Severity withing Limit) will be written to stdout using io:format.
%%
%%-----------------------------------------------------------------
enable_trace(Level, File) when is_list(File) ->
case file:open(File, [write]) of
{ok, Fd} ->
HandleSpec = {fun handle_trace/2, Fd},
dbg:tracer(process, HandleSpec),
set_trace(Level);
Err ->
Err
end;
enable_trace(Level, Port) when is_integer(Port) ->
dbg:tracer(port, dbg:trace_port(ip, Port)),
set_trace(Level);
enable_trace(Level, io) ->
HandleSpec = {fun handle_trace/2, standard_io},
dbg:tracer(process, HandleSpec),
set_trace(Level);
enable_trace(Level, {Fun, _Data} = HandleSpec) when is_function(Fun) ->
dbg:tracer(process, HandleSpec),
set_trace(Level).
%%-----------------------------------------------------------------
%% disable_trace() -> void()
%%
%% Description:
%% This function is used to stop tracing.
%%-----------------------------------------------------------------
disable_trace() ->
%% This is to make handle_trace/2 close the output file (if the
%% event gets there before dbg closes)
report_event(stop_trace, stop_trace, stop_trace, stop_trace, stop_trace),
dbg:stop().
%%-----------------------------------------------------------------
%% set_trace(Level) -> void()
%%
%% Parameters:
%% Level -> max | min | integer()
%%
%% Description:
%% This function is used to change the trace level when tracing has
%% already been started.
%%-----------------------------------------------------------------
set_trace(Level) ->
Pat = et_selector:make_pattern({?MODULE, Level}),
et_selector:change_pattern(Pat).
report_event(DetailLevel, FromTo, Label, Contents) ->
%% N.B External call
?MODULE:report_event(DetailLevel, FromTo, FromTo, Label, Contents).
report_event(_DetailLevel, _From, _To, _Label, _Contents) ->
hopefully_traced.
%% ----------------------------------------------------------------------
%% handle_trace(Event, Verbosity) -> Verbosity
%%
%% Parameters:
%% Event -> The trace event (only megaco 'trace_ts' events are printed)
%% Verbosity -> max | min | integer() (see Level above)
%%
%% Description:
%% This function is "receive" and print the trace events.
%% Events are printed if:
%% - Verbosity is max
%% - Severity is =< Verbosity (e.g. Severity = 30, and Verbosity = 40)
%% Events are not printed if:
%% - Verbosity is min
%% - Severity is > Verbosity
%%-----------------------------------------------------------------
handle_trace(_, closed_file = Fd) ->
Fd;
handle_trace({trace_ts, _Who, call,
{?MODULE, report_event,
[stop_trace, stop_trace, stop_trace, stop_trace, stop_trace]},
_Timestamp},
standard_io = Fd) ->
Fd;
handle_trace({trace_ts, _Who, call,
{?MODULE, report_event,
[stop_trace, stop_trace, stop_trace, stop_trace, stop_trace]},
Timestamp},
Fd) ->
(catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])),
(catch file:close(Fd)),
closed_file;
handle_trace({trace_ts, Who, call,
{?MODULE, report_event,
[Sev, From, To, Label, Content]}, Timestamp},
Fd) ->
(catch print_megaco_trace(Fd, Sev, Who, Timestamp, Label, From, To, Content)),
Fd;
handle_trace(Event, Fd) ->
(catch print_trace(Fd, Event)),
Fd.
print_megaco_trace(Fd, Sev, Who, Timestamp, Label, From, To, Content) ->
Ts = format_timestamp(Timestamp),
io:format(Fd, "[megaco trace ~w ~w ~s] ~s "
"~n From: ~p"
"~n To: ~p"
"~n Content: ~p"
"~n",
[Sev, Who, Ts, Label, From, To, Content]).
print_trace(Fd, {trace, Who, What, Where}) ->
io:format(Fd, "[trace]"
"~n Who: ~p"
"~n What: ~p"
"~n Where: ~p"
"~n", [Who, What, Where]);
print_trace(Fd, {trace, Who, What, Where, Extra}) ->
io:format(Fd, "[trace]"
"~n Who: ~p"
"~n What: ~p"
"~n Where: ~p"
"~n Extra: ~p"
"~n", [Who, What, Where, Extra]);
print_trace(Fd, {trace_ts, Who, What, Where, When}) ->
Ts = format_timestamp(When),
io:format(Fd, "[trace ~s]"
"~n Who: ~p"
"~n What: ~p"
"~n Where: ~p"
"~n", [Ts, Who, What, Where]);
print_trace(Fd, {trace_ts, Who, What, Where, Extra, When}) ->
Ts = format_timestamp(When),
io:format(Fd, "[trace ~s]"
"~n Who: ~p"
"~n What: ~p"
"~n Where: ~p"
"~n Extra: ~p"
"~n", [Ts, Who, What, Where, Extra]);
print_trace(Fd, {seq_trace, What, Where}) ->
io:format(Fd, "[seq trace]"
"~n What: ~p"
"~n Where: ~p"
"~n", [What, Where]);
print_trace(Fd, {seq_trace, What, Where, When}) ->
Ts = format_timestamp(When),
io:format(Fd, "[seq trace ~s]"
"~n What: ~p"
"~n Where: ~p"
"~n", [Ts, What, Where]);
print_trace(Fd, {drop, Num}) ->
io:format(Fd, "[drop trace] ~p~n", [Num]);
print_trace(Fd, Trace) ->
io:format(Fd, "[trace] "
"~n ~p"
"~n", [Trace]).
format_timestamp({_N1, _N2, N3} = Now) ->
{Date, Time} = calendar:now_to_datetime(Now),
{YYYY,MM,DD} = Date,
{Hour,Min,Sec} = Time,
FormatDate =
io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
[YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
lists:flatten(FormatDate).