diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/inets/src/inets_app/Makefile | 3 | ||||
-rw-r--r-- | lib/inets/src/inets_app/inets.app.src | 5 | ||||
-rw-r--r-- | lib/inets/src/inets_app/inets.erl | 267 | ||||
-rw-r--r-- | lib/inets/src/inets_app/inets_internal.hrl | 6 | ||||
-rw-r--r-- | lib/inets/src/inets_app/inets_trace.erl | 357 |
5 files changed, 372 insertions, 266 deletions
diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile index d99e33b4ea..6da6a1d79f 100644 --- a/lib/inets/src/inets_app/Makefile +++ b/lib/inets/src/inets_app/Makefile @@ -46,7 +46,8 @@ MODULES = \ inets_service \ inets_app \ inets_sup \ - inets_regexp + inets_regexp \ + inets_trace INTERNAL_HRL_FILES = inets_internal.hrl EXTERNAL_HRL_FILES = ../../include/httpd.hrl \ diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src index 1db7ed2c30..4aea2ef3d7 100644 --- a/lib/inets/src/inets_app/inets.app.src +++ b/lib/inets/src/inets_app/inets.app.src @@ -18,14 +18,15 @@ %% {application,inets, - [{description,"INETS CXC 138 49"}, - {vsn,"%VSN%"}, + [{description, "INETS CXC 138 49"}, + {vsn, "%VSN%"}, {modules,[ inets, inets_sup, inets_app, inets_service, inets_regexp, + inets_trace, %% FTP ftp, diff --git a/lib/inets/src/inets_app/inets.erl b/lib/inets/src/inets_app/inets.erl index 054468e445..8e79f8d456 100644 --- a/lib/inets/src/inets_app/inets.erl +++ b/lib/inets/src/inets_app/inets.erl @@ -28,7 +28,7 @@ stop/0, stop/2, services/0, services_info/0, service_names/0]). --export([enable_trace/2, enable_trace/3, disable_trace/0, set_trace/1, +-export([enable_trace/2, enable_trace/3, disable_trace/0, set_trace/1, report_event/4]). -export([versions/0, print_version_info/0, print_version_info/1]). @@ -409,47 +409,8 @@ service_names() -> %% Severity withing Limit) will be written to stdout using io:format. %% %%----------------------------------------------------------------- -enable_trace(Level, Dest) -> - enable_trace(Level, Dest, all). - -enable_trace(Level, Dest, Service) -> - case valid_trace_service(Service) of - true -> - enable_trace2(Level, Dest, Service); - false -> - {error, {invalid_service, Service}} - end. - -enable_trace2(Level, File, Service) - when is_list(File) -> - case file:open(File, [write]) of - {ok, Fd} -> - HandleSpec = {fun handle_trace/2, {Service, Fd}}, - do_enable_trace(Level, process, HandleSpec); - Err -> - Err - end; -enable_trace2(Level, Port, _) when is_integer(Port) -> - do_enable_trace(Level, port, dbg:trace_port(ip, Port)); -enable_trace2(Level, io, Service) -> - HandleSpec = {fun handle_trace/2, {Service, standard_io}}, - do_enable_trace(Level, process, HandleSpec); -enable_trace2(Level, {Fun, _Data} = HandleSpec, _) when is_function(Fun) -> - do_enable_trace(Level, process, HandleSpec). - -do_enable_trace(Level, Type, HandleSpec) -> - case dbg:tracer(Type, HandleSpec) of - {ok, _} -> - set_trace(Level), - ok; - Error -> - Error - end. - -valid_trace_service(all) -> - true; -valid_trace_service(Service) -> - lists:member(Service, [httpc, httpd, ftpc, tftp]). +enable_trace(Level, Dest) -> inets_trace:enable(Level, Dest). +enable_trace(Level, Dest, Service) -> inets_trace:enable(Level, Dest, Service). %%----------------------------------------------------------------- @@ -458,12 +419,7 @@ valid_trace_service(Service) -> %% 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) - inets:report_event(100, "stop trace", stop_trace, [stop_trace]), - dbg:stop(). - +disable_trace() -> inets_trace:disable(). %%----------------------------------------------------------------- @@ -476,60 +432,7 @@ disable_trace() -> %% This function is used to change the trace level when tracing has %% already been started. %%----------------------------------------------------------------- -set_trace(Level) -> - set_trace(Level, all). - -set_trace(Level, Service) -> - Pat = make_pattern(?MODULE, Service, Level), - change_pattern(Pat). - -make_pattern(Mod, Service, Level) - when is_atom(Mod) andalso is_atom(Service) -> - case Level of - min -> - {Mod, Service, []}; - max -> - Head = ['$1', '_', '_', '_'], - Body = [], - Cond = [], - {Mod, Service, [{Head, Cond, Body}]}; - DetailLevel when is_integer(DetailLevel) -> - Head = ['$1', '_', '_', '_'], - Body = [], - Cond = [{ '=<', '$1', DetailLevel}], - {Mod, Service, [{Head, Cond, Body}]}; - _ -> - exit({bad_level, Level}) - end. - -change_pattern({Mod, Service, Pattern}) - when is_atom(Mod) andalso is_atom(Service) -> - MFA = {Mod, report_event, 4}, - case Pattern of - [] -> - try - error_to_exit(ctp, dbg:ctp(MFA)), - error_to_exit(p, dbg:p(all, clear)) - catch - exit:{Where, Reason} -> - {error, {Where, Reason}} - end; - List when is_list(List) -> - try - error_to_exit(ctp, dbg:ctp(MFA)), - error_to_exit(tp, dbg:tp(MFA, Pattern)), - error_to_exit(p, dbg:p(all, [call, timestamp])) - catch - exit:{Where, Reason} -> - {error, {Where, Reason}} - end - end, - ok. - -error_to_exit(_Where, {ok, _} = OK) -> - OK; -error_to_exit(Where, {error, Reason}) -> - exit({Where, Reason}). +set_trace(Level) -> inets_trace:set_level(Level). %%----------------------------------------------------------------- @@ -546,164 +449,8 @@ error_to_exit(Where, {error, Reason}) -> %% put trace on this function. %%----------------------------------------------------------------- -report_event(Severity, Label, Service, Content) - when (is_integer(Severity) andalso - (Severity >= 0) andalso (100 >= Severity)) andalso - is_list(Label) andalso - is_atom(Service) andalso - is_list(Content) -> - hopefully_traced. - - -%% ---------------------------------------------------------------------- -%% handle_trace(Event, Fd) -> Verbosity -%% -%% Parameters: -%% Event -> The trace event (only megaco 'trace_ts' events are printed) -%% Fd -> standard_io | file_descriptor() | trace_port() -%% -%% Description: -%% This function is used to "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, - [_Sev, "stop trace", stop_trace, [stop_trace]]}, - Timestamp}, - {_, standard_io} = Fd) -> - (catch io:format(standard_io, "stop trace at ~s~n", [format_timestamp(Timestamp)])), - Fd; -handle_trace({trace_ts, _Who, call, - {?MODULE, report_event, - [_Sev, "stop trace", stop_trace, [stop_trace]]}, - Timestamp}, - standard_io = Fd) -> - (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), - Fd; -handle_trace({trace_ts, _Who, call, - {?MODULE, report_event, - [_Sev, "stop trace", stop_trace, [stop_trace]]}, - Timestamp}, - {_Service, 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, "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, Label, Service, Content]}, Timestamp}, - Fd) -> - (catch print_inets_trace(Fd, Sev, Timestamp, Who, - Label, Service, Content)), - Fd; -handle_trace(Event, Fd) -> - (catch print_trace(Fd, Event)), - Fd. - - -print_inets_trace({Service, Fd}, - Sev, Timestamp, Who, Label, Service, Content) -> - do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content); -print_inets_trace({ServiceA, Fd}, - Sev, Timestamp, Who, Label, ServiceB, Content) - when (ServiceA =:= all) -> - do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, ServiceB, Content); -print_inets_trace({ServiceA, _Fd}, - _Sev, _Timestamp, _Who, _Label, ServiceB, _Content) - when ServiceA =/= ServiceB -> - ok; -print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) -> - do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content). - -do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) -> - Ts = format_timestamp(Timestamp), - io:format(Fd, "[inets ~w trace ~w ~w ~s] ~s " - "~n Content: ~p" - "~n", - [Service, Sev, Who, Ts, Label, Content]). - -print_trace({_, Fd}, Event) -> - do_print_trace(Fd, Event); -print_trace(Fd, Event) -> - do_print_trace(Fd, Event). - -do_print_trace(Fd, {trace, Who, What, Where}) -> - io:format(Fd, "[trace]" - "~n Who: ~p" - "~n What: ~p" - "~n Where: ~p" - "~n", [Who, What, Where]); - -do_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]); - -do_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]); - -do_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]); - -do_print_trace(Fd, {seq_trace, What, Where}) -> - io:format(Fd, "[seq trace]" - "~n What: ~p" - "~n Where: ~p" - "~n", [What, Where]); - -do_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]); - -do_print_trace(Fd, {drop, Num}) -> - io:format(Fd, "[drop trace] ~p~n", [Num]); - -do_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). +report_event(Severity, Label, Service, Content) -> + inets_trace:report_event(Severity, Label, Service, Content). %%-------------------------------------------------------------------- diff --git a/lib/inets/src/inets_app/inets_internal.hrl b/lib/inets/src/inets_app/inets_internal.hrl index 55c3669e4a..e56af3b59d 100644 --- a/lib/inets/src/inets_app/inets_internal.hrl +++ b/lib/inets/src/inets_app/inets_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -24,8 +24,8 @@ %% Various trace macros -define(report(Severity, Label, Service, Content), - inets:report_event(Severity, Label, Service, - [{module, ?MODULE}, {line, ?LINE} | Content])). + inets_trace:report_event(Severity, Label, Service, + [{module, ?MODULE}, {line, ?LINE} | Content])). -define(report_important(Label, Service, Content), ?report(20, Label, Service, Content)). -define(report_verbose(Label, Service, Content), diff --git a/lib/inets/src/inets_app/inets_trace.erl b/lib/inets/src/inets_app/inets_trace.erl new file mode 100644 index 0000000000..9369293d28 --- /dev/null +++ b/lib/inets/src/inets_app/inets_trace.erl @@ -0,0 +1,357 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% Purpose: The main interface module of the inets application +%%---------------------------------------------------------------------- + +-module(inets_trace). + +%% API +-export([enable/2, enable/3, + disable/0, + set_level/1, set_level/2, + report_event/4]). + + +%%==================================================================== +%% API +%%==================================================================== + +%%----------------------------------------------------------------- +%% enable(Level, Destination) -> void() +%% enable(Level, Destination, Service) -> void() +%% +%% Parameters: +%% Level -> max | min | integer() +%% Destination -> File | Port | io | HandlerSpec +%% Service -> httpc | httpd | ftpc | tftp | all +%% 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, the port Port or to a +%% trace handler. +%% Note that it starts a tracer server. +%% When Destination is the atom io (or the tuple {io, Verbosity}), +%% all (printable) inets trace events (trace_ts events which has +%% Severity withing Limit) will be written to stdout using io:format. +%% +%%----------------------------------------------------------------- +enable(Level, Dest) -> + enable(Level, Dest, all). + +enable(Level, Dest, Service) -> + case valid_trace_service(Service) of + true -> + enable2(Level, Dest, Service); + false -> + {error, {invalid_service, Service}} + end. + +enable2(Level, File, Service) + when is_list(File) -> + case file:open(File, [write]) of + {ok, Fd} -> + HandleSpec = {fun handle_trace/2, {Service, Fd}}, + do_enable(Level, process, HandleSpec); + Err -> + Err + end; +enable2(Level, Port, _) when is_integer(Port) -> + do_enable(Level, port, dbg:trace_port(ip, Port)); +enable2(Level, io, Service) -> + HandleSpec = {fun handle_trace/2, {Service, standard_io}}, + do_enable(Level, process, HandleSpec); +enable2(Level, {Fun, _Data} = HandleSpec, _) when is_function(Fun) -> + do_enable(Level, process, HandleSpec). + +do_enable(Level, Type, HandleSpec) -> + case dbg:tracer(Type, HandleSpec) of + {ok, _} -> + set_level(Level), + ok; + Error -> + Error + end. + +valid_trace_service(all) -> + true; +valid_trace_service(Service) -> + lists:member(Service, [httpc, httpd, ftpc, tftp]). + + +%%----------------------------------------------------------------- +%% disable() -> void() +%% +%% Description: +%% This function is used to stop tracing. +%%----------------------------------------------------------------- + +disable() -> + %% This is to make handle_trace/2 close the output file (if the + %% event gets there before dbg closes) + inets_trace:report_event(100, "stop trace", stop_trace, [stop_trace]), + dbg:stop(). + + + +%%----------------------------------------------------------------- +%% set_level(Level) -> void() +%% +%% Parameters: +%% Level -> max | min | integer() +%% +%% Description: +%% This function is used to change the trace level when tracing has +%% already been started. +%%----------------------------------------------------------------- +set_level(Level) -> + set_level(Level, all). + +set_level(Level, Service) -> + Pat = make_pattern(?MODULE, Service, Level), + change_pattern(Pat). + +make_pattern(Mod, Service, Level) + when is_atom(Mod) andalso is_atom(Service) -> + case Level of + min -> + {Mod, Service, []}; + max -> + Head = ['$1', '_', '_', '_'], + Body = [], + Cond = [], + {Mod, Service, [{Head, Cond, Body}]}; + DetailLevel when is_integer(DetailLevel) -> + Head = ['$1', '_', '_', '_'], + Body = [], + Cond = [{ '=<', '$1', DetailLevel}], + {Mod, Service, [{Head, Cond, Body}]}; + _ -> + exit({bad_level, Level}) + end. + +change_pattern({Mod, Service, Pattern}) + when is_atom(Mod) andalso is_atom(Service) -> + MFA = {Mod, report_event, 4}, + case Pattern of + [] -> + try + error_to_exit(ctp, dbg:ctp(MFA)), + error_to_exit(p, dbg:p(all, clear)) + catch + exit:{Where, Reason} -> + {error, {Where, Reason}} + end; + List when is_list(List) -> + try + error_to_exit(ctp, dbg:ctp(MFA)), + error_to_exit(tp, dbg:tp(MFA, Pattern)), + error_to_exit(p, dbg:p(all, [call, timestamp])) + catch + exit:{Where, Reason} -> + {error, {Where, Reason}} + end + end. + +error_to_exit(_Where, {ok, _} = OK) -> + OK; +error_to_exit(Where, {error, Reason}) -> + exit({Where, Reason}). + + +%%----------------------------------------------------------------- +%% report_event(Severity, Label, Service, Content) +%% +%% Parameters: +%% Severity -> 0 =< integer() =< 100 +%% Label -> string() +%% Service -> httpd | httpc | ftp | tftp +%% Content -> [{tag, term()}] +%% +%% Description: +%% This function is used to generate trace events, that is, +%% put trace on this function. +%%----------------------------------------------------------------- + +report_event(Severity, Label, Service, Content) + when (is_integer(Severity) andalso + (Severity >= 0) andalso (100 >= Severity)) andalso + is_list(Label) andalso + is_atom(Service) andalso + is_list(Content) -> + hopefully_traced. + + +%% ---------------------------------------------------------------------- +%% handle_trace(Event, Fd) -> Verbosity +%% +%% Parameters: +%% Event -> The trace event +%% Fd -> standard_io | file_descriptor() | trace_port() +%% +%% Description: +%% This function is used to "receive" and pretty 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, + [_Sev, "stop trace", stop_trace, [stop_trace]]}, + Timestamp}, + {_, standard_io} = Fd) -> + (catch io:format(standard_io, "stop trace at ~s~n", [format_timestamp(Timestamp)])), + Fd; +handle_trace({trace_ts, _Who, call, + {?MODULE, report_event, + [_Sev, "stop trace", stop_trace, [stop_trace]]}, + Timestamp}, + standard_io = Fd) -> + (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), + Fd; +handle_trace({trace_ts, _Who, call, + {?MODULE, report_event, + [_Sev, "stop trace", stop_trace, [stop_trace]]}, + Timestamp}, + {_Service, 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, "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, Label, Service, Content]}, Timestamp}, + Fd) -> + (catch print_inets_trace(Fd, Sev, Timestamp, Who, + Label, Service, Content)), + Fd; +handle_trace(Event, Fd) -> + (catch print_trace(Fd, Event)), + Fd. + + +print_inets_trace({Service, Fd}, + Sev, Timestamp, Who, Label, Service, Content) -> + do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content); +print_inets_trace({ServiceA, Fd}, + Sev, Timestamp, Who, Label, ServiceB, Content) + when (ServiceA =:= all) -> + do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, ServiceB, Content); +print_inets_trace({ServiceA, _Fd}, + _Sev, _Timestamp, _Who, _Label, ServiceB, _Content) + when ServiceA =/= ServiceB -> + ok; +print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) -> + do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content). + +do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) -> + Ts = format_timestamp(Timestamp), + io:format(Fd, "[inets ~w trace ~w ~w ~s] ~s " + "~n Content: ~p" + "~n", + [Service, Sev, Who, Ts, Label, Content]). + +print_trace({_, Fd}, Event) -> + do_print_trace(Fd, Event); +print_trace(Fd, Event) -> + do_print_trace(Fd, Event). + +do_print_trace(Fd, {trace, Who, What, Where}) -> + io:format(Fd, "[trace]" + "~n Who: ~p" + "~n What: ~p" + "~n Where: ~p" + "~n", [Who, What, Where]); + +do_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]); + +do_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]); + +do_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]); + +do_print_trace(Fd, {seq_trace, What, Where}) -> + io:format(Fd, "[seq trace]" + "~n What: ~p" + "~n Where: ~p" + "~n", [What, Where]); + +do_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]); + +do_print_trace(Fd, {drop, Num}) -> + io:format(Fd, "[drop trace] ~p~n", [Num]); + +do_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). + + |