在服务器管理过程中,经常需要了解各个网站的访问情况,尤其是在需要迁移服务器时,了解哪些网站仍在被用户使用变得尤为重要。这样才能确保在新服务器上继续托管这些网站,以维持业务的连续性。
IIS服务器通过日志文件记录了网站的访问历史。这些日志文件通常位于"\inetpub\logs\LogFiles"目录下。每个文件夹对应IIS中的一个节点。例如,如果"AdvancedSearch"网站的站点ID为3,则"W3SVC3"文件夹包含了该网站的日志文件。
要确定哪个日志文件夹属于哪个网站,可以在IIS网站属性中找到站点ID。例如,"AdvancedSearch"网站的站点ID为3,因此"W3SVC3"文件夹包含了该网站的日志文件。
一个典型的日志文件结构如下所示。可以看到,页面访问序列是default.aspx->Display.aspx>MoreInfo.aspx。然后一个新的会话开始,访问序列是default.aspx,然后发生了重新加载(或回发),这是日志文件中的第二行。但在此期间,还有其他行并不是真正的访问,而是加载相关的样式表和脚本。因此需要从计数中排除它们。
代码实现相对简单,主要是一些字符串操作和特定的检查。程序界面包括一个文件夹浏览对话框,用于定位日志文件夹。此外,还有一个日期检查功能;如果提供了日期,则只考虑该日期之前的所有访问。
程序使用一个字典对象来保存计数:
Dictionary
需要计数的网站(和子网站)在配置文件中添加。配置文件还包含其他一些键,这些键的名字很容易理解。因此,对于网站(Books, PearlSBuck, SidneySheldon, SatyajitRoy, HumayunAhmed)的访问计数完成后,输出将提供在名为"Hit_Stats.txt"的文本文件中,该文件位于可执行文件的同一目录下。
XML配置文件示例:
<add key="APP_TITLE" value="Web Log Counter"/>
<add key="STAT_FILE_NAME" value="Hit_Stats.txt"/>
<add key="SITE_NAMES" value="Books, PearlSBuck, SidneySheldon, SatyajitRoy"/>
<add key="URL_HEADER" value="cs-uri-stem"/>
由于每天都会为每个网站创建一个单独的日志文件,因此需要浏览和解析所有网站的日志文件。这是通过以下代码实现的:
foreach (string LogFile in Files)
{
StreamReader SReader = new StreamReader(LogFile);
StatusLabel.Text = "Parsing file: " + LogFile;
FileCountLabel.Text = "Processing file: " + count++.ToString() + " of " + Files.Count().ToString();
Application.DoEvents();
// Refresh the labels.
ParseFile(SReader, ConfigurationManager.AppSettings["URL_HEADER"]);
SReader.Close();
}
所有行都在一个WHILE循环中读取和处理。然后丢弃行,直到达到最后一个元数据(包含"#fields")。
接下来这段代码开始处理行,直到达到下一个元数据(请参见上面的文件结构快照)。首先,它检查是否打算进行日期检查。如果是,则检查在该日期之前(<=)的网站访问。否则,它开始检查与日期无关的访问。
Line = SReader.ReadLine();
// Read the line next to the #Fields line,
// these subsequent lines actually contain the site hits.
while (Line != null && !Line.Substring(0, 1).Equals("#"))
{
bool SiteHitFound = false;
Strings = Line.Split(',');
if (!CheckFindAllHits.Checked)
{
var Regex = new Regex(@"\d{4}-\d{2}-\d{2}", RegexOptions.Compiled);
IsSuccess = Regex.Match(Strings[0]).Success;
// Check for a valid date format.
if (IsSuccess)
HitDate = Convert.ToDateTime(Strings[0]);
if (HitDate <= Convert.ToDateTime(HitsBeforeThisDate.Text))
// If the log date
// is over the check date, then no need to proceed with this line,
CheckSiteHit(ref CurrentSite, Strings, UrlIndex, ref LastSite, ref SiteHitFound);
}
else
CheckSiteHit(ref CurrentSite, Strings, UrlIndex, ref LastSite, ref SiteHitFound);
Line = SReader.ReadLine();
// Proceed with the next line in the log file.
}
"CheckSiteHit"方法实际上执行计数。它浏览列出的网站进行检查,并查看是否发生了访问。它还跟踪最后一个遇到的网站。这是为了忽略连续的相同网站,因为实际上那只是一个访问,而随后的访问是加载CSS、JS、图片等,或者是回发(POST)。原因已经在上面的文件结构快照中解释过了。然后,如果它发现了一个访问,它就将其输入到字典中(或者如果它已经在那里,就增加计数器)。