1
%% ``The contents of this file are subject to the Erlang Public License,
2
%% Version 1.1, (the "License"); you may not use this file except in
3
%% compliance with the License. You should have received a copy of the
4
%% Erlang Public License along with this software. If not, it can be
5
%% retrieved via the world wide web at http://www.erlang.org/.
7
%% Software distributed under the License is distributed on an "AS IS"
8
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9
%% the License for the specific language governing rights and limitations
12
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
13
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
14
%% AB. All Rights Reserved.''
16
%% $Id: mod_disk_log.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
18
-module(mod_disk_log).
19
-export([do/1,error_log/5,security_log/2,load/2,store/2,remove/1]).
21
-export([report_error/2]).
23
-define(VMODULE,"DISK_LOG").
24
-include("httpd_verbosity.hrl").
26
-include("httpd.hrl").
31
AuthUser = auth_user(Info#mod.data),
33
log_internal_info(Info,Date,Info#mod.data),
34
LogFormat = get_log_format(Info#mod.config_db),
35
case httpd_util:key1search(Info#mod.data,status) of
36
%% A status code has been generated!
37
{StatusCode,PhraseArgs,Reason} ->
38
transfer_log(Info, "-", AuthUser, Date, StatusCode, 0, LogFormat),
41
error_log(Info, Date, Reason, LogFormat);
45
{proceed,Info#mod.data};
46
%% No status code has been generated!
48
case httpd_util:key1search(Info#mod.data,response) of
49
{already_sent,StatusCode,Size} ->
50
transfer_log(Info, "-", AuthUser, Date, StatusCode,
52
{proceed,Info#mod.data};
54
{response, Head, Body} ->
55
Size = httpd_util:key1search(Head, content_length, 0),
56
Code = httpd_util:key1search(Head, code, 200),
57
transfer_log(Info, "-", AuthUser, Date, Code,
59
{proceed,Info#mod.data};
61
{StatusCode,Response} ->
62
transfer_log(Info, "-", AuthUser, Date, 200,
63
httpd_util:flatlength(Response), LogFormat),
64
{proceed,Info#mod.data};
66
transfer_log(Info, "-", AuthUser, Date, 200,
68
{proceed,Info#mod.data}
73
LocalTime = calendar:local_time(),
74
UniversalTime = calendar:universal_time(),
75
Minutes = round(diff_in_minutes(LocalTime,UniversalTime)),
76
{{YYYY,MM,DD},{Hour,Min,Sec}} = LocalTime,
78
io_lib:format("~.2.0w/~.3s/~.4w:~.2.0w:~.2.0w:~.2.0w ~c~.2.0w~.2.0w",
79
[DD,httpd_util:month(MM),YYYY,Hour,Min,Sec,sign(Minutes),
80
abs(Minutes) div 60,abs(Minutes) rem 60]),
83
diff_in_minutes(L,U) ->
84
(calendar:datetime_to_gregorian_seconds(L) -
85
calendar:datetime_to_gregorian_seconds(U))/60.
87
sign(Minutes) when Minutes > 0 ->
93
case httpd_util:key1search(Data,remote_user) of
102
log_internal_info(Info,Date,[]) ->
104
log_internal_info(Info,Date,[{internal_info,Reason}|Rest]) ->
105
Format = get_log_format(Info#mod.config_db),
106
error_log(Info,Date,Reason,Format),
107
log_internal_info(Info,Date,Rest);
108
log_internal_info(Info,Date,[_|Rest]) ->
109
log_internal_info(Info,Date,Rest).
114
transfer_log(Info,RFC931,AuthUser,Date,StatusCode,Bytes,Format) ->
115
case httpd_util:lookup(Info#mod.config_db,transfer_disk_log) of
119
{PortNumber,RemoteHost}=(Info#mod.init_data)#init_data.peername,
120
Entry = io_lib:format("~s ~s ~s [~s] \"~s\" ~w ~w~n",
121
[RemoteHost,RFC931,AuthUser,Date,
122
Info#mod.request_line,StatusCode,Bytes]),
123
write(TransferDiskLog, Entry, Format)
129
error_log(Info, Date, Reason, Format) ->
130
Format=get_log_format(Info#mod.config_db),
131
case httpd_util:lookup(Info#mod.config_db,error_disk_log) of
135
{PortNumber,RemoteHost}=(Info#mod.init_data)#init_data.peername,
137
io_lib:format("[~s] access to ~s failed for ~s, reason: ~p~n",
138
[Date, Info#mod.request_uri,
139
RemoteHost, Reason]),
140
write(ErrorDiskLog, Entry, Format)
143
error_log(SocketType, Socket, ConfigDB, {PortNumber, RemoteHost}, Reason) ->
144
Format = get_log_format(ConfigDB),
145
case httpd_util:lookup(ConfigDB,error_disk_log) of
149
Date = custom_date(),
151
io_lib:format("[~s] server crash for ~s, reason: ~p~n",
152
[Date,RemoteHost,Reason]),
153
write(ErrorDiskLog, Entry, Format),
160
security_log(ConfigDB, Event) ->
161
Format = get_log_format(ConfigDB),
162
case httpd_util:lookup(ConfigDB,security_disk_log) of
166
Date = custom_date(),
167
Entry = io_lib:format("[~s] ~s ~n", [Date, Event]),
168
write(DiskLog, Entry, Format),
172
report_error(ConfigDB, Error) ->
173
Format = get_log_format(ConfigDB),
174
case httpd_util:lookup(ConfigDB, error_disk_log) of
178
Date = custom_date(),
179
Entry = io_lib:format("[~s] reporting error: ~s",[Date,Error]),
180
write(ErrorDiskLog, Entry, Format),
184
%%----------------------------------------------------------------------
185
%% Get the current format of the disklog
186
%%----------------------------------------------------------------------
187
get_log_format(ConfigDB)->
188
httpd_util:lookup(ConfigDB,disk_log_format,external).
197
load([$T,$r,$a,$n,$s,$f,$e,$r,$D,$i,$s,$k,$L,$o,$g,$S,$i,$z,$e,$ |
198
TransferDiskLogSize],[]) ->
199
case regexp:split(TransferDiskLogSize," ") of
200
{ok,[MaxBytes,MaxFiles]} ->
201
case httpd_conf:make_integer(MaxBytes) of
202
{ok,MaxBytesInteger} ->
203
case httpd_conf:make_integer(MaxFiles) of
204
{ok,MaxFilesInteger} ->
205
{ok,[],{transfer_disk_log_size,
206
{MaxBytesInteger,MaxFilesInteger}}};
209
?NICE(httpd_conf:clean(TransferDiskLogSize)++
210
" is an invalid TransferDiskLogSize")}
213
{error,?NICE(httpd_conf:clean(TransferDiskLogSize)++
214
" is an invalid TransferDiskLogSize")}
217
load([$T,$r,$a,$n,$s,$f,$e,$r,$D,$i,$s,$k,$L,$o,$g,$ |TransferDiskLog],[]) ->
218
{ok,[],{transfer_disk_log,httpd_conf:clean(TransferDiskLog)}};
220
load([$E,$r,$r,$o,$r,$D,$i,$s,$k,$L,$o,$g,$S,$i,$z,$e,$ | ErrorDiskLogSize],[]) ->
221
case regexp:split(ErrorDiskLogSize," ") of
222
{ok,[MaxBytes,MaxFiles]} ->
223
case httpd_conf:make_integer(MaxBytes) of
224
{ok,MaxBytesInteger} ->
225
case httpd_conf:make_integer(MaxFiles) of
226
{ok,MaxFilesInteger} ->
227
{ok,[],{error_disk_log_size,
228
{MaxBytesInteger,MaxFilesInteger}}};
230
{error,?NICE(httpd_conf:clean(ErrorDiskLogSize)++
231
" is an invalid ErrorDiskLogSize")}
234
{error,?NICE(httpd_conf:clean(ErrorDiskLogSize)++
235
" is an invalid ErrorDiskLogSize")}
238
load([$E,$r,$r,$o,$r,$D,$i,$s,$k,$L,$o,$g,$ |ErrorDiskLog],[]) ->
239
{ok, [], {error_disk_log, httpd_conf:clean(ErrorDiskLog)}};
241
load([$S,$e,$c,$u,$r,$i,$t,$y,$D,$i,$s,$k,$L,$o,$g,$S,$i,$z,$e,$ |SecurityDiskLogSize],[]) ->
242
case regexp:split(SecurityDiskLogSize, " ") of
243
{ok, [MaxBytes, MaxFiles]} ->
244
case httpd_conf:make_integer(MaxBytes) of
245
{ok, MaxBytesInteger} ->
246
case httpd_conf:make_integer(MaxFiles) of
247
{ok, MaxFilesInteger} ->
248
{ok, [], {security_disk_log_size,
249
{MaxBytesInteger, MaxFilesInteger}}};
251
{error, ?NICE(httpd_conf:clean(SecurityDiskLogSize)++
252
" is an invalid SecurityDiskLogSize")}
255
{error, ?NICE(httpd_conf:clean(SecurityDiskLogSize)++
256
" is an invalid SecurityDiskLogSize")}
259
load([$S,$e,$c,$u,$r,$i,$t,$y,$D,$i,$s,$k,$L,$o,$g,$ |SecurityDiskLog],[]) ->
260
{ok, [], {security_disk_log, httpd_conf:clean(SecurityDiskLog)}};
262
load([$D,$i,$s,$k,$L,$o,$g,$F,$o,$r,$m,$a,$t,$ |Format],[]) ->
263
case httpd_conf:clean(Format) of
265
{ok, [], {disk_log_format,internal}};
267
{ok, [], {disk_log_format,external}};
269
{ok, [], {disk_log_format,external}}
274
store({transfer_disk_log,TransferDiskLog},ConfigList) ->
275
case create_disk_log(TransferDiskLog, transfer_disk_log_size, ConfigList) of
277
{ok,{transfer_disk_log,TransferDB}};
281
store({security_disk_log,SecurityDiskLog},ConfigList) ->
282
case create_disk_log(SecurityDiskLog, security_disk_log_size, ConfigList) of
284
{ok,{security_disk_log,SecurityDB}};
288
store({error_disk_log,ErrorDiskLog},ConfigList) ->
289
case create_disk_log(ErrorDiskLog, error_disk_log_size, ConfigList) of
291
{ok,{error_disk_log,ErrorDB}};
297
%%----------------------------------------------------------------------
298
%% Open or creates the disklogs
299
%%----------------------------------------------------------------------
300
log_size(ConfigList, Tag) ->
301
httpd_util:key1search(ConfigList, Tag, {500*1024,8}).
303
create_disk_log(LogFile, SizeTag, ConfigList) ->
304
Filename = httpd_conf:clean(LogFile),
305
{MaxBytes, MaxFiles} = log_size(ConfigList, SizeTag),
306
case filename:pathtype(Filename) of
308
create_disk_log(Filename, MaxBytes, MaxFiles, ConfigList);
310
create_disk_log(Filename, MaxBytes, MaxFiles, ConfigList);
312
case httpd_util:key1search(ConfigList,server_root) of
316
" is an invalid ErrorLog beacuse ServerRoot is not defined")};
318
AbsoluteFilename = filename:join(ServerRoot,Filename),
319
create_disk_log(AbsoluteFilename, MaxBytes, MaxFiles,
324
create_disk_log(Filename, MaxBytes, MaxFiles, ConfigList) ->
325
Format = httpd_util:key1search(ConfigList, disk_log_format, external),
326
open(Filename, MaxBytes, MaxFiles, Format).
332
lists:foreach(fun([DiskLog]) -> close(DiskLog) end,
333
ets:match(ConfigDB,{transfer_disk_log,'$1'})),
334
lists:foreach(fun([DiskLog]) -> close(DiskLog) end,
335
ets:match(ConfigDB,{error_disk_log,'$1'})),
340
%% Some disk_log wrapper functions:
343
%%----------------------------------------------------------------------
345
%% Description: Open a disk log file.
346
%% Control which format the disk log will be in. The external file
347
%% format is used as default since that format was used by older
348
%% implementations of inets.
350
%% When the internal disk log format is used, we will do some extra
351
%% controls. If the files are valid, try to repair them and if
352
%% thats not possible, truncate.
353
%%----------------------------------------------------------------------
355
open(Filename, MaxBytes, MaxFiles, internal) ->
356
Opts = [{format, internal}, {repair, truncate}],
357
open1(Filename, MaxBytes, MaxFiles, Opts);
358
open(Filename, MaxBytes, MaxFiles, _) ->
359
Opts = [{format, external}],
360
open1(Filename, MaxBytes, MaxFiles, Opts).
362
open1(Filename, MaxBytes, MaxFiles, Opts0) ->
363
Opts1 = [{name, Filename}, {file, Filename}, {type, wrap}] ++ Opts0,
364
case open2(Opts1, {MaxBytes, MaxFiles}) of
368
?vlog("failed opening disk log with args:"
374
"~n ~p", [Filename, MaxBytes, MaxFiles, Opts0, Reason]),
376
?NICE("Can't create " ++ Filename ++
377
lists:flatten(io_lib:format(", ~p",[Reason])))};
379
{error, ?NICE("Can't create "++Filename)}
383
case disk_log:open(Opts) of
384
{error, {badarg, size}} ->
385
%% File did not exist, add the size option and try again
386
disk_log:open([{size, Size} | Opts]);
392
%%----------------------------------------------------------------------
393
%% Actually writes the entry to the disk_log. If the log is an
394
%% internal disk_log write it with log otherwise with blog.
395
%%----------------------------------------------------------------------
396
write(Log, Entry, internal) ->
397
disk_log:log(Log, Entry);
399
write(Log, Entry, _) ->
400
disk_log:blog(Log, Entry).
402
%% Close the log file