用定制的PHP应用程序来获取Web服务器的状态信息


Posted in PHP onOctober 09, 2006

大多数网站托管(Web hosting)公司都支持客户对Web站点统计数据的访问,但是你往往会觉得服务器所产生的状态信息不够全面。例如,配置不正确的Web服务器不能识别某些文件类型,这些类型的文件就不会出现在状态信息之中。幸好,你可以用PHP来定制状态信息收集程序,这样你就可以获取你所需要的信息了。

公共日志文件格式(Common Logfile Format,CLF)的结构

 
CLF最初是NCSA为HTTPd(全球网服务器软件)而设计的。CERN HTTPd是一个由万维网联盟(World Wide Web Consortium,W3C)维护的公共域Web服务器。W3C网站列出了该日志文件规范。基于微软和UNIX的Web服务器都可以生成CLF格式的日志文件。CLF格式如下:
Host IdentAuthuserTime_Stamp "request" Status_codeFile_size

例如:
21.53.48.83 - - [22/Apr/2002:22:19:12 -0500] "GET /cnet.gif HTTP/1.0" 200 8237

下面是日志条目的细目分类:

Host是网站访问者的IP地址或者DNS名;在上面的例子中,它是21.53.48.83。
Ident是该访客的远端身份(RFC 931)。破折号表明“未指定”。
Authuser是用户ID(如果Web服务器已经验证了验证网站访问者的身份的话)。
Time_Stam是服务器以“日/月/年”这种格式返回的时间。
Request是网站访问者的HTTP请求,例如GET或者POST。
Status_Code是服务器所返回的状态代码,例如:200代表“正确——浏览器请求成功”。
File_Size是用户所请求文件的大小。在本例中,它为 8237字节。

服务器状态代码

 
你可以在HTTP标准中找到W3C所开发的服务器状态代码规范。这些由服务器所产生的状态代码表示了浏览器和服务器之间的数据传输成功与否。这些代码一般传递给浏览器(例如非常有名的404错误“页面没有找到“)或者添加到服务器日志中去。

收集数据

创建我们的自定义应用程序的第一步就是获取用户数据。每当用户选择网站的某个资源时,我们就希望创建一个对应的日志条目。幸好,服务器变量的存在使得我们能够查询用户浏览器并获取数据。

报头中的服务器变量携带了从浏览器传递到服务器的信息。REMOTE_ADDR就是一个服务器变量的例子。这个变量返回了用户的IP地址:
例子输出:27.234.125.222

下面的PHP代码将显示出当前用户的IP地址:
<?php echo $_SERVER['REMOTE_ADDR']; ?>

让我们看看我们的PHP应用程序的代码。首先,我们需要定义我们想跟踪的网站资源并指定文件大小:
//获取我们想记录的文件名称
$fileName="cnet-banner.gif";
$fileSize="92292";

你无需把这些值保存到静态变量中去。如果你要跟踪许多条目,那么你可以把它们保存到数组或者数据库中去。在这种情况下,你可能会希望通过一个外部链接来找到每个条目,如下所示:
<a href="weblogger.php?bannerid=123"><imgsrc="cnet-banner.gif" border="0"></a>

其中“123”表示“cnet-banner.gif”所对应的记录。然后,我们通过服务器变量来查询用户浏览器。这样我们就得到在我们的日志文件中添加新条目所需的数据:
//得到网站浏览者的CLF信息
$host=$_SERVER['REMOTE_ADDR'];
$ident=$_SERVER['REMOTE_IDENT'];
$auth=$_SERVER['REMOTE_USER'];
$timeStamp=date("d/M/Y:H:i:s O");
$reqType=$_SERVER['REQUEST_METHOD'];
$servProtocol=$_SERVER['SERVER_PROTOCOL'];
$statusCode="200";

