/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.googlecode.perftrace.log4j.lf5.util;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.SwingUtilities;
import org.googlecode.perftrace.log4j.lf5.Log4JLogRecord;
import org.googlecode.perftrace.log4j.lf5.LogLevel;
import org.googlecode.perftrace.log4j.lf5.LogLevelFormatException;
import org.googlecode.perftrace.log4j.lf5.LogRecord;
import org.googlecode.perftrace.log4j.lf5.viewer.LogBrokerMonitor;
import org.googlecode.perftrace.log4j.lf5.viewer.LogFactor5ErrorDialog;
import org.googlecode.perftrace.log4j.lf5.viewer.LogFactor5LoadingDialog;
/**
* Provides utility methods for input and output streams.
*
* @author Brad Marlborough
* @author Richard Hurst
*/
// Contributed by ThoughtWorks Inc.
public class LogFileParser implements Runnable {
//--------------------------------------------------------------------------
// Constants:
//--------------------------------------------------------------------------
public static final String RECORD_DELIMITER = "[slf5s.start]";
public static final String ATTRIBUTE_DELIMITER = "[slf5s.";
public static final String DATE_DELIMITER = ATTRIBUTE_DELIMITER + "DATE]";
public static final String THREAD_DELIMITER = ATTRIBUTE_DELIMITER + "THREAD]";
public static final String CATEGORY_DELIMITER = ATTRIBUTE_DELIMITER + "CATEGORY]";
public static final String LOCATION_DELIMITER = ATTRIBUTE_DELIMITER + "LOCATION]";
public static final String MESSAGE_DELIMITER = ATTRIBUTE_DELIMITER + "MESSAGE]";
public static final String PRIORITY_DELIMITER = ATTRIBUTE_DELIMITER + "PRIORITY]";
public static final String NDC_DELIMITER = ATTRIBUTE_DELIMITER + "NDC]";
//--------------------------------------------------------------------------
// Protected Variables:
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// Private Variables:
//--------------------------------------------------------------------------
private static SimpleDateFormat _sdf = new SimpleDateFormat("dd MMM yyyy HH:mm:ss,S");
private LogBrokerMonitor _monitor;
LogFactor5LoadingDialog _loadDialog;
private InputStream _in = null;
//--------------------------------------------------------------------------
// Constructors:
//--------------------------------------------------------------------------
public LogFileParser(File file) throws IOException,
FileNotFoundException {
this(new FileInputStream(file));
}
public LogFileParser(InputStream stream) throws IOException {
_in = stream;
}
//--------------------------------------------------------------------------
// Public Methods:
//--------------------------------------------------------------------------
/**
* Starts a new thread to parse the log file and create a LogRecord.
* See run().
* @param monitor LogBrokerMonitor
*/
public void parse(LogBrokerMonitor monitor) throws RuntimeException {
_monitor = monitor;
Thread t = new Thread(this);
t.start();
}
/**
* Parses the file and creates new log records and adds the record
* to the monitor.
*/
public void run() {
int index = 0;
int counter = 0;
LogRecord temp;
boolean isLogFile = false;
_loadDialog = new LogFactor5LoadingDialog(
_monitor.getBaseFrame(), "Loading file...");
try {
String logRecords = loadLogFile(_in);
while ((counter = logRecords.indexOf(RECORD_DELIMITER, index)) != -1) {
temp = createLogRecord(logRecords.substring(index, counter));
isLogFile = true;
if (temp != null) {
_monitor.addMessage(temp);
}
index = counter + RECORD_DELIMITER.length();
}
if (index < logRecords.length() && isLogFile) {
temp = createLogRecord(logRecords.substring(index));
if (temp != null) {
_monitor.addMessage(temp);
}
}
if (isLogFile == false) {
throw new RuntimeException("Invalid log file format");
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
destroyDialog();
}
});
} catch (RuntimeException e) {
destroyDialog();
displayError("Error - Invalid log file format.\nPlease see documentation"
+ " on how to load log files.");
} catch (IOException e) {
destroyDialog();
displayError("Error - Unable to load log file!");
}
_in = null;
}
//--------------------------------------------------------------------------
// Protected Methods:
//--------------------------------------------------------------------------
protected void displayError(String message) {
LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
_monitor.getBaseFrame(), message);
}
//--------------------------------------------------------------------------
// Private Methods:
//--------------------------------------------------------------------------
private void destroyDialog() {
_loadDialog.hide();
_loadDialog.dispose();
}
/**
* Loads a log file from a web server into the LogFactor5 GUI.
*/
private String loadLogFile(InputStream stream) throws IOException {
BufferedInputStream br = new BufferedInputStream(stream);
int count = 0;
int size = br.available();
StringBuffer sb = null;
if (size > 0) {
sb = new StringBuffer(size);
} else {
sb = new StringBuffer(1024);
}
while ((count = br.read()) != -1) {
sb.append((char) count);
}
br.close();
br = null;
return sb.toString();
}
private String parseAttribute(String name, String record) {
int index = record.indexOf(name);
if (index == -1) {
return null;
}
return getAttribute(index, record);
}
private long parseDate(String record) {
try {
String s = parseAttribute(DATE_DELIMITER, record);
if (s == null) {
return 0;
}
Date d = _sdf.parse(s);
return d.getTime();
} catch (ParseException e) {
return 0;
}
}
private LogLevel parsePriority(String record) {
String temp = parseAttribute(PRIORITY_DELIMITER, record);
if (temp != null) {
try {
return LogLevel.valueOf(temp);
} catch (LogLevelFormatException e) {
return LogLevel.DEBUG;
}
}
return LogLevel.DEBUG;
}
private String parseThread(String record) {
return parseAttribute(THREAD_DELIMITER, record);
}
private String parseCategory(String record) {
return parseAttribute(CATEGORY_DELIMITER, record);
}
private String parseLocation(String record) {
return parseAttribute(LOCATION_DELIMITER, record);
}
private String parseMessage(String record) {
return parseAttribute(MESSAGE_DELIMITER, record);
}
private String parseNDC(String record) {
return parseAttribute(NDC_DELIMITER, record);
}
private String parseThrowable(String record) {
return getAttribute(record.length(), record);
}
private LogRecord createLogRecord(String record) {
if (record == null || record.trim().length() == 0) {
return null;
}
LogRecord lr = new Log4JLogRecord();
lr.setMillis(parseDate(record));
lr.setLevel(parsePriority(record));
lr.setCategory(parseCategory(record));
lr.setLocation(parseLocation(record));
lr.setThreadDescription(parseThread(record));
lr.setNDC(parseNDC(record));
lr.setMessage(parseMessage(record));
lr.setThrownStackTrace(parseThrowable(record));
return lr;
}
private String getAttribute(int index, String record) {
int start = record.lastIndexOf(ATTRIBUTE_DELIMITER, index - 1);
if (start == -1) {
return record.substring(0, index);
}
start = record.indexOf("]", start);
return record.substring(start + 1, index).trim();
}
//--------------------------------------------------------------------------
// Nested Top-Level Classes or Interfaces
//--------------------------------------------------------------------------
}