package org.fusesource.cloudmix.agent.logging;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.google.common.base.Predicate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Facilitates the retrieval of and search for log records independently of the actual log format
*/
public class LogHandler {
private static final transient Log LOG = LogFactory.getLog(LogHandler.class);
private LogParser logParser = new DefaultLogParser();
private List<LogRecord> records = new ArrayList<LogRecord>();
public LogHandler(InputStream logStream) {
parseLog(toReader(logStream));
}
public LogHandler(InputStream logStream, LogParser logParser) {
this.logParser = logParser;
parseLog(toReader(logStream));
}
public LogHandler(Reader logStream) {
parseLog(logStream);
}
public LogHandler(Reader logStream, LogParser logParser) {
this.logParser = logParser;
parseLog(logStream);
}
public LogHandler(File logFile) {
parseFromFile(logFile);
}
public LogHandler(File logFile, LogParser logParser) {
this.logParser = logParser;
parseFromFile(logFile);
}
private void parseFromFile(File logFile) {
try {
parseLog(toReader(new FileInputStream(logFile)));
} catch (FileNotFoundException ex) {
LOG.error(logFile.getAbsolutePath() + " can not be found");
throw new RuntimeException(ex);
}
}
/**
* Gets all the records
*
* @return records
*/
public List<LogRecord> getAllRecords() {
return Collections.unmodifiableList(records);
}
/**
* Gets all the records matching a provided level
*
* @param level such as INFO or ERROR
* @return the records
*/
public List<LogRecord> findLevelRecords(String level) {
return findLevelRecords(level, 0, records.size());
}
/**
* Gets all the records matching a provided level
*
* @param level such as INFO or ERROR
* @param start position in the list where to start searching from
* @param max max number of records
* @return the records
*/
public List<LogRecord> findLevelRecords(String level, int start, int max) {
if (start > records.size()) {
throw new IllegalArgumentException();
}
List<LogRecord> recs = new ArrayList<LogRecord>();
for (int i = start; i < records.size() && recs.size() <= max; i++) {
LogRecord record = records.get(i);
if (level.equalsIgnoreCase(record.getLevel())) {
recs.add(record);
}
}
return recs;
}
/**
* Gets all the records matching a provided category
*
* @param category the category such as OsgiServiceFactoryBean, may be a regular expression
* @return the records
*/
public List<LogRecord> findCategoryRecords(String category) {
return findCategoryRecords(category, 0, records.size());
}
/**
* Gets all the records matching a provided category
*
* @param category the category such as OsgiServiceFactoryBean, may be a regular expression
* @param start position in the list where to start searching from
* @param max max number of records
* @return the records
*/
public List<LogRecord> findCategoryRecords(String category, int start, int max) {
if (start > records.size()) {
throw new IllegalArgumentException();
}
List<LogRecord> recs = new ArrayList<LogRecord>();
for (int i = start; i < records.size() && recs.size() <= max; i++) {
LogRecord record = records.get(i);
if (matches(record.getCategory(), category)) {
recs.add(record);
}
}
return recs;
}
/**
* Gets all the records matching provided level, category
*
* @param level such as INFO or ERROR
* @param category the category such as OsgiServiceFactoryBean, may be a regular expression
* @return the records
*/
public List<LogRecord> findRecords(String level, String category) {
return findRecords(level, category, 0, records.size());
}
/**
* Gets all the records matching provided level and category
*
* @param level such as INFO or ERROR
* @param category the category such as OsgiServiceFactoryBean, may be a regular expression
* @param start position in the list where to start searching from
* @param max max number of records
* @return the records
*/
public List<LogRecord> findRecords(String level, String category, int start, int max) {
if (start > records.size()) {
throw new IllegalArgumentException();
}
List<LogRecord> recs = new ArrayList<LogRecord>();
for (int i = start; i < records.size() && recs.size() <= max; i++) {
LogRecord record = records.get(i);
if (level.equalsIgnoreCase(record.getLevel()) && matches(record.getCategory(), category)) {
recs.add(record);
}
}
return recs;
}
/**
* Gets all the records matching provided level, category and classname
*
* @param level such as INFO or ERROR
* @param category the category such as OsgiServiceFactoryBean, may be a regular expression
* @param className the className, may be a regular expression
* @return the records
*/
public List<LogRecord> findRecords(String level, String category, String className) {
return findRecords(level, category, className, 0, records.size());
}
/**
* Gets all the records matching provided level and category
*
* @param level such as INFO or ERROR
* @param category the category such as OsgiServiceFactoryBean, may be a regular expression
* @param className the className, may be a regular expression
* @param start position in the list where to start searching from
* @param max max number of records
* @return the records
*/
public List<LogRecord> findRecords(String level, String category, String className, int start, int max) {
if (start > records.size()) {
throw new IllegalArgumentException();
}
List<LogRecord> recs = new ArrayList<LogRecord>();
for (int i = start; i < records.size() && recs.size() <= max; i++) {
LogRecord record = records.get(i);
if (level.equalsIgnoreCase(record.getLevel()) && matches(record.getCategory(), category)
&& matches(record.getClassName(), className)) {
recs.add(record);
}
}
return recs;
}
/**
* Gets all the records using a predicate
*
* @param Predicate
* @param start position in the list where to start searching from
* @param max max number of records
* @return the records
*/
public List<LogRecord> findWithPredicate(Predicate<LogRecord> p) {
return findWithPredicate(p, 0, records.size());
}
/**
* Gets all the records using a predicate
*
* @param Predicate
* @param start position in the list where to start searching from
* @param max max number of records
* @return the records
*/
public List<LogRecord> findWithPredicate(Predicate<LogRecord> p, int start, int max) {
if (start > records.size()) {
throw new IllegalArgumentException();
}
List<LogRecord> recs = new ArrayList<LogRecord>();
for (int i = start; i < records.size() && recs.size() <= max; i++) {
LogRecord record = records.get(i);
if (p.apply(record)) {
recs.add(record);
}
}
return recs;
}
private boolean matches(String value, String regex) {
if (value == null) {
return false;
}
return value.matches(regex);
}
/**
* Reads the log file and converts the matching records into LogRecords Huge logs may be handled
* differently, for ex, by copying LogRecords into db tables or using NIO file channels, etc
*
* @param r log reader
*/
private void parseLog(Reader r) {
BufferedReader reader = new BufferedReader(r);
try {
String s = null;
StringBuilder sb = new StringBuilder();
boolean validRecordAdded = false;
while ((s = reader.readLine()) != null) {
boolean possibleMatch = logParser.isPossibleMatch(s);
if (!validRecordAdded && possibleMatch) {
sb.append(s);
validRecordAdded = true;
} else if (!possibleMatch) {
// it appears to be a multi-line log message
sb.append(s);
} else {
addRecord(sb.toString());
sb.delete(0, sb.length());
sb.append(s);
}
}
if (sb.length() > 0) {
addRecord(sb.toString());
}
} catch (IOException ex) {
LOG.warn("Problems reading the logs file, log records may not be available");
}
}
protected void addRecord(String s) {
LogRecord rec = logParser.parseRecord(s);
if (rec != null) {
records.add(rec);
}
}
private Reader toReader(InputStream is) {
Reader r = null;
try {
r = new InputStreamReader(is, "UTF-8");
} catch (UnsupportedEncodingException ex) {
LOG.fatal("This should not've happened : UTF-8 is unsupported");
throw new RuntimeException(ex);
}
return r;
}
}