File Coverage

blib/lib/FAQ/OMatic/Log.pm
Criterion Covered Total %
statement 9 136 6.6
branch 0 52 0.0
condition 0 17 0.0
subroutine 3 12 25.0
pod 0 9 0.0
total 12 226 5.3


line stmt bran cond sub pod time code
1             ##############################################################################
2             # The Faq-O-Matic is Copyright 1997 by Jon Howell, all rights reserved. #
3             # #
4             # This program is free software; you can redistribute it and/or #
5             # modify it under the terms of the GNU General Public License #
6             # as published by the Free Software Foundation; either version 2 #
7             # of the License, or (at your option) any later version. #
8             # #
9             # This program is distributed in the hope that it will be useful, #
10             # but WITHOUT ANY WARRANTY; without even the implied warranty of #
11             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
12             # GNU General Public License for more details. #
13             # #
14             # You should have received a copy of the GNU General Public License #
15             # along with this program; if not, write to the Free Software #
16             # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.#
17             # #
18             # Jon Howell can be contacted at: #
19             # 6211 Sudikoff Lab, Dartmouth College #
20             # Hanover, NH 03755-3510 #
21             # jonh@cs.dartmouth.edu #
22             # #
23             # An electronic copy of the GPL is available at: #
24             # http://www.gnu.org/copyleft/gpl.html #
25             # #
26             ##############################################################################
27              
28 1     1   7 use strict;
  1         2  
  1         56  
29              
30             ##
31             ## FaqLog.pm -- access logging facilities
32             ##
33              
34             package FAQ::OMatic::Log;
35              
36 1     1   7 use FAQ::OMatic;
  1         3  
  1         22  
37 1     1   1003 use Time::Local;
  1         1522  
  1         1814  
