diff --git a/.gitignore b/.gitignore index d7b690f2..1e8da44e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,7 @@ doc/* /.rebar _build .DS_Store +log +.edts +.rebar3 +erl_crash.dump diff --git a/include/webmachine_logger.hrl b/include/webmachine_logger.hrl deleted file mode 100644 index 82a15a13..00000000 --- a/include/webmachine_logger.hrl +++ /dev/null @@ -1,17 +0,0 @@ --record(wm_log_data, - {resource_module :: atom(), - start_time :: tuple(), - method :: atom(), - headers, - peer, - sock, - path :: string(), - version, - response_code, - response_length, - end_time :: undefined | tuple(), - finish_time :: undefined | tuple(), - notes}). --type wm_log_data() :: #wm_log_data{}. - --define(EVENT_LOGGER, webmachine_log_event). diff --git a/rebar.config b/rebar.config index ea770995..25d427c6 100644 --- a/rebar.config +++ b/rebar.config @@ -1,16 +1,27 @@ %%-*- mode: erlang -*- -{erl_opts, [warnings_as_errors]}. +{erl_opts, + [ + warnings_as_errors + ,{parse_transform, lager_transform} + ,{lager_extra_sinks, [wm_access]} + ]}. + {cover_enabled, true}. {edoc_opts, [{preprocess, true}]}. {xref_checks, [undefined_function_calls]}. -{deps, [{mochiweb, "2.12.2"}]}. +{deps, + [ + {mochiweb, "2.12.2"} + ,{lager, "3.2.1"} + ]}. -{eunit_opts, [ - no_tty, - {report, {eunit_progress, [colored, profile]}} - ]}. +{eunit_opts, + [ + no_tty, + {report, {eunit_progress, [colored, profile]}} + ]}. {profiles, [{test, diff --git a/rebar.lock b/rebar.lock index 57d5070b..1014f8b6 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,6 +1,10 @@ {"1.1.0", -[{<<"mochiweb">>,{pkg,<<"mochiweb">>,<<"2.12.2">>},0}]}. +[{<<"goldrush">>,{pkg,<<"goldrush">>,<<"0.1.8">>},1}, + {<<"lager">>,{pkg,<<"lager">>,<<"3.2.1">>},0}, + {<<"mochiweb">>,{pkg,<<"mochiweb">>,<<"2.12.2">>},0}]}. [ {pkg_hash,[ + {<<"goldrush">>, <<"2024BA375CEEA47E27EA70E14D2C483B2D8610101B4E852EF7F89163CDB6E649">>}, + {<<"lager">>, <<"EEF4E18B39E4195D37606D9088EA05BF1B745986CF8EC84F01D332456FE88D17">>}, {<<"mochiweb">>, <<"80804AD342AFA3D7F3524040D4EED66CE74B17A555DE454AC85B07C479928E46">>}]} ]. diff --git a/src/webmachine.app.src b/src/webmachine.app.src index ad14f7b1..d14c580d 100644 --- a/src/webmachine.app.src +++ b/src/webmachine.app.src @@ -21,6 +21,7 @@ %% module that has rewrite/5 ,{server_name, undefined} %% string() for the "Server" response header + ,{timezone, "+0000"} ]}, {contributors,["Sean Cribbs", "Joe DeVivo" "Bryan Fink", diff --git a/src/webmachine_access_log_handler.erl b/src/webmachine_access_log_handler.erl deleted file mode 100644 index dedd2b1b..00000000 --- a/src/webmachine_access_log_handler.erl +++ /dev/null @@ -1,185 +0,0 @@ -%% Copyright (c) 2011-2014 Basho Technologies, Inc. All Rights Reserved. -%% -%% This file is provided to you under the Apache License, -%% Version 2.0 (the "License"); you may not use this file -%% except in compliance with the License. You may obtain -%% a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, -%% software distributed under the License is distributed on an -%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -%% KIND, either express or implied. See the License for the -%% specific language governing permissions and limitations -%% under the License. - -%% @doc Default access log handler for webmachine - --module(webmachine_access_log_handler). - --behaviour(gen_event). - -%% gen_event callbacks --export([init/1, - handle_call/2, - handle_event/2, - handle_info/2, - terminate/2, - code_change/3]). - --include("webmachine_logger.hrl"). - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). --endif. - --record(state, {hourstamp, - filename, - handle, - request_timing=false :: boolean()}). - --define(FILENAME, "access.log"). - -%% =================================================================== -%% gen_event callbacks -%% =================================================================== - -%% @private -init([BaseDir]) -> - {ok,_} = webmachine_log:defer_refresh(?MODULE), - FileName = filename:join(BaseDir, ?FILENAME), - {Handle, DateHour} = webmachine_log:log_open(FileName), - {ok, #state{filename=FileName, handle=Handle, hourstamp=DateHour}}; -init([BaseDir, true]) -> - {ok,_} = webmachine_log:defer_refresh(?MODULE), - FileName = filename:join(BaseDir, ?FILENAME), - {Handle, DateHour} = webmachine_log:log_open(FileName), - {ok, #state{filename=FileName, - handle=Handle, - hourstamp=DateHour, - request_timing=true}}; -init([BaseDir, _]) -> - init([BaseDir]). - -%% @private -handle_call({_Label, MRef, get_modules}, State) -> - {ok, {MRef, [?MODULE]}, State}; -handle_call({refresh, Time}, State) -> - {NewHour, NewHandle} = webmachine_log:maybe_rotate(?MODULE, - State#state.filename, - State#state.handle, - Time, - State#state.hourstamp), - {ok, ok, State#state{hourstamp=NewHour, handle=NewHandle}}; -handle_call(_Request, State) -> - {ok, ok, State}. - -%% @private -handle_event({log_access, LogData}, State) -> - {NewHour, NewHandle} = webmachine_log:maybe_rotate(?MODULE, - State#state.filename, - State#state.handle, - os:timestamp(), - State#state.hourstamp), - NewState = State#state{hourstamp=NewHour, handle=NewHandle}, - Msg = format_req(LogData, NewState#state.request_timing), - _ = webmachine_log:log_write(NewState#state.handle, Msg), - {ok, NewState}; -handle_event(_Event, State) -> - {ok, State}. - -%% @private -handle_info(_Info, State) -> - {ok, State}. - -%% @private -terminate(_Reason, _State) -> - ok. - -%% @private -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%% =================================================================== -%% Internal functions -%% =================================================================== - -format_req(#wm_log_data{method=Method, - headers=Headers, - peer=Peer, - path=Path, - version=Version, - response_code=ResponseCode, - response_length=ResponseLength, - start_time=StartTime, - end_time=EndTime}, - RequestTiming) -> - User = "-", - Time = webmachine_log:fmtnow(), - Status = case ResponseCode of - {Code, _ReasonPhrase} when is_integer(Code) -> - integer_to_list(Code); - _ when is_integer(ResponseCode) -> - integer_to_list(ResponseCode); - _ -> - ResponseCode - end, - Length = integer_to_list(ResponseLength), - Referer = - case mochiweb_headers:get_value("Referer", Headers) of - undefined -> ""; - R -> R - end, - UserAgent = - case mochiweb_headers:get_value("User-Agent", Headers) of - undefined -> ""; - U -> U - end, - case RequestTiming of - true -> - fmt_alog(Time, Peer, User, Method, Path, Version, - Status, Length, Referer, UserAgent, timer:now_diff(EndTime, StartTime)); - false -> - fmt_alog(Time, Peer, User, Method, Path, Version, - Status, Length, Referer, UserAgent) - end. - -fmt_alog(Time, Ip, User, Method, Path, Version, - Status, Length, Referer, UserAgent) when is_atom(Method) -> - fmt_alog(Time, Ip, User, atom_to_list(Method), Path, Version, - Status, Length, Referer, UserAgent); -fmt_alog(Time, Ip, User, Method, Path, {VM,Vm}, - Status, Length, Referer, UserAgent) -> - [webmachine_log:fmt_ip(Ip), " - ", User, [$\s], Time, [$\s, $"], Method, " ", Path, - " HTTP/", integer_to_list(VM), ".", integer_to_list(Vm), [$",$\s], - Status, [$\s], Length, [$\s,$"], Referer, - [$",$\s,$"], UserAgent, [$",$\n]]. - -fmt_alog(Time, Ip, User, Method, Path, Version, - Status, Length, Referer, UserAgent, ReqDuration) when is_atom(Method) -> - fmt_alog(Time, Ip, User, atom_to_list(Method), Path, Version, - Status, Length, Referer, UserAgent, ReqDuration); -fmt_alog(Time, Ip, User, Method, Path, {VM,Vm}, - Status, Length, Referer, UserAgent, ReqDuration) -> - [webmachine_log:fmt_ip(Ip), " - ", User, [$\s], Time, [$\s, $"], Method, " ", Path, - " HTTP/", integer_to_list(VM), ".", integer_to_list(Vm), [$",$\s], - Status, [$\s], Length, [$\s,$"], Referer, - [$",$\s,$"], UserAgent, [$",$\s], integer_to_list(ReqDuration), [$\n]]. - - --ifdef(TEST). - -non_standard_method_test() -> - LogData = #wm_log_data{method="FOO", - headers=mochiweb_headers:make([]), - peer={127,0,0,1}, - path="/", - version={1,1}, - response_code=501, - response_length=1234}, - LogEntry = format_req(LogData, false), - ?assert(is_list(LogEntry)), - ok. - --endif. diff --git a/src/webmachine_app.erl b/src/webmachine_app.erl index 35fa74a3..2ca7c3d0 100644 --- a/src/webmachine_app.erl +++ b/src/webmachine_app.erl @@ -25,25 +25,18 @@ -export([start/2, stop/1]). --include("webmachine_logger.hrl"). - -define(QUIP, "cafe not found"). %% @spec start(_Type, _StartArgs) -> ServerRet %% @doc application start callback for webmachine. start(_Type, _StartArgs) -> + webmachine_lager:start(), webmachine_deps:ensure(), %% Populate dynamic defaults on load: load_default_app_config(), {ok, _Pid} = SupLinkRes = webmachine_sup:start_link(), - Handlers = application:get_env(webmachine, log_handlers, []), - - %% handlers failing to start are handled in the handler_watcher - _ = [supervisor:start_child(webmachine_logger_watcher_sup, - [?EVENT_LOGGER, Module, Config]) || - {Module, Config} <- Handlers], SupLinkRes. %% @spec stop(_State) -> ServerRet @@ -60,6 +53,7 @@ load_default_app_config() -> _ -> set_default_server_name() end, + set_timezone(), ok. @@ -77,3 +71,18 @@ set_default_server_name() -> application:set_env( webmachine, server_name, ServerName), ok. + +set_timezone() -> + Time = erlang:universaltime(), + LocalTime = calendar:universal_time_to_local_time(Time), + DiffSecs = calendar:datetime_to_gregorian_seconds(LocalTime) - + calendar:datetime_to_gregorian_seconds(Time), + Zone = lists:flatten(zone((DiffSecs/3600)*100)), + application:set_env(webmachine, timezone, Zone). + +%% Ugly reformatting code to get times like +0000 and -1300 +-spec zone(float()) -> string(). +zone(Val) when Val < 0 -> + io_lib:format("-~4..0w", [trunc(abs(Val))]); +zone(Val) when Val >= 0 -> + io_lib:format("+~4..0w", [trunc(abs(Val))]). diff --git a/src/webmachine_decision_core.erl b/src/webmachine_decision_core.erl index 4a50d09a..306f7bbb 100644 --- a/src/webmachine_decision_core.erl +++ b/src/webmachine_decision_core.erl @@ -22,7 +22,6 @@ -author('Andy Gross '). -author('Bryan Fink '). -export([handle_request/2]). --include("webmachine_logger.hrl"). handle_request(Resource, ReqState) -> _ = [erase(X) || X <- [decision, code, req_body, bytes_written, tmp_reqstate]], @@ -91,11 +90,14 @@ finish_response({Code, _}=CodeAndPhrase, Resource, EndTime) -> wrcall({send_response, CodeAndPhrase}), RMod = wrcall({get_metadata, 'resource_module'}), Notes = wrcall(notes), - LogData0 = wrcall(log_data), - LogData = LogData0#wm_log_data{resource_module=RMod, - end_time=EndTime, - notes=Notes}, - spawn(fun() -> do_log(LogData) end), + + webmachine_lager:put_metadata( + [ + {wm_resource_module, RMod}, + {wm_end_time, EndTime}, + {wm_notes, Notes} + ]), + wm_access:info(""), webmachine_resource:stop(Resource). error_response(Reason) -> @@ -147,9 +149,6 @@ decision_flow(X, TestResult) when is_integer(X) -> end; decision_flow(X, _TestResult) when is_atom(X) -> d(X). -do_log(LogData) -> - webmachine_log:log_access(LogData). - log_decision(DecisionID) -> Resource = get(resource), webmachine_resource:log_d(DecisionID, Resource). diff --git a/src/webmachine_dispatcher.erl b/src/webmachine_dispatcher.erl index dffa9aa7..5a66983f 100644 --- a/src/webmachine_dispatcher.erl +++ b/src/webmachine_dispatcher.erl @@ -289,18 +289,18 @@ run_guard(Fun, RD) when is_function(Fun) -> try Fun(RD) == true catch _Type : Msg -> - webmachine_log:log_error(["Error running guard ", Fun, ": ", Msg, $\n]), + lager:error(["Error running guard ", Fun, ": ", Msg, $\n]), throw({error_running_guard, Fun, Msg}) end; run_guard({Mod, Fun}, RD) -> try Mod:Fun(RD) == true catch _Type : Msg -> - webmachine_log:log_error(["Error running guard ", Mod, $:, Fun, "/1: ", Msg, $\n]), + lager:error(["Error running guard ", Mod, $:, Fun, "/1: ", Msg, $\n]), throw({error_running_guard, {Mod, Fun}, Msg}) end; run_guard(Other, _) -> - webmachine_log:log_error(["Unknown guard type in webmachine_dispatcher: ", Other, $\n]), + lager:error(["Unknown guard type in webmachine_dispatcher: ", Other, $\n]), throw({unknown_guard_type, Other}). bind([], [], Bindings, Depth) -> diff --git a/src/webmachine_error_handler.erl b/src/webmachine_error_handler.erl index a028b98c..d02946a7 100644 --- a/src/webmachine_error_handler.erl +++ b/src/webmachine_error_handler.erl @@ -49,11 +49,11 @@ render_error_body(500, Req, Reason) -> ErrorIOList = [ErrorStart,STString,ErrorEnd], {erlang:iolist_to_binary(ErrorIOList), ReqState}; -render_error_body(501, Req, Reason) -> +render_error_body(501, Req, _Reason) -> {ok, ReqState} = webmachine_request:add_response_header("Content-Type", "text/html", Req), - {Method,_} = webmachine_request:method(Req), - webmachine_log:log_error(501, Req, Reason), + maybe_log(501, Req, undefined), + {Method, _} = webmachine_request:method(Req), ErrorStr = io_lib:format("Codestin Search App" "

Not Implemented

" "The server does not support the ~p method.
" @@ -62,10 +62,10 @@ render_error_body(501, Req, Reason) -> [Method]), {erlang:iolist_to_binary(ErrorStr), ReqState}; -render_error_body(503, Req, Reason) -> +render_error_body(503, Req, _Reason) -> {ok, ReqState} = webmachine_request:add_response_header("Content-Type", "text/html", Req), - webmachine_log:log_error(503, Req, Reason), + maybe_log(503, Req, undefined), ErrorStr = "Codestin Search App" "

Service Unavailable

" "The server is currently unable to handle " @@ -95,5 +95,15 @@ maybe_log(_Code, _Req, {error, {exit, normal, _Stack}}) -> %% message. This usually happens when a chunked upload is %% interrupted by network failure. ok; -maybe_log(Code, Req, Reason) -> - webmachine_log:log_error(Code, Req, Reason). +maybe_log(undefined, _, Msg) -> + lager:error(Msg); +maybe_log(501, Req, _) -> + {Path, _} = webmachine_request:path(Req), + {Method, _} = webmachine_request:method(Req), + lager:error("Webmachine does not support method ~p: path=~p", [Method, Path]); +maybe_log(503, Req, _) -> + {Path, _} = webmachine_request:path(Req), + lager:error("Webmachine cannot fulfill the request at this time: path=~p", [Path]); +maybe_log(_Code, Req, Reason) -> + {Path, _} = webmachine_request:path(Req), + lager:error("path=~p ~p", [Path, Reason]). diff --git a/src/webmachine_error_log_handler.erl b/src/webmachine_error_log_handler.erl deleted file mode 100644 index 9c50691d..00000000 --- a/src/webmachine_error_log_handler.erl +++ /dev/null @@ -1,133 +0,0 @@ -%% Copyright (c) 2011-2014 Basho Technologies, Inc. All Rights Reserved. -%% -%% This file is provided to you under the Apache License, -%% Version 2.0 (the "License"); you may not use this file -%% except in compliance with the License. You may obtain -%% a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, -%% software distributed under the License is distributed on an -%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -%% KIND, either express or implied. See the License for the -%% specific language governing permissions and limitations -%% under the License. - -%% @doc Default log handler for webmachine - --module(webmachine_error_log_handler). - --behaviour(gen_event). - -%% gen_event callbacks --export([init/1, - handle_call/2, - handle_event/2, - handle_info/2, - terminate/2, - code_change/3]). - --include("webmachine_logger.hrl"). - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). --endif. - --record(state, {hourstamp, filename, handle}). - --define(FILENAME, "wm_error.log"). - -%% =================================================================== -%% gen_event callbacks -%% =================================================================== - -%% @private -init([BaseDir]) -> - {ok,_} = webmachine_log:defer_refresh(?MODULE), - FileName = filename:join(BaseDir, ?FILENAME), - {Handle, DateHour} = webmachine_log:log_open(FileName), - {ok, #state{filename=FileName, handle=Handle, hourstamp=DateHour}}. - -%% @private -handle_call({_Label, MRef, get_modules}, State) -> - {ok, {MRef, [?MODULE]}, State}; -handle_call({refresh, Time}, State) -> - {NewHour, NewHandle} = webmachine_log:maybe_rotate(?MODULE, - State#state.filename, - State#state.handle, - Time, - State#state.hourstamp), - {ok, ok, State#state{hourstamp=NewHour, handle=NewHandle}}; -handle_call(_Request, State) -> - {ok, ok, State}. - -%% @private -handle_event({log_error, Msg}, State) -> - {NewHour, NewHandle} = webmachine_log:maybe_rotate(?MODULE, - State#state.filename, - State#state.handle, - os:timestamp(), - State#state.hourstamp), - NewState = State#state{hourstamp=NewHour, handle=NewHandle}, - FormattedMsg = format_req(error, undefined, undefined, Msg), - _ = webmachine_log:log_write(NewState#state.handle, FormattedMsg), - {ok, NewState}; -handle_event({log_error, Code, _Req, _Reason}, State) when Code < 500 -> - {ok, State}; -handle_event({log_error, Code, Req, Reason}, State) -> - {NewHour, NewHandle} = webmachine_log:maybe_rotate(?MODULE, - State#state.filename, - State#state.handle, - os:timestamp(), - State#state.hourstamp), - NewState = State#state{hourstamp=NewHour, handle=NewHandle}, - Msg = format_req(error, Code, Req, Reason), - _ = webmachine_log:log_write(NewState#state.handle, Msg), - {ok, NewState}; -handle_event({log_info, Msg}, State) -> - {NewHour, NewHandle} = webmachine_log:maybe_rotate(?MODULE, - State#state.filename, - State#state.handle, - os:timestamp(), - State#state.hourstamp), - NewState = State#state{hourstamp=NewHour, handle=NewHandle}, - FormattedMsg = format_req(info, undefined, undefined, Msg), - _ = webmachine_log:log_write(NewState#state.handle, FormattedMsg), - {ok, NewState}; -handle_event(_Event, State) -> - {ok, State}. - -%% @private -handle_info(_Info, State) -> - {ok, State}. - -%% @private -terminate(_Reason, _State) -> - ok. - -%% @private -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%% =================================================================== -%% Internal functions -%% =================================================================== - -format_req(info, undefined, _, Msg) -> - ["[info] ", Msg]; -format_req(error, undefined, _, Msg) -> - ["[error] ", Msg]; -format_req(error, 501, Req, _) -> - {Path, _} = Req:path(), - {Method, _} = Req:method(), - Reason = "Webmachine does not support method ", - ["[error] ", Reason, Method, ": path=", Path, $\n]; -format_req(error, 503, Req, _) -> - {Path, _} = Req:path(), - Reason = "Webmachine cannot fulfill the request at this time", - ["[error] ", Reason, ": path=", Path, $\n]; -format_req(error, _Code, Req, Reason) -> - {Path, _} = Req:path(), - Str = io_lib:format("~p", [Reason]), - ["[error] path=", Path, $\x20, Str, $\n]. diff --git a/src/webmachine_headers.erl b/src/webmachine_headers.erl index 677f7923..7d6023cb 100644 --- a/src/webmachine_headers.erl +++ b/src/webmachine_headers.erl @@ -33,7 +33,7 @@ | 'Server' | 'Vary' | 'Warning' - |'Www-Authenticate' + | 'Www-Authenticate' | 'Allow' | 'Content-Base' | 'Content-Encoding' diff --git a/src/webmachine_httpd_formatter.erl b/src/webmachine_httpd_formatter.erl new file mode 100644 index 00000000..4a35b4c4 --- /dev/null +++ b/src/webmachine_httpd_formatter.erl @@ -0,0 +1,263 @@ +%% @doc This lager formatter is a partial implementation of the custom +%% formatters available in the Apache mod_log_config +%% module_loaded. http://httpd.apache.org/docs/current/mod/mod_log_config.html + +%% The initial version of mod_log_config support must handle the +%% access and perf formats from webmachine 1.x + +%% access: "%h %l %u %t \"%r\" %>s %b %{Referer}i %{User-agent}i" +%% perf: "%h %l %t \"%r\" %>s %b %R %{msec}T %wm-ppt" + +%% %h - Remote hostname, but webmachine used remote IP +%% %l - Remote log name, (webmachine used `-`) because there's no mod_ident +%% %u - User (webmachine always used `-`) +%% %t - Datetime +%% %r - HTTP Request Method, Path, Protocol, and Protocol Version +%% %>s - Response Status Code (> means final status) +%% %b - Response Byte Size +%% %R - resource module that served the request +%% %{UNIT}T - time taken to serve the request, webmachine 1 used `ms` only. +%% %{VARNAME}i + +%% The perf handler has an addtional feature specific to webmachine: +%% %wm-ppt - The time to handle any post-request processing including +%% gathering any notes added during the request and th log data +%% generated by the request. + +-module(webmachine_httpd_formatter). + +-export([ + format/2, + format/3 + ]). + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). +-endif. + +-spec format(lager_msg:lager_msg(), list()) -> any(). +format(Msg, Config) -> + %% Transform Config string into something easily substitutable + {ErlFmtStr, HttpdFmtStrs} = + transform(Config), + IOFmtArgs = [output(F, Msg) || F <- HttpdFmtStrs], + lists:flatten(io_lib:format(ErlFmtStr, IOFmtArgs)). + +%% @doc It looks like from the lager spec, that you need a format/3 if +%% you choose to use the lager console backend, but since we won't +%% need any console support for colors, we can route everything +%% through `format/2` +%% @see format/2 +-spec format(lager_msg:lager_msg(), list(), list()) -> any(). +format(Msg, Config, _) -> + format(Msg, Config). + +transform(Config) -> + transform(Config, [], []). + +%% [] means we're done, reverse the accumulators and let's get out of +%% here! +transform([], Erl, Httpd) -> + {lists:reverse([$n,$~|Erl]), + lists:reverse(Httpd)}; +%% The trickiest case is for `%{SOMETHING}c`, e.g. `%{Referer}i`. +transform([$%,${|Rest], Erl, Httpd) -> + Idx = string:str(Rest, "}") - 1, + {Extra, [$},C|NewRest]} = lists:split(Idx, Rest), + transform(NewRest, [$s,$~|Erl], [{[$%,C], Extra}|Httpd]); +%% %% just escapes %, drop one and move on. +transform([$%,$%|Rest], Erl, Httpd) -> + transform(Rest, [$%|Erl], Httpd); +%% %>s +transform([$%,$>,$s|Rest], Erl, Httpd) -> + transform(Rest, [$s,$~|Erl], [[$%,$>,$s]|Httpd]); +%% %wm-ppt +transform([$%,$w,$m,$-,$p,$p,$t|Rest], Erl, Httpd) -> + transform(Rest, [$s,$~|Erl], [[$%,$w,$m,$-,$p,$p,$t]|Httpd]); +%% all one character codes caught by this clause +transform([$%,C|Rest], Erl, Httpd) -> + transform(Rest, [$s,$~|Erl], [[$%,C]| Httpd]); +%% Default case for all basic characters +transform([H|Rest], Erl, Httpd) -> + transform(Rest, [H|Erl], Httpd). + +-spec output(string(), lager_msg:lager_msg()) -> string(). +output("%wm-ppt", Msg) -> + Metadata = lager_msg:metadata(Msg), + {_, EndTime} = lists:keyfind(wm_end_time, 1, Metadata), + {_, FinishTime} = lists:keyfind(wm_finish_time, 1, Metadata), + TTPD = webmachine_util:now_diff_milliseconds(FinishTime, EndTime), + integer_to_list(TTPD); +output("%b", Msg) -> + Metadata = lager_msg:metadata(Msg), + {_, ResponseLength} = lists:keyfind(wm_response_length, 1, Metadata), + integer_to_list(ResponseLength); +output("%>s", Msg) -> + Metadata = lager_msg:metadata(Msg), + {_, ResponseCode} = lists:keyfind(wm_response_code, 1, Metadata), + Status = + case ResponseCode of + {Code, _ReasonPhrase} when is_integer(Code) -> + integer_to_list(Code); + _ when is_integer(ResponseCode) -> + integer_to_list(ResponseCode); + _ -> + ResponseCode + end, + Status; +output("%R", Msg) -> + Metadata = lager_msg:metadata(Msg), + {_, Mod} = lists:keyfind(wm_resource_module, 1, Metadata), + atom_to_list(Mod); +output({"%T", "usec"}, Msg) -> + Metadata = lager_msg:metadata(Msg), + {_, StartTime} = lists:keyfind(wm_start_time, 1, Metadata), + {_, EndTime} = lists:keyfind(wm_end_time, 1, Metadata), + TTPD = timer:now_diff(EndTime, StartTime), + integer_to_list(TTPD); +output({"%T", "msec"}, Msg) -> + Metadata = lager_msg:metadata(Msg), + {_, StartTime} = lists:keyfind(wm_start_time, 1, Metadata), + {_, EndTime} = lists:keyfind(wm_end_time, 1, Metadata), + TTPD = webmachine_util:now_diff_milliseconds(EndTime, StartTime), + integer_to_list(TTPD); +output({"%i", Header}, Msg) -> + Metadata = lager_msg:metadata(Msg), + {_, Headers} = lists:keyfind(wm_headers, 1, Metadata), + case mochiweb_headers:get_value(Header, Headers) of + undefined -> "\"\""; + R -> io_lib:format("\"~s\"", [R]) + end; +output("%r", Msg) -> + Metadata = lager_msg:metadata(Msg), + {_, Meth} = lists:keyfind(wm_method, 1, Metadata), + Method = case is_atom(Meth) of + true -> atom_to_list(Meth); + false -> Meth + end, + {_, Path} = lists:keyfind(wm_path, 1, Metadata), + {_, {PMaj, PMin}} = lists:keyfind(wm_version, 1, Metadata), + io_lib:format("~s ~s HTTP/~w.~w", + [ + Method + ,Path + ,PMaj + ,PMin + ]); +output("%u", _Msg) -> + "-"; +output("%l", _Msg) -> + "-"; +output("%t", Msg) -> + {ok, Zone} = application:get_env(webmachine, timezone), + {[Year,45,Month,45,Date], + [Hour,58,Min,58,Sec,46,_Milli|_]} = lager_msg:datetime(Msg), + lists:flatten( + io_lib:format("[~s/~s/~s:~s:~s:~s ~s]", + [Date, month(Month), Year, Hour, Min, Sec, Zone] + )); +output("%h", Msg) -> + Metadata = lager_msg:metadata(Msg), + {_, Peer} = lists:keyfind(wm_peer, 1, Metadata), + fmt_ip(Peer). + +%% @doc Format an IP address or host name +-spec fmt_ip(undefined | string() | inet:ip4_address() | inet:ip6_address()) -> string(). +fmt_ip(IP) when is_tuple(IP) -> + inet_parse:ntoa(IP); +fmt_ip(undefined) -> + "0.0.0.0"; +fmt_ip(HostName) -> + HostName. + +%% @doc Convert numeric month value to the abbreviation +-spec month(string()) -> string(). +month("01") -> + "Jan"; +month("02") -> + "Feb"; +month("03") -> + "Mar"; +month("04") -> + "Apr"; +month("05") -> + "May"; +month("06") -> + "Jun"; +month("07") -> + "Jul"; +month("08") -> + "Aug"; +month("09") -> + "Sep"; +month("10") -> + "Oct"; +month("11") -> + "Nov"; +month("12") -> + "Dec". + +-ifdef(TEST). + +access_test() -> + application:set_env(webmachine, timezone, "-0700"), + application:set_env(sasl, utc_log, true), + Headers = mochiweb_headers:make( + [{"Referer", "Place"} + ,{"User-Agent", "User something..."} + ]), + Msg = lager_msg:new( + "", + {1476,809544,123363}, + info, + [ + {wm_peer, {1,2,3,4}} + ,{wm_version, {1,1}} + ,{wm_path, "/thing"} + ,{wm_method, "FOO"} + ,{wm_response_code, {200, "cool!"}} + ,{wm_response_length, 1024} + ,{wm_headers, Headers} + ], + []), + Str = format(Msg, "%h - %u %t \"%r\" %>s %b %{Referer}i %{User-Agent}i"), + ?assertEqual( + "1.2.3.4 - - [18/Oct/2016:16:52:24 -0700] \"FOO /thing HTTP/1.1\" 200 1024 \"Place\" \"User something...\"\n", + Str + ), + ok. + +perf_test() -> + application:set_env(webmachine, timezone, "-0700"), + application:set_env(sasl, utc_log, true), + Headers = mochiweb_headers:make( + [{"Referer", "Place"} + ,{"User-Agent", "User something..."} + ]), + Msg = lager_msg:new( + "", + {1476,809544,123363}, + info, + [ + {wm_peer, {1,2,3,4}} + ,{wm_version, {1,1}} + ,{wm_path, "/thing"} + ,{wm_method, "FOO"} + ,{wm_response_code, {200, "cool!"}} + ,{wm_response_length, 1024} + ,{wm_headers, Headers} + ,{wm_start_time, {0,0, 1000}} + ,{wm_end_time, {0,0,11000}} + ,{wm_finish_time, {0,0,12001}} + ,{wm_resource_module, my_mod} + ], + []), + + Str = format(Msg, "%h %l %t \"%r\" %>s %b %R %{msec}T %wm-ppt"), + ?assertEqual( + "1.2.3.4 - [18/Oct/2016:16:52:24 -0700] \"FOO /thing HTTP/1.1\" 200 1024 my_mod 10 1\n", + Str + ), + ok. + +-endif. diff --git a/src/webmachine_lager.erl b/src/webmachine_lager.erl new file mode 100644 index 00000000..2b73a951 --- /dev/null +++ b/src/webmachine_lager.erl @@ -0,0 +1,105 @@ +%% Webmachine uses the default lager sink to log most errors and info +%% logs. + +%% Webmachine will also set up a `wm_access` sink for the access and +%% perf logs. + +%% Rather than try to reverse engineer old style configurations. We'll +%% start with the successful new style configuration which will just +%% assume a primary lager sink for the regular logs. That means that +%% if you don't have lager configured for your application, you won't +%% see any webmachine logs, but if you do, they'll be in the place you +%% expect them. + +%% Here's how you can configure the `access` and `perf` logs in the +%% webmachine application env. + +%% {webmachine, +%% {log_handlers, +%% [ +%% {access, Filename}, +%% {perf, Filename} +%% ]} +%% } + +%% Filename can be relative, or absolute. Relative will use +%% `lager.log_root`, whereas absolute will use the full path. Lager +%% will then use its rotation magic to make more than one file based +%% on the name. + +-module(webmachine_lager). + +-export([ + start/0 + ,put_metadata/1 + ]). + +-type log_type() :: access | perf. +-type wm_log_handler() :: {log_type(), file:filename()}. + +start() -> + ExtraSinks = application:get_env(lager, extra_sinks, []), + WMLogHandlers = application:get_env( + webmachine, + log_handlers, + []), + + NewExtraSinks = build_extra_sinks(WMLogHandlers) ++ ExtraSinks, + application:set_env(lager, extra_sinks, NewExtraSinks), + lager:start(). + +-spec build_extra_sinks([wm_log_handler()]) -> LagerSinkConfig :: list(). +build_extra_sinks([]) -> []; +build_extra_sinks(WMLogHandlers) -> + Handlers = build_wm_access_handlers(WMLogHandlers, []), + + [{wm_access_lager_event, + [ + {handlers, Handlers} + ] + }]. + +-spec build_wm_access_handlers([wm_log_handler()], [tuple()]) -> [tuple()]. +build_wm_access_handlers([], Acc) -> + Acc; +build_wm_access_handlers([LogHandler|Rest], Acc) -> + build_wm_access_handlers( + Rest, + [build_handler(LogHandler)|Acc]). + +-spec build_handler(wm_log_handler()) -> LagerFileBackend :: tuple(). +build_handler({access, Filename}) -> + {lager_file_backend, [ + {file, Filename} + ,{level, info} + ,{date, "$D0"} + ,{count, 5} + ,{size, 10485760} + ,{formatter, webmachine_httpd_formatter} + ,{formatter_config, + "%h %l %u %t \"%r\" %>s %b %{Referer}i %{User-agent}i" + } + ]}; +build_handler({perf, Filename}) -> + {lager_file_backend, [ + {file, Filename} + ,{level, info} + ,{date, "$D0"} + ,{count, 5} + ,{size, 10485760} + ,{formatter, webmachine_httpd_formatter} + ,{formatter_config, + "%h %l %t \"%r\" %>s %b %R %{msec}T %wm-ppt" + } + ]}. + +put_metadata(PList) -> + MD = lager:md(), + NewMD = put_metadata(PList, MD), + lager:md(NewMD). + +put_metadata([], MD) -> + MD; +put_metadata([{Key, Value}|Rest], MD) -> + NewMD = lists:keystore(Key, 1, MD, {Key, Value}), + put_metadata(Rest, NewMD). diff --git a/src/webmachine_log.erl b/src/webmachine_log.erl index 724e2d06..5a2880bf 100644 --- a/src/webmachine_log.erl +++ b/src/webmachine_log.erl @@ -18,94 +18,10 @@ -module(webmachine_log). --include("webmachine_logger.hrl"). --include("wm_reqdata.hrl"). - --export([add_handler/2, - call/2, - call/3, - datehour/0, - datehour/1, - defer_refresh/1, - delete_handler/1, - fix_log/2, +-export([ fmt_ip/1, - fmtnow/0, - log_access/1, - log_close/3, - log_error/1, - log_error/3, - log_info/1, - log_open/1, - log_open/2, - log_write/2, - maybe_rotate/5, - month/1, - refresh/2, - suffix/1, - zeropad/2, - zone/0]). - -%% @doc Add a handler to receive log events --type add_handler_result() :: ok | {'EXIT', term()} | term(). --spec add_handler(atom() | {atom(), term()}, term()) -> add_handler_result(). -add_handler(Mod, Args) -> - gen_event:add_handler(?EVENT_LOGGER, Mod, Args). - -%% @doc Make a synchronous call directly to a specific event handler -%% module --type error() :: {error, bad_module} | {'EXIT', term()} | term(). --spec call(atom(), term()) -> term() | error(). -call(Mod, Msg) -> - gen_event:call(?EVENT_LOGGER, Mod, Msg). - -%% @doc Make a synchronous call directly to a specific event handler -%% module --spec call(atom(), term(), timeout()) -> term() | error(). -call(Mod, Msg, Timeout) -> - gen_event:call(?EVENT_LOGGER, Mod, Msg, Timeout). - -%% @doc Return a four-tuple containing year, month, day, and hour -%% of the current time. --type datehour() :: {non_neg_integer(), 1..12, 1..31, 0..23}. --spec datehour() -> datehour(). -datehour() -> - datehour(os:timestamp()). - -%% @doc Return a four-tuple containing year, month, day, and hour -%% of the specified time. --spec datehour(erlang:timestamp()) -> datehour(). -datehour(TS) -> - {{Y, M, D}, {H, _, _}} = calendar:now_to_universal_time(TS), - {Y, M, D, H}. - -%% @doc Defer the refresh of a log file. --spec defer_refresh(atom()) -> {ok, timer:tref()} | {error, term()}. -defer_refresh(Mod) -> - {_, {_, M, S}} = calendar:universal_time(), - Time = 1000 * (3600 - ((M * 60) + S)), - timer:apply_after(Time, ?MODULE, refresh, [Mod, os:timestamp()]). - -%% @doc Remove a log handler --type delete_handler_result() :: term() | {error, module_not_found} | {'EXIT', term()}. --spec delete_handler(atom() | {atom(), term()}) -> delete_handler_result(). -delete_handler(Mod) -> - gen_event:delete_handler(?EVENT_LOGGER, Mod, []). - -%% Seek backwards to the last valid log entry --spec fix_log(file:io_device(), non_neg_integer()) -> ok. -fix_log(_FD, 0) -> - ok; -fix_log(FD, 1) -> - {ok, 0} = file:position(FD, 0), - ok; -fix_log(FD, Location) -> - case file:pread(FD, Location - 1, 1) of - {ok, [$\n | _]} -> - ok; - {ok, _} -> - fix_log(FD, Location - 1) - end. + fmtnow/0 + ]). %% @doc Format an IP address or host name -spec fmt_ip(undefined | string() | inet:ip4_address() | inet:ip6_address()) -> string(). @@ -123,73 +39,6 @@ fmtnow() -> io_lib:format("[~2..0w/~s/~4..0w:~2..0w:~2..0w:~2..0w ~s]", [Date,month(Month),Year, Hour, Min, Sec, zone()]). -%% @doc Notify registered log event handler of an access event. --spec log_access(wm_log_data()) -> ok. -log_access(#wm_log_data{}=LogData) -> - gen_event:sync_notify(?EVENT_LOGGER, {log_access, LogData}). - -%% @doc Close a log file. --spec log_close(atom(), string(), file:io_device()) -> ok | {error, term()}. -log_close(Mod, Name, FD) -> - error_logger:info_msg("~p: closing log file: ~s~n", [Mod, Name]), - file:close(FD). - -%% @doc Notify registered log event handler of an error event. --spec log_error(iolist()) -> ok. -log_error(LogMsg) -> - gen_event:sync_notify(?EVENT_LOGGER, {log_error, LogMsg}). - -%% @doc Notify registered log event handler of an error event. --spec log_error(pos_integer(), webmachine_request:t(), term()) -> ok. -log_error(Code, Req, Reason) -> - gen_event:sync_notify(?EVENT_LOGGER, {log_error, Code, Req, Reason}). - -%% @doc Notify registered log event handler of an info event. --spec log_info(iolist()) -> ok. -log_info(LogMsg) -> - gen_event:sync_notify(?EVENT_LOGGER, {log_info, LogMsg}). - -%% @doc Open a new log file for writing --spec log_open(string()) -> {file:io_device(), datehour()}. -log_open(FileName) -> - DateHour = datehour(), - {log_open(FileName, DateHour), DateHour}. - -%% @doc Open a new log file for writing --spec log_open(string(), datehour()) -> file:io_device(). -log_open(FileName, DateHour) -> - LogName = FileName ++ suffix(DateHour), - error_logger:info_msg("opening log file: ~p~n", [LogName]), - ok = filelib:ensure_dir(LogName), - {ok, FD} = file:open(LogName, [read, write, raw]), - {ok, Location} = file:position(FD, eof), - fix_log(FD, Location), - ok = file:truncate(FD), - FD. - --spec log_write(file:io_device(), iolist()) -> ok | {error, term()}. -log_write(FD, IoData) -> - file:write(FD, IoData). - -%% @doc Rotate a log file if the hour it represents -%% has passed. --spec maybe_rotate(atom(), string(), file:io_device(), erlang:timestamp(), datehour()) -> - {datehour(), file:io_device()}. -maybe_rotate(Mod, FileName, Handle, Time, Hour) -> - Rotate = datehour(Time) == Hour, - maybe_rotate(Mod, FileName, Handle, Time, Hour, Rotate). - --spec maybe_rotate(atom(), string(), file:io_device(), erlang:timestamp(), datehour(), boolean()) -> - {datehour(), file:io_device()}. -maybe_rotate(_Mod, _FileName, Handle, _Time, Hour, true) -> - {Hour, Handle}; -maybe_rotate(Mod, FileName, Handle, Time, _Hour, false) -> - NewHour = datehour(Time), - {ok,_} = defer_refresh(Mod), - ok = log_close(Mod, FileName, Handle), - NewHandle = log_open(FileName, NewHour), - {NewHour, NewHandle}. - %% @doc Convert numeric month value to the abbreviation -spec month(1..12) -> string(). month(1) -> @@ -217,31 +66,6 @@ month(11) -> month(12) -> "Dec". -%% @doc Make a synchronous call to instruct a log handler to refresh -%% itself. --spec refresh(atom(), erlang:timestamp()) -> ok | {error, term()}. -refresh(Mod, Time) -> - call(Mod, {refresh, Time}, infinity). - --spec suffix(datehour()) -> string(). -suffix({Y, M, D, H}) -> - YS = zeropad(Y, 4), - MS = zeropad(M, 2), - DS = zeropad(D, 2), - HS = zeropad(H, 2), - lists:flatten([$., YS, $_, MS, $_, DS, $_, HS]). - --spec zeropad(integer(), integer()) -> string(). -zeropad(Num, MinLength) -> - NumStr = integer_to_list(Num), - zeropad_str(NumStr, MinLength - length(NumStr)). - --spec zeropad_str(string(), integer()) -> string(). -zeropad_str(NumStr, Zeros) when Zeros > 0 -> - zeropad_str([$0 | NumStr], Zeros - 1); -zeropad_str(NumStr, _) -> - NumStr. - -spec zone() -> string(). zone() -> Time = erlang:universaltime(), @@ -251,7 +75,6 @@ zone() -> zone((DiffSecs/3600)*100). %% Ugly reformatting code to get times like +0000 and -1300 - -spec zone(float()) -> string(). zone(Val) when Val < 0 -> io_lib:format("-~4..0w", [trunc(abs(Val))]); diff --git a/src/webmachine_logger_watcher.erl b/src/webmachine_logger_watcher.erl deleted file mode 100644 index 816b126a..00000000 --- a/src/webmachine_logger_watcher.erl +++ /dev/null @@ -1,88 +0,0 @@ -%% Copyright (c) 2011-2012 Basho Technologies, Inc. All Rights Reserved. -%% -%% This file is provided to you under the Apache License, -%% Version 2.0 (the "License"); you may not use this file -%% except in compliance with the License. You may obtain -%% a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, -%% software distributed under the License is distributed on an -%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -%% KIND, either express or implied. See the License for the -%% specific language governing permissions and limitations -%% under the License. - -%% @doc A process that does a gen_event:add_sup_handler and attempts to re-add -%% event handlers when they exit. - -%% @private - --module(webmachine_logger_watcher). - --behaviour(gen_server). - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). --endif. - -%% callbacks --export([init/1, - handle_call/3, - handle_cast/2, - handle_info/2, - terminate/2, - code_change/3]). - --export([start_link/3, - start/3]). - --record(state, {module, config, event}). - -start_link(Event, Module, Config) -> - gen_server:start_link(?MODULE, [Event, Module, Config], []). - -start(Event, Module, Config) -> - gen_server:start(?MODULE, [Event, Module, Config], []). - -init([Event, Module, Config]) -> - install_handler(Event, Module, Config), - {ok, #state{event=Event, module=Module, config=Config}}. - -handle_call(_Call, _From, State) -> - {reply, ok, State}. - -handle_cast(_Request, State) -> - {noreply, State}. - -handle_info({gen_event_EXIT, Module, normal}, #state{module=Module} = State) -> - {stop, normal, State}; -handle_info({gen_event_EXIT, Module, shutdown}, #state{module=Module} = State) -> - {stop, normal, State}; -handle_info({gen_event_EXIT, Module, _Reason}, #state{module=Module, - config=Config, event=Event} = State) -> - install_handler(Event, Module, Config), - {noreply, State}; -handle_info(reinstall_handler, #state{module=Module, config=Config, event=Event} = State) -> - install_handler(Event, Module, Config), - {noreply, State}; -handle_info(_Info, State) -> - {noreply, State}. - -terminate(_Reason, _State) -> - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%% internal - -install_handler(Event, Module, Config) -> - case gen_event:add_sup_handler(Event, Module, Config) of - ok -> - ok; - _Error -> - erlang:send_after(5000, self(), reinstall_handler), - ok - end. diff --git a/src/webmachine_logger_watcher_sup.erl b/src/webmachine_logger_watcher_sup.erl deleted file mode 100644 index 9fe96ca7..00000000 --- a/src/webmachine_logger_watcher_sup.erl +++ /dev/null @@ -1,39 +0,0 @@ -%% Copyright (c) 2011-2012 Basho Technologies, Inc. All Rights Reserved. -%% -%% This file is provided to you under the Apache License, -%% Version 2.0 (the "License"); you may not use this file -%% except in compliance with the License. You may obtain -%% a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, -%% software distributed under the License is distributed on an -%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -%% KIND, either express or implied. See the License for the -%% specific language governing permissions and limitations -%% under the License. - -%% @doc A supervisor for monitoring webmachine_logger_handler_watcher processes. - -%% @private - --module(webmachine_logger_watcher_sup). - --behaviour(supervisor). - -%% API --export([start_link/0]). - -%% Callbacks --export([init/1]). - -start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). - -init([]) -> - {ok, {{simple_one_for_one, 10, 60}, - [ - {webmachine_logger_watcher, {webmachine_logger_watcher, start_link, []}, - transient, 5000, worker, [webmachine_logger_watcher]} - ]}}. diff --git a/src/webmachine_mochiweb.erl b/src/webmachine_mochiweb.erl index 6b59daee..893b64c6 100644 --- a/src/webmachine_mochiweb.erl +++ b/src/webmachine_mochiweb.erl @@ -20,7 +20,6 @@ -author('Andy Gross '). -export([start/1, stop/0, stop/1, loop/2, new_webmachine_req/1]). --include("webmachine_logger.hrl"). -include("wm_reqstate.hrl"). -include("wm_reqdata.hrl"). @@ -139,16 +138,19 @@ new_webmachine_req(Request) -> Socket = mochiweb_request:get(socket, Request), InitialReqData = wrq:create(Method,Scheme,Version,RawPath,Headers), - InitialLogData = #wm_log_data{start_time=os:timestamp(), - method=Method, - headers=Headers, - path=RawPath, - version=Version, - response_code=404, - response_length=0}, + InitialLogData = + [ + {wm_start_time, os:timestamp()}, + {wm_method, Method}, + {wm_headers, Headers}, + {wm_path, RawPath}, + {wm_version, Version}, + {wm_response_code, 404}, + {wm_response_length, 0} + ], + webmachine_lager:put_metadata(InitialLogData), InitState = #wm_reqstate{socket=Socket, - log_data=InitialLogData, reqdata=InitialReqData}, InitReq = {webmachine_request,InitState}, @@ -159,16 +161,17 @@ new_webmachine_req(Request) -> {Peer, _ReqState} -> case webmachine_request:get_sock(InitReq) of {ErrorGetSock = {error,_}, ErrorGetSockReqState} -> - LogDataWithPeer = InitialLogData#wm_log_data {peer=Peer}, - ReqStateWithSockErr = - ErrorGetSockReqState#wm_reqstate{log_data=LogDataWithPeer}, - { ErrorGetSock, webmachine_request:new (ReqStateWithSockErr) }; + webmachine_lager:put_metadata( + [{wm_peer, Peer}]), + {ErrorGetSock, webmachine_request:new(ErrorGetSockReqState)}; {Sock, ReqState} -> - ReqData = wrq:set_sock(Sock, wrq:set_peer(Peer, InitialReqData)), - LogData = - InitialLogData#wm_log_data {peer=Peer, sock=Sock}, - webmachine_request:new(ReqState#wm_reqstate{log_data=LogData, - reqdata=ReqData}) + ReqData = wrq:set_sock(Sock, wrq:set_peer(Peer, InitialReqData)), + webmachine_lager:put_metadata( + [ + {wm_peer, Peer}, + {wm_sock, Sock} + ]), + webmachine_request:new(ReqState#wm_reqstate{reqdata=ReqData}) end end. @@ -186,9 +189,8 @@ handle_error(Code, Error, Req) -> {ErrorHTML,Req1} = ErrorHandler:render_error(Code, Req, Error), {ok,Req2} = webmachine_request:append_to_response_body(ErrorHTML, Req1), - {ok,Req3} = webmachine_request:send_response(Code, Req2), - {LogData,_ReqState4} = webmachine_request:log_data(Req3), - spawn(webmachine_log, log_access, [LogData]), + {ok, _Req3} = webmachine_request:send_response(Code, Req2), + wm_access:info(""), ok. get_wm_option(OptName, {WMOptions, OtherOptions}) -> diff --git a/src/webmachine_perf_log_handler.erl b/src/webmachine_perf_log_handler.erl deleted file mode 100644 index ffb50b22..00000000 --- a/src/webmachine_perf_log_handler.erl +++ /dev/null @@ -1,148 +0,0 @@ -%% Copyright (c) 2011-2014 Basho Technologies, Inc. All Rights Reserved. -%% -%% This file is provided to you under the Apache License, -%% Version 2.0 (the "License"); you may not use this file -%% except in compliance with the License. You may obtain -%% a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, -%% software distributed under the License is distributed on an -%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -%% KIND, either express or implied. See the License for the -%% specific language governing permissions and limitations -%% under the License. - -%% @doc Default performance log handler for webmachine - --module(webmachine_perf_log_handler). - --behaviour(gen_event). - -%% gen_event callbacks --export([init/1, - handle_call/2, - handle_event/2, - handle_info/2, - terminate/2, - code_change/3]). - --include("webmachine_logger.hrl"). - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). --endif. - --record(state, {hourstamp, filename, handle}). - --define(FILENAME, "perf.log"). - -%% =================================================================== -%% gen_event callbacks -%% =================================================================== - -%% @private -init([BaseDir]) -> - {ok,_} = webmachine_log:defer_refresh(?MODULE), - FileName = filename:join(BaseDir, ?FILENAME), - {Handle, DateHour} = webmachine_log:log_open(FileName), - {ok, #state{filename=FileName, handle=Handle, hourstamp=DateHour}}. - -%% @private -handle_call({_Label, MRef, get_modules}, State) -> - {ok, {MRef, [?MODULE]}, State}; -handle_call({refresh, Time}, State) -> - {NewHour, NewHandle} = webmachine_log:maybe_rotate(?MODULE, - State#state.filename, - State#state.handle, - Time, - State#state.hourstamp), - {ok, ok, State#state{hourstamp=NewHour, handle=NewHandle}}; -handle_call(_Request, State) -> - {ok, ok, State}. - -%% @private -handle_event({log_access, LogData}, State) -> - {NewHour, NewHandle} = webmachine_log:maybe_rotate(?MODULE, - State#state.filename, - State#state.handle, - os:timestamp(), - State#state.hourstamp), - NewState = State#state{hourstamp=NewHour, handle=NewHandle}, - Msg = format_req(LogData), - _ = webmachine_log:log_write(NewState#state.handle, Msg), - {ok, NewState}; -handle_event(_Event, State) -> - {ok, State}. - -%% @private -handle_info(_Info, State) -> - {ok, State}. - -%% @private -terminate(_Reason, _State) -> - ok. - -%% @private -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%% =================================================================== -%% Internal functions -%% =================================================================== - -format_req(#wm_log_data{resource_module=Mod, - start_time=StartTime, - method=Method, - peer=Peer, - path=Path, - version=Version, - response_code=ResponseCode, - response_length=ResponseLength, - end_time=EndTime, - finish_time=FinishTime}) -> - Time = webmachine_log:fmtnow(), - Status = case ResponseCode of - {Code, _ReasonPhrase} when is_integer(Code) -> - integer_to_list(Code); - _ when is_integer(ResponseCode) -> - integer_to_list(ResponseCode); - _ -> - ResponseCode - end, - Length = integer_to_list(ResponseLength), - TTPD = webmachine_util:now_diff_milliseconds(EndTime, StartTime), - TTPS = webmachine_util:now_diff_milliseconds(FinishTime, EndTime), - fmt_plog(Time, Peer, Method, Path, Version, - Status, Length, atom_to_list(Mod), integer_to_list(TTPD), - integer_to_list(TTPS)). - -fmt_plog(Time, Ip, Method0, Path, {VM,Vm}, Status, Length, Mod, TTPD, TTPS) - when is_atom(Method0) -> - Method = atom_to_list(Method0), - fmt_plog(Time, Ip, Method, Path, {VM,Vm}, Status, Length, Mod, TTPD, TTPS); -fmt_plog(Time, Ip, Method, Path, {VM,Vm}, Status, Length, Mod, TTPD, TTPS) -> - [webmachine_log:fmt_ip(Ip), " - ", [$\s], Time, [$\s, $"], Method, " ", Path, - " HTTP/", integer_to_list(VM), ".", integer_to_list(Vm), [$",$\s], - Status, [$\s], Length, " " , Mod, " ", TTPD, " ", TTPS, $\n]. - - --ifdef(TEST). - -non_standard_method_test() -> - LogData = #wm_log_data{resource_module=foo, - start_time=os:timestamp(), - method="FOO", - peer={127,0,0,1}, - path="/", - version={1,1}, - response_code=501, - response_length=1234, - end_time=os:timestamp(), - finish_time=os:timestamp()}, - LogEntry = format_req(LogData), - ?assert(is_list(LogEntry)), - ok. - --endif. diff --git a/src/webmachine_request.erl b/src/webmachine_request.erl index 7b980bc5..fd297a79 100644 --- a/src/webmachine_request.erl +++ b/src/webmachine_request.erl @@ -96,7 +96,6 @@ log_data/1 ]). --include("webmachine_logger.hrl"). -include("wm_reqstate.hrl"). -include("wm_reqdata.hrl"). @@ -274,9 +273,9 @@ call({send_response, {Code, ReasonPhrase}=CodeAndReason}, Req) _ -> send_response(CodeAndReason, Req) end, - LogData = NewState#wm_reqstate.log_data, - NewLogData = LogData#wm_log_data{finish_time=os:timestamp()}, - {Reply, NewState#wm_reqstate{log_data=NewLogData}}; + webmachine_lager:put_metadata( + [{wm_finish_time, os:timestamp()}]), + {Reply, NewState}; call(resp_body, {?MODULE, ReqState}) -> {wrq:resp_body(ReqState#wm_reqstate.reqdata), ReqState}; call({set_resp_body, Body}, {?MODULE, ReqState}) -> @@ -424,11 +423,12 @@ send_response(Code, PassedState=#wm_reqstate{reqdata=RD}, _Req) -> Length end end, - InitLogData = PassedState#wm_reqstate.log_data, - FinalLogData = InitLogData#wm_log_data{response_code=Code, - response_length=FinalLength}, - {ok, PassedState#wm_reqstate{reqdata=wrq:set_response_code(Code, RD), - log_data=FinalLogData}}. + webmachine_lager:put_metadata( + [ + {wm_response_code, Code}, + {wm_response_length, FinalLength} + ]), + {ok, PassedState#wm_reqstate{reqdata=wrq:set_response_code(Code, RD)}}. %% @doc Infer body length from transfer-encoding and content-length headers. body_length(Req) -> diff --git a/src/webmachine_sup.erl b/src/webmachine_sup.erl index bcf3d348..09cf5b8c 100644 --- a/src/webmachine_sup.erl +++ b/src/webmachine_sup.erl @@ -26,8 +26,6 @@ %% supervisor callbacks -export([init/1]). --include("webmachine_logger.hrl"). - %% @spec start_link() -> ServerRet %% @doc API for starting the supervisor. start_link() -> @@ -59,11 +57,4 @@ init([]) -> {webmachine_router, {webmachine_router, start_link, []}, permanent, 5000, worker, [webmachine_router]}, - LogHandler = - [{webmachine_logger, - {gen_event, start_link, [{local, ?EVENT_LOGGER}]}, - permanent, 5000, worker, [dynamic]}, - {webmachine_logger_watcher_sup, - {webmachine_logger_watcher_sup, start_link, []}, - permanent, 5000, supervisor, [webmachine_logger_watcher_sup]}], - {ok, {{one_for_one, 9, 10}, LogHandler ++ [Router]}}. + {ok, {{one_for_one, 9, 10}, [Router]}}.