然后,我们检查服务器是否返回了空值(null)。根据CLF规范,空值应该用破折号来代替。这样,下一个代码块的任务就是寻找空值并用破折号来取代它:
//给空值添加破折号(根据规范)
if ($host==""){ $host="-"; }
if ($ident==""){ $ident="-"; }
if ($auth==""){ $auth="-"; }
if ($reqType==""){ $reqType="-"; }
if ($servProtocol==""){ $servProtocol="-"; }

一旦我们获取了必要的信息,这些值将被组织成一种符合CLF规范的格式:
//创建CLF格式的字符串
$clfString=$host." ".$ident." ".$auth." [".$timeStamp."] \"".$reqType." /".$fileName." ".$servProtocol."\" ".$statusCode." ".$fileSize."\r\n";

创建自定义日志文件

现在,格式化之后的数据可以存放到我们的自定义日志文件中去。首先,我们将创建一种文件命名协定,并编写每日产生一个新日志文件的方法(函数)。在本文所举的例子中,每个文件都以“weblog-”开头,然后是按月/日/年表示的日期,文件扩展名为.log。.log扩展名一般表示服务器日志文件。(实际上,绝大多数日志分析器都搜索.log文件。)
// 用当前日期来命名日志文件
$logPath="./log/";
$logFile=$logPath."weblog-".date("mdy").".log";

现在,我们需要判断当前日志文件是否存在。如果存在,我们就向它添加条目;否则,应用程序就创建新的日志文件。(新日志文件的创建一般发生在日期更改时,因为这时文件名发生变化了。)
//检查日志文件是否已经存在
if (file_exists($logFile)){
//如果存在,则打开已存在的日志文件
$fileWrite = fopen($logFile,"a");}
else {
//否则,创建新的日志文件
$fileWrite = fopen($logFile,"w"); }

如果你在写或者追加文件时,收到“权限不足(Permission Denied)”错误信息,请更改目标日志文件夹的权限来允许写操作。绝大多数Web服务器的默认权限为“可读可执行”。你可以用CHMOD命令或者使用FTP客户端来改变文件夹的权限。

然后,我们创建文件锁定机制,这样当两个或者更多用户同时访问日志文件时,只有其中的一个用户可以对该文件进行写操作:
//创建文件写操作的锁定机制
flock($fileWrite, LOCK_SH);

最后,我们写入条目的内容:
//写CLF条目
fwrite($fileWrite,$clfString);
//解除文件锁定状态
flock($fileWrite, LOCK_UN);
//关闭日志文件
fclose($fileWrite);

处理日志数据

 
在该系统产品化之后,客户希望得到对所收集到的访问者数据的详细统计分析。由于所有的定制日志文件都是按照一个标准的格式组织的,因此任何一个日志分析器都可以处理它们。日志分析器是一个工具,它分析大的日志文件并产生饼图、直方图以及其它统计图形。日志分析器也用来收集数据,并综合出提供哪些用户访问你的网站、点击数等方面的信息。

下面列出了几个比较流行的日志分析器:

WebTrends是一个非常不错的日志分析器,它适用于大规模网站以及企业级的网络。
Analog是一个颇受欢迎的免费日志分析器。
Webalizer是一个免费的分析程序。它可以产生HTML报告,这样大多数网络浏览器都可以查看它的报告。

遵守标准

我们可以轻松的扩展该应用程序来让它支持其它类型的日志记录。这样你就可以捕获到更多的数据,如浏览器类型以及referrer(referrer指得是链接到当前网页的前一个网页)。这里的经验就是:在你编程的时候遵循标准或者惯例终究会简化工作。

