大多数网站托管(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应用程序的代码。首先,我们需要定义我们想跟踪的网站资源并指定文件大小:
//获取我们想记录的文件名称
$fileName="cnet-banner.gif";
$fileSize="92292";
你无需把这些值保存到静态变量中去。如果你要跟踪许多条目,那么你可以把它们保存到数组或者数据库中去。在这种情况下,你可能会希望通过一个外部链接来找到每个条目,如下所示:
其中“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是一個受歡迎的免費日誌分析器。
我們可以輕鬆的擴展該應用程式來讓它支援其它類型的日誌記錄。這樣你就可以擷取到更多的數據,如瀏覽器類型以及referrer(referrer指得是連結到目前網頁的前一個網頁)。這裡的經驗就是:在你編程的時候遵循標準或慣例終究會簡化工作。