home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programming Languages Suite
/
ProgLangD.iso
/
VCAFE.3.0A
/
Sample.bin
/
Report.java
< prev
next >
Wrap
Text File
|
1998-11-05
|
23KB
|
558 lines
// Copyright (c) 1997, 1998 Symantec, Inc. All Rights Reserved.
import java.io.Serializable;
import java.util.Date;
import java.util.Calendar;
import java.util.Hashtable;
import java.util.Vector;
import java.util.NoSuchElementException;
import java.util.Enumeration;
/*
This class encapsulates a single web log "report".
It contains:
1) The report options (specified/changed by New/Edit Report dialogs),
including a LogFile object representing the log file specified in the
report options.
2) The report results (available after analyzing the log file).
3) Working fields (used during log file analysis).
4) Methods to analyze the log file and generate the results.
Statistics are gathered over a day long interval (i.e. hits per day, etc) and
also gathered over an interval chosen to be appropriate for the timespan of
the report.
It was written manually.
It has no UI code.
*/
public class Report implements Serializable {
// -- The Report Options
// possible timeframe choice values
public static final int TIMEFRAME_ALL = 0;
public static final int TIMEFRAME_TODAY = 1;
public static final int TIMEFRAME_YESTERDAY = 2;
public static final int TIMEFRAME_PAST_2_DAYS = 3;
public static final int TIMEFRAME_PAST_7_DAYS = 4;
public static final int TIMEFRAME_PAST_14_DAYS = 5;
public static final int TIMEFRAME_PAST_30_DAYS = 6;
public static final int TIMEFRAME_PAST_60_DAYS = 7;
public static final int TIMEFRAME_PAST_90_DAYS = 8;
public static final int TIMEFRAME_SPECIFIC = 9;
String title;
int timeframe; // TIMEFRAME_
Date dateStart; // valid if timeframe == TIMEFRAME_SPECIFIC
Date dateEnd; // valid if timeframe == TIMEFRAME_SPECIFIC
LogFile log = new LogFile();
String homePageFile;
boolean optUsageChart;
boolean optGeneralStatistics;
boolean optMostActiveDomains;
// -- The Report Results
Date analyzedDate; // date report last run on
long startTimestamp; // actual starting time (millisecs)
long endTimestamp; // actual ending time (millisecs)
int totalHits; // total number of hits
int homePageHits; // hits on the home page
int totalUserSessionCount; // total number of user sessions
long avgUserSessionLength = 0; // average session length (millisecs)
DomainRecord sortedDomains[]; // all domains, sorted by number of hits
// "per day" statistics
int hitsVsDay[]; // array to track hits VS day
int userSessionsVsDay[]; // array to sessions VS day
double avgHitsPerDay; // average hits per day
double avgUserSessionsPerDay; // average sessions per day
// "per <time interval>" statistics
long hitTimeInterval; // interval statistics are gathered over (millisecs)
int hitsVsTimeInterval[]; // array to track hits VS hitTimeInterval
int userSessionsVsTimeInterval[];//array to sessions VS hitTimeInterval
double avgHitsPerHitTimeInterval; // average number of hits per hitTimeInterval
double avgUserSessionsPerHitTimeInterval; // average sessions per hitTimeInterval
//SessionRecord sortedSessions[];
// -- Working fields. Used during log file analysis
String normalizedHomePageFile; // home page file with "/" added at start as needed and "\" changed to "/" as needed
long newUserSessionThreshold; // user idle time to create a new session (millisecs)
long timeTillFirstInterval; // millisecs till first full interval of time being analyzed
long timeTillFirstDayInterval; // millisecs till first full day of time being analyzed
long totalUserSessionLength; // total length in time of all user sessions
Hashtable domains; // of DomainRecord
Hashtable sessions; // of SessionRecord
SessionRecord sessionListHead = null; // list of all SessionRecords
/**
* Constructs a Report.
*/
public Report() {
//{{INIT_CONTROLS
//}}
}
/**
* Frees up memory used to analyze (but not report) a web log.
*/
void freeUpMem() {
// Free up mem used to analyze, but not report, log info
domains = null;
sessions = null;
sessionListHead = null;
log.freeUpMem();
}
// -- ACCESS
// Returns true if this report has been run (analyzed) before.
public boolean hasBeenRun() {
return analyzedDate != null;
}
// -- Handy shortcuts to get/set LogFile attributes
public String getLogFileURL() {
return log.getLogFileURL();
}
public void setLogFileURL(String logFileURL) {
log.setLogFileURL(logFileURL);
}
public int getLogFileFormat() {
return log.getLogFileFormat();
}
public void setLogFileFormat(int logFileFormat) {
log.setLogFileFormat(logFileFormat);
}
// -- ANALYSIS
/*
Runs the report, analyzing the log file and gathering statistics.
The TrackProgress interface is used to report analysis status to UI code.
*/
public void run(TrackProgress trackProgress) {
try {
// -- Setup analysis
// Reading and parse the log file, getting the results
Analyzer.throwExceptionIfCurrentThreadCancelled();
LogRecord records[] = log.readAndParseLogFile(trackProgress);
if(records == null || records.length == 0) {
trackProgress.okAlert("No valid HTTP records to process (wrong log file format?).");
trackProgress.step("ABORTED", 0);
// Note that we're done in the UI
trackProgress.done();
return;
}
// determine starting and ending time for this report
Analyzer.throwExceptionIfCurrentThreadCancelled();
startTimestamp = log.getEarliestTimestamp();
endTimestamp = log.getLatestTimestamp();
if(timeframe != TIMEFRAME_ALL) {
startTimestamp = dateStart.getTime();
endTimestamp = dateEnd.getTime();
}
//
totalHits = 0;
homePageHits = 0;
totalUserSessionCount = 0;
avgUserSessionLength = 0;
sortedDomains = null;
// init per day stats
timeTillFirstDayInterval = determineTimeTillFirstFullInterval(WLAUtil.MILLISECS_PER_DAY, CAL_HOUR);
// table: hitsVsDay
int numTimeIntervals = 1 + timestampToDayIntervalIndex(endTimestamp);
hitsVsDay = new int[numTimeIntervals];
// table: userSessionsVsDay
userSessionsVsDay = new int[numTimeIntervals];
avgHitsPerDay = 0;
avgUserSessionsPerDay = 0;
// init per <time interval> stats
// determine the <time interval> units: min, hour, day, week, or year
long millisecs = endTimestamp - startTimestamp;
if(millisecs <= WLAUtil.MILLISECS_PER_HOUR) {
hitTimeInterval = WLAUtil.MILLISECS_PER_MINUTE;
timeTillFirstInterval = determineTimeTillFirstFullInterval(WLAUtil.MILLISECS_PER_MINUTE, CAL_SECOND);
} else if(millisecs <= 2*WLAUtil.MILLISECS_PER_DAY) {
hitTimeInterval = WLAUtil.MILLISECS_PER_HOUR;
timeTillFirstInterval = determineTimeTillFirstFullInterval(WLAUtil.MILLISECS_PER_HOUR, CAL_MINUTE);
} else if(millisecs <= 2*WLAUtil.MILLISECS_PER_WEEK) {
hitTimeInterval = WLAUtil.MILLISECS_PER_DAY;
timeTillFirstInterval = determineTimeTillFirstFullInterval(WLAUtil.MILLISECS_PER_DAY, CAL_HOUR);
} else if(millisecs <= 2*WLAUtil.MILLISECS_PER_YEAR) {
hitTimeInterval = WLAUtil.MILLISECS_PER_WEEK;
timeTillFirstInterval = determineTimeTillFirstFullInterval(WLAUtil.MILLISECS_PER_WEEK, CAL_DAY_OF_WEEK);
} else {
hitTimeInterval = WLAUtil.MILLISECS_PER_YEAR;
timeTillFirstInterval = determineTimeTillFirstFullInterval(WLAUtil.MILLISECS_PER_YEAR, CAL_WEEK_OF_YEAR);
}
// table: hitsVsTimeInterval
numTimeIntervals = 1 + timestampToIntervalIndex(endTimestamp);
hitsVsTimeInterval = new int[numTimeIntervals];
// table: userSessionsVsTimeInterval
userSessionsVsTimeInterval = new int[numTimeIntervals];
avgHitsPerHitTimeInterval = 0;
avgUserSessionsPerHitTimeInterval = 0;
// "normalize" home page file name
int lim = homePageFile.length();
if(lim == 0) {
normalizedHomePageFile = homePageFile;
} else {
StringBuffer buf = new StringBuffer(lim + 1);
int idx = 0;
char ch = homePageFile.charAt(0);
// if no leading slash, add one
buf.append('/');
if(ch == '/' || ch == java.io.File.separatorChar) {
idx = 1;
}
while(idx < lim) {
ch = homePageFile.charAt(idx++);
if(ch == java.io.File.separatorChar) {
buf.append('/');
} else {
buf.append(ch);
}
}
normalizedHomePageFile = buf.toString();
}
// init remaining working fields
newUserSessionThreshold = Data.getDataInstance().userSessionIdleLimit * WLAUtil.MILLISECS_PER_MINUTE;
totalUserSessionLength = 0;
domains = new Hashtable(); // of DomainRecord
sessions = new Hashtable(); // of SessionRecord
sessionListHead = null;
// init local info
int hitCount = 0;
int totalLogHits = log.getTotalHits();
// Done with analysis setup
if(totalLogHits > 0) {
// -- SCAN LOG LINES AND ACCUMULATE DATA
for(int r = 0; r < totalLogHits; r++) {
// percent for this phase ranges from 96 -> 100
int percent = 96 + ((4 * hitCount)/totalLogHits);
trackProgress.step("Processing log file...", percent);
// Note the line's stats
noteLogRecord(records[r]);
}
// -- CALC STATISTICS
// Calc averages
double timespan = endTimestamp - startTimestamp;
timespan = timespan / WLAUtil.MILLISECS_PER_DAY;
avgHitsPerDay = (double)totalHits / timespan;
avgUserSessionsPerDay = (double)totalUserSessionCount / timespan;
timespan = endTimestamp - startTimestamp;
timespan = timespan / hitTimeInterval;
avgHitsPerHitTimeInterval = (double)totalHits / timespan;
avgUserSessionsPerHitTimeInterval = (double)totalUserSessionCount / timespan;
// Calc total length of user sessions
Analyzer.throwExceptionIfCurrentThreadCancelled();
SessionRecord sr = sessionListHead;
while(sr != null) {
long delta = sr.lastTimestamp - sr.firstTimestamp;
totalUserSessionLength += delta;
sr = sr.next;
}
// Calc avg user session length
if(totalUserSessionCount > 0) {
avgUserSessionLength = totalUserSessionLength / totalUserSessionCount;
}
// Sort domains in order of most hits to least hits
sortedDomains = new DomainRecord[domains.size()];
// copy into array...
int idx = 0;
for(Enumeration e = domains.elements(); e.hasMoreElements();) {
DomainRecord dr = (DomainRecord)e.nextElement();
sortedDomains[idx++] = dr;
}
// ...and sort
Analyzer.throwExceptionIfCurrentThreadCancelled();
WLAUtil.quickSort(sortedDomains);
}
// -- DONE OK, note date/time report run
analyzedDate = new Date(); // date report last run on
trackProgress.step("Done", 100);
} catch(NoSuchElementException x) {
trackProgress.okAlert(x.toString());
trackProgress.step("ABORTED", 0);
} catch(ParseLogException x) {
// These have already been reported.
} catch(OutOfMemoryError x) {
// free the main memory occupiers
Data.freeUpMem();
trackProgress.okAlert(x.toString());
trackProgress.step("ABORTED", 0);
}
// Note that we're done in the UI
trackProgress.done();
}
/*
Called by the run() method for each log record to analyze.
Accumulates report statistics.
*/
void noteLogRecord(LogRecord logRec) throws ParseLogException {
Analyzer.throwExceptionIfCurrentThreadCancelled();
// -- Filter out undesired log records
// Does this record occur within the timeframe we're analyzing??
if(startTimestamp > logRec.requestTimestamp || logRec.requestTimestamp > endTimestamp) {
// No. Ignore it.
return;
}
// HTTP error on this request?
if(logRec.httpStatusCode >= 500) {
// Yes. Ignore it.
return;
}
// Determine type of HTTP request
int idx = logRec.clientRequest.indexOf(' ');
if(idx < 0) {
// Unexpected clientRequest. Ignore
return;
}
String str = logRec.clientRequest.substring(0, idx);
if(!str.equals("GET")) {
// Ignore non-GET HTTP requests
return;
}
// -- Use this log record
// Note info for General Statistics
totalHits++;
// Note info for Hits VS Time chart
int hitInterval = timestampToIntervalIndex(logRec.requestTimestamp);
hitsVsTimeInterval[hitInterval]++;
// Note info for Hits VS Day for day-related averages
hitInterval = timestampToDayIntervalIndex(logRec.requestTimestamp);
hitsVsDay[hitInterval]++;
// Request for home page?
int idx2 = logRec.clientRequest.indexOf(' ', ++idx);
if(idx2 > 0) {
str = logRec.clientRequest.substring(idx, idx2);
if(str.equals("/") || str.equalsIgnoreCase(normalizedHomePageFile)) {
homePageHits++;
}
}
// Track # of hits for each domain
DomainRecord dr = (DomainRecord)domains.get(logRec.remoteHost);
if(dr == null) {
dr = new DomainRecord(logRec.remoteHost);
domains.put(logRec.remoteHost, dr);
}
// update the domain record
dr.hits++;
// -- Track sessions
String sessionStr = logRec.remoteHost + " " + logRec.remoteUserLogname + " " + logRec.authenticatedUserName;
SessionRecord sr = (SessionRecord)sessions.get(sessionStr);
if(sr == null
|| (logRec.requestTimestamp - sr.lastTimestamp >= newUserSessionThreshold)) {
// New user session
sr = new SessionRecord(sessionStr, logRec);
// Add to linked list of all Session Records
sr.next = sessionListHead;
sessionListHead = sr;
// Update the hashtable which tracks current "live" sessions
sessions.put(sessionStr, sr);
totalUserSessionCount++; // total number of user sessions
// Track userSessions Vs TimeInterval and Vs Day
// Note info for Hits VS Time chart
hitInterval = timestampToIntervalIndex(logRec.requestTimestamp);
userSessionsVsTimeInterval[hitInterval]++;
// Note info for Hits VS Day for day-related averages
hitInterval = timestampToDayIntervalIndex(logRec.requestTimestamp);
userSessionsVsDay[hitInterval]++;
}
// update the session record
sr.hits++;
sr.lastTimestamp = logRec.requestTimestamp;
}
/* determineTimeTillFirstFullInterval()
Time intervals are aligned on natural boundries (i.e. midnight for a
day long interval). This method determines the length of the first
interval such that it ends on a natural boundry. Typically it is smaller
than a full interval.
*/
private static final int CAL_SECOND = 0;
private static final int CAL_MINUTE = 1;
private static final int CAL_HOUR = 2;
private static final int CAL_DAY_OF_WEEK = 3;
private static final int CAL_WEEK_OF_YEAR = 4;
private static final int CALENDAR_UNIT_VALUES[] = {
Calendar.SECOND,
Calendar.MINUTE,
Calendar.HOUR_OF_DAY,
Calendar.DAY_OF_WEEK,
Calendar.WEEK_OF_YEAR
};
private long determineTimeTillFirstFullInterval(long MILLISECS_PER_, int CAL_) {
// Get a calendar and set its time to the report starting time + interval length
Calendar cal = Calendar.getInstance();
cal.setTime(new Date(startTimestamp+MILLISECS_PER_));
// zero the appropriate fields, truncating time to interval natural boundry
do {
cal.set(CALENDAR_UNIT_VALUES[CAL_], 0);
} while(--CAL_ >= 0);
// Get resultant time, and calculate delta to that time from report starting time
Date date = cal.getTime();
long deltaTime = date.getTime() - startTimestamp;
// done
return deltaTime;
}
/*
Calculates the day interval index that the given time falls into.
*/
private int timestampToDayIntervalIndex(long timestamp) {
int intervalIndex = 0;
long ms = timestamp - startTimestamp;
if(ms >= timeTillFirstDayInterval) {
ms -= timeTillFirstDayInterval;
intervalIndex = (int)(ms / WLAUtil.MILLISECS_PER_DAY) + 1;
}
return intervalIndex;
}
/*
Calculates the <time interval> index that the given time falls into.
*/
private int timestampToIntervalIndex(long timestamp) {
int intervalIndex = 0;
long ms = timestamp - startTimestamp;
if(ms >= timeTillFirstInterval) {
ms -= timeTillFirstInterval;
intervalIndex = (int)(ms / hitTimeInterval) + 1;
}
return intervalIndex;
}
/*
This class tracks info on a single user session.
All SessionRecords are kept on a linked-list (sessionListHead refers
to the most recently created SessionRecord).
This class implements the Sortable interface to allow the quicksort
utility routine to sort it.
*/
class SessionRecord implements Serializable, Sortable {
// The next SessionRecord in the linked-list.
SessionRecord next = null;
// This session's identifying string (a concatenation of the domain
// name, remote user log name, and authenticated user name)
String sessionStr;
// The number of hits by this session
int hits;
// The session's starting time
long firstTimestamp;
// The time of this session's last request
long lastTimestamp;
/*
Constructs a new session.
*/
SessionRecord(String sessionStr, LogRecord logRec) {
this.sessionStr = sessionStr;
firstTimestamp = logRec.requestTimestamp;
lastTimestamp = logRec.requestTimestamp;
hits = 0;
}
// -- Implement the Sortable interface
// A Sortable interface method.
// Returns true if this "is before" the given obj
public boolean isLessThan(Object obj) {
if(obj instanceof SessionRecord) {
SessionRecord r = (SessionRecord)obj;
if(hits != r.hits) {
return hits < r.hits;
}
return sessionStr.compareTo(r.sessionStr) > 0;
}
return false;
}
// A Sortable interface method.
// Returns true if this "is before or equal to" the given obj
public boolean isLessThanOrEqual(Object obj) {
if(obj instanceof SessionRecord) {
SessionRecord r = (SessionRecord)obj;
if(hits != r.hits) {
return hits < r.hits;
}
return sessionStr.compareTo(r.sessionStr) >= 0;
}
return false;
}
}
/*
This class tracks info on a single user session.
This class implements the Sortable interface to allow the quicksort
utility routine to sort it.
*/
class DomainRecord implements Serializable, Sortable {
// The domain name
String domain;
// The number of hits this domain has generated
int hits;
// Creates a new DomainRecord with the given name.
DomainRecord(String domain) {
this.domain = domain;
}
// -- Implement the Sortable interface
// A Sortable interface method.
// Returns true if this "is before" the given obj
public boolean isLessThan(Object obj) {
if(obj instanceof DomainRecord) {
DomainRecord r = (DomainRecord)obj;
if(hits != r.hits) {
return hits < r.hits;
}
return domain.compareTo(r.domain) < 0;
}
return false;
}
// A Sortable interface method.
// Returns true if this "is before or equal to" the given obj
public boolean isLessThanOrEqual(Object obj) {
if(obj instanceof DomainRecord) {
DomainRecord r = (DomainRecord)obj;
if(hits != r.hits) {
return hits < r.hits;
}
return domain.compareTo(r.domain) <= 0;
}
return false;
}
}
//{{DECLARE_CONTROLS
//}}
}