%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2001-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%
%%
%%%---------------------------------------------------------------------
%%% File : webappmon.erl
%%% Author : Martin G. <[email protected]>
%%% Purpose : Frontend to the webbased version of appmon.
%%% Created : 24 Apr 2001 by Martin G. <[email protected]>
%%%---------------------------------------------------------------------
-module(appmon_web).
%% The functions that the user can call to interact with the genserver
-export([init/1,handle_call/3,handle_cast/2,handle_info/2]).
-export([terminate/2,code_change/3]).
-export([node_info/2,application_info/2,application_env/2]).
-export([proc_info/2,trace/2]).
-export([start/0,stop/0,start_link/0]).
%% Export the function that returns the configuration data needed by
%% webtool
-export([configData/0]).
-behaviour(gen_server).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Start the genserver %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
start_link()->
gen_server:start_link({local,webappmon_server},appmon_web,[],[]).
start()->
gen_server:start({local,webappmon_server},appmon_web,[],[]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Stop the genserver %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
stop()->
gen_server:call(webappmon_server,stop,1000).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Get the page that shows the nodes and the apps on the sel node %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
node_info(Env,Input)->
gen_server:call(webappmon_server,{node_data,Env,Input}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Get the application process tree %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
application_info(Env,Input)->
gen_server:call(webappmon_server,{app_data,Env,Input}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Get the page that shows the data about the process %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
proc_info(Env,Input)->
gen_server:call(webappmon_server,{proc_data,Env,Input}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Get the spec on the app %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
application_env(Env,Input)->
gen_server:call(webappmon_server,{app_env,Env,Input}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Toggle the trace flag for the selected process %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
trace(Env,Input)->
gen_server:call(webappmon_server,{trace,Env,Input}).
configData()->
{appmon,[{web_data,{"WebAppmon","/appmon/main_frame.html"}},
{alias,{"/appmon",code:priv_dir(appmon)}},
{alias,{erl_alias,"/erl",[appmon_web]}},
{start,{child,{backend,{process_info,start_link,[]},
permanent,100,worker,[process_info]}}},
{start,{child,{{local,webappmon_server},
{appmon_web,start_link,[]},
permanent,100,worker,[appmon_web]}}}
]}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% Callback functions for the genserver %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init(_Arg)->
{ok,[]}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Create the different pages %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
handle_call({node_data,_Env,Input},_From,State)->
{reply,app_and_node_sel_page(Input),State};
handle_call({app_data,_Env,Input},_From,State)->
{reply,process_tree_page(Input),State};
handle_call({proc_data,_Env,Input},_From,State)->
{reply,process_specifickation_page(Input),State};
handle_call({app_env,_Env,Input},_From,State)->
{reply,application_specifickation_page(Input),State};
handle_call({trace,_Env,Input},_From,State)->
{reply,toggle_trace(Input),State}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Shutdown the genserver %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
terminate(_,_State)->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Callback function currently not used ... %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
handle_cast(_,State)->
{noreply,State}.
handle_info(_,State)->
{noreply,State}.
code_change(_OldVsn,State,_Extra)->
{ok,State}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
%% Private functions to create the part of the sides that is common %%
%% to all sides %%
%% %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create the Header for the page If we now the mimetype use that type%%
%% otherwise use text %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
header() ->
header("text/html").
header(MimeType) ->
"Content-type: " ++ MimeType ++ "\r\n\r\n".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create the Htmlheader sett the title of the side to nothing if %%
%% we dont know the name of the side %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
html_header()->
html_header("").
html_header(Part) ->
"<HTML BGCOLOR=\"#FFFFFF\">\n" ++
"<HEAD>\n" ++
"<TITLE>Appmon " ++ Part ++ "</TITLE>\n" ++
"</HEAD>\n".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Close the Html tag and if neccessay add some clean upp %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
html_end()->
"</HTML>".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
%% The functions that creates the whole pages by collecting %%
%% the necessary data %%
%% %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Returns the page where the user see's which nodes and apps that %%
%% are availible for monitoring %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
app_and_node_sel_page(Input)->
[header(),
html_header(),
node_body(httpd:parse_query(Input)),
html_end()].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Returns the process tree for the application whose name is %%
%% the first value in the Inputs key/value list %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
process_tree_page(Input)->
[header(),
html_header(),
application_javascript(httpd:parse_query(Input)),
application_body(httpd:parse_query(Input)),
html_end()].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Send trace on or off to the process thats pid is the third arg of %%
%% the inputs key/val list. Then it returns the process tree for the %%
%% the application that is the first key/val pair of input %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
toggle_trace(Input)->
send_trace(httpd:parse_query(Input)),
[header(),
html_header(),
application_javascript(httpd:parse_query(Input)),
application_body(httpd:parse_query(Input)),
html_end()].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Creates the page that shows all information about the process that %%
%% that is the first arg of th input key/val pairs %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
process_specifickation_page(Input)->
[header(),
html_header(),
process_body(httpd:parse_query(Input)),
html_end()].
application_specifickation_page(Input)->
[header(),
html_header(),
application_env_body(httpd:parse_query(Input)),
html_end()].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% The Private functions that do the job %%
%% To build the the page that shows the applications %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Build the body of the side that shows the node name and %%
%% the application list %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
node_body([{"node",Node}|_Rest])->
case process_info:is_node(Node) of
{true,Controlled_node,Name} ->
"<BODY BGCOLOR=\"#FFFFFF\">" ++
node_selections_javascripts() ++
node_selection(Controlled_node) ++
node_title() ++
application_tree(Controlled_node,Name) ++
"</BODY>";
{false,Server_node,Name} ->
"<BODY BGCOLOR=\"#FFFFFF\">" ++
node_selections_javascripts() ++
node_selection(Server_node) ++
node_title() ++
application_tree(Server_node,Name) ++
"</BODY>"
end;
node_body(_Whatever)->
node_body([{atom_to_list(node),atom_to_list(node())}]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Returns the javascript that sets a new node to monitor %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
node_selections_javascripts()->
"<SCRIPT>
function node_selected()
{
parent.frames.base_frames.location=\"../../appmon/start_info.html\"
window.location =\"./node_info?node=\" + " ++
"document.node_selection.nodes[document.node_selection.nodes.selectedIndex].value;
}
</SCRIPT>".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Insert the html code that shows the combobox where the user can %%
%% select another node to monitor %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
node_selection(Node)->
" <FORM ACTION=\"./node_info\" NAME=node_selection>\n
<TABLE WIDTH=\"100%\" BORDER=\"0\">\n
<TR><TD ALIGN=\"center\">\n
<SELECT NAME=nodes onChange=\"node_selected()\">\n" ++
print_nodes(order_nodes(Node,process_info:get_nodes())) ++
"</SELECT>\n
</TD></TR>\n
</TABLE>\n
</FORM>".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Add the node we are working with in the beginning of the list and %%
%% remove it from other places so its always the first in the listbox %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
order_nodes(Node,Node_list)->
[Node|lists:delete(Node,Node_list)].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Take the list of nodes and make it to a list of options to the %%
%% the combobox %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
print_nodes([])->
[];
print_nodes([Node|Rest])->
"<OPTION value=\"" ++
atom_to_list(Node) ++
"\">" ++
atom_to_list(Node) ++
"\n" ++
print_nodes(Rest).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create the header of the node info page i.e. the name of the node %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
node_title()->
" <TABLE WIDTH=\"100%\" BORDER=\"0\">
<TR><TD ALIGN=\"center\"><FONT SIZE=5>Applications</FONT></TD></TR>
</TABLE>\n".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Build the body of the side that shows the node i.e the application %%
%% list %%
%% Node and Node_name are the same just different types %%
%% Node are the atom Node_name the string of the node name %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
application_tree(Node,Node_name)->
Fun=fun(Name)->
Name_str =atom_to_list(Name),
"<LI><A HREF=\"./application_info?name=" ++ Name_str ++
"&mode=all&node=" ++ Node_name ++
"\" TARGET=main><B>" ++ Name_str ++
"</B></A>" ++ print_space(15-length(Name_str),[]) ++
"<A HREF=\"./application_env?name=" ++ Name_str ++
"&node=" ++ Node_name ++
"\" TARGET=\"main\"><FONT SIZE=2>spec</FONT></A></LI>\n"
end,
"<UL>" ++
lists:map(Fun, (process_info:get_applications(Node))) ++
"</UL>" ++
"<FORM Name=reload>" ++
"<INPUT TYPE=\"button\" onClick=\"node_selected()\"
VALUE=\"Reload\">\n" ++
"</FORM>" ++
"<!--<A HREF=\"../../appmon/application_help.html\" TARGET=main>Help</A>-->".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Print spaces between the application name and the spec link %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
print_space(N,Space)when N >0 ->
print_space(N-1," " ++ Space);
print_space(_N,Space)->
Space.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% The Private functions that do the job %%
%% To build the the page that shows process in an application %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%Generates the javascript that govern the look of the page that %%
%%the processes of an application %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%Args is the list whit input args should be App Mode, Node
application_javascript(Args)when length(Args)>=3 ->
Vars=
"<SCRIPT>
var proc;
var app=\"" ++ element(2,lists:nth(1,Args)) ++ "\";
var node=\"" ++ element(2,lists:nth(3,Args)) ++ "\";",
CommonFuncs=
" function reload_bottom_frame()
{parent.proc_data.location.href=\"/appmon/blank.html\";}
function show_process_info()
{
if(proc.indexOf(\"#Port\")== -1)
{
if(proc.charAt(0)==\'<\')
window.location=\"./proc_info?name=\" + proc + \"&node=\" + node
else
{
start=proc.indexOf(\"<\");
endpoint=proc.lastIndexOf(\">\");
window.location=\"./proc_info?name=\" + proc.slice(start,endpoint+1) + \"&node=\" + node ;
}
}
}
function trace()
{
if(proc.charAt(0)==\'<\')
window.location=\"./trace?name=\" + app + \"&mode=\" + get_mode() + \"&node=\" + node + \"&proc=\" + proc;
else
{
start=proc.indexOf(\"<\");
endpoint=proc.lastIndexOf(\">\");
window.location=\"./trace?name=\" + app + \"&mode=\" + get_mode() + \"&node=\" + node + \"&proc=\" +
proc.slice(start,endpoint+1) ;
}
}
function reload_page()\n
{
window.location=\"./application_info?name=\" + app + \"&mode=\" + get_mode() + \"&node=\" + node ;
}
function get_mode()
{
for (i= 0; i < document.reload_form.mode.length; i++)
{
if (document.reload_form.mode[i].checked)
return(document.reload_form.mode[i].value);
}
return(\"all\");
}",
Vars++CommonFuncs++"</SCRIPT>";
application_javascript(_)->
"<SCRIPT></SCRIPT>".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create the body i.e the process tree for the applications whose %%
%% name is the second arg in the first tuple %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%args is the list [{_,Appname},{_,Mode},{_Node}]
application_body(Args) when is_list(Args),length(Args) >= 3 ->
App=element(2,lists:nth(1,Args)),
Mode=element(2,lists:nth(2,Args)),
Node=element(2,lists:nth(3,Args)),
"<BODY BGCOLOR=\"FFFFFF\" onLoad=reload_bottom_frame() >"
++ mode_selection(Mode) ++
selected_app_header(App,Node) ++ process_tree(App,Mode,Node)++
"</BODY>";
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% If the pattern above ain't match then something is wrong %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
application_body(_Whatever)->
"Please use the links to the left".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create the part of the process tree page side where the user can %%
%% select the mode the view the tree in. %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
mode_selection(Mode)->
"<FORM NAME=\"reload_form\">\n" ++
"<TABLE>" ++
"<TR>\n" ++
"<!--<TD><INPUT TYPE=\"button\" NAME=\"refresh_but\" VALUE=\"Reload\" onClick=\"reload_page()\">
</TD>\n-->" ++
print_radio_buttons([{"all","All processes"},{"sup_child","Supervised processes"},
{"sup","Only supervisors"}],Mode) ++
"</TR>\n </TABLE>\n" ++
"</FORM>".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Print the radiobuttons. if the mode is the one the current %%
%% radiobutton represent set the one checked %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
print_radio_buttons([],_)->
[];
print_radio_buttons([{Mode,Name}|Rest],Mode)->
"<TD><INPUT TYPE=\"radio\" NAME=\"mode\" CHECKED=\"true\" VALUE=\""++
Mode ++"\" onClick=\"reload_page()\"> " ++Name ++
"</TD>\n" ++ print_radio_buttons(Rest,Mode);
print_radio_buttons([{Mode1,Name}|Rest],Mode)->
"<TD><INPUT TYPE=\"radio\" NAME=\"mode\" VALUE=\""++ Mode1 ++
"\" onClick=\"reload_page()\"> " ++ Name ++
"</TD>\n" ++
print_radio_buttons(Rest,Mode).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% The part that shows the name of the application that the process %%
%% tree represent %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
selected_app_header(App,Node)->
{Year,Mon,Day}=date(),
"<TABLE>
<TR>
<TD>Node:</TD>
<TD>" ++Node ++"</TD>
</TR>
<TR>
<TD>Application:</TD>
<TD>" ++App ++"</TD>
</TR>
<TR>
<TD>Date:</TD>
<TD>" ++ integer_to_list(Day) ++ "/" ++
integer_to_list(Mon) ++" - "++
integer_to_list(Year) ++
"</TD>
</TR>
</TABLE>
<TABLE WIDTH=100%>
<TR>
<TD>
<HR WIDTH=\"80%\">
<!--<FONT SIZE=4>Process tree</FONT>
<HR ALIGN=\"center\" WIDTH=\"80%\">-->
</TD>
</TR>
</TABLE>".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%get the process tree from process_info and build the nested %%
%% unordered list that represent the applications process tree %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
process_tree(App,Mode,Node)->
case process_info:get_processes(list_to_atom(App),
list_to_atom(Mode),
list_to_atom(Node)) of
unknown->
"Unknown application please update application tree";
{Tree,Traced_dict} ->
"<UL>" ++
htmlify_tree(Tree,Traced_dict,1,Node,Mode,App) ++
"</UL>"
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Build each node in the tree and then build its children %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
htmlify_tree({Pid,Childs,Childs2},Proc_tab,N,Node,Mode,App)->
case ets:lookup(Proc_tab,Pid) of
[] when N<3->
print_pid(Pid,Node,Mode,App,notrace)++
htmlify_prim_child(Childs,Proc_tab,N+1,Node,Mode,App) ++
htmlify_sec_child(Childs2);
[_Traced]->
print_pid(Pid,Node,Mode,App,"<FONT SIZE=2 COLOR=\"firebrick\">Stop Trace</FONT>")++
htmlify_prim_child(Childs,Proc_tab,N+1,Node,Mode,App) ++
htmlify_sec_child(Childs2);
[]->
print_pid(Pid,Node,Mode,App,"<FONT SIZE=2>Start Trace</FONT>")++
htmlify_prim_child(Childs,Proc_tab,N+1,Node,Mode,App) ++
htmlify_sec_child(Childs2)
end.
print_pid(Pid,Node,_Mode,_App,notrace)->
"<LI><A TARGET=\"proc_data\" STYLE=\"text-decoration:none; color:blue\" HREF=\"./proc_info?name=" ++ urlify_pid(Pid) ++
"&node="++ Node ++" \" >"++ htmlify_pid(Pid,[]) ++
"</A>";
print_pid([$P,$o,$r,$t|Rest],_Node,_Mode,_App,_TrMode)->
"<LI>" ++ htmlify_pid([$P,$o,$r,$t|Rest],[]);
print_pid(Pid,Node,Mode,App,TrMode)->
"<LI><A TARGET=\"proc_data\" STYLE=\"text-decoration:none; color:blue\" HREF=\"./proc_info?name=" ++
urlify_pid(Pid) ++ "&node="++ Node ++" \" >"++
htmlify_pid(Pid,[]) ++ "</A>"++
"  
<A HREF=\"./trace?app="++App++"&mode="++Mode++
"&node="++Node++"&proc="++urlify_pid(Pid)++"\">
"++TrMode++"</A>".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Change the '<' sign and the '>' sign to the html representation %%
%% of the sign %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
htmlify_pid([60|Pid],New)->
htmlify_pid(Pid,";tl&"++New);
htmlify_pid([139|Pid],New)->
htmlify_pid(Pid,";tl&"++New);
htmlify_pid([62|Pid],New)->
htmlify_pid(Pid,";tg&"++New);
htmlify_pid([155|Pid],New)->
htmlify_pid(Pid,";tg&"++New);
htmlify_pid([Chr|Pid],New)->
htmlify_pid(Pid,[Chr|New]);
htmlify_pid([],New)->
lists:reverse(New).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Change the < and > sign to the representation of the signs in %%
%% the HTTP protocol %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
urlify_pid(Pid) ->
case re:run(Pid,"[<](.*)[>]",[{capture,all_but_first,list}]) of
{match,[PidStr]}->
"%3C"++PidStr++"%3E";
_->
Pid
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Change the < and > sign from the representation of the signs in %%
%% the HTTP protocol %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
unurlify_pid(Pid)->
unurlify_pid(Pid,[]).
unurlify_pid([$%,$3,$C|Rest],New)->
unurlify_pid(Rest,[60|New]);
unurlify_pid([$%,$3,$E|Rest],New)->
unurlify_pid(Rest,[62|New]);
unurlify_pid([Chr|Rest],New)->
unurlify_pid(Rest,[Chr|New]);
unurlify_pid([],New)->
lists:reverse(New).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Make html of the list of primary childs %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
htmlify_prim_child([],_Proc_tab,_N,_Node,_Mode,_App)->
[];
htmlify_prim_child(Childs,Proc_tab,N,Node,Mode,App)->
Fun=fun(Child)->
htmlify_tree(Child,Proc_tab,N,Node,Mode,App)
end,
"<UL>\n" ++ lists:map(Fun,Childs) ++ "</UL>\n".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Make html of hte list whit sedondary childs, they has no childs %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
htmlify_sec_child([])->
[];
htmlify_sec_child(Sec_child_list)->
Htmlify_child=fun(Pid1)->
"<LI><FONT COLOR=\"#FF2222\">" ++ Pid1 ++
"</FONT></LI>\n"
end,
"<UL>" ++ lists:map(Htmlify_child,Sec_child_list) ++ "</UL>\n".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% The Private functions that do the job %%
%% To build the the page that shows process data %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% The function that creates the collects the various part of %%
%% the side that shows information about a specific process, %%
%% Pid_name should be the list representation of a pid %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
process_body(Args) when length(Args)==2->
Pid=element(2,lists:nth(1,Args)),
Node=element(2,lists:nth(2,Args)),
"<BODY BGCOLOR=\"#FFFFFF\">" ++
process_information_table(Pid,Node) ++ "</BODY>";
process_body(_)->
"<BODY BGCOLOR=\"#FFFFFF\">Please dont call this side manually</BODY>".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create the table that shows the name of the pid to show extended %%
%% info about %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Get the table that shows the extended info about a process %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
process_information_table(Pid_name,Node)->
PidID=unurlify_pid(Pid_name),
case catch list_to_pid(PidID) of
Pid when is_pid(Pid) ->
get_process_table(Pid,Node);
_Other ->
io_lib:format("Not a process id ~s",[PidID])
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create the table that shoows the extended info about processes %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
get_process_table(Pid,Node_name) when is_list(Node_name)->
Node=list_to_atom(Node_name),
get_process_table(Pid,Node);
get_process_table(Pid,Node) when is_atom(Node)->
case lists:member(Node,[node()|nodes()]) of
true->
Proc_data=process_info:get_process_data(Pid,Node),
"<TABLE BORDER=1 >
<TR BGCOLOR=\"#8899AA\"><TD COLSPAN=6 ALIGN=\"center\" >
<FONT size=4> Process" ++
htmlify_pid(pid_to_list(Pid),[]) ++ "</FONT>
</TD></TR>" ++
start_process_proc_data(Proc_data) ++
"</TABLE><BR><BR>";
_ ->
"Please try again the Node dont exists"
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% The process information is quite messy tidi it up by creating a %%
%% table that looks like key val %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
start_process_proc_data(Proc_data)->
%%Pic out the special cases the links and the process dict
{Special,Usual}=split_proc_data(Proc_data),
Usual2=append_empty(Usual),
UsualProcData=process_proc_data(Usual2,0),
SpecProcData=process_proc_data(Special),
UsualProcData++SpecProcData.
append_empty(List) when length(List) rem 2 == 0 ->
List;
append_empty(List)->
append_empty(lists:append(List,[empty])).
split_proc_data(Proc_data)->
Spec=lists:map(fun(Key)->
case lists:keysearch(Key,1,Proc_data) of
{value,Data}->
Data;
_ ->
not_included
end
end,[links,dictionary,messages]),
Spec2=clear(Spec,[]),
Usual=lists:filter(fun({Key,_Val})->
case Key of
messages ->
false;
links ->
false;
dictionary ->
false;
_ ->
true
end
end,Proc_data),
{Spec2,Usual}.
clear([],New)->
New;
clear([not_included|Spec],New)->
clear(Spec,New);
clear([Other|Spec],New)->
clear(Spec,[Other|New]).
process_proc_data(Data,3)->
"</TR>"++process_proc_data(Data,0);
process_proc_data([],_N)->
[];
process_proc_data(Data,0)->
"<TR>"++process_proc_data(Data,1);
process_proc_data([empty|Data],N)->
"<TD> </TD><TD> </TD> "++process_proc_data(Data,N+1);
process_proc_data([{current_function,MFA}|Rest],N)->
"<TD NOWRAP=true><FONT SIZE=3><B>Current function:</B></TD><TD><FONT SIZE=3>"++
io_lib:format("~p",[MFA]) ++"</TD>\n " ++
process_proc_data(Rest,N+1);
process_proc_data([{error_handler,Mod}|Rest],N)->
"<TD NOWRAP=\"true\"><B><FONT SIZE=3>Error handler:</B></TD><TD><FONT SIZE=3>"
++ atom_to_list(Mod) ++ "</TD>\n" ++
process_proc_data(Rest,N+1);
process_proc_data([{group_leader,Grp}|Rest],N)->
"<TD NOWRAP=\"true\"><FONT SIZE=3><B>Group leader:</B></TD><TD><FONT SIZE=3>" ++
htmlify_pid(pid_to_list(Grp),[]) ++ "</TD>\n " ++
process_proc_data(Rest,N+1);
process_proc_data([{heap_size,Size}|Rest],N)->
"<TD NOWRAP=\"true\"><FONT SIZE=3><B>Heap size:</B></TD><TD><FONT SIZE=3>"
++ integer_to_list(Size) ++ "</TD>\n " ++
process_proc_data(Rest,N+1);
process_proc_data([{initial_call,MFA}|Rest],N)->
"<TD NOWRAP=\"true\"><FONT SIZE=3><B>Initial call:</B></TD><TD><FONT SIZE=3>"++
io_lib:format("~p",[MFA]) ++"</TD>\n " ++
process_proc_data(Rest,N+1);
process_proc_data([{message_queue_len,Size}|Rest],N)->
"<TD NOWRAP=\"true\"><FONT SIZE=3><B>Message queue length:</B></TD><TD><FONT SIZE=3>" ++
integer_to_list(Size) ++ "</TD>\n " ++
process_proc_data(Rest,N+1);
process_proc_data([{priority,Level}|Rest],N)->
"<TD><FONT SIZE=3><B>Process priority:</B></TD><TD><FONT SIZE=3>" ++
atom_to_list(Level) ++ "</TD>\n " ++
process_proc_data(Rest,N+1);
process_proc_data([{reductions,Number}|Rest],N)->
"<TD ><FONT SIZE=3><B>Number of executed reductions:</B></TD>
<TD><FONT SIZE=3>" ++ integer_to_list(Number) ++ "</TD>\n " ++
process_proc_data(Rest,N+1);
process_proc_data([{registered_name,Name}|Rest],N)->
"<TD NOWRAP=\"true\"><FONT SIZE=3><B>Process Name:</B></TD><TD><FONT SIZE=3>"
++ atom_to_list(Name) ++ "</TD>\n" ++
process_proc_data(Rest,N+1);
process_proc_data([{stack_size,Size}|Rest],N)->
"<TD NOWRAP=\"true\"><FONT SIZE=3><B>Stack size:</B></TD><TD><FONT SIZE=3>"
++ integer_to_list(Size) ++ "</TD>\n " ++
process_proc_data(Rest,N+1);
process_proc_data([{status,Status}|Rest],N)->
"<TD NOWRAP=\"true\"><FONT SIZE=3><B>Process status:</B></TD><TD><FONT SIZE=3>"
++ atom_to_list(Status) ++ "</TD>\n " ++
process_proc_data(Rest,N+1);
process_proc_data([{trap_exit,Boolean}|Rest],N)->
"<TD NOWRAP=\"true\" ><FONT SIZE=3><B>Trap Exit:</B></TD><TD><FONT SIZE=3>"
++ atom_to_list(Boolean) ++ "</TD>\n " ++
process_proc_data(Rest,N+1);
process_proc_data([{Key,Val}|Rest],N)->
"<TD NOWRAP=\"true\" ><FONT SIZE=3><B>" ++ io_lib:write(Key) ++
"</B></TD><TD><FONT SIZE=3>" ++ io_lib:write(Val) ++
"</TD>\n " ++
process_proc_data(Rest,N).
process_proc_data([])->
[];
process_proc_data([{links,List_of_pids}|Rest])->
"<TR><TD NOWRAP=\"true\"><FONT SIZE=3><B>Links:</B></TD><TD COLSPAN=5><FONT SIZE=3>"++ print_links(List_of_pids) ++"</TD></TR>\n " ++
process_proc_data(Rest);
process_proc_data([{messages,Queue}|Rest])->
"<TR><TD NOWRAP=\"true\"><FONT SIZE=3><B>Message Queue:</B></TD><TD COLSPAN=5><FONT SIZE=3>" ++ io_lib:write(Queue) ++ "</TD></TR>\n " ++
process_proc_data(Rest);
process_proc_data([{dictionary,Dict}|Rest])->
"<TR><TD NOWRAP=\"true\"><FONT SIZE=3><B>Process dictionary:</B></TD><TD COLSPAN=5><FONT SIZE=3> </TD></TR>\n " ++
get_dictionary_data(Dict) ++
process_proc_data(Rest).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% In the process info there are the links to other processes print %%
%% this pid %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
print_links(Pids)->
print_links(Pids,[]).
print_links([],Links)->
htmlify_pid(Links,[]);
print_links([Pid],Links) when is_pid(Pid) ->
print_links([],Links ++ pid_to_list(Pid));
print_links([Pid],Links) when is_port(Pid) ->
print_links([],Links ++ erlang:port_to_list(Pid));
print_links([Pid|Rest],Links) when is_pid(Pid) ->
print_links(Rest,Links ++ pid_to_list(Pid) ++ ", ");
print_links([Pid|Rest],Links) when is_port(Pid) ->
print_links(Rest,Links ++ erlang:port_to_list(Pid) ++ ", ").
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Fix the data in the process dictionary %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
get_dictionary_data([])->
[];
get_dictionary_data([{Key,Val}|Dict])->
FormatedVal=add_space(htmlify_pid(lists:flatten(fix_type(Val)),[])),
"<TR><TD><FONT SIZE=3>" ++
htmlify_pid(lists:flatten(fix_type(Key)),[]) ++
"</TD><TD COLSPAN=5><FONT SIZE=3>" ++
FormatedVal++ "</TD></TR>\n" ++
get_dictionary_data(Dict).
add_space(List)->
add_space(List,0,[]).
add_space([],_Len,New) ->
lists:reverse(New);
add_space([Char|Rest],Len,New)when Len<50 ->
add_space(Rest,Len+1,[Char|New]);
add_space([$\,|Rest],_Len,New) ->
add_space(Rest,0,[$\ ,$,|New]);
add_space([Char|Rest],Len,New) ->
add_space(Rest,Len+1,[Char|New]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Interpret the type of the data and make it to a list %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fix_type(Val) when is_atom(Val) ->
atom_to_list(Val);
fix_type(Val) when is_float(Val) ->
float_to_list(Val);
fix_type(Val) when is_integer(Val) ->
integer_to_list(Val);
fix_type(Val) when is_list(Val) ->
case io_lib:printable_list(Val) of
true->
case Val of
[]->
io_lib:write(Val);
_->
Val
end;
_->
io_lib:write(Val)
end;
fix_type(Val) when is_pid(Val) ->
pid_to_list(Val);
fix_type(Val) when is_port(Val) ->
erlang:port_to_list(Val);
fix_type(Val) when is_tuple(Val) ->
io_lib:write(Val);
fix_type(_Val) ->
[].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% The Private functions that send the trace signal to the process %%
%% thats the 4 member of the Arg list %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
send_trace(Args)when length(Args)>=4->
{_,Proc}=lists:nth(4,Args),
Pid2=unurlify_pid(Proc),
process_info:send_trace(Pid2);
send_trace(_Args)->
arg_error.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Private functions that prints the application environment %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
application_env_body(Args)when length(Args)>=2 ->
App=element(2,lists:nth(1,Args)),
Node=element(2,lists:nth(2,Args)),
"<SCRIPT>
function reload_bottom_frame()
{parent.proc_data.location.href=\"/appmon/blank.html\";}
</SCRIPT>
<BODY BGCOLOR=\"#FFFFFF\" onLoad=reload_bottom_frame()>"
++ application_env_table(App,Node) ++ "</BODY>";
application_env_body(_)->
"<BODY BGCOLOR=\"#FFFFFF\">Please dont call this side manually</BODY>".
application_env_table(App,Node)->
case process_info:get_application_keys(list_to_atom(App),
list_to_atom(Node)) of
{ok,List}->
"<TABLE BORDER=1>" ++ application_env_head(App,Node) ++
print_key_list(List,[]) ++ "</TABLE>";
_ ->
"Please try again,something went wrong"
end.
application_env_head(App,Node)->
"<TR BGCOLOR=\"#8899AA\"><TD ALIGN=\"center\" COLSPAN=3>
<FONT SIZE=6>" ++ App ++ "@" ++ Node ++ "</FONT>\n
</TD></TR>
<TR><TD COLSPAN=3> </TD></TR>
<TR BGCOLOR=\"#8899AA\">
<TD><B>Key</B></TD><TD><B>Val/Sec. key</B></TD><TD><B>Sec. Val</B></TD>
</TR>".
print_key_list([],Result)->
Result;
print_key_list([{application,Name}|Rest],Result)->
print_key_list(Rest,Result ++ print_key("Application name :",Name));
print_key_list([{description,Desc}|Rest],Result)->
print_key_list(Rest,Result ++ print_key("Description :",Desc));
print_key_list([{vsn,Ver}|Rest],Result)->
print_key_list(Rest,Result ++ print_key("Version :",Ver));
print_key_list([{id,Id}|Rest],Result)->
print_key_list(Rest,Result ++ print_key("ID:",fix_type(Id)));
print_key_list([{modules,Mods}|Rest],Result)->
print_key_list(Rest,Result ++ print_key("Modules:"," ") ++
print_secondary_list(Mods,[]));
print_key_list([{maxP,Max}|Rest],Result)->
print_key_list(Rest,Result ++
print_key("Max nr of processes",fix_type(Max)));
print_key_list([{maxT,Max}|Rest],Result)->
print_key_list(Rest,Result ++
print_key("Max running sec:",fix_type(Max)));
print_key_list([{registered,Names}|Rest],Result)->
print_key_list(Rest,Result ++
print_key("Registered names:"," ") ++
print_secondary_list(Names,[]));
print_key_list([{applications,Apps}|Rest],Result)->
print_key_list(Rest,Result ++ print_key("Depends on:"," ") ++
print_secondary_list(Apps,[]));
print_key_list([{included_applications,Inc_apps}|Rest],Result)->
print_key_list(Rest,Result ++
print_key("Included applications:",
fix_type(Inc_apps)));
print_key_list([{env,Env}|Rest],Result)->
print_key_list(Rest,Result ++
print_key("Environment:",fix_type(Env)));
print_key_list([{mod,Mod}|Rest],Result)->
print_key_list(Rest,Result ++
print_key("Application callback mod:",
fix_type(Mod)));
print_key_list([{start_phases,Phase_arg}|Rest],Result)->
print_key_list(Rest,Result ++
print_key("Application callback mod:",
fix_type(Phase_arg)));
print_key_list([_|Rest],Result)->
print_key_list(Rest,Result).
print_key(Label,Val)->
"<TR>
<TD><B>" ++ Label ++ "</B></TD><TD>" ++ Val ++
"</TD><TD> </TD>
</TR>".
print_key2(Label,Val)->
"<TR>
<TD> </TD><TD>" ++ Label ++ "</TD><TD>" ++ Val ++ "</TD>
</TR>".
print_secondary_list([],Result)->
Result;
print_secondary_list([{Mod,Ver}|Rest],Result) ->
print_secondary_list(Rest,Result ++
print_key2(fix_type(Mod),fix_type(Ver)));
print_secondary_list([Mod|Rest],Result) ->
print_secondary_list(Rest,Result ++
print_key2(fix_type(Mod)," ")).