BackwardReader { public List<LogEntry> readEntries(final Log log, final ACCESSORTYPE logAccess, LogPointer startOffset, int entriesNumber) throws IOException, FormatException { int avgSizePerEntry = 255; entriesNumber = -entriesNumber; final int origNumber = entriesNumber; final ArrayList<LogEntry> revEntries = new ArrayList<LogEntry>(); LogPointer revPointer = null; while (revEntries.size() < origNumber && !startOffset.isSOF() && (revPointer == null || !revPointer.isSOF())) { revPointer = logAccess .absolute(logAccess.getDifference(null, startOffset) - (entriesNumber + 1) * avgSizePerEntry).get(); final BufferedConsumer bufferedConsumer = new BufferedConsumer(Integer.MAX_VALUE); final BoundedConsumerProxy boundedConsumer = new BoundedConsumerProxy(bufferedConsumer, startOffset); forwardReader.readEntries(log, logAccess, revPointer, boundedConsumer); final List<LogEntry> entries = bufferedConsumer.getBuffer(); if (logger.isDebugEnabled()) { logger.debug( "Found {} entries by reverse read iteration from={} and to={} to fill remaining {} entries with avg-size/entry {}", entries.size(), revPointer, startOffset, entriesNumber, avgSizePerEntry); } if (entries.size() > 0) { if (entries.get(0).getStartOffset().isSOF()) { logger.debug("Start reached, cancel reverse reading with {} found entries and {} wanted", revEntries.size(), origNumber); final int found = Math.min(entries.size(), entriesNumber); revEntries.addAll(0, entries.subList(entries.size() - found, entries.size())); break; } else { final int found = Math.min(entries.size() - 1, entriesNumber); revEntries.addAll(0, entries.subList(entries.size() - found, entries.size())); entriesNumber -= found; } } if (entries.size() > 1) { startOffset = revEntries.get(0).getStartOffset(); avgSizePerEntry = (int) (logAccess.getDifference(revEntries.get(0).getStartOffset(), revEntries.get(revEntries.size() - 1).getEndOffset()) / revEntries.size()) + 1; } else { avgSizePerEntry = Math.min(avgSizePerEntry * 2, Integer.MAX_VALUE); } } return revEntries; } BackwardReader(final LogEntryReader<ACCESSORTYPE> forwardReader); List<LogEntry> readEntries(final Log log, final ACCESSORTYPE logAccess, LogPointer startOffset,
			int entriesNumber);  }
@Test public void testRevReadingAtStart() throws FormatException, UnsupportedEncodingException, IOException { Assert.assertEquals(0, revReader.readEntries(log, log, log.createRelative(null, 0), -1).size()); }
@Test public void testRevReadingAllFromTail() throws FormatException, UnsupportedEncodingException, IOException { final List<LogEntry> entries = revReader.readEntries(log, log, log.createRelative(null, Long.MAX_VALUE), -10); Assert.assertEquals(3, entries.size()); Assert.assertEquals(logLines[0] + "\n" + logLines[1] + "\n" + logLines[2] + "\n" + logLines[3], entries.get(0).getRawContent()); Assert.assertEquals(logLines[4], entries.get(1).getRawContent()); Assert.assertEquals(logLines[5], entries.get(2).getRawContent()); }
@Test public void testRevReadingIncrementallyFromTail() throws FormatException, UnsupportedEncodingException, IOException { List<LogEntry> entries = revReader.readEntries(log, log, log.createRelative(null, Long.MAX_VALUE), -1); Assert.assertEquals(1, entries.size()); Assert.assertEquals(logLines[5], entries.get(0).getRawContent()); entries = revReader.readEntries(log, log, entries.get(0).getStartOffset(), -1); Assert.assertEquals(1, entries.size()); Assert.assertEquals(logLines[4], entries.get(0).getRawContent()); entries = revReader.readEntries(log, log, entries.get(0).getStartOffset(), -1); Assert.assertEquals(1, entries.size()); Assert.assertEquals(logLines[0] + "\n" + logLines[1] + "\n" + logLines[2] + "\n" + logLines[3], entries.get(0).getRawContent()); Assert.assertEquals(0, revReader.readEntries(log, log, entries.get(0).getStartOffset(), -1).size()); }
@Test public void testRecursionBug() throws FormatException, UnsupportedEncodingException, IOException { log = Log4jTextReaderTest.createLog(0, StringUtils.repeat(StringUtils.repeat("abc", 72) + "\n", 1000)); final Log4jTextReader log4jReader = new Log4jTextReader("unknown format", "UTF-8"); log4jReader.setMaxUnfomattedLines(1); final BackwardReader<ByteLogAccess> reader = new BackwardReader<ByteLogAccess>(log4jReader); final List<LogEntry> entries = reader.readEntries(log, log, log.createRelative(null, 52000), -100); Assert.assertEquals(100, entries.size()); }