IIS Home @ it-notebook.org

For a virtual directory, calculate the number of requests and when it was last accessed (C#)

(Kristofer Gafvert, July 7, 2010)

This example shows you several examples, listed below. It uses the Microsoft.Web.Administration class, which provides all the necessary classes for configuring and administrating IIS 7. It does not do any error checking, which is recommended if used in a production environment.

  • Use Log Parser 2.2 from C#, on Windows Server 2008
  • Get a list of websites hosted on a IIS 7 machine, and its properties. Example of properties are website id, the name of the website, where the log file resides.
  • Get a list of applications on each of the websites
  • Get a list of Virtual Directories and its properties for each website. Example of properties are the physical path, and the relative path.

For additional information, see comments inline, and the System.Web.Administration reference (link at the bottom).

The application creates a semi-colon separated text file, which can be imported into Microsoft Excel to sort and filter based on the requirements.

using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.IO;
using Microsoft.Web.Administration;
 
using LogQuery = Interop.MSUtil.LogQueryClassClass;
using IISW3CInputFormat = Interop.MSUtil.COMIISW3CInputContextClassClass;
using LogRecordSet = Interop.MSUtil.ILogRecordset;
using LogRecord = Interop.MSUtil.ILogRecord;
 
namespace IT_notebook.IIS
{
    /*
    Created by: Kristofer Gafvert
    Published at: http://www.it-notebook.org/iis/article/cs_requests_per_folder.htm
    Purpose: Get statistics from IIS log files to find out how many requests there are per virtual directory.
             IIS 7.0, IIS 7.5.
    
    Free to use, but not publish in its entire to other website.
    
    */
    class Program
    {
        static void Main(string[] args)
        {
            int days = Convert.ToInt32(args[0]); //Number of days we want to calculate statistics on.
            
            DateTime getStatFrom = System.DateTime.Today.AddDays(-days);
            
            Console.WriteLine("Now starting to calculate statistics. Processing requests newer than " + getStatFrom);
            
            List<Website> websites = new List<Website>();
            
            ServerManager serverManager = new ServerManager();
            
            SiteCollection sitesOnServer = serverManager.Sites; //Get a list of websites on the server
            
            //iterate all websites on the server
            foreach(Site siteElement in sitesOnServer)
            {
                Website w1 = new Website();
                w1.Id = siteElement.Id.ToString();
                w1.FriendlyName = siteElement.Name;
                w1.LogFilePath = Environment.ExpandEnvironmentVariables(siteElement.LogFile.Directory + "\\W3SVC" + w1.Id);
                
                foreach (Application app in siteElement.Applications)
                {
                    foreach (VirtualDirectory vdir in app.VirtualDirectories)
                    {
                        
                        string physicalpath = Environment.ExpandEnvironmentVariables(vdir.PhysicalPath);
                        string path = Environment.ExpandEnvironmentVariables(vdir.Path);
                        Folder vfolder = new Folder(physicalpath, path);
                        
                        w1.Folders.Add(vfolder);
                        
                        // TODO, CHECK NON-VIRTUAL FOLDERS
                        //List<Folder> systemFolders = GetPhysicalFolders(physicalpath);
                        //w1.Folders.AddRange(systemFolders);
                    }
                }
                websites.Add(w1);
            }
            
            
            // We now have all websites and where the log file is, and also the folders we need to count 
            // requests on.
            LogQuery logParser = new LogQuery();
            IISW3CInputFormat W3Clog = new IISW3CInputFormat();
            
            LogRecordSet rsNumberOfRequests = null;
            LogRecordSet rsLastRequest = null;
            LogRecord rowNumberOfRequests = null;
            LogRecord rowLastRequest = null;
            
            foreach(Website w in websites)
            {
                
                foreach(Folder f in w.Folders)
                {
                    string strSQL = "SELECT COUNT(*) FROM " + w.LogFilePath + "\\*.log " +
                                    "WHERE cs-uri-stem LIKE '" + f.Path + "%' " +
                                    "AND date > '" + String.Format("{0:yyyy-MM-dd}",getStatFrom) + "'";
                                    
                    rsNumberOfRequests = logParser.Execute(strSQL, W3Clog);
                    rowNumberOfRequests = rsNumberOfRequests.getRecord();
                    f.NumberOfRequests = Convert.ToInt32(rowNumberOfRequests.getValue(0));                                        
                    
                    strSQL = "SELECT date FROM " + w.LogFilePath + "\\*.log " +
                                    "WHERE cs-uri-stem LIKE '" + f.Path + "%' " +
                                    "AND date > '" + String.Format("{0:yyyy-MM-dd}",getStatFrom) + "'" +
                                    "ORDER BY date DESC";
                                    
                    rsLastRequest = logParser.Execute(strSQL, W3Clog);
                    if (rsLastRequest.atEnd())
                    {
                        f.LastRequest = "NEVER ACCESSED";
                    }
                    else
                    {
                        rowLastRequest = rsLastRequest.getRecord();
                        f.LastRequest = String.Format("{0:yyyy-MM-dd}", rowLastRequest.getValue(0).ToString());
                    }
                }
            }
            
            TextWriter fileWriter = new StreamWriter("log.txt");
            //Write header
            fileWriter.WriteLine("#Website ID;Website description;Physical Path;Virtual Path;Last Request;Number of Requests");
            foreach(Website w in websites)
            {
                
                foreach(Folder f in w.Folders)
                {
                    fileWriter.WriteLine(w.Id + ";" + w.FriendlyName + ";" + f.PhysicalPath + ";" + f.Path + ";" + f.LastRequest + ";" + f.NumberOfRequests);
                }
            }
            
            fileWriter.Close();
            
            Console.WriteLine("Finished, please see the log file for the statistics.");
        }
        
        private static List<Folder> GetPhysicalFolders(string rootFolder)
        {
            //This is where we store all folders
            List<Folder> fileSystemFolders = new List<Folder>();
            
            //This is a temporary stack to keep the folder we need to go thru
            Stack<string> folders = new Stack<string>();
            
            //Start with the root folder
            folders.Push(rootFolder);
            
            while (folders.Count > 0)
            {
                string currentFolder = folders.Pop();
                Folder f = new Folder(currentFolder, currentFolder);
                fileSystemFolders.Add(f);
                
                foreach(string currentFolders in Directory.GetDirectories(currentFolder))
                {
                    //We have found new folders, so add them to the stack so we can find more folders
                    folders.Push(currentFolders);
                }
            }
            return fileSystemFolders;
        }
    }
    public class Website
    {
        private List<Folder> folders;
        private string id;
        private string friendlyName;
        private int numberOfRequests;
        private string lastRequest;
        private string logFilePath;
        
        public Website()
        {
            folders = new List<Folder>();
        }
        public List<Folder> Folders
        {
            get{ return folders;}
            set{ folders = value;}
        }
        public string Id
        {
            get{ return id;}
            set{ id = value;}
        }
        public string FriendlyName
        {
            get{ return friendlyName;}
            set{ friendlyName = value;}
        }
        public int NumberOfRequests
        {
            get{ return numberOfRequests;}
            set{ numberOfRequests = value;}
        }
        public string LastRequest
        {
            get{ return lastRequest;}
            set{ lastRequest = value;}
        }
        public string LogFilePath
        {
            get{ return logFilePath;}
            set{ logFilePath = value;}
        }
    }
    public class Folder
    {
        private string physicalPath;
        private string path;
        private int numberOfRequests;
        private string lastRequest;
        
        public Folder(string physicalPath, string path)
        {
            this.physicalPath = physicalPath;
            this.path = path;
        }
        public string PhysicalPath
        {
            get{ return physicalPath;}
            set{ physicalPath = value;}
        }
        public string Path
        {
            get{ return path;}
            set{ path = value;}
        }
        public int NumberOfRequests
        {
            get{ return numberOfRequests;}
            set{ numberOfRequests = value;}
        }
        public string LastRequest
        {
            get{ return lastRequest;}
            set{ lastRequest = value;}
        }
    }
    
}

To compile from the command line, run the following on a default installation of Windows Server 2008:

C:\Windows\Microsoft.NET\Framework\v2.0.50727>csc /reference:"C:\windows\system32\inetsrv\Microsoft.Web.Administration.dll"
/r:"c:\documents\Interop.MSUtil.dll" /target:exe /out:"c:\DOCUMENTS\statisticRequestsPerFolders.exe" c:\DOCUMENTS\statisticRequestsPerFolder.cs

You also need to build an interop assembly containing the Runtime Callable Wrappers for the Log Parser scriptable COM components. To do this from a command line, you need the Windows SDK for Windows Server 2008 and .NET Framework 3.5, and locate the file tlbimp.exe. Then run the following:

tlbimp "C:\Program Files\Log Parser 2.2\LogParser.dll" /out:"C:\DOCUMENTS\Interop.MSUtil.dll"

You need to run the application as a user with privilegies to access the configuration files.

Applies to [?]

IIS 7.0, IIS 7.5

Resources

Download source and runnable application
Microsoft.Web.Administration Namespace
Windows SDK for Windows Server 2008 and .NET Framework 3.5