PHP 相关文章推荐
我的论坛源代码(八)
Oct 09 PHP
在mysql数据库原有字段后增加新内容
Nov 26 PHP
php中json_decode()和json_encode()的使用方法
Jun 04 PHP
PHP缓存技术的多种方法小结
Aug 14 PHP
PHP 的异常处理、错误的抛出及回调函数等面向对象的错误处理方法
Dec 07 PHP
回帖脱衣服的图片实现代码
Feb 15 PHP
PHP Streams(流)详细介绍及使用
May 12 PHP
[原创]php常用字符串输出方法分析(echo,print,printf及sprintf)
Jul 09 PHP
PHP中include/require/include_once/require_once使用心得
Aug 28 PHP
php 读取文件夹下所有图片、文件的实例
Oct 17 PHP
PHP面向对象程序设计子类扩展父类(子类重新载入父类)操作详解
Jun 14 PHP
php使用redis的有序集合zset实现延迟队列应用示例
Feb 20 PHP
PHP在Web开发领域的优势
Oct 09 #PHP
优化NFR之一 --MSSQL Hello Buffer Overflow
Oct 09 #PHP
风格模板初级不完全修改教程
Oct 09 #PHP
如何对PHP程序中的常见漏洞进行攻击
Oct 09 #PHP
PHP安全配置
Oct 09 #PHP
用PHP 4.2书写安全的脚本
Oct 09 #PHP
3.从实例开始
Oct 09 #PHP
You might like
一个ubbcode的函数,速度很快.
2006/10/09 PHP
php字符串截取的简单方法
2013/07/04 PHP
PHP FATAL ERROR: CALL TO UNDEFINED FUNCTION BCMUL()解决办法
2014/05/04 PHP
浅谈php://filter的妙用
2019/03/05 PHP
学习YUI.Ext第七日-View&amp;JSONView Part Two-一个画室网站的案例
2007/03/10 Javascript
asp.net和asp下ACCESS的参数化查询
2008/06/11 Javascript
微博@符号的用户名提示效果。(想@到谁?)
2010/11/05 Javascript
悬浮数字的实现案例
2014/02/19 Javascript
Js实现网页键盘控制翻页的方法
2014/10/30 Javascript
使用bootstrapValidator插件进行动态添加表单元素并校验
2016/09/28 Javascript
深入浅析Vue组件开发
2016/11/25 Javascript
JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)
2016/12/14 Javascript
解析JavaScript实现DDoS攻击原理与保护措施
2016/12/26 Javascript
AngularJs上传前预览图片的实例代码
2017/01/20 Javascript
JQuery查找子元素find()和遍历集合each的方法总结
2017/03/07 Javascript
Vue自定义图片懒加载指令v-lazyload详解
2020/12/31 Javascript
VUE长按事件需求详解
2017/10/18 Javascript
Vue.js 中的 v-cloak 指令及使用详解
2018/11/19 Javascript
Vue项目部署在Spring Boot出现页面空白问题的解决方案
2018/11/26 Javascript
使用vue脚手架(vue-cli)搭建一个项目详解
2019/05/09 Javascript
Koa从零搭建到Api实现项目的搭建方法
2019/07/30 Javascript
从零开始在vue-cli4配置自适应vw布局的实现
2020/06/08 Javascript
JS canvas实现画板和签字板功能
2021/02/23 Javascript
Python实现快速排序和插入排序算法及自定义排序的示例
2016/02/16 Python
解决pycharm py文件运行后停止按钮变成了灰色的问题
2018/11/29 Python
用Python读取几十万行文本数据
2018/12/24 Python
python实现剪切功能
2019/01/23 Python
两个元祖T1=('a', 'b'),T2=('c', 'd')使用匿名函数将其转变成[{'a': 'c'},{'b': 'd'}]的几种方法
2019/03/05 Python
Python面向对象程序设计构造函数和析构函数用法分析
2019/04/12 Python
CSS3 Pie工具推荐--让IE6-8支持一些优秀的CSS3特性
2014/09/02 HTML / CSS
Swanson中国官网:美国斯旺森健康产品公司
2021/03/01 全球购物
前台接待岗位职责
2013/12/03 职场文书
劲霸男装广告词
2014/03/21 职场文书
幼儿园教研工作总结2015
2015/05/12 职场文书
《打电话》教学反思
2016/02/22 职场文书
美德少年事迹材料(2016推荐版)
2016/02/25 职场文书