Report IIS Server Traffic
After spending significant time Googling for an easy way to report IIS traffic on a per server basis and coming up empty handed I have decided to write my own Powershell script that will report web traffic server-wide, site-wide, chart images and automatically email it. The real magic behind it is Microsoft’s Log Parser. The query I have configured for mine is to count all the hits on aspx pages to give me the most accurate report. If you use anything else, you can add in an OR clause or replace aspx
with whatever you need in the Log Parser lines: WHERE (cs-uri-stem LIKE '%
aspx
%'
). This script was designed to search two IIS hosts, but you could modify it with a little Powershell knowledge.
*9/9/2016 update* After revisiting this page much later after posting, I realize how rudimentary the coding is and would like to encourage someone who uses this to clean it up and break it up into functions and enable the use of parameters so as to make it easier for the end user. Please either post the code on github or visit my Contact page and I’ll revise this post with credit to you.
I’ve added as many comment sections as I could to make this more understandable but here is a breakdown of what it is doing:
- Declares variables
- Creates the LogFiles folder, if it exists remove everything inside it
- Copy IIS server logs from the previous month to the LogFiles local folder
- Runs LogParser.exe to generate per server reports into CSV files
- Runs LogParser.exe to generate per site reports into CSV files
- Runs LogParser.exe to generate per server chart images
- Runs LogParser.exe to generate chart image with combined servers
- Tests if the site hits CSV files were actually generated by Log Parser and imports them into variables
- Counts the server-wide page hits by using an array
- Adds a thousands separator to the server-wide hit numbers
- Puts the chart images into a variable
- Sends an email containing server-wide, site-wide hits with chart images as attachments
- Nullify the variables
- Deletes the locally copied log files
To get started:
- Requires Powershell 3.0+
- Install Log Parser and Microsoft Office 2003 Web Components
- Enable logging of date, cs-uri-stem, cs-host on your IIS servers
- Create the folders: D:\WebserverReport and D:\WebserverReport\ReportTemp
- Modify the script paths and server names to your needs
#Purpose: IIS Server Log Monthly Hit Counter #By: Travis Runyard #Date: 1/8/2015 #Variables $LastMonth = (Get-Date).AddMonths(-1) $LastMonthName = Get-Date -year $LastMonth.year -month $LastMonth.month -format Y $CurrentDate = Get-Date $FirstDayofLastMonth = Get-Date -year $LastMonth.year -month $LastMonth.month -day 1 -format d $FirstDayofThisMonth = Get-Date -year $CurrentDate.year -month $CurrentDate.month -day 1 -format d $RemoteLogPathIIS1 = "\\SERVER1\logfiles" $RemoteLogPathIIS2 = "\\SERVER2\logfiles" $LocalLogPathIIS1 = "D:\WebserverReport\LogFiles\SERVER1" $LocalLogPathIIS2 = "D:\WebserverReport\LogFiles\SERVER2" $SiteHitsIIS1path = "D:\WebserverReport\ReportTemp\results-IIS1-sitehits.csv" $SiteHitsIIS2path = "D:\WebserverReport\ReportTemp\results-IIS2-sitehits.csv" $ReportTemp = "D:\WebserverReport\ReportTemp" #Create folders and delete logs if they exist if(!(Test-Path -Path $LocalLogPathIIS1 )){ New-Item -ItemType directory -Path $LocalLogPathIIS1 } Else { Remove-Item -path ( $LocalLogPathIIS1 + "\*") -Recurse -Force } if(!(Test-Path -Path $LocalLogPathIIS2 )){ New-Item -ItemType directory -Path $LocalLogPathIIS2 } Else { Remove-Item -path ( $LocalLogPathIIS2 + "\*") -Recurse -Force } #Copy IIS1 log files from last month Get-ChildItem -Path $RemoteLogPathIIS1 -Recurse | ? { $_.LastWriteTime -gt $FirstDayofLastMonth -and (($_.LastWriteTime -lt $FirstDayofThisMonth) -or ($_.PSIsContainer)) } | Copy-Item -Destination { if ($_.PSIsContainer) { Join-Path $LocalLogPathIIS1 $_.Parent.FullName.Substring($RemoteLogPathIIS1.length) } else { Join-Path $LocalLogPathIIS1 $_.FullName.Substring($RemoteLogPathIIS1.length) } } -Force #Remove-Item ($LocalLogPathIIS1 + "\*.*") | Where { ! $_.PSIsContainer } #Copy IIS2 log files from last month Get-ChildItem -Path $RemoteLogPathIIS2 -Recurse | ? { $_.LastWriteTime -gt $FirstDayofLastMonth -and (($_.LastWriteTime -lt $FirstDayofThisMonth) -or ($_.PSIsContainer)) } | Copy-Item -Destination { if ($_.PSIsContainer) { Join-Path $LocalLogPathIIS2 $_.Parent.FullName.Substring($RemoteLogPathIIS2.length) } else { Join-Path $LocalLogPathIIS2 $_.FullName.Substring($RemoteLogPathIIS2.length) } } -Force CD "C:\Program Files (x86)\Log Parser 2.2\" #Run the Log Parser utility and generate the server report CSV file (SERVER1) &"C:\Program Files (x86)\Log Parser 2.2\LogParser.exe" --% -i:IISW3C "SELECT date, cs-uri-stem AS Page, COUNT(*) AS Hits INTO D:\WebserverReport\ReportTemp\results-IIS1-svrhits.csv FROM D:\WebserverReport\LogFiles\SERVER1\*.log WHERE (cs-uri-stem LIKE '%aspx%') GROUP BY Date,Page ORDER BY Hits DESC" -o:CSV -headers:ON -recurse #Run the Log Parser utility and generate the server report CSV file (SERVER2) &"C:\Program Files (x86)\Log Parser 2.2\LogParser.exe" --% -i:IISW3C "SELECT date, cs-uri-stem AS Page, COUNT(*) AS Hits INTO D:\WebserverReport\ReportTemp\results-IIS2-svrhits.csv FROM D:\WebserverReport\LogFiles\SERVER2\*.log WHERE (cs-uri-stem LIKE '%aspx%') GROUP BY Date,Page ORDER BY Hits DESC" -o:CSV -headers:ON -recurse #Run the Log Parser utility and generate website hit stats (SERVER1) &"C:\Program Files (x86)\Log Parser 2.2\LogParser.exe" --% -i:IISW3C "SELECT cs-host AS Host, COUNT(*) AS Hits INTO D:\WebserverReport\ReportTemp\results-IIS1-sitehits.csv FROM D:\WebserverReport\LogFiles\SERVER1\*.log WHERE (cs-uri-stem LIKE '%aspx%') AND cs-host IS NOT NULL GROUP BY Host ORDER BY Hits, Host DESC" -o:CSV -headers:ON -recurse #Run the Log Parser utility and generate website hit stats (SERVER2) &"C:\Program Files (x86)\Log Parser 2.2\LogParser.exe" --% -i:IISW3C "SELECT cs-host AS Host, COUNT(*) AS Hits INTO D:\WebserverReport\ReportTemp\results-IIS2-sitehits.csv FROM D:\WebserverReport\LogFiles\SERVER2\*.log WHERE (cs-uri-stem LIKE '%aspx%') AND cs-host IS NOT NULL GROUP BY Host ORDER BY Hits, Host DESC" -o:CSV -headers:ON -recurse #Run the Log Parser utility and generate chart for (SERVER1) &"C:\Program Files (x86)\Log Parser 2.2\LogParser.exe" --% -i:IISW3C "SELECT cs-host AS Host, COUNT(*) AS Hits INTO D:\WebserverReport\ReportTemp\chart-iis1.gif FROM D:\WebserverReport\LogFiles\SERVER1\*.log WHERE (cs-uri-stem LIKE '%aspx%') AND cs-host IS NOT NULL GROUP BY Host ORDER BY Hits, Host DESC" -o:chart -chartType:ColumnClustered -chartTitle:"Traffic IIS1" -config:FormatChart.js -recurse #Run the Log Parser utility and generate chart for (SERVER2) &"C:\Program Files (x86)\Log Parser 2.2\LogParser.exe" --% -i:IISW3C "SELECT cs-host AS Host, COUNT(*) AS Hits INTO D:\WebserverReport\ReportTemp\chart-iis2.gif FROM D:\WebserverReport\LogFiles\SERVER2\*.log WHERE (cs-uri-stem LIKE '%aspx%') AND cs-host IS NOT NULL GROUP BY Host ORDER BY Hits, Host DESC" -o:chart -chartType:ColumnClustered -chartTitle:"Traffic IIS2" -config:FormatChart.js -recurse #Run the Log Parser utility and generate chart all servers &"C:\Program Files (x86)\Log Parser 2.2\LogParser.exe" --% -i:IISW3C "SELECT cs-host AS Host, COUNT(*) AS Hits INTO D:\WebserverReport\ReportTemp\chart-totals.gif FROM D:\WebserverReport\LogFiles\*.log WHERE (cs-uri-stem LIKE '%aspx%') AND cs-host IS NOT NULL GROUP BY Host ORDER BY Hits, Host DESC" -o:chart -chartType:ColumnClustered -chartTitle:"Traffic Totals" -config:FormatChart.js -recurse #Test if file exists and Import IIS1 site hits if(!(Test-Path -Path $SiteHitsIIS1path )){ $SiteHitsIIS1 = "There were no website statistics generated by Log Parsern" } Else { $SiteHitsIIS1 = Import-CSV D:\WebserverReport\ReportTemp\results-IIS1-sitehits.csv | Format-Table @{Expression={$_.Host};Label="Host";width=45;align='left'},
@{Expression={$_.Hits};Label="Hits";width=15;align='left'} | Out-String } #Test if file exists and Import IIS2 site hits if(!(Test-Path -Path $SiteHitsIIS2path )){ $SiteHitsIIS2 = "There were no website statistics generated by Log Parsern" } Else { $SiteHitsIIS2 = Import-CSV D:\WebserverReport\ReportTemp\results-IIS2-sitehits.csv | Format-Table @{Expression={$_.Host};Label="Host";width=45;align='left'},
@{Expression={$_.Hits};Label="Hits";width=15;align='left'} | Out-String } #Count all server hits for email report IIS1 [int] $sumIIS1 = 0; $File = Import-CSV D:\WebserverReport\ReportTemp\results-IIS1-svrhits.csv foreach ($arrayHits in $File) { #Write-Host $arrayHits.Hits; $sumIIS1 = $sumIIS1 + $arrayHits.Hits; } $arrayHits = $NULL #Count all server hits for email report IIS2 [int] $sumIIS2 = 0; $File = Import-CSV D:\WebserverReport\ReportTemp\results-IIS2-svrhits.csv foreach ($arrayHits in $File) { #Write-Host $arrayHits.Hits; $sumIIS2 = $sumIIS2 + $arrayHits.Hits; } #Add thousands separator $sumIIS1sep = [string]::Format('{0:N0}',$sumIIS1) $sumIIS2sep = [string]::Format('{0:N0}',$sumIIS2) #Get attachments from Temp folder $Attachments = Get-ChildItem $ReportTemp\*.* -include *.gif #Send the report by email $body = "Report is for the month of $LastMonthNamen
Report:n
hits for SERVER1: $sumIIS1sepnTotal hits for SERVER2: $sumIIS2sep
nSite Hits (SERVER1):
$SiteHitsIIS1nSite Hits (SERVER2):
$SiteHitsIIS2" send-mailmessage -to "Administrator <administrator@company.com>" -from "Webserver Reporting <logs@company.com>" -subject "Webserver Report" -body $body -Attachments $Attachments -smtpserver YourEmailServer #Release variables $arrayHits = $NULL $sumIIS1 = $NULL $sumIIS1sep = $NULL $sumIIS2 = $NULL $sumIIS2sep = $NULL $SiteHitsIIS1 = $NULL $SiteHitsIIS2 = $NULL #Remove Log files Remove-Item -path ( $LocalLogPathIIS1 + "\*") -Recurse -Force Remove-Item -path ( $LocalLogPathIIS2 + "\*") -Recurse -Force
Once you’ve tweaked enough and get it working you’ll end up with an email that looks a little something like this:
Report is for the month of December 2014 Server Report: Total hits for Server1: 6,701 Total hits for Server2: 68,190 Site Hits (Server1): Host Hits ---- ---- blah1.company.com 1485 whatever.company.com 1429 weee.company.com 1398 whoami.company.com 1380 test.company.com 1009 Site Hits (Server2): Host Hits ---- ---- blah2.company.com 5324 whatever2.company.com 2135 weee2.company.com 1632 whoami2.company.com 5 test2.company.com 59094
And the chart should look like this:
The reason the chart is so pretty and colorful is because I used this color script by Robert McMurray
As always if you have any questions or run into issues with the script, leave a comment below and I will address them as soon as possible ;)
May 10, 2017 10:07 am @ 10:07
Hello Travis,
I am trying to execute above given script but getting below error. I have edited all require values. So please guide me for script execution.
At C:Chinmay_IIS_LogsWebreport.ps1:68 char:106
+ … hter-language=”generic” class=”EnlighterJSRAW”>n”
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unexpected token ‘generic” class=”EnlighterJSRAW”>n”‘ in expression or statement.
At C:Chinmay_IIS_LogsWebreport.ps1:72 char:176
+ … ;align=’left’},
Thanks.
May 11, 2017 10:23 pm @ 22:23
It looks like WordPress or my code syntax plugin screwed up the script by adding code data-enlighter-language=”generic” class=”EnlighterJSRAW” and /code all over it. All that needs to be done is to remove that wherever you see it. I’ll clean up the code tonight so please check back soon!
*EDIT* 3 months later and I still have not cleaned up the code. Come on people, I wrote the damn entire thing, you clean it up.