38              
39             # given 'YYYY-MM-DD' and a number of days, return a 'YYYY-MM-DD' that far
40             # away.
41             sub adddays {
42 0     0 0   my $date = shift; # YYYY-MM-DD
43 0           my $daydiff = shift; # int
44 0           my ($year,$mo,$day) = split('-',$date);
45 0           my (@localt) = (0,0,9,$day,$mo-1,$year-1900);
46             # I use 9:00 AM because 24 hours before midnight 1997-04-07 is
47             # 11 pm 1997-04-05, which is not what I meant. Darn DST. Watch
48             # out for that.
49              
50 0           my $unixtime = Time::Local::timelocal(@localt);
51 0           my ($sec2,$min2,$hr2,$day2,$mo2,$yr2,$wday2,$yday2,$isdst2) =
52             localtime($unixtime+3600*24*$daydiff);
53 0           return sprintf("%04d-%02d-%02d",
54             $yr2+1900, $mo2+1, $day2);
55             }
56              
57             # man, why doesn't anybody ever include this or max or min in their
58             # dumb libraries?
59             sub round {
60 0     0 0   my $arg = shift;
61 0 0         return ($arg < 0) ? int($arg-0.5) : int($arg+0.5);
62             }
63              
64             # give the difference in days between two given days in 'YYYY-MM-DD' form
65             sub subTwoDays {
66 0     0 0   my $day1 = shift; # YYYY-MM-DD
67 0           my $day2 = shift; # YYYY-MM-DD
68              
69 0           my ($y,$m,$d) = split('-', $day1);
70 0           my @localt = (0,0,9,$d,$m-1,$y-1900);
71 0           my $unixt1 = Time::Local::timelocal(@localt);
72              
73 0           my ($y2,$m2,$d2) = split('-', $day2);
74 0           my @localt2 = (0,0,9,$d2,$m2-1,$y2-1900);
75 0           my $unixt2 = Time::Local::timelocal(@localt2);
76              
77 0           return round(($unixt1 - $unixt2)/86400);
78             }
79              
80             # return today in 'YYYY-MM-DD' form
81             sub numericToday {
82 0     0 0   my ($sec,$min,$hr,$day,$mo,$yr,$wday,$yday,$isdst) = localtime(time());
83 0           return sprintf("%04d-%02d-%02d",
84             $yr+1900, $mo+1, $day);
85             }
86              
87             # return today in 'YYYY-MM-DD-HH-MM' form
88             sub numericDate {
89 0     0 0   my ($sec,$min,$hr,$day,$mo,$yr,$wday,$yday,$isdst) = localtime(time());
90 0           return sprintf("%04d-%02d-%02d-%02d-%02d-%02d",
91             $yr+1900, $mo+1, $day, $hr, $min, $sec);
92             }
93              
94             sub logEvent {
95 0     0 0   my $params = shift;
96              
97 0           my $date = numericDate();
98 0   0       my $host = $ENV{'REMOTE_HOST'} || 'unknown-host';
99 0 0         $host = '-' if ($host eq '');
100 0           my $prog = FAQ::OMatic::commandName();
101 0   0       my $args = $params->{'file'} || '';
102 0   0       my $browser = $ENV{'HTTP_USER_AGENT'} || 'unknown-agent';
103 0           $browser =~ s/\s//g;
104              
105 0 0         $args .= "/".$params->{'partnum'}
106             if (defined $params->{'partnum'});
107              
108 0           my $logfile = $FAQ::OMatic::Config::metaDir."/".numericToday().".rawlog";
109 0 0         if (not open LOG, ">>$logfile") {
110 0           FAQ::OMatic::gripe('problem',
111             "FAQ::OMatic::Log::logEvent: The access logging system is "
112             ."not working. open failed ($!)");
113 0           return;
114             }
115 0           print LOG "$date $host $prog $args $browser\n";
116 0           close LOG;
117             }
118              
119             sub summarizeDay {
120 0     0 0   my $date = shift;
121 0 0         $date = numericToday() if (not $date); # summarize today
122 0           my $prevdate = adddays($date, -1);
123 0           my %uniquehosts;
124              
125 0           $date =~ m/([\d-]*)/; # untaint $date
126 0           $date = $1;
127              
128             #$ENV{'IFS'} = '';
129             #$ENV{'PATH'} = '';
130             # First, copy the unique hosts database from the previous day to today
131 0 0         if ($FAQ::OMatic::Config::statUniqueHosts) {
132 0           my $dbfile;
133 0           foreach $dbfile (FAQ::OMatic::safeGlob($FAQ::OMatic::Config::metaDir,
134             "^$prevdate.uhdb")) {
135 0           my $newname = $dbfile;
136 0           $newname =~ s#/$prevdate#/$date#;
137 0           my @msrc = FAQ::OMatic::mySystem("cp $dbfile $newname");
138 0 0         if (scalar(@msrc)) {
139 0           FAQ::OMatic::gripe('note',
140             "FAQ::OMatic::Log::summarizeDay: cp $dbfile $newname failed: "
141             .join(', ', @msrc));
142             # assume yesterday is just plain broken, and start fresh
143 0           my $file;
144 0           foreach $file (FAQ::OMatic::safeGlob(
145             $FAQ::OMatic::Config::metaDir,
146             "^$date.uhdb")) {
147 0           unlink $file;
148             }
149              
150             # touch the dbm files so we'll see them later
151 0 0         if (not dbmopen(%uniquehosts,
152             "$FAQ::OMatic::Config::metaDir/$date.uhdb", 0600)) {
153 0           FAQ::OMatic::gripe('abort',
154             "FAQ::OMatic::Log::summarizeDay: Can't create $FAQ::OMatic::Config::metaDir/$date.uhdb");
155             }
156 0           dbmclose %uniquehosts;
157 0           last;
158             }
159             }
160             }
161              
162             # now open $date's dbfile and insert the new hosts as we compute the
163             # other statistics for the day.
164 0 0         if ($FAQ::OMatic::Config::statUniqueHosts) {
165 0 0         if (not dbmopen(%uniquehosts,
166             "$FAQ::OMatic::Config::metaDir/$date.uhdb", 0400)) {
167 0           FAQ::OMatic::gripe('abort',
168             "FAQ::OMatic::Log::summarizeDay: Couldn't open "
169             ."dbm file $FAQ::OMatic::Config::metaDir/$date.uhdb. ($!)");
170             }
171             }
172              
173             # recycle nice hashed property mechanism of FAQ::OMatic::Items for summaries
174 0           my $oldItem = new FAQ::OMatic::Item("$prevdate.smry", $FAQ::OMatic::Config::metaDir);
175 0           my $item = new FAQ::OMatic::Item();
176 0           $item->setProperty('Title', 'Faq-O-Matic Access Summary');
177              
178             # treat missing logs as very uninteresting days
179 0 0         if (open LOG, "$FAQ::OMatic::Config::metaDir/$date.rawlog") {
180 0           while (defined($_=)) {
181 0           chomp;
182 0           my ($date,$host,$op,$arg) = split(' ');
183 0 0         $host = '' if (not defined $host);
184             #$op =~ s/\.pl$//; # '.pl' suffix is ugly, 'pl' is worse
185 0           $op =~ s/\W//g; # prevent bogus property keys
186 0 0         if ($FAQ::OMatic::Config::statUniqueHosts) {
187             # TODO: still not sure how to keep this from producing
188             # bogus warnings.
189 0           $uniquehosts{$host} = 1;
190             }
191 0           $item->{"Operation-$op"}++;
192 0           $item->{'Hits'}++;
193             }
194 0           close LOG;
195             }
196              
197             # store unique hosts stats
198 0 0         if ($FAQ::OMatic::Config::statUniqueHosts) {
199 0   0       my $oldCum = $oldItem->{'CumUniqueHosts'} || 0;
200 0           $item->{'CumUniqueHosts'} = scalar(keys %uniquehosts);
201 0           $item->{'UniqueHosts'} = $item->{'CumUniqueHosts'} - $oldCum;
202             }
203              
204             # compute cumulative stats for Operations and Hits
205 0           my %opnames=('Hits'=>1);
206 0           my $key;
207 0           foreach $key (keys %{$oldItem}) {
  0            
208 0 0         $opnames{$key}=1 if ($key =~ m/^Oper/);
209 0           $key =~ s/^Cum//;
210 0 0         $opnames{$key}=1 if ($key =~ m/^Oper/);
211             }
212 0           foreach $key (keys %{$item}) {
  0            
213 0 0         $opnames{$key}=1 if ($key =~ m/^Oper/);
214             }
215 0           foreach $key (keys %opnames) {
216 0   0       my $newv = $item->{$key} || 0;
217 0   0       my $oldc = $oldItem->{"Cum$key"} || 0;
218 0           $item->{"Cum$key"} = $newv + $oldc;
219 0 0         $item->{$key} = 0 if (not defined $item->{$key});
220             }
221              
222             # compute derived stats
223 0 0 0       if (($item->{'CumUniqueHosts'}||0) != 0) {
224 0           $item->{'HitsPerHost'} = $item->{'CumHits'} / $item->{'CumUniqueHosts'};
225             } else {
226 0           $item->{'HitsPerHost'} = 0;
227             }
228              
229 0           $date =~ m/^([\d-]*)$/;
230 0           $date = $1;
231 0           $item->saveToFile("$date.smry", $FAQ::OMatic::Config::metaDir);
232 0 0         if ($FAQ::OMatic::Config::statUniqueHosts) {
233 0           dbmclose(%uniquehosts);
234             #dbmclose %uniquehosts
235             }
236             }
237              
238             # return the 'YYYY-MM-DD' of the earliest .smry file in metaDir.
239             sub earliestSmry {
240 0     0 0   my $direntry;
241             my $earliest;
242 0           undef $earliest;
243              
244             # check for a hint
245 0 0         if (open(HINT, "<$FAQ::OMatic::Config::metaDir/earliestLogHint")) {
246 0           $earliest = ;
247 0           chomp $earliest;
248 0           close HINT;
249             }
250              
251             # make sure the hint is valid
252 0 0 0       if ((not defined $earliest) or
253             (not -f "$FAQ::OMatic::Config::metaDir/$earliest.smry")) {
254              
255             # rediscover the earliest .smry
256 0           $earliest = 'Z'; # should sort before anything
257 0           opendir META, $FAQ::OMatic::Config::metaDir;
258 0           while (defined($direntry = readdir META)) {
259 0 0         next if (not $direntry =~ m/\.smry$/);
260 0           $direntry =~ s/\.smry$//;
261 0 0         $earliest = $direntry if ($direntry lt $earliest);
262             }
263 0           closedir META;
264 0 0         return (undef) if ($earliest eq 'Z');
265              
266             # write out the hint
267 0 0         if (open(HINT, ">$FAQ::OMatic::Config::metaDir/earliestLogHint")) {
268 0           print HINT "$earliest\n";
269 0           close HINT;
270             }
271             }
272              
273 0           return $earliest;
274             }
275              
276             sub rebuildAllSummaries {
277             # TODO:
278             # notice we start at earliestSmry -- not the earliest rawlog. If
279             # we weren't lame, we'd figure out the earliest rawlog and work from
280             # there.
281 0     0 0   my $earliest = earliestSmry();
282 0           my $today = numericToday();
283 0           my $dayi;
284              
285 0           for ($dayi=$earliest; $dayi lt $today; $dayi = adddays($dayi, 1)) {
286 0           summarizeDay($dayi);
287              
288 0           my $twoDaysAgo = adddays($dayi, -2);
289             # delete those uhdbs
290             }
291             }
292              
293             1;