From b57248e82216ffc152db8c6ed06e4e21ccb9a122 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 16 Jun 2009 13:52:12 +0000 Subject: [PATCH] Added accesslog parameter to record requests log in a file similar to Apache (thanks to Jerome Sautret) SVN Revision: 2184 --- src/web/mod_http_fileserver.erl | 115 +++++++++++++++++++++++++++++--- 1 file changed, 106 insertions(+), 9 deletions(-) diff --git a/src/web/mod_http_fileserver.erl b/src/web/mod_http_fileserver.erl index 248870c8e..f04cf113c 100644 --- a/src/web/mod_http_fileserver.erl +++ b/src/web/mod_http_fileserver.erl @@ -15,14 +15,16 @@ -behaviour(gen_mod). -export([ - start/2, - stop/1, - process/2 + start/2, + stop/1, + process/2, + ctl_process/2 ]). -include("ejabberd.hrl"). -include("jlib.hrl"). -include("ejabberd_http.hrl"). +-include("ejabberd_ctl.hrl"). %%%---------------------------------------------------------------------- %%% REQUEST HANDLERS @@ -30,7 +32,7 @@ %%----------------------------------------------------------------------- %% FUNCTION -%% +%% %% process/2 %% %% PURPOSE @@ -48,8 +50,21 @@ %% %%----------------------------------------------------------------------- -process(LocalPath, _Request) -> + +process(LocalPath, Request) -> ?DEBUG("Requested ~p", [LocalPath]), + + Result = serve(LocalPath), + case ets:lookup(mod_http_fileserver, accessfile) of + undefined -> + ok; + [{accessfile, AccessFile}] -> + {Code, _, _} = Result, + log(AccessFile, Code, Request) + end, + Result. + +serve(LocalPath) -> [{docroot, DocRoot}] = ets:lookup(mod_http_fileserver, docroot), FileName = filename:join(filename:split(DocRoot) ++ LocalPath), case file:read_file(FileName) of @@ -68,11 +83,40 @@ process(LocalPath, _Request) -> end end. +ctl_process(_Val, ["reopen-weblog"]) -> + mod_http_fileserver_server ! reopenlog, + ?STATUS_SUCCESS; +ctl_process(Val, _Args) -> + Val. %%%---------------------------------------------------------------------- %%% UTILITIES %%%---------------------------------------------------------------------- +join([], _) -> + ""; +join([E], _) -> + E; +join([H | T], Separator) -> + lists:foldl(fun(E, Acc) -> lists:concat([Acc, Separator, E]) end, H, T). + +log(File, Code, Request) -> + {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:local_time(), + IP = join(tuple_to_list(Request#request.ip), "."), + Path = join(Request#request.path, "/"), + Query = case join(lists:map(fun(E) -> lists:concat([element(1, E), "=", element(2, E)]) end, + Request#request.q), "&") of + [] -> + ""; + String -> + [$? | String] + end, + % combined apache like log format : + % 127.0.0.1 - - [28/Mar/2007:18:41:55 +0200] "GET / HTTP/1.1" 302 303 "-" "tsung" + % XXX TODO some fields are harcoded/missing (reply size, user agent or referer for example) + io:format(File, "~p - - [~p/~p/~p:~p:~p:~p] \"~s /~s~s\" ~p -1 \"-\" \"-\"~n", + [IP, Day, Month, Year, Hour, Minute, Second, Request#request.method, Path, Query, Code]). + content_type(Filename) -> case httpd_util:to_lower(filename:extension(Filename)) of ".jpg" -> "image/jpeg"; @@ -88,25 +132,78 @@ content_type(Filename) -> _Else -> "application/octet-stream" end. +open_file(Filename) -> + case file:open(Filename, [append]) of + {ok, File} -> + ets:insert(mod_http_fileserver, {accessfile, File}), + ok; + {error, _Reason} -> + {'EXIT', {unaccessible_accessfile, ?MODULE}} + end. + +loop(Filename) -> + receive + reopenlog -> + case ets:lookup(mod_http_fileserver, accessfile) of + undefined -> + ok; + [{accessfile, AccessFile}] -> + file:close(AccessFile), + case open_file(Filename) of + ok -> + ok; + _ -> + error + end + end, + loop(Filename); + stop -> + ok + end. + %%%---------------------------------------------------------------------- %%% BEHAVIOUR CALLBACKS %%%---------------------------------------------------------------------- start(_Host, Opts) -> + ejabberd_ctl:register_commands([{"reopen-weblog", "reopen http fileserver log file"}], + ?MODULE, ctl_process), case gen_mod:get_opt(docroot, Opts, undefined) of undefined -> {'EXIT', {missing_document_root, ?MODULE}}; DocRoot -> case filelib:is_dir(DocRoot) of true -> - ets:new(mod_http_fileserver, [named_table, bag]), + % XXX WARNING, using a single ets table name will + % not work with virtual hosts + ets:new(mod_http_fileserver, [named_table, public]), ets:insert(mod_http_fileserver, [{docroot, DocRoot}]), - ok; + case gen_mod:get_opt(accesslog, Opts, undefined) of + undefined -> + ok; + Filename -> + % XXX same remark as above for proc name + register(mod_http_fileserver_server, spawn(?MODULE, loop, [Filename])), + open_file(Filename) + end; _Else -> {'EXIT', {unaccessible_document_root, ?MODULE}} - end + end end. stop(_Host) -> - ok. + case ets:info(mod_http_fileserver, name) of + undefined -> + ok; + _ -> + case ets:lookup(mod_http_fileserver, accessfile) of + undefined -> + ok; + [{accessfile, AccessFile}] -> + mod_http_fileserver_server ! stop, + file:close(AccessFile) + end, + ets:delete(mod_http_fileserver) + end, + ok.