MultipleFileLogReader.java
/*******************************************************************************
* Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.common.logging.file.logback;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.rdf4j.common.logging.LogRecord;
import org.eclipse.rdf4j.common.logging.base.AbstractLogReader;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.RollingPolicy;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
/**
* Date range-enabled wrapper for FileLogReader. Reads multiple log files chunked by dates as a single log.
*
* @author alex
*/
public class MultipleFileLogReader extends AbstractLogReader {
private Date startDate = null;
private Date endDate = null;
private Date minDate = new Date();
private Date maxDate = new Date();
private String fileNamePattern = null;
private Vector<File> logFiles = new Vector<>();
private Iterator<File> logFilesIterator = null;
private LogRecord next = null;
private int count = 0;
private FileLogReader currentReader = null;
@Override
public boolean supportsDateRanges() {
return true;
}
@Override
public void setAppender(Appender<?> appender) {
super.setAppender(appender);
if (appender instanceof RollingFileAppender) {
RollingPolicy rp = ((RollingFileAppender<?>) appender).getRollingPolicy();
if (rp instanceof TimeBasedRollingPolicy) {
fileNamePattern = ((TimeBasedRollingPolicy) rp).getFileNamePattern();
} else {
throw new UnsupportedOperationException("Must be TimeBasedRollingPolicy!");
}
} else {
throw new RuntimeException("MultipleFileLogReader appender must be an instance of RollingFileAppender!");
}
}
@Override
public void init() throws Exception {
if (this.getAppender() == null) {
throw new RuntimeException("Appender must be set before initialization!");
}
count = 0;
logFiles = new Vector<>();
Calendar startCal = null;
Calendar endCal = null;
if (startDate != null) {
startCal = Calendar.getInstance();
startCal.setTime(startDate);
}
if (endDate != null) {
endCal = Calendar.getInstance();
endCal.setTime(endDate);
}
// Extracts date pattern
Pattern dfPattern = Pattern.compile("(.+)%d\\{(.+)\\}");
Matcher dfMatcher = dfPattern.matcher(fileNamePattern);
if (!dfMatcher.matches()) {
throw new RuntimeException("Wrong filename pattern: " + fileNamePattern);
}
String dfs = dfMatcher.group(2);
SimpleDateFormat df = new SimpleDateFormat(dfs);
String spattern = new File(fileNamePattern).getName();
spattern = spattern.replace(".", "\\.");
spattern = spattern.replace("%d{" + dfs + "}", "(.*)");
Pattern pattern = Pattern.compile(spattern);
File dir = new File(fileNamePattern).getParentFile();
String[] files = dir.list(new DateRangeFilenameFilter(pattern, df, startCal, endCal));
Arrays.sort(files);
for (int i = files.length - 1; i >= 0; i--) {
File f = new File(dir, files[i]);
logFiles.add(f);
System.out.println(f.getAbsolutePath());
}
logFilesIterator = logFiles.iterator();
if (logFilesIterator.hasNext()) {
currentReader = new FileLogReader(logFilesIterator.next());
currentReader.init();
next = getNext();
if (getOffset() > 0) {
doSkip(getOffset());
}
}
}
@Override
public boolean hasNext() {
if (getLimit() == 0) {
return isMoreAvailable();
}
return isMoreAvailable() && (count < (getOffset() + getLimit()));
}
@Override
public boolean isMoreAvailable() {
return next != null;
}
@Override
public LogRecord next() {
LogRecord result = next;
try {
next = getNext();
count++;
} catch (Exception ex) {
throw new RuntimeException("Unable to get next log record.", ex);
}
if (!hasNext()) {
try {
destroy();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
/**
* Get next log record
*
* @return log record
* @throws Exception
*/
private LogRecord getNext() throws Exception {
if (currentReader.hasNext()) {
return currentReader.next();
}
if (logFilesIterator.hasNext()) {
currentReader = new FileLogReader(logFilesIterator.next());
currentReader.init();
return getNext();
}
return null;
}
/**
* Skip for a specific offset
*
* @param offset offset
*/
private void doSkip(int offset) {
while (this.hasNext() && (count < offset)) {
this.next();
}
}
@Override
public void destroy() throws IOException {
if (currentReader.hasNext()) {
currentReader.destroy();
}
}
/**
* Return the start date
*
* @return start date.
*/
@Override
public Date getStartDate() {
return startDate;
}
/**
* Set start date
*
* @param startDate The startDate to set.
*/
@Override
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
/**
* Return the end date
*
* @return end date
*/
@Override
public Date getEndDate() {
return endDate;
}
/**
* Set the end date
*
* @param endDate The endDate to set.
*/
@Override
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
@Override
public Date getMaxDate() {
return this.maxDate;
}
@Override
public Date getMinDate() {
return this.minDate;
}
/**
* Custom filename filter
*
* @author alex
*/
public class DateRangeFilenameFilter implements FilenameFilter {
Pattern pattern;
SimpleDateFormat df;
Calendar startCal, endCal;
/**
* Constructor
*
* @param pattern
* @param df
* @param startCal
* @param endCal
*/
public DateRangeFilenameFilter(Pattern pattern, SimpleDateFormat df, Calendar startCal, Calendar endCal) {
this.pattern = pattern;
this.df = df;
this.startCal = startCal;
this.endCal = endCal;
}
@Override
public boolean accept(File dir, String name) {
Matcher matcher = pattern.matcher(name);
if (!matcher.matches()) {
return false;
}
String ds = matcher.group(1);
Date d;
try {
d = df.parse(ds);
} catch (ParseException e) {
e.printStackTrace();
return false;
}
if ((maxDate == null) || (d.compareTo(maxDate) > 0)) {
maxDate = d;
}
if ((minDate == null) || (d.compareTo(minDate) < 0)) {
minDate = d;
}
Calendar cal = Calendar.getInstance();
cal.setTime(d);
return (((startCal == null) || (cal.compareTo(startCal) >= 0))
&& ((endCal == null) || (cal.compareTo(endCal) <= 0)));
}
}
}