<commit_before>%%----------------------------------------------------------------------
%%% File    : yaws_log.erl
%%% Author  : <NAME> <<EMAIL>>
%%% Purpose : 
%%% Created : 26 Jan 2002 by <NAME> <<EMAIL>>
%%%----------------------------------------------------------------------

-module(yaws_log).
-author('<EMAIL>').
-include_lib("kernel/include/file.hrl").
-compile(export_all).
%%-export([Function/Arity, ...]).

-behaviour(gen_server).

%% External exports
-export([start_link/0, uid_change/1]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).

-include_lib("yaws/include/yaws.hrl").
-include_lib("yaws/include/yaws_api.hrl").
-include("yaws_debug.hrl").


-record(state, {
	  running,
	  dir,
	  now,
	  ack,
	  tracefd,
	  tty_trace = false,
	  alogs =  []}).
		

%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start_link() ->
    gen_server:start_link({local, yaws_log}, yaws_log, [], []).

accesslog(ServerName, Ip, Req, Status, Length) ->
    gen_server:cast(?MODULE, {access, ServerName, Ip, Req, 
			      Status, Length}).
errlog(F, A) ->     
    gen_server:cast(?MODULE, {errlog, F, A}).
infolog(F, A) ->     
    gen_server:cast(?MODULE, {infolog, F, A}).
sync_errlog(F, A) ->     
    gen_server:call(?MODULE, {errlog, F, A}).
setdir(Dir, Sconfs) ->
    gen_server:call(?MODULE, {setdir, Dir, Sconfs}).

open_trace(What) ->
    gen_server:call(?MODULE, {open_trace, What}).
trace_traffic(ServerOrClient , Data) ->
    gen_server:cast(?MODULE, {trace, ServerOrClient, Data}).



