by Mark Trescowthick - GUI Computing
I don't know about you, but I find managing remote IIS Servers a bit of a pain. About half the time remote administration isn't possible for security or other reasons and about 100% of the time the IIS Log I only ever need to look at at the end of a busy day (when it's about 2.5MB) and I'm on a slow link.
I finally got frustrated enough by this to do something about it. I figured this would be pretty easy. After all, the log's in text file format (unless you log to a SQL database, which we normally don't). All I needed to do was find the format and away I went. About an hour later, I stumbled over the format documented in the IIS online documentation in chapter 7.
Another hour or so later and I was happily viewing logs on screen. Only an hour after that I'd found the buglet which meant I was skipping a line every now and then… I wonder if Microsoft will ever standardise on either 1-based or 0-based, or is it just their way of keeping mug developers like me on our toes?
In any event, the result was one pretty simple ASP script, which is available for download.
The first step was to create some sort of input form and I wanted to keep this pretty basic, so I only allow the choice of a log file date and whether or not you want to read from the start or end of the log. Log files are typically held in the C:Winnt\System32\LogFiles subdirectory, and I hard coded that path. Obviously your mileage may vary.
Log files are named as "IN<date>.LOG", where date is YYMMDD. As this was a quickie utility, I expect the date in exactly that format. Obviously, if this were to be deployed somewhere users might see it, I'd be a bit friendlier (well, at least I hope I would).
Once the ASP is run, the first thing it does is see if cmdView has any value. Obviously, if we're running it ab initio, it won't and the code then simply displays the input form. And keeps displaying that form until you provide it with a valid file name.
One interesting thing is my use of cmdView - it's probably poorly named now, but it is in fact the name of every button in the application. I often use this technique when I have ASPs feeding themselves multiple times as this one does. It makes, I find, for less cluttered code. Of course, it would fail a strict code review, but then having ASPs feeding themselves like this I would probably fail as a technique per se for anything less trivial than this little utility. I'll grant having three buttons with the same name on one form is not something most developers are going to find intuitive, but it really reduces the number of branches in code.
cmdView can have the following values :
Once we have a file name cmdView has a value and, assuming it's not "Done" we are ready to start displaying.
Obviously, the first thing is to open that file to ensure it's there and bug out if it isn't. ASPs error handling isn't up to much, but it is up to this, thank goodness. The FileScripting Object isn't much better, but it does all I need in this case (i.e. read lines!).
Set fs = CreateObject("Scripting.FileSystemObject") on error resume next set objLogFile = fs.OpenTextFile(sBasePath & sFileName) if Err.Source <> "" then bErrFlag = 1 sErrDescription = "Invalid File name or Path. Please try again." set fs = Nothing end if
As I offer the option of reading from the start or the end of the log, I've got to branch on that basis, and obviously choosing to start at the end is going to be considerably slower, as the only way to establish where the end is is to read each line sequentially. I was tempted to read the whole lot into a session array, but figured that, with log files over 1MB commonplace, perhaps other users on the server wouldn't be so keen on me hogging quite so much resource - though it does mean that I have to re-read chunks of the file every time we go back and forward.
As I also offer the option of viewing the next (and previous) 20 entries, I have to branch on that as well. I thought initially that this would be a pain, but two session variables indicating the start and end line I've just read meant that next and previous were relatively straightforward (once I figured my 0-base/1-base bug!).
if bErrFlag = 0 then ' no file error Select Case sReadOption Case "BEGIN" iLineCount = 0 InitialiseTable sFileName, 1 ' write header guff do while iLineCount < 20 and objLogFile.AtEndOfStream = FALSE strLine = objLogFile.readline CreateTableRow strLine iLineCount = iLineCount + 1 Loop Session("iLineStart") = 1 Session("iLineEnd") = 21 response.write "</table></Html>" Case "END" iLineCount = 0 InitialiseTable sFileName, 2 ' write header guff ' read everything, keeping the last 20 in the array do while objLogFile.AtEndOfStream = FALSE sLines(iLineCount) = objLogFile.readline iLineCount = iLineCount + 1 if iLineCount > 19 then iLineCount = 0 Loop ' print oldest in array, then the rest for iPosCount = iLineCount to 19 CreateTableRow sLines(iPosCount) next for iPosCount = 0 to iLineCount -1 CreateTableRow sLines(iPosCount) next Session("iLineStart") = objLogFile.Line - 20 Session("iLineEnd") = objLogFile.Line response.write "</table></Html>" Case "FROM" iLineCount = 0 ' read lines until we get to the first one we want do while objLogFile.Line <> iLineStart sLines(iLineCount) = objLogFile.readline Loop iLineCount = iLineCount + 1 ' now get the next 19 do while iLineCount < 20 and objLogFile.AtEndOfStream = FALSE sLines(iLineCount) = objLogFile.readline iLineCount = iLineCount + 1 Loop ' Figure out what buttons to Exclude and initialise iExcludeButton = 0 if iLineStart = 1 then iExcludeButton = 1 if objLogFile.AtEndOfStream = TRUE then iExcludeButton = iExcludeButton + 2 InitialiseTable sFileName, iExcludeButton ' generate table for iLineCount = 0 to 19 CreateTableRow sLines(iLineCount) next ' set session info Session("iLineStart") = iLineStart Session("iLineEnd") = objLogFile.Line + 1 response.write "</table></Html>" end Select ' clean up objLogFile.close set objLogFile = Nothing set fs = Nothing
Having read the lines in each case, I had to unpick the comma-delimited list each line contains, and for this I would normally call my utility DLL, which contains (amongst other goodies) the 'ToolBook string handling' routines I documented a few issues back. But my DLL won't be on every server (some would say "lucky servers!") so I just copied and pasted the function from VB straight into ASP. Remove a couple of extraneous $ signs, a couple of typed dims, and (strangely enough) the "t" in "next t" and hey presto.
I really love the way code is so portable these days. I will forgive MS most of the things I dislike about VB, ASP, etc simply and solely because my code reuse is so easy. And so flexible. I especially like it when it saves me from being exposed to VB's woefully unfriendly string handling…
This function (GetItem) is wrapped in a CreateTableRow Sub, just to get those annoying response.writes out of my hair. I have come to thoroughly hate HTML embedded in code, and try to wrap Functions or Subs around it whenever I can.
In any event, this simple little ASP utility has served me well. What you end up with looks something like this...
I trust someone out there will find it useful.
PS : Before Ross Mack and Jim Karabatsos jump down my throat… yes, guys, I know Option Explicit isn't on. It's only a utility! Lighten up!
These 'real' programmers get soooo anal!