DevTrain

Autor: Bernhard Elbl

Usage Analyse selfmade - IIS Logfiles auswerten

Wenn Sie das Verhalten der Benutzer Ihrer Webseite analysieren wollen, brauchen Sie dazu nicht zwingend einen Analyzer zu kaufen. Auch mit "puren" ASP ist es z.B. möglich zu analysieren, wieviele Leute welche Browser benutzen. Das ganze ist natürlich nur rentabel, wenn Sie keine allzu grossen Logfiles haben. Script basiertes Analysing ist langsam. Dafür brauchen Sie aber keine zusätzliche Software installieren. Wenn Sie mehr Power benötigen, müssen Sie anders vorgehen. Zum Beispiel können Sie mit einem EINZIGEN SQL-Statement ein komplettes Logfile in eine SQL-DB importieren. Die Analyse-Vorgänge könnten zum Teil über SQL-Statements auf der anderen Seite ähnlich wie im ASP-Script vollzogen werden. Hier ein kleiner Ausschnitt aus der Session "Usage Analyse selfmade" der 5. ASP-Konferenz 2002 mit dem Schwerpunkt auf das W3C-Logfile Format.

I. Mit ASP-Script analysieren
Folgender Artikel beschreibt das Verwenden der "MSWC.IISLog" Komponente, die für ASP optimiert ist.
http://www.devtrain.de/news.asp?artnr=627

Ähnlich wie bei einem Recordset können Sie durch ein Logfile loopen und alle Werte über die entsprechende Eigenschaft der Komponente erfragen. Wenn Sie also herausfinden wollen, zu welcher Zeit/Stunde heute die meisten Zugriffe erfolgt sind könnten Sie folgendes Script benutzen.
<%
 Set oLog = Server.CreateObject("MSWC.IISLog")
 aLogFile = "E:WINDOWSsystem32LogfilesW3SVC1ex" & sDatum & ".log"
 oLog.OpenLogFile aLogFile, 1, "W3SVC", 1, "W3C Extended Log File Format"
 
 dim aHours(23) ' Array von 0 bis 23 = enthält für jede Stunden die Zugriffe
 dim selDate, selHour
 dim dDateTime
 dim counter
 Do While not oLog.AtEndOfLog
    counter = counter + 1
    oLog.ReadLogRecord
   
    if( not isnull(oLog.DateTime) ) then
   selDate = cdate(oLog.DateTime) ' Zeit eintrag abfragen
   selHour = hour(selDate)  ' Stunde isolieren
   aHours(selHour) = aHours(selHour) + 1 ' Stunde in Array eintragen
    end if
 Loop
 
 sQuery = "?" ' QueryString für grafische Ausgabe
 for i = 0 to 23 ' Ausgabe der Zugriffe pro Stunde
  sQuery = sQuery & "h=" & cdbl(aHours(i)) & "&"
  Response.Write i & ":--- " & cdbl(aHours(i)) & "<br>"
 next
 
 sQuery = left(sQuery,len(sQuery)-1)
%>

Wenn Sie dieses Ergebnis jetzt Grafisch darstellen wollen, können Sie mit ASP ein Chart erzeugen. Das Chart selbst wird durch die Office XP WebComponents erzeugt. Diese können Explizit installiert werden. Wie das geht können Sie hier nachlesen...
http://www.devtrain.de/news.asp?artnr=664

sQuery??? Wie Ihr oben im Code sehen könnt, wird hier ein QueryString zusammen gesetzt. Dieser wird einer ASP-Seite übergeben, welche ein binäres Chart als Response gibt. Wir können die ASP Seite also wie ein Bild behandeln und Bilder werden in HTML wie folgt eingebunden.
<img src="Hot_Access_Chart.asp?<%=sQuery%>">

