一、环境:freebsd(linux)+apache+php+mysql
二、概述:对于使用apache作为web服务器的网站来说,apache的access_log是进行流量统计的重要的依据。
根据access_log文件来进行流量统计的方法有两种,一种是直接在程序中对log文件进行分析,这种
方法的一个明显的缺点是速度比较慢,比较耗费资源,因为一个浏览量较高的网站的log文件是非常
大的,一个典型的例子是老外用c写的analog;另一种方法是把每日的log文件记录导入数据库,再
用程序对数据库进行分析,这种方法相对前者来说有比较大的速度和性能上的优势。这里我写的这个 程序的功能虽然不及analog等强大,但是……,自己的东西用的还是比较舒服的,呵呵,不过这只是
我的方法,中间可能会有一些不对的地方,请大家指正。
三、具体实现:主要利用freebsd(linux)的crontab每天自动定时执行程序产生每日的html格式的流量报表文件。
注意:以下命令以apache默认的log文件格式处理,如果你定制了不同的log文件格式,具体命令要根据具体情况修改。
并且要以cgi方式编译一次php,以便以命令行方式运行php程序。
1.your_log数据表结构
根据apache默认的log文件中的子段,可以很容易地设计your_log数据表的结构:
CREATE TABLE your_log (
host varchar(15) NOT NULL,
ident varchar(8) NOT NULL,
auth varchar(20) NOT NULL,
time varchar(24) NOT NULL,
zone varchar(8) NOT NULL,
method varchar(6) NOT NULL,
file varchar(255) NOT NULL,
protocol varchar(10) NOT NULL,
status varchar(5) NOT NULL,
bytes int(10) DEFAULT '0' NOT NULL,
KEY host (host),
KEY file (file),
KEY status (status),
KEY bytes (bytes)
);
2.分离log文件,形成每天一个log文件,以日期为目录,文件名自定(假设是your_log)
考虑这个步骤时,想到过利用linux的logrotate功能和apache自带的rotatelogs功能,但是由于我用的是freebsd,没有找到
logrotate命令,而apache的rotatelogs我用之后没有发现能够指定生成文件名的功能,只能用timestamp作为文件名,对于
程序来说不好处理,所以我自己想了以下的方法,如果有人认为这样不好而能提供更好的方法,请不吝赐教:)
mkdir /your_log_dir/`date -v-1d "+%Y%m%d"` #建立每天的log文件目录
cat /your_apache_log_dir/access_log | grep `date -v-1d "+%d/%b/%Y"` > your_log_dir/`date -v-1d "+%Y%m%d"`/your_log
将此二命令放入crontab,我设为每天凌晨1点自动运行(时间可自己定,但是一定要在0点以后)。
3.将日报文件导入数据库
/your_mysql_bin_dir/mysqlimport -u mysql_username -p mysql_password -d --fields-terminated-by=' ' your_db_name your_log_dir/`date -v-1d "+%Y%m%d"`/your_log
这个命令先将your_db_name数据库中的your_log数据表清空,再从已生成的your_log文本文件里导入数据,以空格为字段分界。
将此命令放入crontab,我设为每天凌晨1:30自动运行(时间可自己定,但是一定要在执行第2步以后,先后顺序不能颠倒)。
4.生成每日流量统计日报html文件的php程序
a.假设网站的document_root下有以下四个子目录(栏目):aaa,bbb,ccc,ddd,如有更多,依此类推;
b.这里只在日报页面上计算以下几个统计数字:数据总流量、总请求次数、访问主机数、总pageview数量、
各个栏目之pageview数量、每小时的pageview数量;
c.根据RFC2616标准,请求状态编码中以4和5开头的均为错误或无效请求,在统计时应过滤掉这些数字;
d.这里我假设整个网站以php构建,计算流量时只考虑*.php和*.html文件以及纯目录请求的次数。
e.可以根据个人的需要增加很多统计数字,如工作时间与非工作时间的流量等等,这里就不赘述了,请大家自己考虑吧:)
[dreport.php代码文件]
//--连接数据库--
$db=@mysql_connect("localhost","user","password") or die(mysql_error());
@mysql_select_db("your_db_name",$db) or die(mysql_error());
//--取前一天的timestamp--
$yestoday_array=getdate(time()-24*60*60);
$start_time=mktime(0,0,0,$yestoday_array["mon"],$yestoday_array["mday"],$yestoday_array["year"]);
$end_time=mktime(23,59,59,$yestoday_array["mon"],$yestoday_array["mday"],$yestoday_array["year"]);
//--计算总数据流量--
$bytes=0;
$sql0="select bytes from your_log";
$res0=mysql_query($sql0) or die(mysql_error());
while($row0=@mysql_fetch_row($res0)){
$bytes+=$row0[0];
}
//--计算总请求次数--
$sql1="select count(host) from your_log";
$res1=mysql_query($sql1) or die(mysql_error());
$row1=@mysql_fetch_row($res1);
$hits=$row1[0];
//--计算访问主机数量--
$sql2="select distinct host from your_log";
$res2=mysql_query($sql2) or die(mysql_error());
$ips=@mysql_num_rows($res2);
//--计算总pageviews--
$sql4="select count(host) from your_log where (file like '%.html%' ";
$sql4.="or file like '%.php%' or file not like '%.%') ";
$sql4.="and left(status,1) <> '4' and left(status,1) <> '5'";
$res4=mysql_query($sql4) or die(mysql_error());
$row4=@mysql_fetch_row($res4);
$pageviews=$row4[0];
//--计算aaa栏目的pageviews--
$sql5="select count(host) from your_log where file like '%/aaa/%' ";
$sql5.="and (file like '%.php%' or file like '%.html%' or file not like '%.%') ";
$sql5.="and left(status,1) <> '4' and left(status,1) <> '5'";
$res5=mysql_query($sql5) or die(mysql_error());
$row5=@mysql_fetch_row($res5);
$aaapvs=$row5[0];
//--计算bbb栏目的pageviews--
$sql6="select count(host) from your_log where file like '%/bbb/%' ";
$sql6.="and (file like '%.php%' or file like '%.html%' or file not like '%.%') ";
$sql6.="and left(status,1) <> '4' and left(status,1) <> '5'";
$res6=mysql_query($sql6) or die(mysql_error());
$row6=@mysql_fetch_row($res6);
$bbbpvs=$row6[0];
//--计算ccc栏目的pageviews--
$sql7="select count(host) from your_log where file like '%/ccc/%' ";
$sql7.="and (file like '%.php%' or file like '%.html%' or file not like '%.%') ";
$sql7.="and left(status,1) <> '4' and left(status,1) <> '5'";
$res7=mysql_query($sql7) or die(mysql_error());
$row7=@mysql_fetch_row($res7);
$cccpvs=$row7[0];
//--计算ddd栏目的pageviews--
$sql8="select count(host) from your_log where file like '%/ddd/%' ";
$sql8.="and (file like '%.php%' or file like '%.html%' or file not like '%.%') ";
$sql8.="and left(status,1) <> '4' and left(status,1) <> '5'";
$res8=mysql_query($sql8) or die(mysql_error());
$row8=@mysql_fetch_row($res8);
$dddpvs=$row8[0];
//--计算首页的pageviews--
$sqlg="select count(host) from your_log where (file = '/' or file = '/index.html') ";
$sqlg.="and left(status,1) <> '4' and left(status,1) <> '5'";
$resg=mysql_query($sqlg) or die(mysql_error());
$rowg=@mysql_fetch_row($resg);
$indexpvs=$rowg[0];
//--report是生成日报文件的字符串--
$report="n";
$report.="n";
$report.="n"; $report.="流量统计日报n";
$report.="n";
$report.="n";
$report.="时间:".date("Y-m-d H:i:s",$start_time)." -- ".date("Y-m-d H:i:s",$end_time)."
n";
$report.="PageViews:n";
$report.="数据吞吐总量:".$bytes." Bytes 请求次数:".$hits." IP主机数:".$ips." n";
$report.=" PageViews:".$pageviews." n";
$report.="各个栏目PageViews: n";
$report.="n";
$report.=" n";
$report.=" n";
$report.=" / | n";
$report.=" 首页 | n";
$report.=" ".$indexpvs." | n";
$report.=" n";
$report.=" n";
$report.=" /aaa/ | n";
$report.=" aaa栏目 | n";
$report.=" ".$aaapvs." | n";
$report.=" n";
$report.=" n";
$report.=" /bbb/ | n";
$report.=" bbb栏目 | n";
$report.=" ".$bbbpvs." | n";
$report.=" n";
$report.=" n";
$report.=" /ccc/ | n";
$report.=" ccc栏目 | n";
$report.=" ".$cccpvs." | n";
$report.=" n";
$report.=" n";
$report.=" /ddd/ | n";
$report.=" ddd栏目 | n";
$report.=" ".$dddpvs." | n";
$report.=" n";
$report.=" n";
$report.=" | n";
$report.=" 总数 | n";
$report.=" ".$pageviews." | n";
$report.=" n";
$report.=" n";
$report.=" n";
$report.="每小时PageViews: n";
$report.="n";
$report.=" n";
$report.=" n";
$report.=" n";
$report.=" Hour | n";
$report.=" Page Views | n";
$report.=" n";
//--每小时的pageview--
for($i=0;$i<24;$i++){
$date_temp=date("[d/M/Y:",time()-24*60*60).sprintf("d",$i);
$sqlh="select count(host) from your_log where (file like '%.html%' or file like '%.php%' ";
$sqlh.="or file not like '%.%') and time like '$date_temp%' ";
$sqlh.="and left(status,1) <> '4' and left(status,1) <> '5'";
$resh=mysql_query($sqlh) or die(mysql_error());
|
|
|
|
$rowh=@mysql_fetch_row($resh);
$report.=" n";
$report.=" ".sprintf("d",$i).":00 - ".sprintf("d",$i).":59 | n";
$report.=" ".$rowh[0]." | n";
$report.=" n";
}
//--关闭数据库--
mysql_close();
$report.=" | n";
$report.="n";
$report.="HTML>";
//--生成每天的流量统计html页面文件,以日期为文件名--
$report_file="/your_report_dir/".date("Ymd",time()-24*60*60).".html";
$fp=fopen($report_file,"w");
fwrite($fp,$report);
fclose($fp);
?>
5.每天凌晨执行2、3步后执行此程序,将自动产生前一天的流量统计html页面
/your_php_bin_dir/php /some_dir/dreport.php
将此命令放入crontab,我设为每天凌晨2:00自动运行(时间可自己定,但是一定要在第3步以后,先后顺序不能颠倒)。
四、结束语
终于要写完了,这里还要补充一些东西。对于流量非常大的大型网站,可能并不适合把以上的步骤都放在web服务器上
自动定时运行,因为可能会对服务器造成比较大的负荷,所以当遇到这种情况时,也许用php来完成这个任务并不是很好的
选择,可以考虑用效率最高的c/c++来写程序,还有就是可以考虑把apache的日志文件下载到本地的服务器,再用以上方法
来完成任务。还要提醒朋友们,这里主要阐述的是一个解决方案、一种思路,遇到不同的情况时还是要具体问题具体分析
的:),我把它写出来希望能够起到抛砖引玉的作用,期待您的意见与建议!(是不是有人嫌我太罗嗦了?我走……)
|