uid_change(GC) ->
    case GC#gconf.username of
	undefined ->  
	    %% uid change feature not used
	    ok;
	Uname when GC#gconf.uid /= "0" ->
	    %% we're not root and can't do anything about the sitiation
	    ok;
	Uname ->
	    %% let's change the owner of logdir
	    %% as well as all the files in that directory
	    Int = list_to_integer(os:cmd("uname -u " ++ Uname) -- [10]),
	    S = gen_server:call(?MODULE, state),
	    {ok, Files} = file:list_dir(S#state.dir),
	    ok = file:change_owner(S#state.dir, Int),
	    lists:foreach(
	      fun(F) ->
		      NF = S#state.dir ++ [$/ | F],
		      ok = file:change_owner(NF, Int)
	      end, Files)
    end.


%%%----------------------------------------------------------------------
%%% Callback functions from gen_server
%%%----------------------------------------------------------------------

%%----------------------------------------------------------------------
%% Func: init/1
%% Returns: {ok, State}          |
%%          {ok, State, Timeout} |
%%          ignore               |
%%          {stop, Reason}
%%----------------------------------------------------------------------
init([]) ->
    {ok, #state{running = false, now = fmtnow(), ack = []}}.

%%----------------------------------------------------------------------
%% Func: handle_call/3
%% Returns: {reply, Reply, State}          |
%%          {reply, Reply, State, Timeout} |
%%          {noreply, State}               |
%%          {noreply, State, Timeout}      |
%%          {stop, Reason, Reply, State}   | (terminate/2 is called)
%%          {stop, Reason, State}            (terminate/2 is called)
%%----------------------------------------------------------------------
handle_call({setdir, Dir, Sconfs}, _From, State) 
  when State#state.running == false ->
    ?Debug("setdir ~s~n~p", [Dir, State#state.ack]),

    error_logger:logfile({open,filename:join([Dir, "report.log"])}),
    SCs = lists:flatten(Sconfs),
    
    L = lists:zf(
	  fun(SC) ->
		  A = filename:join([Dir, SC#sconf.servername ++ ".access"]),
		  case file:open(A, [write, raw, append]) of
		      {ok, Fd} ->
			  {true, {SC#sconf.servername, Fd, A}};
		      _Err ->
			  error_logger:format("Cannot open ~p",[A]),
			  false
		  end
	  end, SCs),

    lists:foreach(fun({err, F, A}) ->
			  error_logger:format(F, A);
		     ({info, F, A}) ->
			  error_logger:info_msg(F,A);
		     ({access, ServerName, Ip, Req, Status, Length}) ->
			  do_alog(ServerName, Ip, Req, Status, Length, State)
		  end, State#state.ack),

    S2 = State#state{running = true,
		     ack = [],
		     dir  = Dir,
		     now = fmtnow(),
		     alogs = L},

    yaws:ticker(3000, secs3),
    yaws:ticker(60 * 1000, minute),

    {reply, ok, S2};



%% We can't ever change logdir, we can however
%% change logging opts for various servers

handle_call({setdir, _DirIgnore, Sconfs}, _From, State) 
  when State#state.running == true ->

    Dir = State#state.dir,
    
    %% close all files
    lists:map(
      fun({_Sname, FD, _Filename}) ->
	      file:close(FD)
      end, State#state.alogs),
    SCs = lists:flatten(Sconfs),
    
    %% reopen logfiles
    L = lists:zf(
	  fun(SC) ->
		  A = filename:join([Dir, SC#sconf.servername ++ ".access"]),
		  case file:open(A, [write, raw, append]) of
		      {ok, Fd} ->
			  {true, {SC#sconf.servername, Fd, A}};
		      _Err ->
			  error_logger:format("Cannot open ~p",[A]),
			  false
		  end
	  end, SCs),
    S2 = State#state{running = true,
		     ack = [],
		     dir  = Dir,
		     now = fmtnow(),
		     alogs = L},
    
    {reply, ok, S2};


handle_call({errlog, F, A}, _From, State) when State#state.running == true ->
    error_logger:format(F, A),
    {reply, ok, State};
handle_call({errlog, F, A}, _From, State) when State#state.running == false ->
    {reply, ok, State#state{ack = [{err, F, A} | State#state.ack]}};

handle_call({open_trace, What}, _From, State) ->
    case State#state.tracefd of
	undefined ->
	    ok;
	Fd0 ->
	    file:close(Fd0)
    end,
    F = lists:concat(["trace.", What]),
    case file:open(filename:join([State#state.dir, F]),[write, raw]) of
	{ok, Fd} ->
	    {reply, ok, State#state{tracefd = Fd}};
	Err ->
	    {reply,  Err, State}
    end;

handle_call({trace_tty, What}, _From, State) ->
    {reply, ok, State#state{tty_trace = What}};
handle_call(state, _From, State) ->
    {reply, State, State}.







%%----------------------------------------------------------------------
%% Func: handle_cast/2
%% Returns: {noreply, State}          |
%%          {noreply, State, Timeout} |
%%          {stop, Reason, State}            (terminate/2 is called)
%%----------------------------------------------------------------------
handle_cast({errlog, F, A}, State) when State#state.running == true ->
    error_logger:format(F, A),
    {noreply, State};
handle_cast({errlog, F, A}, State) when State#state.running == false ->
    {noreply, State#state{ack = [{err, F, A} | State#state.ack]}};


handle_cast({infolog, F, A}, State) when State#state.running == true ->
    error_logger:info_report(F, A),
    {noreply, State};
handle_cast({infolog, F, A}, State) when State#state.running == false ->
    {noreply, State#state{ack = [{info, F, A} | State#state.ack]}};



handle_cast({access, ServerName, Ip, Req, Status, Length}, State) ->
    case State#state.running of 
	true ->
	    do_alog(ServerName, Ip, Req, Status, Length, State),
	    {noreply, State};
	false ->
	    {noreply, State#state{ack = [{access, ServerName, Ip, Req, 
					  Status,  Length} |
					 State#state.ack]}}
    end;
handle_cast({trace, from_server, Data}, State) ->
    Str = ["*** SRV -> CLI *** ", Data],
    file:write(State#state.tracefd, Str),
    tty_trace(Str, State),
    {noreply, State};
handle_cast({trace, from_client, Data}, State) ->
    Str = ["*** CLI -> SRV *** ", Data],
    file:write(State#state.tracefd, Str),
    tty_trace(Str, State),
    {noreply, State}.



do_alog(ServerName, Ip, Req, Status, Length, State) ->
    case lists:keysearch(ServerName, 1, State#state.alogs) of
	{value, {_, FD, _}} ->
	    I = fmt_alog(State#state.now, Ip, Req, Status,  Length),
	    file:write(FD, I),
	    tty_trace(I, State);
	_ ->
	    false
    end.

<commit_msg>""<commit_after>%%----------------------------------------------------------------------
%%% File    : yaws_log.erl
%%% Author  : <NAME> <<EMAIL>>
%%% Purpose : 
%%% Created : 26 Jan 2002 by <NAME> <<EMAIL>>
%%%----------------------------------------------------------------------

-module(yaws_log).
-author('<EMAIL>').
-include_lib("kernel/include/file.hrl").
-compile(export_all).
%%-export([Function/Arity, ...]).

-behaviour(gen_server).

%% External exports
-export([start_link/0, uid_change/1]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).

-include_lib("yaws/include/yaws.hrl").
-include_lib("yaws/include/yaws_api.hrl").
-include("yaws_debug.hrl").


-record(state, {
	  running,
	  dir,
	  now,
	  ack,
	  tracefd,
	  tty_trace = false,
	  alogs =  []}).
		

%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start_link() ->
    gen_server:start_link({local, yaws_log}, yaws_log, [], []).

accesslog(ServerName, Ip, Req, Status, Length) ->
    gen_server:cast(?MODULE, {access, ServerName, Ip, Req, 
			      Status, Length}).
errlog(F, A) ->     
    gen_server:cast(?MODULE, {errlog, F, A}).
infolog(F, A) ->     
    gen_server:cast(?MODULE, {infolog, F, A}).
sync_errlog(F, A) ->     
    gen_server:call(?MODULE, {errlog, F, A}).
setdir(Dir, Sconfs) ->
    gen_server:call(?MODULE, {setdir, Dir, Sconfs}).

open_trace(What) ->
    gen_server:call(?MODULE, {open_trace, What}).
trace_traffic(ServerOrClient , Data) ->
    gen_server:cast(?MODULE, {trace, ServerOrClient, Data}).



uid_change(GC) ->
    case GC#gconf.username of
	undefined ->  
	    %% uid change feature not used
	    ok;
	Uname when GC#gconf.uid /= "0" ->
	    %% we're not root and can't do anything about the sitiation
	    ok;
	Uname ->
	    %% let's change the owner of logdir
	    %% as well as all the files in that directory
	    Int = list_to_integer(os:cmd("uname -u " ++ Uname) -- [10]),
	    S = gen_server:call(?MODULE, state),
	    {ok, Files} = file:list_dir(S#state.dir),
	    ok = file:change_owner(S#state.dir, Int),
	    lists:foreach(
	      fun(F) ->
		      NF = S#state.dir ++ [$/ | F],
		      ok = file:change_owner(NF, Int)
	      end, Files)
    end.


%%%----------------------------------------------------------------------
%%% Callback functions from gen_server
%%%----------------------------------------------------------------------

%%----------------------------------------------------------------------
%% Func: init/1
%% Returns: {ok, State}          |
%%          {ok, State, Timeout} |
%%          ignore               |
%%          {stop, Reason}
%%----------------------------------------------------------------------
init([]) ->
    {ok, #state{running = false, now = fmtnow(), ack = []}}.

%%----------------------------------------------------------------------
%% Func: handle_call/3
%% Returns: {reply, Reply, State}          |
%%          {reply, Reply, State, Timeout} |
%%          {noreply, State}               |
%%          {noreply, State, Timeout}      |
%%          {stop, Reason, Reply, State}   | (terminate/2 is called)
%%          {stop, Reason, State}            (terminate/2 is called)
%%----------------------------------------------------------------------
handle_call({setdir, Dir, Sconfs}, _From, State) 
  when State#state.running == false ->
    ?Debug("setdir ~s~n~p", [Dir, State#state.ack]),

    error_logger:logfile({open,filename:join([Dir, "report.log"])}),
    SCs = lists:flatten(Sconfs),
    
    L = lists:zf(
	  fun(SC) ->
		  A = filename:join([Dir, SC#sconf.servername ++ ".access"]),
		  case file:open(A, [write, raw, append]) of
		      {ok, Fd} ->
			  {true, {SC#sconf.servername, Fd, A}};
		      _Err ->
			  error_logger:format("Cannot open ~p",[A]),
			  false
		  end
	  end, SCs),

    lists:foreach(fun({err, F, A}) ->
			  error_logger:format(F, A);
		     ({info, F, A}) ->
			  error_logger:info_msg(F,A);
		     ({access, ServerName, Ip, Req, Status, Length}) ->
			  do_alog(ServerName, Ip, Req, Status, Length, State)
		  end, State#state.ack),

    S2 = State#state{running = true,
		     ack = [],
		     dir  = Dir,
		     now = fmtnow(),
		     alogs = L},

    yaws:ticker(3000, secs3),
    yaws:ticker(60 * 1000, minute),

    {reply, ok, S2};



%% We can't ever change logdir, we can however
%% change logging opts for various servers

handle_call({setdir, _DirIgnore, Sconfs}, _From, State) 
  when State#state.running == true ->

    Dir = State#state.dir,
    
    %% close all files
    lists:map(
      fun({_Sname, FD, _Filename}) ->
	      file:close(FD)
      end, State#state.alogs),
    SCs = lists:flatten(Sconfs),
    
    %% reopen logfiles
    L = lists:zf(
	  fun(SC) ->
		  A = filename:join([Dir, SC#sconf.servername ++ ".access"]),
		  case file:open(A, [write, raw, append]) of
		      {ok, Fd} ->
			  {true, {SC#sconf.servername, Fd, A}};
		      _Err ->
			  error_logger:format("Cannot open ~p",[A]),
			  false
		  end
	  end, SCs),
    S2 = State#state{running = true,
		     ack = [],
		     dir  = Dir,
		     now = fmtnow(),
		     alogs = L},
    
    {reply, ok, S2};


handle_call({errlog, F, A}, _From, State) when State#state.running == true ->
    error_logger:format(F, A),
    {reply, ok, State};
handle_call({errlog, F, A}, _From, State) when State#state.running == false ->
    {reply, ok, State#state{ack = [{err, F, A} | State#state.ack]}};

handle_call({open_trace, What}, _From, State) ->
    case State#state.tracefd of
	undefined ->
	    ok;
	Fd0 ->
	    file:close(Fd0)
    end,
    F = lists:concat(["trace.", What]),
    case file:open(filename:join([State#state.dir, F]),[write, raw]) of
	{ok, Fd} ->
	    {reply, ok, State#state{tracefd = Fd}};
	Err ->
	    {reply,  Err, State}
    end;

handle_call({trace_tty, What}, _From, State) ->
    {reply, ok, State#state{tty_trace = What}};
handle_call(state, _From, State) ->
    {reply, State, State}.







%%----------------------------------------------------------------------
%% Func: handle_cast/2
%% Returns: {noreply, State}          |
%%          {noreply, State, Timeout} |
%%          {stop, Reason, State}            (terminate/2 is called)
%%----------------------------------------------------------------------
handle_cast({errlog, F, A}, State) when State#state.running == true ->
    error_logger:format(F, A),
    {noreply, State};
handle_cast({errlog, F, A}, State) when State#state.running == false ->
    {noreply, State#state{ack = [{err, F, A} | State#state.ack]}};


handle_cast({infolog, F, A}, State) when State#state.running == true ->
    error_logger:info_report(F, A),
    {noreply, State};
handle_cast({infolog, F, A}, State) when State#state.running == false ->
    {noreply, State#state{ack = [{info, F, A} | State#state.ack]}};



handle_cast({access, ServerName, Ip, Req, Status, Length}, State) ->
    case State#state.running of 
	true ->
	    do_alog(ServerName, Ip, Req, Status, Length, State),
	    {noreply, State};
	false ->
	    {noreply, State#state{ack = [{access, ServerName, Ip, Req, 
					  Status,  Length} |
					 State#state.ack]}}
    end;
handle_cast({trace, from_server, Data}, State) ->
    Str = ["*** SRV -> CLI *** ", Data],
    file:write(State#state.tracefd, Str),
    tty_trace(Str, State),
    {noreply,  State};
handle_cast({trace, from_client, Data}, State) ->
    Str = ["*** CLI -> SRV *** ", Data],
    file:write(State#state.tracefd, Str),
    tty_trace(Str, State),
    {noreply, State}.



do_alog(ServerName, Ip, Req, Status, Length, State) ->
    case lists:keysearch(ServerName, 1, State#state.alogs) of
	{value, {_, FD, _}} ->
	    I = fmt_alog(State#state.now, Ip, Req, Status,  Length),
	    file:write(FD, I),
	    tty_trace(I, State);
	_ ->
	    false
    end.


{'fraction_non_alphanumeric': 0.2699075832383846, 'fraction_numerical': 0.0050639321433092795, 'mean_word_length': 1.9497759522031366, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 115, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6671356', 'n_tokens_mistral': 5624, 'n_tokens_neox': 5136, 'n_words': 1182}