1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2012-2017. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20%%---------------------------------------------------------------------- 21%% CT hook for logging of connections. 22%% 23%% HookOptions can be hardcoded in the test suite: 24%% 25%% suite() -> 26%% [{ct_hooks, [{cth_conn_log, 27%% [{conn_mod(),hook_options()}]}]}]. 28%% 29%% or specified in a configuration file: 30%% 31%% {ct_conn_log,[{conn_mod(),hook_options()}]}. 32%% 33%% The conn_mod() is the common test module implementing the protocol, 34%% e.g. ct_netconfc, ct_telnet, etc. This module must log by calling 35%% 36%% error_logger:info_report(ConnLogInfo,Data). 37%% ConnLogInfo = #conn_log{} | {ct_connection,Action,ConnName} 38%% Action = open | close | send | recv | term() 39%% ConnName = atom() - The 'KeyOrName' argument used when opening the connection 40%% 41%% ct_conn_log_h will print to html log or separate file (depending on 42%% log_type() option). conn_mod() must implement and export 43%% 44%% format_data(log_type(), Data). 45%% 46%% If logging to separate file, ct_conn_log_h will also log error 47%% reports which are witten like this: 48%% 49%% error_logger:error_report([{ct_connection,ConnName} | Report]). 50%% 51%%---------------------------------------------------------------------- 52-module(cth_conn_log). 53 54-include_lib("common_test/include/ct.hrl"). 55 56-export([init/2, 57 pre_init_per_testcase/4, 58 post_end_per_testcase/5]). 59 60%%---------------------------------------------------------------------- 61%% Type declarations 62%%---------------------------------------------------------------------- 63-type hook_options() :: ct:conn_log_options(). 64-type log_type() :: ct:conn_log_type(). 65-type conn_mod() :: ct:conn_log_mod(). 66%%---------------------------------------------------------------------- 67 68-spec init(Id, HookOpts) -> Result when 69 Id :: term(), 70 HookOpts :: hook_options(), 71 Result :: {ok,[{conn_mod(),{log_type(),[ct:key_or_name()]}}]}. 72init(_Id, HookOpts) -> 73 ConfOpts = ct:get_config(ct_conn_log,[]), 74 {ok,merge_log_info(ConfOpts,HookOpts)}. 75 76merge_log_info([{Mod,ConfOpts}|ConfList],HookList) -> 77 {Opts,HookList1} = 78 case lists:keytake(Mod,1,HookList) of 79 false -> 80 {ConfOpts,HookList}; 81 {value,{_,HookOpts},HL1} -> 82 {ConfOpts ++ HookOpts, HL1} % ConfOpts overwrites HookOpts! 83 end, 84 [{Mod,get_log_opts(Mod,Opts)} | merge_log_info(ConfList,HookList1)]; 85merge_log_info([],HookList) -> 86 [{Mod,get_log_opts(Mod,Opts)} || {Mod,Opts} <- HookList]. 87 88get_log_opts(Mod,Opts) -> 89 DefaultLogType = if Mod == ct_telnet -> raw; 90 true -> html 91 end, 92 LogType = proplists:get_value(log_type,Opts,DefaultLogType), 93 Hosts = proplists:get_value(hosts,Opts,[]), 94 {LogType,Hosts}. 95 96pre_init_per_testcase(_Suite,TestCase,Config,CthState) -> 97 Logs = 98 lists:map( 99 fun({ConnMod,{LogType,Hosts}}) -> 100 ct_util:set_testdata({{?MODULE,ConnMod},LogType}), 101 case LogType of 102 LogType when LogType==raw; LogType==pretty -> 103 Dir = ?config(priv_dir,Config), 104 TCStr = atom_to_list(TestCase), 105 ConnModStr = atom_to_list(ConnMod), 106 DefLogName = TCStr ++ "-" ++ ConnModStr ++ ".txt", 107 DefLog = filename:join(Dir,DefLogName), 108 Ls = [{Host, 109 filename:join(Dir,TCStr ++ "-"++ 110 atom_to_list(Host) ++ "-" ++ 111 ConnModStr ++ 112 ".txt")} 113 || Host <- Hosts] 114 ++[{default,DefLog}], 115 Str = 116 "<table borders=1>" 117 "<b>" ++ ConnModStr ++ " logs:</b>\n" ++ 118 [io_lib:format( 119 "<tr><td>~tp</td><td><a href=\"~ts\">~ts</a>" 120 "</td></tr>", 121 [S,ct_logs:uri(L),filename:basename(L)]) 122 || {S,L} <- Ls] ++ 123 "</table>", 124 ct:log(Str,[],[no_css]), 125 {ConnMod,{LogType,Ls}}; 126 _ -> 127 {ConnMod,{LogType,[]}} 128 end 129 end, 130 CthState), 131 132 GL = group_leader(), 133 Update = 134 fun(Init) when Init == undefined; Init == [] -> 135 error_logger:add_report_handler(ct_conn_log_h,{GL,Logs}), 136 [TestCase]; 137 (PrevUsers) -> 138 error_logger:info_report(update,{GL,Logs}), 139 receive 140 {updated,GL} -> 141 [TestCase|PrevUsers] 142 after 143 5000 -> 144 {error,no_response} 145 end 146 end, 147 ct_util:update_testdata(?MODULE, Update, [create]), 148 {Config,CthState}. 149 150post_end_per_testcase(_Suite,TestCase,_Config,Return,CthState) -> 151 Update = 152 fun(PrevUsers) -> 153 case lists:delete(TestCase, PrevUsers) of 154 [] -> 155 '$delete'; 156 PrevUsers1 -> 157 PrevUsers1 158 end 159 end, 160 case ct_util:update_testdata(?MODULE, Update) of 161 deleted -> 162 _ = [ct_util:delete_testdata({?MODULE,ConnMod}) || 163 {ConnMod,_} <- CthState], 164 error_logger:delete_report_handler(ct_conn_log_h); 165 {error,no_response} -> 166 exit({?MODULE,no_response_from_logger}); 167 _PrevUsers -> 168 ok 169 end, 170 {Return,CthState}. 171 172