1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2008-2016. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20%% 21-module(httpd_log). 22 23-include("httpd.hrl"). 24 25-export([access_entry/8, error_entry/5, error_report_entry/5, 26 security_entry/5]). 27 28 29%%%========================================================================= 30%%% Internal Application API 31%%%========================================================================= 32 33-spec access_entry(Log :: term(), % Id of the log 34 NoLog :: term(), % What to return when no log is found 35 Info :: #mod{}, 36 RFC931 :: string(), 37 AuthUser :: string(), 38 Date :: string(), 39 StatusCode :: pos_integer(), 40 Size :: 0 | pos_integer() | string()) -> 41 {Log :: atom() | pid(), Entry :: string()} | term() . 42 43%% Somethime the size in the form of the content_length is put here, which 44%% is actually in the form of a string 45%% So it can either be the size as an integer, the size as a string 46%% or, worst case scenario, bytes. 47access_entry(Log, NoLog, Info, RFC931, AuthUser, Date, StatusCode, 48 SizeStrOrBytes) 49 when is_list(SizeStrOrBytes) -> 50 Size = 51 case (catch list_to_integer(SizeStrOrBytes)) of 52 I when is_integer(I) -> 53 %% This is from using the content_length (which is a string) 54 I; 55 _ -> 56 %% This is better than nothing 57 httpd_util:flatlength(SizeStrOrBytes) 58 end, 59 access_entry(Log, NoLog, Info, RFC931, AuthUser, Date, StatusCode, Size); 60access_entry(Log, NoLog, 61 #mod{config_db = ConfigDB, 62 init_data = #init_data{peername = {_, RemoteHost}}, 63 request_line = RequestLine, 64 parsed_header = Headers}, 65 RFC931, AuthUser, Date, StatusCode, Size) -> 66 MakeEntry = 67 fun() -> 68 do_access_entry(ConfigDB, Headers, RequestLine, 69 RemoteHost, RFC931, AuthUser, 70 Date, StatusCode, Size) 71 end, 72 log_entry(Log, NoLog, ConfigDB, MakeEntry). 73 74 75-spec error_entry(Log :: term(), % Id of the log 76 NoLog :: term(), % What to return when no log is found 77 Info :: #mod{}, 78 Date :: string(), 79 Reason :: term()) -> 80 {Log :: atom() | pid(), Entry :: string()} | term(). 81 82error_entry(Log, NoLog, 83 #mod{config_db = ConfigDB, 84 init_data = #init_data{peername = {_, RemoteHost}}, 85 request_uri = URI}, Date, Reason) -> 86 MakeEntry = 87 fun() -> 88 do_error_entry(ConfigDB, RemoteHost, URI, Date, Reason) 89 end, 90 log_entry(Log, NoLog, ConfigDB, MakeEntry). 91 92 93-spec error_report_entry(Log :: term(), 94 NoLog :: term(), 95 ConfigDB :: term(), 96 Date :: string(), 97 ErrroStr :: string()) -> 98 {Log :: atom() | pid(), Entry :: string()} | term(). 99 100error_report_entry(Log, NoLog, ConfigDb, Date, ErrorStr) -> 101 MakeEntry = fun() -> io_lib:format("[~s], ~s~n", [Date, ErrorStr]) end, 102 log_entry(Log, NoLog, ConfigDb, MakeEntry). 103 104 105-spec security_entry(Log :: term(), 106 NoLog :: term(), 107 ConfigDB :: term(), 108 Date :: string(), 109 Reason :: term()) -> 110 {Log :: atom() | pid(), Entry :: string()} | term(). 111 112security_entry(Log, NoLog, #mod{config_db = ConfigDB}, Date, Reason) -> 113 MakeEntry = fun() -> io_lib:format("[~s] ~s~n", [Date, Reason]) end, 114 log_entry(Log, NoLog, ConfigDB, MakeEntry). 115 116 117log_entry(Log, NoLog, ConfigDb, MakeEntry) when is_function(MakeEntry) -> 118 case httpd_util:lookup(ConfigDb, Log) of 119 undefined -> 120 NoLog; 121 LogRef -> 122 {LogRef, MakeEntry()} 123 end. 124 125 126%%%======================================================================== 127%%% Internal functions 128%%%======================================================================== 129 130do_access_entry(ConfigDB, Headers, RequestLine, 131 RemoteHost, RFC931, AuthUser, Date, StatusCode, 132 Size) -> 133 case httpd_util:lookup(ConfigDB, log_format, common) of 134 common -> 135 lists:flatten(io_lib:format("~s ~s ~s [~s] \"~s\" ~w ~w~n", 136 [RemoteHost, RFC931, AuthUser, Date, 137 RequestLine, 138 StatusCode, Size])); 139 combined -> 140 Referer = 141 proplists:get_value("referer", Headers, "-"), 142 UserAgent = 143 proplists:get_value("user-agent", 144 Headers, "-"), 145 io_lib:format("~s ~s ~s [~s] \"~s\" ~w ~w ~s ~s~n", 146 [RemoteHost, RFC931, AuthUser, Date, 147 RequestLine, StatusCode, Size, 148 Referer, UserAgent]) 149 end. 150 151 152do_error_entry(ConfigDB, RemoteHost, undefined, Date, Reason) -> 153 case httpd_util:lookup(ConfigDB, error_log_format, pretty) of 154 pretty -> 155 io_lib:format("[~s] server crash for ~s, reason: ~n~p~n~n", 156 [Date, RemoteHost, Reason]); 157 compact -> 158 io_lib:format("[~s] server crash for ~s, reason: ~w~n", 159 [Date, RemoteHost, Reason]) 160 161 end; 162 163do_error_entry(ConfigDB, RemoteHost, URI, Date, Reason) -> 164 case httpd_util:lookup(ConfigDB, error_log_format, pretty) of 165 pretty -> 166 io_lib:format("[~s] access to ~s failed for ~s, reason: ~n~p~n", 167 [Date, URI, RemoteHost, Reason]); 168 compact -> 169 io_lib:format( "[~s] access to ~s failed for ~s, reason: ~w~n", 170 [Date, URI, RemoteHost, Reason]) 171 end. 172