1%%%  This program is free software; you can redistribute it and/or modify
2%%%  it under the terms of the GNU General Public License as published by
3%%%  the Free Software Foundation; either version 2 of the License, or
4%%%  (at your option) any later version.
5%%%
6%%%  This program is distributed in the hope that it will be useful,
7%%%  but WITHOUT ANY WARRANTY; without even the implied warranty of
8%%%  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9%%%  GNU General Public License for more details.
10%%%
11%%%  You should have received a copy of the GNU General Public License
12%%%  along with this program; if not, write to the Free Software
13%%%  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
14%%%
15%%%  In addition, as a special exception, you have the permission to
16%%%  link the code of this program with any library released under
17%%%  the EPL license and distribute linked combinations including
18%%%  the two; the MPL (Mozilla Public License), which EPL (Erlang
19%%%  Public License) is based on, is included in this exception.
20
21%%%-------------------------------------------------------------------
22%%% File    : ts_session_cache.erl
23%%% Author  : Nicolas Niclausse <nniclausse@niclux.org>
24%%% Description : cache sessions request from ts_config_server
25%%%
26%%% Created :  2 Dec 2003 by Nicolas Niclausse <nicolas@niclux.org>
27%%%-------------------------------------------------------------------
28
29
30-module(ts_mon_cache).
31
32-behaviour(gen_server).
33%%--------------------------------------------------------------------
34%% Include files
35%%--------------------------------------------------------------------
36
37%%--------------------------------------------------------------------
38%% External exports
39-export([start/0, add/1, add_match/2, dump/1]).
40
41%% gen_server callbacks
42-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
43         code_change/3]).
44
45
46-record(state, {
47          stats=[],         % cache stats msgs
48          transactions=[],  % cache transaction stats msgs
49          pages=[],         % cache pages stats msgs
50          requests=[],      % cache requests stats msgs
51          connections=[],   % cache connect stats msgs
52          match=[],         % cache match logs
53          protocol=[],      % cache dump=protocol data
54          sum               % cache sum stats msgs
55         }).
56
57
58-include("ts_config.hrl").
59
60%%====================================================================
61%% External functions
62%%====================================================================
63%%--------------------------------------------------------------------
64%% Function: start_link/0
65%% Description: Starts the server
66%%--------------------------------------------------------------------
67start() ->
68    ?LOG("Starting~n",?INFO),
69    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
70
71%%--------------------------------------------------------------------
72%% Function: add/1
73%% Description: Add stats data. Will be accumulated sent periodically
74%%              to ts_mon
75%%--------------------------------------------------------------------
76add(Data) ->
77    gen_server:cast(?MODULE, {add, Data}).
78
79%% @spec add_match(Data::list(),{UserId::integer(),SessionId::integer(),RequestId::integer(),
80%%                  TimeStamp::tuple(),Transactions::list(),Name::atom()}) -> ok;
81%%                (Data::list(),{UserId::integer(),SessionId::integer(),RequestId::integer(),
82%%                  TimeStamp::tuple(),Bin::list(),Transactions::list(),Name::atom()}) -> ok.
83add_match(Data,{UserId,SessionId,RequestId,Tr,Name}) ->
84    add_match(Data,{UserId,SessionId,RequestId,[],Tr,Name});
85add_match(Data,{UserId,SessionId,RequestId,Bin,Tr,Name}) ->
86    TimeStamp=?TIMESTAMP,
87    add_match(Data,{UserId,SessionId,RequestId,TimeStamp,Bin,Tr,Name});
88add_match(Data=[Head|_],{UserId,SessionId,RequestId,TimeStamp,Bin,Tr,Name}) ->
89    put(last_match,Head),
90    gen_server:cast(?MODULE, {add_match, Data, {UserId,SessionId,RequestId,TimeStamp,Bin,Tr,Name}}).
91
92
93%% @spec dump({Type, Who, What}) -> ok @end
94dump({none, _, _})       ->  skip;
95dump({_Type, Who, What}) ->
96    gen_server:cast(?MODULE, {dump, Who, ?TIMESTAMP, What}).
97
98%%====================================================================
99%% Server functions
100%%====================================================================
101
102%%--------------------------------------------------------------------
103%% Function: init/1
104%% Description: Initiates the server
105%% Returns: {ok, State}          |
106%%          {ok, State, Timeout} |
107%%          ignore               |
108%%          {stop, Reason}
109%%--------------------------------------------------------------------
110init([]) ->
111    erlang:start_timer(?CACHE_DUMP_STATS_INTERVAL, self(), dump_stats ),
112    {ok, #state{sum=dict:new()}}.
113
114%%--------------------------------------------------------------------
115%% Function: handle_call/3
116%% Description: Handling call messages
117%% Returns: {reply, Reply, State}          |
118%%          {reply, Reply, State, Timeout} |
119%%          {noreply, State}               |
120%%          {noreply, State, Timeout}      |
121%%          {stop, Reason, Reply, State}   | (terminate/2 is called)
122%%          {stop, Reason, State}            (terminate/2 is called)
123%%--------------------------------------------------------------------
124handle_call(_Request, _From, State) ->
125    Reply = ok,
126    {reply, Reply, State}.
127
128%%--------------------------------------------------------------------
129%% Function: handle_cast/2
130%% Description: Handling cast messages
131%% Returns: {noreply, State}          |
132%%          {noreply, State, Timeout} |
133%%          {stop, Reason, State}            (terminate/2 is called)
134%%--------------------------------------------------------------------
135handle_cast({add, Data}, State) when is_list(Data) ->
136    LastState = lists:foldl(fun(NewData,NewState)->
137                        update_stats(NewData,NewState)
138                end, State, Data),
139    {noreply, LastState };
140handle_cast({add, Data}, State) when is_tuple(Data) ->
141    {noreply,update_stats(Data, State)};
142handle_cast({add_match, Data=[First|_Tail],{UserId,SessionId,RequestId,TimeStamp,Bin,Tr,Name}},
143            State=#state{stats=List, match=MatchList})->
144    NewMatchList=lists:append([{UserId,SessionId,RequestId,TimeStamp,First, Bin, Tr,Name}], MatchList),
145    {noreply, State#state{stats = lists:append(Data, List), match = NewMatchList}};
146
147handle_cast({dump, Who, When, What}, State=#state{protocol=Cache}) ->
148    Log = io_lib:format("~w;~w;~s~n",[ts_utils:time2sec_hires(When),Who,What]),
149    {noreply, State#state{protocol=[Log|Cache]}};
150
151handle_cast(_Msg, State) ->
152    {noreply, State}.
153
154%%--------------------------------------------------------------------
155%% Function: handle_info/2
156%% Description: Handling all non call/cast messages
157%% Returns: {noreply, State}          |
158%%          {noreply, State, Timeout} |
159%%          {stop, Reason, State}            (terminate/2 is called)
160%%--------------------------------------------------------------------
161handle_info({timeout, _Ref, dump_stats}, State =#state{protocol=ProtocolData, stats= Stats, match=MatchList}) ->
162    Fun = fun(Key,Val, Acc) -> [{sum,Key,Val}| Acc] end,
163    NewStats=dict:fold(Fun, Stats, State#state.sum),
164    ts_stats_mon:add(NewStats),
165    ts_stats_mon:add(State#state.requests,request),
166    ts_stats_mon:add(State#state.connections,connect),
167    ts_stats_mon:add(State#state.transactions,transaction),
168    ts_stats_mon:add(State#state.pages,page),
169    ts_mon:dump({cached, list_to_binary(lists:reverse(ProtocolData))}),
170    ts_match_logger:add(MatchList),
171    erlang:start_timer(?CACHE_DUMP_STATS_INTERVAL, self(), dump_stats ),
172    {noreply, State#state{protocol=[],stats=[],match=[],pages=[],requests=[],transactions=[],connections=[],sum=dict:new()}};
173
174handle_info(_Info, State) ->
175    {noreply, State}.
176
177%%--------------------------------------------------------------------
178%% Function: terminate/2
179%% Description: Shutdown the server
180%% Returns: any (ignored by gen_server)
181%%--------------------------------------------------------------------
182terminate(Reason, _State) ->
183    ?LOGF("Die ! (~p)~n",[Reason],?ERR),
184    ok.
185
186%%--------------------------------------------------------------------
187%% Func: code_change/3
188%% Purpose: Convert process state when code is changed
189%% Returns: {ok, NewState}
190%%--------------------------------------------------------------------
191code_change(_OldVsn, State, _Extra) ->
192    {ok, State}.
193
194
195update_stats({sample, request, Val}, State=#state{requests=L}) ->
196    State#state{requests=lists:append([Val],L)};
197
198update_stats({sample, page, Val}, State=#state{pages=L}) ->
199    State#state{pages=lists:append([Val],L)};
200
201update_stats({sample, connect, Val}, State=#state{connections=L}) ->
202    State#state{connections=lists:append([Val],L)};
203
204update_stats(S={sample, _Type, _}, State=#state{transactions=L}) ->
205    State#state{transactions=lists:append([S],L)};
206
207update_stats({sum, Type, Val}, State=#state{sum=Sum}) ->
208    NewSum=dict:update_counter(Type,Val,Sum),
209    State#state{sum=NewSum};
210update_stats({count, Type}, State=#state{sum=Sum}) ->
211    NewSum=dict:update_counter(Type,1,Sum),
212    State#state{sum=NewSum};
213update_stats(Data, State=#state{stats=L})  when is_tuple(Data)->
214    State#state{stats=lists:append([Data],L)}.
215
216
217