home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / VCAFE.3.0A / Sample.bin / LogFile.java < prev    next >
Text File  |  1998-11-05  |  31KB  |  706 lines

  1. // Copyright (c) 1997, 1998 Symantec, Inc. All Rights Reserved.
  2.  
  3. /*
  4. Parses Common Log Format (CLF), the Extended Log Format, and 
  5. Microsoft Professional Internet Services log file format (MPIS)
  6.  
  7. ---- The Common Logfile Format ----
  8.  
  9. The common logfile format is as follows: 
  10.  
  11.     remotehost rfc931 authuser [date] "request" status bytes
  12.  
  13. remotehost
  14.      Remote hostname (or IP number if DNS hostname is not available, or if DNSLookup is
  15.      Off. 
  16. rfc931
  17.      The remote logname of the user. 
  18. authuser
  19.      The username as which the user has authenticated himself. 
  20. [date]
  21.      Date and time of the request. 
  22. "request"
  23.      The request line exactly as it came from the client. 
  24. status
  25.      The HTTP status code returned to the client. 
  26. bytes
  27.      The content-length of the document transferred. 
  28.      
  29. ---- The Extended Logfile Format adds ----
  30.  
  31. referringURL
  32.     The URL of the previous gotten
  33. userAgent
  34.     program generating the HTTP requests
  35.  
  36. ---- MPIS Format ----
  37. Client IP Address,
  38. Client Username,
  39. Date,
  40. Time,
  41. Service,
  42. Computer Name,
  43. IP address of server,
  44. Processing time (ms),
  45. Bytes received,
  46. Bytes sent,
  47. Service status code,
  48. Windows NT status code,
  49. Name of operation,
  50. Target of operation
  51.  
  52. */
  53.  
  54. import java.net.URL;
  55. import java.net.URLConnection;
  56. import java.io.Serializable;
  57. import java.io.File;
  58. import java.io.InputStream;
  59. import java.io.InputStreamReader;
  60. import java.io.BufferedReader;
  61. import java.util.Enumeration;
  62. import java.util.NoSuchElementException;
  63. import java.util.Vector;
  64. import java.util.Date;
  65. import java.util.Calendar;
  66.  
  67. /*
  68. This class represents a single log file.
  69. It tracks the log file URL and expected format.
  70. It parses the log file and saves the result in an array of LogRecords.
  71. */
  72. class LogFile implements Serializable {
  73.     // Calendar and month strings used for converting log timestamp into millisecs
  74.     static Calendar cal = Calendar.getInstance();
  75.     static final String MONTHS[] = {
  76.         "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 
  77.     };
  78.     // Possible log file formats
  79.     public static final int FORMAT_AUTO_DETECT = 0;
  80.     public static final int FORMAT_COMMON = 1;
  81.     public static final int FORMAT_EXTENDED = 2;
  82.     public static final int FORMAT_MPIS = 3;
  83.     // Always present data. Defines log file this class represents
  84.     String logFileURL;      // the URL of the log file
  85.     int logFileFormat;      // the expected log file format. FORMAT_
  86.     // Parsed log file records. Call readAndParseLogFile() to create.
  87.     LogRecord[] records;
  88.     // Info determined when log file is read and parsed.
  89.     long earliestTimestamp;
  90.     long latestTimestamp;
  91.     
  92.     /*
  93.     Constructs a LogFile.
  94.     */
  95.     public LogFile() {
  96.             //{{INIT_CONTROLS
  97.         //}}
  98. }
  99.  
  100.     /*
  101.     Frees up non-essential memory used by the log file.
  102.     */
  103.     void freeUpMem() {
  104.         records = null;
  105.     }
  106.  
  107.     // -- ACCESS ROUTINES
  108.     
  109.     public String getLogFileURL() {
  110.         return logFileURL;
  111.     }
  112.     public void setLogFileURL(String logFileURL) {
  113.         this.logFileURL = logFileURL;
  114.     }
  115.     
  116.     public int getLogFileFormat() {
  117.         return logFileFormat;
  118.     }
  119.     public void setLogFileFormat(int logFileFormat) {
  120.         this.logFileFormat = logFileFormat;
  121.     }
  122.     
  123.     /*
  124.     Read and parse the log file.
  125.     This must be called before using any of the get... routines below.
  126.     */
  127.     public LogRecord[] readAndParseLogFile(TrackProgress trackProgress) throws ParseLogException {
  128.         // init values for scan
  129.         earliestTimestamp = Long.MAX_VALUE;
  130.         latestTimestamp = 0;
  131.         records = null;
  132.         Parser parser = new Parser();
  133.         parser.parse(trackProgress);
  134.         return records;
  135.     }
  136.  
  137.     /*
  138.     Gets the LogRecords parsed from the log file.
  139.     This must be called after readAndParseLogFile() to be valid.
  140.     */
  141.     public LogRecord[] getRecords() {
  142.         return records;
  143.     }
  144.  
  145.     /*
  146.     Returns the total number of hits in the log file.
  147.     This must be called after readAndParseLogFile() to be valid.
  148.     */
  149.     public int getTotalHits() {
  150.         return records.length;
  151.     }
  152.     
  153.     /*
  154.     Gets the earliest timestamp occuring in the log file.
  155.     This must be called after readAndParseLogFile() to be valid.
  156.     */
  157.     public long getEarliestTimestamp() {
  158.         return earliestTimestamp;
  159.     }
  160.     
  161.     /*
  162.     Gets the latest timestamp occuring in the log file.
  163.     This must be called after readAndParseLogFile() to be valid.
  164.     */
  165.     public long getLatestTimestamp() {
  166.         return latestTimestamp;
  167.     }
  168.  
  169.     //  --  The Log File Parser
  170.  
  171.     class Parser {
  172.         
  173.         // This class buffers/stores the result of parsing one line of the log file
  174.         class LogParseRecord {
  175.             // Parse line results
  176.             public StringBuffer remoteHost = new StringBuffer();
  177.             public StringBuffer remoteUserLogname = new StringBuffer();
  178.             public StringBuffer authenticatedUserName = new StringBuffer();
  179.             public long requestTimestamp;
  180.             public StringBuffer clientRequest = new StringBuffer();
  181.             public int httpStatusCode;
  182.             public int bytesTransferred;
  183.             public StringBuffer referringURL = new StringBuffer();
  184.             public StringBuffer userAgent = new StringBuffer();        // browser/agent info
  185.             public boolean bExtraText;     // There is extra text at the end of this line (ignored)
  186.         }
  187.         
  188.         // This class buffers/stores the result of parsing one line of the log file (NT)
  189.         class LogParseRecordMPIS {
  190.             // Parse line results
  191.             public StringBuffer clientIPAddress = new StringBuffer();
  192.             public StringBuffer clientUserName = new StringBuffer();
  193.             public long requestTimestamp;
  194.             public StringBuffer serviceName = new StringBuffer();
  195.             public StringBuffer computerName = new StringBuffer();
  196.             public StringBuffer serverIPAddress = new StringBuffer();
  197.             public int processingMilliseconds;
  198.             public int bytesReceived;
  199.             public int bytesSent;
  200.             public int serviceStatusCode;
  201.             public int ntStatusCode;
  202.             public StringBuffer operationName = new StringBuffer();
  203.             public StringBuffer operationTarget = new StringBuffer();
  204.             public StringBuffer dash = new StringBuffer();  // always seems to be a dash...
  205.             public StringBuffer blank = new StringBuffer(); // always seems to be blank
  206.             public boolean bExtraText;     // There is extra text at the end of this line (ignored)
  207.         }
  208.         
  209.         // Scanning/parsing vars
  210.         BufferedReader logReader;
  211.         String scanStr;     // line currently being parsed
  212.         int scanIdx;        // index into line currently being parsed
  213.         StringBuffer workBuf = new StringBuffer();  // general use
  214.         LogParseRecord logParseRec = new LogParseRecord();          // Results of parsing a line
  215.         // NT log file format support
  216.         StringBuffer workBuf2 = new StringBuffer();  // general use
  217.         LogParseRecordMPIS logParseRecMPIS = new LogParseRecordMPIS();    // Results of parsing a line (NT)
  218.  
  219.         /*
  220.         This method reads and parses the log file.
  221.         User feedback is provided using the given trackProgress interface object.
  222.         A ParseLogException is thrown if a non-ignorable parsing error occurs.
  223.         */
  224.         public void parse(TrackProgress trackProgress) throws ParseLogException {
  225.             URL url;
  226.             URLConnection con;
  227.             InputStream is;
  228.             int contentLength;
  229.             int lineCount = 0;
  230.             // Collect garbage mem before doing this potentially memory-intesive task
  231.             System.gc();
  232.             Vector recordVector = new Vector();
  233.             // Initialize File
  234.             try {
  235.                 // Create URL
  236.                 Analyzer.throwExceptionIfCurrentThreadCancelled();
  237.                 trackProgress.step("Creating URL...", 1);
  238.                 url = new URL(logFileURL);
  239.                 // Get connection to log file
  240.                 Analyzer.throwExceptionIfCurrentThreadCancelled();
  241.                 trackProgress.step("Opening connection...", 2);
  242.                 con = url.openConnection();
  243.                 // Get input stream of log file
  244.                 Analyzer.throwExceptionIfCurrentThreadCancelled();
  245.                 trackProgress.step("Accessing log file...", 3);
  246.                 is = con.getInputStream();
  247.                 logReader = new BufferedReader(new InputStreamReader(is));
  248.                 // Get length of log file (to calc percentage complete)
  249.                 Analyzer.throwExceptionIfCurrentThreadCancelled();
  250.                 trackProgress.step("Parsing log file...", 4);
  251.                 contentLength = con.getHeaderFieldInt("content-length", 100000);
  252.                 String aLine;
  253.                 // Loop, reading a line from the log file and parsing it
  254.                 int contentRead = 0;
  255.                 for(;;) {
  256.                     Analyzer.throwExceptionIfCurrentThreadCancelled();
  257.                     if(null == (aLine = logReader.readLine())) {
  258.                         break;
  259.                     }
  260.                     // percent for this phase ranges from 5 -> 95
  261.                     contentRead += aLine.length() + 1;
  262.                     int percent = 5 + ((90 * contentRead)/contentLength);
  263.                     trackProgress.step("Parsing log file...", percent);
  264.                     // Process the next log line
  265.                     lineCount++;
  266.                     // skip blank lines...and ones that are just too short
  267.                     if(aLine.length() > 4) {
  268.                         try {
  269.                             // Parse the log file line
  270.                             if(!parseALine(aLine)) {
  271.                                 //ignore this line
  272.                                 continue;
  273.                             }
  274.                             // Add this log file line info to the records vector
  275.                             recordVector.addElement(new LogRecord(logParseRec));
  276.                             // maintain earliest timestamp info
  277.                             if(logParseRec.requestTimestamp < earliestTimestamp) {
  278.                                 earliestTimestamp = logParseRec.requestTimestamp;
  279.                             }
  280.                             // maintain latest timestamp info
  281.                             if(logParseRec.requestTimestamp > latestTimestamp) {
  282.                                 latestTimestamp = logParseRec.requestTimestamp;
  283.                             }
  284.                             // as needed, zero fields that may not exists next go-round
  285.                             if(logFileFormat == FORMAT_AUTO_DETECT) {
  286.                                 logParseRec.referringURL.setLength(0);
  287.                                 logParseRec.userAgent.setLength(0);
  288.                             }
  289.                         } catch(ParseLogException x) {
  290.                             // Bad log line encountered. Either silently ignore or give user option.
  291.                             Data data = Data.getDataInstance();
  292.                             if(!data.ignoreUnexpectedLogFileErrors) {
  293.                                 String msg ="Ill-formed log line #" + lineCount 
  294.                                             + " (" + x.getMessage() 
  295.                                             + "). Press OK to ignore, Cancel to abort.";
  296.                                 // Show alert and abort if requested
  297.                                 if(!trackProgress.okCancelAlert(msg)) {
  298.                                     trackProgress.step("ABORTED", 0);
  299.                                     throw x;
  300.                                 }
  301.                             }
  302.                         }
  303.                     }
  304.                 }
  305.                 // The log file has been read and parsed into a vector of LogRecords.
  306.                 // Now now copy'em into an array and sort in chronological order.
  307.                 records = new LogRecord[recordVector.size()];
  308.                 recordVector.copyInto(records);
  309.                 recordVector = null;        // don't need this vector anymore
  310.                 Analyzer.throwExceptionIfCurrentThreadCancelled();
  311.                 WLAUtil.quickSort(records);
  312.                 // Done OK
  313.                 return;
  314.             } catch(java.net.MalformedURLException x) {
  315.                 trackProgress.okAlert(x.toString());
  316.                 trackProgress.step("ABORTED", 0);
  317.                 throw new ParseLogException("Malformed URL", x);
  318.             } catch(java.io.IOException x) {
  319.                 trackProgress.okAlert(x.toString());
  320.                 trackProgress.step("ABORTED", 0);
  321.                 throw new ParseLogException("IO Exception", x);
  322.             }
  323.         }
  324.  
  325. // COMMON LOG FORMAT
  326. //syntax:    remotehost rfc931 authuser [date] "request" status bytes
  327. //155.64.199.67 - - [06/Apr/1998:22:51:45 +0000] "GET /index.htm HTTP/1.0" 200 2596
  328. // EXTENDED FORMAT
  329. //syntax:    remotehost rfc931 authuser [date] "request" status bytes referringURL userAgent
  330. // MS NT FORMAT
  331. //syntax?: remotehost, rfc931, date, time, servicename, appname, somedomain, #1, #2, #3, status#, #5, theRequest, file, dash?, blank?
  332. //155.64.199.156, -, 4/2/98, 18:20:19, W3SVC, VPAGE2, 155.64.35.204, 712224, 271, 28796, 200, 0, GET, /jeannette/text.htm, -, 
  333. //155.64.198.182, guest@unknown, 4/2/98, 18:33:55, MSFTPSVC, VPAGE2, -, 190, 916, 0, 0, 0, [64]  created , itnews01.htm, -, 
  334. //155.64.198.182, bbubb, 4/2/98, 18:42:16, MSFTPSVC, VPAGE2, -, 190, 921, 0, 0, 0, [68]  created , itnews01.htm, -, 
  335.         
  336.         // Parses the given log file line into the logParseRec object
  337.         private boolean parseALine(String aLine) throws ParseLogException {
  338.             // ini vars used in the getNext...() methods
  339.             scanIdx = 0;
  340.             scanStr = aLine;
  341.             // Determine if NT logfile format
  342.             int idx = aLine.indexOf(' ');
  343.             int idx2 = aLine.indexOf(',');
  344.             if((idx != -1) && (idx2 != -1) && (idx2 < idx)) {
  345.                 // Is NT Logfile format
  346.                 if((logFileFormat != FORMAT_AUTO_DETECT) && (logFileFormat != FORMAT_MPIS)) {
  347.                     // err...
  348.                     String str = (logFileFormat == FORMAT_COMMON) ? "not Common Log Format" : "not Extended Log Format";
  349.                     throw new ParseLogException(str, null);
  350.                 }
  351.                 return parseALineMPIS(aLine);
  352.             }
  353.             try {
  354.                 // host name or IP number
  355.                 getNextWord(logParseRec.remoteHost);
  356.                 // remote user name
  357.                 getNextWord(logParseRec.remoteUserLogname);
  358.                 // authenticated user name
  359.                 getNextWord(logParseRec.authenticatedUserName);
  360.                 // date/time of request
  361.                 logParseRec.requestTimestamp = getNextTimestamp();
  362.                 // the request
  363.                 getNextQuotedString(logParseRec.clientRequest);
  364.                 // the request return http status
  365.                 logParseRec.httpStatusCode = getNextInt();
  366.                 // the length of the transfer
  367.                 logParseRec.bytesTransferred = getNextInt();
  368.                 // Determine if we're at the end of the line...
  369.                 logParseRec.bExtraText = scanIdx < aLine.length();
  370.                 
  371.                 // End of Common Log Format. Check for log format compliance
  372.                 if(logFileFormat == FORMAT_COMMON) {
  373.                     // Expecting CLF specificly, no extra text allowed
  374.                     if(logParseRec.bExtraText) {
  375.                         throw new ParseLogException("extra text at end of log line - not Common Log Format", null);
  376.                     }
  377.                     // All OK and done with this line
  378.                     return true;
  379.                 }
  380.                 // If auto-detect format and ends here, OK
  381.                 if(logFileFormat == FORMAT_AUTO_DETECT && !logParseRec.bExtraText) {
  382.                     return true;
  383.                 }
  384.                 
  385.                 // Either auto with extra text or extended log format. 
  386.                 // Either way, continue parsing
  387.                 getNextQuotedString(logParseRec.referringURL);
  388.                 // browser info
  389.                 getNextQuotedString(logParseRec.userAgent);
  390.  
  391.                 // Determine if we're at the end of the line...
  392.                 logParseRec.bExtraText = scanIdx < aLine.length();
  393.                 // If Extended specificly requested, no extra text allowed
  394.                 if(logFileFormat == FORMAT_EXTENDED && logParseRec.bExtraText) {
  395.                     throw new ParseLogException("extra text at end of log line - not Extended Log Format", null);
  396.                 }
  397.                 // All OK
  398.             } catch(ArrayIndexOutOfBoundsException x) {
  399.                 throw new ParseLogException("unexpected end of line");
  400.             }
  401.             return true;
  402.         }
  403.  
  404.         /*
  405.         Parses the next "word" in the current line.
  406.         On entry and exit scanStr[scanIdx] always refers to non-whitespace 
  407.         character (or past end of line).
  408.         */
  409.         private void getNextWord(StringBuffer destBuf) throws ParseLogException {
  410.             // find end of word
  411.             int endIdx = scanStr.indexOf(' ', scanIdx+1);
  412.             if(endIdx < 0) {
  413.                 endIdx = scanStr.length();
  414.             }
  415.             // extract substring into destBuf
  416.             destBuf.setLength(0);
  417.             destBuf.append(scanStr.substring(scanIdx, endIdx));
  418.             // keep track of where we are scanning
  419.             scanIdx = endIdx+1;
  420.         }
  421.         
  422.         /*
  423.         Parses the next integer in the current line.
  424.         On entry and exit scanStr[scanIdx] always refers to non-whitespace 
  425.         character (or past end of line).
  426.         */
  427.         private int getNextInt() throws ParseLogException {
  428.             //Gather word
  429.             getNextWord(workBuf);
  430.             // Convert
  431.             try {
  432.                 if(workBuf.length() == 1 && workBuf.charAt(0) == '-') {
  433.                     return 0;
  434.                 }
  435.                 return Integer.parseInt(workBuf.toString());
  436.             } catch(NumberFormatException x) {
  437.                 throw new ParseLogException("invalid number format", x);
  438.             }
  439.         }
  440.  
  441.         /*
  442.         Parses a double-quoted string in the current line.
  443.         The string
  444.         On entry scanStr[scanIdx] always should be a '"' character.
  445.         On exit scanStr[scanIdx] always refers to non-whitespace character 
  446.         (or past end of line).
  447.         */
  448.         private void getNextQuotedString(StringBuffer destBuf) throws ParseLogException {
  449.             // Check for expected initial quote
  450.             char ch = scanStr.charAt(scanIdx);
  451.             if(ch != '"') {
  452.                 throw new ParseLogException("expecting starting quote");
  453.             }
  454.             // skip leading quote
  455.             scanIdx++;
  456.             // find end of string, signalled by quote followed by a space
  457.             int endIdx = scanIdx;
  458.             int lim = scanStr.length();
  459.             do {
  460.                 // determine end of quote. 
  461.                 // If newline within quote, append next line to quoted string.. 
  462.                 int wasEndIdx = endIdx;
  463.                 for(;;) {
  464.                     endIdx = scanStr.indexOf('"', endIdx);
  465.                     if(endIdx >= 0) {
  466.                         break;
  467.                     }
  468.                     String appendStr;
  469.                     try {
  470.                         appendStr = logReader.readLine();
  471.                     } catch(java.io.IOException x) {
  472.                         throw new ParseLogException("IO exception", x);
  473.                     }
  474.                     if(appendStr == null) {
  475.                         throw new ParseLogException("EOF instead of ending quote");
  476.                     }
  477.                     scanStr += "\n" + appendStr;
  478.                     // try again
  479.                     endIdx = wasEndIdx;
  480.                 }
  481.             } while(++endIdx < lim && scanStr.charAt(endIdx) != ' ');
  482.             // extract substring into destBuf
  483.             destBuf.setLength(0);
  484.             destBuf.append(scanStr.substring(scanIdx, endIdx-1));
  485.             // keep track of where we are scanning
  486.             scanIdx = endIdx+1;
  487.         }
  488.  
  489.         /*
  490.         Parses a log timestamp in the current line (like: [02/Feb/1998:02:52:36 -0800]).
  491.         On entry scanStr[scanIdx] always should be a '[' character.
  492.         On exit scanStr[scanIdx] always refers to non-whitespace character 
  493.         (or past end of line).
  494.         */
  495.         private long getNextTimestamp() throws ParseLogException {
  496.  
  497.             // -- Gather timestamp
  498.             
  499.             // Check for expected initial quote
  500.             char ch = scanStr.charAt(scanIdx);
  501.             if(ch != '[') {
  502.                 throw new ParseLogException("expecting starting square bracket");
  503.             }
  504.             // skip leading square bracket
  505.             scanIdx++;
  506.             // find end of timestamp
  507.             int endIdx = scanStr.indexOf(']', scanIdx);
  508.             if(endIdx < 0) {
  509.                 throw new ParseLogException("EOL instead of ending square bracket");
  510.             }
  511.             // note start of timestamp
  512.             int idx = scanIdx;
  513.             // keep track of where we are scanning
  514.             scanIdx = endIdx+2;
  515.  
  516.             // -- Parse the timestamp: 02/Feb/1998:02:52:36 -0800
  517.             
  518.             try {
  519.                 // Convert parts of timestamp 
  520.                 int day     = Integer.parseInt(scanStr.substring(idx, idx+2));
  521.                 int month   = MonthStringToInt(scanStr.substring(idx+3, idx+6));
  522.                 int year    = Integer.parseInt(scanStr.substring(idx+7, idx+11));
  523.                 int hour    = Integer.parseInt(scanStr.substring(idx+12, idx+14));
  524.                 int min     = Integer.parseInt(scanStr.substring(idx+15, idx+17));
  525.                 int sec     = Integer.parseInt(scanStr.substring(idx+18, idx+20));
  526.                 int zoneSign= 1;
  527.                 // parseInt doesn't handle leading positive sign: "+0". Check for it.
  528.                 int i = idx+21;
  529.                 char zoneSignChar = scanStr.charAt(i);
  530.                 if(zoneSignChar == '+') {
  531.                     i++;
  532.                 } else if(zoneSignChar == '-') {
  533.                     i++;
  534.                     zoneSign = -1;
  535.                 }
  536.                 int zoneHour= Integer.parseInt(scanStr.substring(i, idx+24));
  537.                 int zoneMin = Integer.parseInt(scanStr.substring(idx+24, idx+26));
  538.                 // Combine parts into whole
  539.                 cal.set(year, month, day, hour, min, sec);
  540.                 // Calc timestamp zone adjustment
  541.                 int zoneAdj = zoneSign * (zoneHour * (int)WLAUtil.MILLISECS_PER_HOUR + zoneMin * (int)WLAUtil.MILLISECS_PER_MINUTE);
  542.                 // adjust for timezone we're in
  543.                 int addToUTC = cal.getTimeZone().getRawOffset();
  544.                 addToUTC -= zoneAdj;
  545.                 Date date = cal.getTime();
  546.                 long millisecs = date.getTime();
  547.                 millisecs += addToUTC;
  548.                 // Done OK
  549.                 return millisecs;
  550.             } catch(NumberFormatException x) {
  551.                 throw new ParseLogException("invalid number format", x);
  552.             }
  553.         }
  554.  
  555.         // Converts month string into month number (0 == Jan)
  556.         private int MonthStringToInt(String monthStr) throws ParseLogException {
  557.             for(int m = 0; m < MONTHS.length; m++) {
  558.                 if(monthStr.equalsIgnoreCase(MONTHS[m])) {
  559.                     return m;
  560.                 }
  561.             }
  562.             throw new ParseLogException("unrecognized month: " + monthStr);
  563.         }
  564.  
  565.         //  --------  MPIS FORMAT  --------
  566.     
  567. // MS NT FORMAT
  568. //syntax?: remotehost, rfc931, date, time, servicename, appname, somedomain, #1, #2, #3, #4, #5, theRequest, file, dash?, blank?
  569. //155.64.199.156, -, 4/2/98, 18:20:19, W3SVC, VPAGE2, 155.64.35.204, 712224, 271, 28796, 200, 0, GET, /jeannette/text.htm, -, 
  570. //155.64.198.182, guest@unknown, 4/2/98, 18:33:55, MSFTPSVC, VPAGE2, -, 190, 916, 0, 0, 0, [64]  created , itnews01.htm, -, 
  571. //155.64.198.182, bbubb, 4/2/98, 18:42:16, MSFTPSVC, VPAGE2, -, 190, 921, 0, 0, 0, [68]  created , itnews01.htm, -, 
  572.         
  573.         // Parses the given log file line into the logParseRec object
  574.         private boolean parseALineMPIS(String aLine) throws ParseLogException {
  575.             // ini vars used in the getNext...() methods
  576.             scanIdx = 0;
  577.             scanStr = aLine;
  578.             try {
  579.                 getNextWordMPIS(logParseRecMPIS.clientIPAddress);
  580.                 getNextWordMPIS(logParseRecMPIS.clientUserName);
  581.                 logParseRecMPIS.requestTimestamp = getNextTimestampMPIS();
  582.                 getNextWordMPIS(logParseRecMPIS.serviceName);
  583.                 getNextWordMPIS(logParseRecMPIS.computerName);
  584.                 getNextWordMPIS(logParseRecMPIS.serverIPAddress);
  585.                 logParseRecMPIS.processingMilliseconds = getNextIntMPIS();
  586.                 logParseRecMPIS.bytesReceived = getNextIntMPIS();
  587.                 logParseRecMPIS.bytesSent = getNextIntMPIS();
  588.                 logParseRecMPIS.serviceStatusCode = getNextIntMPIS();
  589.                 logParseRecMPIS.ntStatusCode = getNextIntMPIS();
  590.                 getWordsTillComma(logParseRecMPIS.operationName);
  591.                 getNextWordMPIS(logParseRecMPIS.operationTarget);
  592.                 getNextWordMPIS(logParseRecMPIS.dash);
  593.                 getNextWordMPIS(logParseRecMPIS.blank);
  594.                 // Determine if we're at the end of the line...
  595.                 logParseRecMPIS.bExtraText = scanIdx < aLine.length();
  596.                 // If Extended specificly requested, no extra text allowed
  597.                 if(logParseRecMPIS.bExtraText) {
  598.                     throw new ParseLogException("extra text at end of log line - not Windows NT format", null);
  599.                 }
  600.                 // Ignore non-HTTP log lines
  601.                 if(!logParseRecMPIS.serviceName.toString().equals("W3SVC")) {
  602.                     return false;
  603.                 }
  604.                 // Convert NT info into std info
  605.                 logParseRec.remoteHost = logParseRecMPIS.clientIPAddress;
  606.                 logParseRec.remoteUserLogname = logParseRecMPIS.clientUserName;
  607.                 logParseRec.authenticatedUserName.setLength(0);
  608.                 logParseRec.requestTimestamp = logParseRecMPIS.requestTimestamp;
  609.                 logParseRec.clientRequest.setLength(0);
  610.                 logParseRec.clientRequest.append(logParseRecMPIS.operationName);
  611.                 logParseRec.clientRequest.append(' ');
  612.                 logParseRec.clientRequest.append(logParseRecMPIS.operationTarget);
  613.                 logParseRec.clientRequest.append(" HTTP/1.0");
  614.                 logParseRec.httpStatusCode = logParseRecMPIS.serviceStatusCode;
  615.                 logParseRec.bytesTransferred = logParseRecMPIS.bytesSent;
  616.                 logParseRec.referringURL.setLength(0);
  617.                 logParseRec.userAgent.setLength(0);
  618.                 logParseRec.bExtraText = logParseRecMPIS.bExtraText;
  619.                 // All OK
  620.                 return true;
  621.             } catch(ArrayIndexOutOfBoundsException x) {
  622.                 throw new ParseLogException("unexpected end of line");
  623.             }
  624.         }
  625.  
  626.         /*
  627.         Parses the next word in the current line, up till the next comma.
  628.         On entry and exit scanStr[scanIdx] always refers to non-whitespace 
  629.         character (or past end of line).
  630.         */
  631.         private void getWordsTillComma(StringBuffer destBuf) throws ParseLogException {
  632.             // find end of word
  633.             int endIdx = scanStr.indexOf(',', scanIdx+1);
  634.             if(endIdx < 0) {
  635.                 endIdx = scanStr.length();
  636.             }
  637.             // extract substring into destBuf
  638.             destBuf.setLength(0);
  639.             destBuf.append(scanStr.substring(scanIdx, endIdx));
  640.             // keep track of where we are scanning
  641.             scanIdx = endIdx+1;
  642.             if(scanIdx < scanStr.length() && scanStr.charAt(scanIdx) == ' ') {
  643.                 scanIdx++;
  644.             }
  645.         }
  646.  
  647.         /*
  648.         Parses the next "word" in the current line.
  649.         On entry and exit scanStr[scanIdx] always refers to non-whitespace 
  650.         character (or past end of line).
  651.         */
  652.         private void getNextWordMPIS(StringBuffer destBuf) throws ParseLogException {
  653.             getWordsTillComma(destBuf);
  654.         }
  655.         
  656.         /*
  657.         Parses the next integer in the current line.
  658.         On entry and exit scanStr[scanIdx] always refers to non-whitespace 
  659.         character (or past end of line).
  660.         */
  661.         private int getNextIntMPIS() throws ParseLogException {
  662.             //Gather word
  663.             getNextWordMPIS(workBuf);
  664.             // Convert
  665.             try {
  666.                 if(workBuf.length() == 1 && workBuf.charAt(0) == '-') {
  667.                     return 0;
  668.                 }
  669.                 return Integer.parseInt(workBuf.toString());
  670.             } catch(NumberFormatException x) {
  671.                 throw new ParseLogException("invalid number format", x);
  672.             }
  673.         }
  674.  
  675.         /*
  676.         Parses a log timestamp in the current line (like: 4/2/98, 18:20:19, ).
  677.         On entry scanStr[scanIdx] always should be the first char of the month int.
  678.         On exit scanStr[scanIdx] always refers to non-whitespace character 
  679.         (or past end of line).
  680.         */
  681.         private long getNextTimestampMPIS() throws ParseLogException {
  682.             Date d;
  683.             // parse timestamp in this format: 4/2/98 18:20:19
  684.             try {
  685.                 getWordsTillComma(workBuf);     // date
  686.                 d = WLAUtil.string2Date(workBuf.toString());
  687.             } catch(java.text.ParseException x) {
  688.                 throw new ParseLogException("Invalid NT date timestamp format: \""+workBuf+"\"", x);
  689.             }
  690.             try {
  691.                 getWordsTillComma(workBuf);
  692.                 long delta = WLAUtil.string2TimeDelta(workBuf.toString());
  693.                 return d.getTime() + delta;
  694.             } catch(java.text.ParseException x) {
  695.                 throw new ParseLogException("Invalid NT time timestamp format: \""+workBuf+"\"", x);
  696.             }
  697.         }
  698.  
  699.     }
  700.     
  701.     
  702.     //{{DECLARE_CONTROLS
  703.     //}}
  704. }
  705.  
  706.