Werfen wir einen Blick auf die Hot_Access_Chart.asp. Hier nur ein kurzer Ausschnitt. Den kompletten Source gibts dann unten zum Download.
1. Die Daten aus dem QueryString werden wieder als Array zusammen gesetzt.
dim iHCount: iHCount = Request.QueryString("h").Count
if iHCount > 0 then
 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
 '''' literal Daten für Chart aufbereiten
 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
 dim aHoursNames(23)   'aHoursNames ist die Stunde als z.B. 4 Uhr.
 dim aHours(23)   'aHours ist der Wert der Stunde also z.B. 500 Zugriffe

 for i = 1 to iHCount
  aHoursNames(i-1) = i & " Uhr"
  aHours(i-1) = Request.QueryString("h")(i)
 next
 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''


2. Die Array-Daten werden dem Chart übergeben
 ' Daten an Chart übergeben!
 ch.SetData c.chDimCategories, c.chDataLiteral, aHoursNames
 ch.SeriesCollection(0).SetData c.chDimValues, c.chDataLiteral, aHours

3. Das Chart wird binär in den Response-Stream geschrieben
 ' ContentType für GIF-Stream setzen
 Response.ContentType = "image/GIF"
 ' Bild aus dem Speicher in den Response schreiben, Höhe und Breite des Bilds = 600*400 Pixel
 Response.BinaryWrite chsp.GetPicture("GIF", 600, 400)


II. Logfiles in eine SQL-DB importieren
Wie bereits erwähnt, eignet sich die oben gezeigte Möglichkeit nur für kleine Logfiles.
Sie könnten die Logfiles auch Zeitgesteuert in eine DB importieren und dann mit einer gesunden Mischung von SQL-Statements und Funktionen auswerten. Das importieren selbst geht mit Hilfe des BULK-INSERT Statements von MS-SQL. Dieses Funktioniert leider erst ab SQL Server 2000 fehlerfrei. Das Statement steht zwar auch in SQL Server 7 zur Verfügung, steht aber auf der Bug-Liste von SQL-S 7 :-(.

Bevor Sie weiterlesen, lesen Sie Bitte folgende MSDN-Artikel. Hier ist genau beschrieben, wie Sie Logfiles importieren.
HOWTO: Use SQL Server to Analyze Web Logs (Q296085)
http://support.microsoft.com/default.aspx?scid=kb;EN-US;q296085

FILE: PrepWebLog Utility Prepares IIS Logs for SQL Bulk Insert (Q296093)
http://support.microsoft.com/default.aspx?scid=kb;en-us;Q296093

Kurze Zusammenfassung:
Als erstes muss ein Logfile mit preplog.exe vorbereitet werden, dann können Sie über das BULK-INSERT Statement das vorbereitete Logfile importieren.
Der Haken an der Geschichte ist, dass die preplog.exe Buggy ist. Genau gesagt, das Ergebnis ist ein mehr oder weniger verstümmeltes Logfile. Und genau deshalb wird die preplog.exe auch mit Source-Code ausgeliefert. Hier der komplette und korrigierte Code für die preplog.exe. (kompilierte EXE im Download enthalten)
#include <stdio.h>
#include <string.h>


int main(int argc, char **argsch)
{
   FILE *stream;
   char line[10000]; // 1. Bug korrigiert: fasch 1000 statt 10000
   int  ch;
   int  ch2;

   if(argc < 2)
   {
    printf("Usage: preplog.exe <weblog> ");
    printf(" The output will go to stdout, so use > filename to direct to an output file ");
    return -1;
 }//if


   if( (stream = fopen( argsch[1], "r" )) != NULL )
   {
  while(fgets(line,10000,stream) != NULL)
  {
     if( ch = strncmp(line,"#",1) !=0 )
     {
      if( (ch = strncmp(line," ",1) !=0) ) // 2. Bug korrigiert: Leerzeilen wurden auch mit importiert
      {
       printf( "%s", line);
      }
     }//if
  }//while
      fclose( stream );
   return 0;
   }//if
   else
   {
    printf("Could not open %s.  Please ensure that the path and filename are correct. ",argsch[1]);
    return -1;
   }//else
}//main


Mit Hilfe eines VB-Script können Sie das importieren automatisiert durchführen.
Hier das Script...

dim sPrepfile:   sPrepfile = "C:logfilesqlpreplog.exe"

dim sLogPath:   sLogPath = "C:logfilesql"
' Der Logfile-Name sollte dynamisch generiert werden.
dim sLogFileName:  sLogFileName = sLogPath & "ex020614.log"
dim sSQLLogFileName: sSQLLogFileName = sLogPath & "sql.log"

' überprüfen ob Logfile bereits in DB
dim cn: set cn = CreateObject("ADODB.Connection")
cn.Open "Provider=SQLOLEDB.1.."
dim rs: set rs = cn.Execute( _
     "SELECT * FROM logfiles WHERE LogfileName = '" & _
     sLogFileName & "'")

if( rs.BOF and rs.EOF ) then
 ' Logfile vorbereiten für BULK INSERT
 dim sCmd: sCmd = "cmd /c " & sPrepfile & " " & _
        sLogFileName & " >" & sSQLLogFileName
 dim shell: set shell = CreateObject("WScript.Shell")
 shell.Run sCmd, 0, true
 set shell = nothing

 ' Logfilenamen in DB speichern
 cn.Execute "INSERT INTO Logfiles (LogfileName) VALUES ('" & _
   sLogFileName & "')"
 
 ' Logfiledaten in DB speichern
 cn.Execute "BULK INSERT [dbo].[iislog] FROM " & _
   "'" & sSQLLogFileName & "'" & _
   "WITH (FIELDTERMINATOR = ' ', ROWTERMINATOR = ' ')"
end if

set rs = nothing
set cn = nothing

Wenn Sie zusätzliche Commands in das Script einfügen, die z.B. den Logfilenamen abhängig vom heutigen Datum generieren, dann könnten Sie dieses Script von den Scheduled Tasks von NT/2000 automatisch ausführen lassen, und somit voll automatisiert Ihre Datenbank mit den neuesten Logfiles füttern.


Erfasst am: 11.07.2002 - Artikel-URL: http://www.devtrain.de/news.aspx?artnr=785
© Copyright 2003 ppedv AG - http://www.ppedv.de