So you built a web application, tested it and deployed it. People are using it and everything is fine, but then one day your site suddenly becomes totally unavailable. You look at the web server and the w3wp.exe process is using up 100% of the cpu time. Well, here's a way to figure out what page is causing the jam without having to pay for any fancy diagnostic tools.
Log Parser
Firstly get and install the latest version of Log Parser from Microsoft. Currently that would be version 2.2.
http://www.microsoft.com/downloads/details.aspx?FamilyID=890cd06b-abf8-4c25-91b2-f8d975cf8c07&displaylang=en
Finding Out Which Site is the Problem
Are you running multiple sites and aren't sure which site is causing the problem? Well if you aren't already try running each site on a separate application pool. This is also a hint at another way to prevent these kind of performance issues which is managing a web garden in IIS. Hopefully I'll cover that another time. This will mean there is a w3wp process for each site. When one of these processes hits 100% you can use process explorer (http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx) to find out which pool is causing the issue. The name of the pool will be at the end of the target field when you click on the properties of the task though I'm sure there's a better way of finding that out.
Configuring IIS Logging
To use log parser effectively you need to ensure that you are logging the right things. Go into IIS manager, open the website tab, click on the logging properties button. Now click on the Extended Properties tab and check Time Taken. This is going to allow you to find pages that take a long time to execute in your logs.
Wait
Now you need to wait for it to happen again. Why not have a cup of tea or dig a hole to nowhere? One thing you could do to speed up this occurring again is to use a site crawler to hammer your application. There are plenty of free trials for these kinds of apps floating about the Internet, though it does assume your problem page is causing it's havoc in something executed during page load and not after user interaction.
Using Log Parser
Huzzah, it's at 100% again! Put down that spade and get back to your server. Open up a command prompt in the directory you installed logparser, copy your most recent logfile for the offending site into the logparser directory and paste this into the command window:
logparser "SELECT TOP 10 cs-uri-stem AS Url, MIN(time-taken) as [Min], AVG(time-taken) AS [Avg], max(time-taken) AS [Max], count(time-taken) AS Hits FROM ex*.log WHERE time-taken < class="blsp-spelling-error" id="SPELLING_ERROR_17">Url ORDER BY [Avg] DESC" -rtp:-1
This will give you the top 50 longest taking pages on your site. Next comes a bit of a tedious task. One or more of those pages is likely to be the page causing your problem. The rest are going to be pages that were held up because of that page that was causing the issue, or for some other erroneous reason like the user is trying to load a page with a dying Internet connection their end. In any case copy the results out of the command prompt and into note pad. Now copy each url, starting from the top, into your browser, maybe fiddle a bit with the ui, until you hit the one that maxes out the CPU of your server. This bit would probably be best done on a test server if you have one. When I last had this issue I didn't have to go any further than the second url to find my problem (though I did check them all for prosperity). It'll likely be something simple and stupid that you didn't expect to happen. Mine was a malformed url that was being generated on the site somewhere that omitted a certain parameter I expected to always be there.
Now, back to that hole to nowhere....
Friday, 12 June 2009
Tuesday, 12 May 2009
Import Data from Google Analytics VB.NET
As far as I know there is no current service available for consuming data from Google Analytics. The way I got round this was by setting up my analytics account to email me xml reports and then setting up an application to check the email account for them. When the application gets an email from Google it consumes the xml. It's best to set up a dedicated email address for analytics and save each xml document as they come in, just in case you want to change what you consume to your database at a later date and need to pull the whole lot back in again.
First get the (free) Indy Sockets dll from:
http://www.indyproject.org/index.en.aspx
The Code
' create a pop3 client using indie
Dim pop3 As New Indy.Sockets.POP3
pop3.Username = "******" 'the name of the account you get analytics to email reports to
pop3.Password = "****" 'password of the account
pop3.Host = "******" 'your mail host
pop3.Connect()
Dim nrOfMsgs As Integer = pop3.CheckMessages
For i As Integer = 1 To nrOfMsgs
Dim msg As New Indy.Sockets.Message
'get message
pop3.Retrieve(i, msg)
'attatchments
'ANALYTICS DATA
For x As Integer = 1 To msg.MessageParts.Count - 1 'search through the message parts
Dim att As Indy.Sockets.Attachment
Dim isAtt As Boolean = False
Try
att = CType(msg.MessageParts.Items(x), Indy.Sockets.Attachment)
isAtt = True
Catch
End Try
Dim ct As String = msg.MessageParts.Items(x).ContentType
If isAtt = True And InStr(msg.Subject, "the subject expected") > 0 Then
Dim stream As New Indy.Sockets.TIdNetMemoryStream
att.SaveToFile("c:\analytics\" & Now.Year & Now.Month & Now.Day & "-" & Now.Hour & Now.Minute & Now.Second & ".xml")
'here you can do whatever you like with the xml document, add all the data to your sql
'database!
End If
Next
pop3.Delete(i) 'delete the email from the server
Next
pop3.Disconnect()
pop3.Dispose()
First get the (free) Indy Sockets dll from:
http://www.indyproject.org/index.en.aspx
The Code
' create a pop3 client using indie
Dim pop3 As New Indy.Sockets.POP3
pop3.Username = "******" 'the name of the account you get analytics to email reports to
pop3.Password = "****" 'password of the account
pop3.Host = "******" 'your mail host
pop3.Connect()
Dim nrOfMsgs As Integer = pop3.CheckMessages
For i As Integer = 1 To nrOfMsgs
Dim msg As New Indy.Sockets.Message
'get message
pop3.Retrieve(i, msg)
'attatchments
'ANALYTICS DATA
For x As Integer = 1 To msg.MessageParts.Count - 1 'search through the message parts
Dim att As Indy.Sockets.Attachment
Dim isAtt As Boolean = False
Try
att = CType(msg.MessageParts.Items(x), Indy.Sockets.Attachment)
isAtt = True
Catch
End Try
Dim ct As String = msg.MessageParts.Items(x).ContentType
If isAtt = True And InStr(msg.Subject, "the subject expected") > 0 Then
Dim stream As New Indy.Sockets.TIdNetMemoryStream
att.SaveToFile("c:\analytics\" & Now.Year & Now.Month & Now.Day & "-" & Now.Hour & Now.Minute & Now.Second & ".xml")
'here you can do whatever you like with the xml document, add all the data to your sql
'database!
End If
Next
pop3.Delete(i) 'delete the email from the server
Next
pop3.Disconnect()
pop3.Dispose()
Monday, 11 May 2009
This row already belongs to another table ASP.NET
Just a quick look at a problem I came across when trying to copy rows from one datatable to another.
I originally tried this:
for i as integer = 0 to DT.rows.count - 1
newDT.rows.add(DT.rows(i))
next
ASP.NET threw a hissy fit at me and gave me the "This row already belongs to another table" error. I haven't looked to far into this but I assume the datarow objects are bound to a row schema inherited from the datatable schema. A bit of googling led me to the following solution:
for i as integer = 0 to DT.rows.count - 1
newDT = DT.Clone
newDT.ImportRow(DT.Rows(i))
next
So there we go, the clone function copies the schema from the first table to the second which allows for the importing of our rows.
I originally tried this:
for i as integer = 0 to DT.rows.count - 1
newDT.rows.add(DT.rows(i))
next
ASP.NET threw a hissy fit at me and gave me the "This row already belongs to another table" error. I haven't looked to far into this but I assume the datarow objects are bound to a row schema inherited from the datatable schema. A bit of googling led me to the following solution:
for i as integer = 0 to DT.rows.count - 1
newDT = DT.Clone
newDT.ImportRow(DT.Rows(i))
next
So there we go, the clone function copies the schema from the first table to the second which allows for the importing of our rows.
Tuesday, 5 May 2009
Posting an Image to a Web Page in asp.net
I recently had to write an application that was able to upload images to a website in order to be filed away and used by it's photo gallery pages. All I did to put this in place was make a timer which periodically checked for new images in a local folder. When it finds new images it loops through the files and passes each image to a posting function. After a successful post the image gets archived away by the local application. The website performs it's own local file and database functions to file away the image and the job is done! Let's look at some snippets:
The business end of the windows app....
Dim postURL as string = "http://www.yourwebsite.com/somepage.aspx"
Private Sub uploadImage(ByVal theImage As Image, ByVal filename As String)
theImage = processImage(theImage) 'this is a function I use for some intelligent resizing of the image
Dim tempFileName As String = Path.GetTempFileName
theImage.Save(tempFileName)
Dim myHTTPRequest As New WebClient
Dim myHTTPResponse As Byte() = myHTTPRequest.UploadFile(postURL, "POST", tempFileName)
End Sub
The website code to get the image...
Page.Response.ContentType = "image/jpg"
Dim myimage As Byte() = New Byte(Page.Request.ContentLength - 1) {}
Dim uppedImage As HttpPostedFile = Request.Files.Item(0)
uppedImage.InputStream.Read(myimage, 0, CInt(Request.Files.Item(0).ContentLength))
Dim theImage As Image
theImage = Image.FromStream(uppedImage.InputStream())
theImage.Save("C:\mylocalsite\" & getImageName() & ".jpg") 'getImageName just returns a unique date stamped file name
The business end of the windows app....
Dim postURL as string = "http://www.yourwebsite.com/somepage.aspx"
Private Sub uploadImage(ByVal theImage As Image, ByVal filename As String)
theImage = processImage(theImage) 'this is a function I use for some intelligent resizing of the image
Dim tempFileName As String = Path.GetTempFileName
theImage.Save(tempFileName)
Dim myHTTPRequest As New WebClient
Dim myHTTPResponse As Byte() = myHTTPRequest.UploadFile(postURL, "POST", tempFileName)
End Sub
The website code to get the image...
Page.Response.ContentType = "image/jpg"
Dim myimage As Byte() = New Byte(Page.Request.ContentLength - 1) {}
Dim uppedImage As HttpPostedFile = Request.Files.Item(0)
uppedImage.InputStream.Read(myimage, 0, CInt(Request.Files.Item(0).ContentLength))
Dim theImage As Image
theImage = Image.FromStream(uppedImage.InputStream())
theImage.Save("C:\mylocalsite\" & getImageName() & ".jpg") 'getImageName just returns a unique date stamped file name
Monday, 4 May 2009
Creating Image Watermarks in VB.NET
Fancy making your own batch watermarking application? Here's a function that'll get you started. I originally adapted this from some code that was serving images in asp.net watermarked at runtime using a http handler. That was pretty nifty but I really needed it to watermark images for some automated uploading to facebook.
Public Function AddWaterMark(ByVal theImage As Image) As Image
Dim canvas As Graphics
'create a new bmp the size of the passed image
Dim bmpNew As Bitmap = New Bitmap(theImage.Width, theImage.Height)
'create a canvas
canvas = Graphics.FromImage(bmpNew)
'draw the passed image onto the canvas
canvas.DrawImage(theImage, New Rectangle(0, 0, _
bmpNew.Width, bmpNew.Height), 0, 0, _
theImage.Width, theImage.Height, GraphicsUnit.Pixel)
theImage = bmpNew
Dim overlayImg As Bitmap
'make the overlay image
overlayImg = Drawing.Image.FromFile("C:\someplace\mywatermark.png", True)
'draw the overlay onto the top left corner
canvas.DrawImage(overlayImg, 0, 0, overlayImg.Width, overlayImg.Height)
Dim tempImage As Image = bmpNew
AddWaterMark = tempImage
End Function
Public Function AddWaterMark(ByVal theImage As Image) As Image
Dim canvas As Graphics
'create a new bmp the size of the passed image
Dim bmpNew As Bitmap = New Bitmap(theImage.Width, theImage.Height)
'create a canvas
canvas = Graphics.FromImage(bmpNew)
'draw the passed image onto the canvas
canvas.DrawImage(theImage, New Rectangle(0, 0, _
bmpNew.Width, bmpNew.Height), 0, 0, _
theImage.Width, theImage.Height, GraphicsUnit.Pixel)
theImage = bmpNew
Dim overlayImg As Bitmap
'make the overlay image
overlayImg = Drawing.Image.FromFile("C:\someplace\mywatermark.png", True)
'draw the overlay onto the top left corner
canvas.DrawImage(overlayImg, 0, 0, overlayImg.Width, overlayImg.Height)
Dim tempImage As Image = bmpNew
AddWaterMark = tempImage
End Function
Sunday, 3 May 2009
Stylesheets for Different Web Browsers
If you're like me and you are required to keep the websites you develop accessible to 95%+ of your customer base then making patch style sheets is a good way to go. To keep everything neat I develop a site with IE7 and FF3 open, testing in each periodically. At the end of the development process I open ie6 and, after my eyes have finished throwing up, I throw in an ie6 only stylesheet to begin the clear up. This stylesheet is called last so that I can override anything in my other css.
I like this way of doing things since it makes it a lot easier to dive in and out of, allows me to code the rest of the site free of ie6 restrictions but still lets me cater for those 15% of our visitors that still insist on using ie6. Plus I like the fact that in a year or two, when that 15% is closer to 0%, I can easily remove all my ie6 fixes with a simple edit to my master pages.
Other html if statements include:
IE7 only stylesheet for resolving any ie7 quirks.
A stylesheet for all browsers except for versions of internet explorer.
<!--ie 6 only!-->
<!--[if IE 6]-->
<link href="/css/ie6.css" rel="stylesheet" type="text/css">
<!--[endif]-->
<!--/ie 6 only!-->
I like this way of doing things since it makes it a lot easier to dive in and out of, allows me to code the rest of the site free of ie6 restrictions but still lets me cater for those 15% of our visitors that still insist on using ie6. Plus I like the fact that in a year or two, when that 15% is closer to 0%, I can easily remove all my ie6 fixes with a simple edit to my master pages.
Other html if statements include:
<!--[if IE 7]-->
<link rel="stylesheet" type="text/css" href="css/ie7.css">
<!--[endif]-->
IE7 only stylesheet for resolving any ie7 quirks.
<!--[if !IE]-->
<link rel="stylesheet" type="text/css" href="/css/nonIE.css">
<!--[endif]-->
A stylesheet for all browsers except for versions of internet explorer.
Subscribe to:
Posts (Atom)
