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