All Downloads are FREE. Search and download functionalities are using the official Maven repository.

info.aduna.logging.file.logback.FileLogReader Maven / Gradle / Ivy

/* 
 * Licensed to Aduna under one or more contributor license agreements.  
 * See the NOTICE.txt file distributed with this work for additional 
 * information regarding copyright ownership. 
 *
 * Aduna licenses this file to you under the terms of the Aduna BSD 
 * License (the "License"); you may not use this file except in compliance 
 * with the License. See the LICENSE.txt file distributed with this work 
 * for the full License.
 *
 * 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 info.aduna.logging.file.logback;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;

import ch.qos.logback.core.Appender;
import ch.qos.logback.core.FileAppender;

import info.aduna.logging.LogLevel;
import info.aduna.logging.LogRecord;
import info.aduna.logging.base.LogReaderBase;
import info.aduna.logging.base.LogRecordBase;

public class FileLogReader extends LogReaderBase {

	private File logFile = null;

	private RandomAccessFile log = null;

	private long fileLength;

	private long byteOffset;

	private LogRecord next = null;

	private int count = 0;

	public FileLogReader() {
	}

	public FileLogReader(File logFile) {
		this.logFile = logFile;
	}

	public void setAppender(Appender appender) {
		super.setAppender(appender);
		if (appender instanceof FileAppender) {
			this.logFile = new File(((FileAppender)appender).getFile());
		}
		else {
			throw new RuntimeException("FileLogReader appender must be an instance of FileAppender!");
		}
		this.next = null;
	}

	public void init()
		throws Exception
	{
		if (logFile == null) {
			throw new RuntimeException("Log file is undefined for this FileLogReader!");
		}
		if (log != null) {
			log.close();
		}
		log = new RandomAccessFile(logFile, "r");
		fileLength = log.length();
		byteOffset = fileLength - 1;
		count = 0;
		next = getNext();
		if (getOffset() > 0) {
			doSkip(getOffset());
		}
	}

	private void doSkip(int offset) {
		while (this.hasNext() && (count < offset)) {
			this.next();
		}
	}

	public boolean isMoreAvailable() {
		return next != null;
	}

	public boolean hasNext() {
		if (getLimit() == 0) {
			return isMoreAvailable();
		}
		return isMoreAvailable() && (count < (getOffset() + getLimit()));
	}

	public LogRecord next() {
		LogRecord result = next;
		try {
			next = getNext();
			count++;
		}
		catch (IOException ioe) {
			throw new RuntimeException("Unable to get next log record.", ioe);
		}
		if (!hasNext()) {
			try {
				destroy();
			}
			catch (IOException e) {
				// too bad
			}
		}
		return result;
	}

	private LogRecord getNext()
		throws IOException
	{
		LogRecordBase result = null;

		StringBuilder message = new StringBuilder();

		List stackTrace = new LinkedList();

		while (result == null && byteOffset > 0) {
			List bytesRead = new LinkedList();
			if (byteOffset < 0) {
				System.err.println("Subzero byteOffset with: ");
				System.err.println("\tMessage: " + message);
				System.err.println("\tStacktrace: " + stackTrace.size());
			}
			// find start of previous line
			byte currentByte;
			do {
				log.seek(byteOffset--);
				currentByte = log.readByte();
				if(currentByte != '\n' && currentByte != '\r') {
					bytesRead.add(0, currentByte);
				}
			}
			while (byteOffset > 0 && currentByte != '\n' && currentByte != '\r');

			// if at start of file, retrieve the byte we just read in the do/while
			if (byteOffset < 1) {
				byteOffset = 0;
				log.seek(0);
			}

			// read the line
			byte[] lineBytes = new byte[bytesRead.size()];
			int index = 0;
			Iterator byteIt = bytesRead.iterator();
			while(byteIt.hasNext()) {
				lineBytes[index] = byteIt.next();
				index++;
			}	
			String lastLine = new String(lineBytes, "UTF-8");

			if (lastLine != null) {
				// is this a log line?
				Matcher matcher = StackTracePatternLayout.DEFAULT_PARSER_PATTERN.matcher(lastLine);
				if (matcher.matches()) {
					try {
						LogLevel level = LogLevel.valueOf(matcher.group(1).trim());
						Date timestamp = LogRecord.ISO8601_TIMESTAMP_FORMAT.parse(matcher.group(2).trim());
						String threadName = matcher.group(3);
						message.insert(0, matcher.group(4));

						result = new LogRecordBase();
						result.setLevel(level);
						result.setTime(timestamp);
						result.setThreadName(threadName);
						result.setMessage(message.toString());
						result.setStackTrace(stackTrace);

						message = new StringBuilder();
						stackTrace = new ArrayList();
					}
					catch (ParseException pe) {
						throw new IOException("Unable to parse timestamp in log record");
					}
				}
				// it may be a message line or a stacktrace line
				else {
					if (!lastLine.trim().equals("")) {
						if (lastLine.startsWith("\t")) {
							stackTrace.add(0, lastLine.trim());
						}
						else {
							message.insert(0, lastLine);
						}
					}
				}
			}
		}

		return result;
	}

	public void destroy()
		throws IOException
	{
		if (log != null) {
			log.close();
		}
		log = null;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy