%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-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%
%%
%%
-module(mod_log).
%% Application internal API
-export([error_log/2, security_log/2, report_error/2]).
%% Callback API
-export([do/1, load/2, store/2, remove/1]).
-include("httpd.hrl").
-include("httpd_internal.hrl").
-define(VMODULE,"LOG").
%%%=========================================================================
%%% API
%%%=========================================================================
%% security log
security_log(Info, ReasonStr) ->
Date = httpd_util:custom_date(),
case httpd_log:security_entry(security_log, no_security_log, Info,
Date, ReasonStr) of
no_security_log ->
ok;
{Log, Entry} ->
io:format(Log, "~s", [Entry])
end.
%% error_log
error_log(Info, Reason) ->
Date = httpd_util:custom_date(),
error_log(Info, Date, Reason).
error_log(Info, Date, Reason) ->
case httpd_log:error_entry(error_log, no_error_log,
Info, Date, Reason) of
no_error_log ->
ok;
{Log, Entry} ->
io:format(Log, "~s", [Entry])
end.
report_error(ConfigDB, Error) ->
Date = httpd_util:custom_date(),
case httpd_log:error_report_entry(error_log, no_error_log, ConfigDB,
Date, Error) of
no_error_log ->
ok;
{Log, Entry} ->
io:format(Log, "~s", [Entry])
end.
%%%=========================================================================
%%% CALLBACK API
%%%=========================================================================
%%--------------------------------------------------------------------------
%% do(ModData) -> {proceed, OldData} | {proceed, NewData} | {break, NewData}
%% | done
%% ModData = #mod{}
%%
%% Description: See httpd(3) ESWAPI CALLBACK FUNCTIONS
%%-------------------------------------------------------------------------
do(Info) ->
AuthUser = auth_user(Info#mod.data),
Date = httpd_util:custom_date(),
log_internal_info(Info,Date,Info#mod.data),
case proplists:get_value(status, Info#mod.data) of
%% A status code has been generated!
{StatusCode, _PhraseArgs, Reason} ->
transfer_log(Info,"-",AuthUser,Date,StatusCode,0),
if
StatusCode >= 400 ->
error_log(Info,Date,Reason);
true ->
not_an_error
end,
{proceed,Info#mod.data};
%% No status code has been generated!
undefined ->
case proplists:get_value(response, Info#mod.data) of
{already_sent,StatusCode,Size} ->
transfer_log(Info,"-",AuthUser,Date,StatusCode,Size),
{proceed,Info#mod.data};
{response, Head, _Body} ->
Size = content_length(Head),
Code = proplists:get_value(code,Head,unknown),
transfer_log(Info, "-", AuthUser, Date, Code, Size),
{proceed, Info#mod.data};
{_StatusCode, Response} ->
transfer_log(Info,"-",AuthUser,Date,200,
httpd_util:flatlength(Response)),
{proceed,Info#mod.data};
undefined ->
transfer_log(Info,"-",AuthUser,Date,200,0),
{proceed,Info#mod.data}
end
end.
%%--------------------------------------------------------------------------
%% load(Line, Context) -> eof | ok | {ok, NewContext} |
%% {ok, NewContext, Directive} |
%% {ok, NewContext, DirectiveList} | {error, Reason}
%% Line = string()
%% Context = NewContext = DirectiveList = [Directive]
%% Directive = {DirectiveKey , DirectiveValue}
%% DirectiveKey = DirectiveValue = term()
%% Reason = term()
%%
%% Description: See httpd(3) ESWAPI CALLBACK FUNCTIONS
%%-------------------------------------------------------------------------
load("TransferLog " ++ TransferLog, []) ->
{ok,[],{transfer_log,string:strip(TransferLog)}};
load("ErrorLog " ++ ErrorLog, []) ->
{ok,[],{error_log,string:strip(ErrorLog)}};
load("SecurityLog " ++ SecurityLog, []) ->
{ok, [], {security_log, string:strip(SecurityLog)}}.
%%--------------------------------------------------------------------------
%% store(Directive, DirectiveList) -> {ok, NewDirective} |
%% {ok, [NewDirective]} |
%% {error, Reason}
%% Directive = {DirectiveKey , DirectiveValue}
%% DirectiveKey = DirectiveValue = term()
%% Reason = term()
%%
%% Description: See httpd(3) ESWAPI CALLBACK FUNCTIONS
%%-------------------------------------------------------------------------
store({transfer_log,TransferLog}, ConfigList) when is_list(TransferLog)->
case create_log(TransferLog,ConfigList) of
{ok,TransferLogStream} ->
{ok,{transfer_log,TransferLogStream}};
{error,Reason} ->
{error,Reason}
end;
store({transfer_log,TransferLog}, _) ->
{error, {wrong_type, {transfer_log, TransferLog}}};
store({error_log,ErrorLog}, ConfigList) when is_list(ErrorLog) ->
case create_log(ErrorLog,ConfigList) of
{ok,ErrorLogStream} ->
{ok,{error_log,ErrorLogStream}};
{error,Reason} ->
{error,Reason}
end;
store({error_log,ErrorLog}, _) ->
{error, {wrong_type, {error_log, ErrorLog}}};
store({security_log, SecurityLog}, ConfigList) when is_list(SecurityLog) ->
case create_log(SecurityLog, ConfigList) of
{ok, SecurityLogStream} ->
{ok, {security_log, SecurityLogStream}};
{error, Reason} ->
{error, Reason}
end;
store({security_log, SecurityLog}, _) ->
{error, {wrong_type, {security_log, SecurityLog}}}.
%%--------------------------------------------------------------------------
%% remove(ConfigDb) -> _
%%
%% Description: See httpd(3) ESWAPI CALLBACK FUNCTIONS
%%-------------------------------------------------------------------------
remove(ConfigDB) ->
lists:foreach(fun([Stream]) -> file:close(Stream) end,
ets:match(ConfigDB,{transfer_log,'$1'})),
lists:foreach(fun([Stream]) -> file:close(Stream) end,
ets:match(ConfigDB,{error_log,'$1'})),
lists:foreach(fun([Stream]) -> file:close(Stream) end,
ets:match(ConfigDB,{security_log,'$1'})),
ok.
%%%========================================================================
%%% Internal functions
%%%========================================================================
%% transfer_log
transfer_log(Info,RFC931,AuthUser,Date,StatusCode,Bytes) ->
case httpd_log:access_entry(transfer_log, no_transfer_log,
Info, RFC931, AuthUser, Date,
StatusCode, Bytes) of
no_transfer_log ->
ok;
{Log, Entry} ->
io:format(Log, "~s", [Entry])
end.
create_log(LogFile, ConfigList) ->
Filename = string:strip(LogFile),
case filename:pathtype(Filename) of
absolute ->
case file:open(Filename, [read, write]) of
{ok,LogStream} ->
file:position(LogStream,{eof,0}),
{ok,LogStream};
{error,_} ->
{error,?NICE("Can't create "++Filename)}
end;
volumerelative ->
case file:open(Filename, [read, write]) of
{ok,LogStream} ->
file:position(LogStream,{eof,0}),
{ok,LogStream};
{error,_} ->
{error,?NICE("Can't create "++Filename)}
end;
relative ->
case proplists:get_value(server_root,ConfigList) of
undefined ->
{error,
?NICE(Filename++
" is an invalid logfile name beacuse "
"ServerRoot is not defined")};
ServerRoot ->
AbsoluteFilename=filename:join(ServerRoot,Filename),
case file:open(AbsoluteFilename, [read, write]) of
{ok,LogStream} ->
file:position(LogStream,{eof,0}),
{ok,LogStream};
{error, _Reason} ->
{error,?NICE("Can't create "++AbsoluteFilename)}
end
end
end.
%% log_internal_info
log_internal_info(_Info, _Date, []) ->
ok;
log_internal_info(Info,Date,[{internal_info,Reason}|Rest]) ->
error_log(Info, Date, Reason),
log_internal_info(Info,Date,Rest);
log_internal_info(Info,Date,[_|Rest]) ->
log_internal_info(Info,Date,Rest).
auth_user(Data) ->
case proplists:get_value(remote_user, Data) of
undefined ->
"-";
RemoteUser ->
RemoteUser
end.
content_length(Head) ->
case proplists:get_value(content_length, Head) of
undefined ->
unknown;
Size ->
list_to_integer(Size)
end.