/** * Read all the errors in a log since the creation of the log. * * @param buffer containing the {@link DistinctErrorLog}. * @param consumer to be called for each exception encountered. * @return the number of entries that has been read. */ public static int read(final AtomicBuffer buffer, final ErrorConsumer consumer) { return read(buffer, consumer, 0); }
private static DistinctObservation find( final DistinctObservation[] existingObservations, final Throwable observation) { DistinctObservation existingObservation = null; for (final DistinctObservation o : existingObservations) { if (equals(o.throwable, observation)) { existingObservation = o; break; } } return existingObservation; }
public void onError(final Throwable throwable) { if (!log.record(throwable)) { errorOverflow.println("Error Log is full, consider increasing length of error buffer"); throwable.printStackTrace(errorOverflow); } } }
@Test public void shouldReadFirstObservation() { final ErrorConsumer consumer = mock(ErrorConsumer.class); final long timestamp = 7; final RuntimeException error = new RuntimeException("Test Error"); when(clock.time()).thenReturn(timestamp); log.record(error); assertThat(ErrorLogReader.read(buffer, consumer), is(1)); verify(consumer).accept(eq(1), eq(timestamp), eq(timestamp), any(String.class)); }
@Test public void shouldReadOneObservationSinceTimestamp() { final ErrorConsumer consumer = mock(ErrorConsumer.class); final long timestampOne = 7; final long timestampTwo = 10; final RuntimeException errorOne = new RuntimeException("Test Error One"); final IllegalStateException errorTwo = new IllegalStateException("Test Error Two"); when(clock.time()).thenReturn(timestampOne).thenReturn(timestampTwo); assertFalse(ErrorLogReader.hasErrors(buffer)); log.record(errorOne); log.record(errorTwo); assertTrue(ErrorLogReader.hasErrors(buffer)); assertThat(ErrorLogReader.read(buffer, consumer, timestampTwo), is(1)); verify(consumer).accept(eq(1), eq(timestampTwo), eq(timestampTwo), any(String.class)); verifyNoMoreInteractions(consumer); } }
errorLog = new DistinctErrorLog(createErrorLogBuffer(cncByteBuffer, cncMetaDataBuffer), epochClock); errorHandler = new LoggingErrorHandler(errorLog);
private DistinctObservation newObservation(final long timestamp, final Throwable observation) { final StringWriter stringWriter = new StringWriter(); observation.printStackTrace(new PrintWriter(stringWriter)); final byte[] encodedError = stringWriter.toString().getBytes(UTF_8); final int length = ENCODED_ERROR_OFFSET + encodedError.length; final int offset = nextOffset; if ((offset + length) > buffer.capacity()) { return INSUFFICIENT_SPACE; } buffer.putBytes(offset + ENCODED_ERROR_OFFSET, encodedError); buffer.putLong(offset + FIRST_OBSERVATION_TIMESTAMP_OFFSET, timestamp); nextOffset = align(offset + length, RECORD_ALIGNMENT); final DistinctObservation distinctObservation = new DistinctObservation(observation, offset); distinctObservations = prepend(distinctObservations, distinctObservation); buffer.putIntOrdered(offset + LENGTH_OFFSET, length); return distinctObservation; }
/** * Record an observation of an error. If it is the first observation of this error type for a stack trace * then a new entry will be created. For subsequent observations of the same error type and stack trace a * counter and time of last observation will be updated. * * @param observation to be logged as an error observation. * @return true if successfully logged otherwise false if insufficient space remaining in the log. */ public boolean record(final Throwable observation) { final long timestamp = clock.time(); DistinctObservation distinctObservation; synchronized (this) { distinctObservation = find(distinctObservations, observation); if (null == distinctObservation) { distinctObservation = newObservation(timestamp, observation); if (INSUFFICIENT_SPACE == distinctObservation) { return false; } } } final int offset = distinctObservation.offset; buffer.getAndAddInt(offset + OBSERVATION_COUNT_OFFSET, 1); buffer.putLongOrdered(offset + LAST_OBSERVATION_TIMESTAMP_OFFSET, timestamp); return true; }
consumer.accept( buffer.getInt(offset + OBSERVATION_COUNT_OFFSET), buffer.getLong(offset + FIRST_OBSERVATION_TIMESTAMP_OFFSET),
@Test public void shouldReadTwoDistinctObservations() { final ErrorConsumer consumer = mock(ErrorConsumer.class); final long timestampOne = 7; final long timestampTwo = 10; final RuntimeException errorOne = new RuntimeException("Test Error One"); final IllegalStateException errorTwo = new IllegalStateException("Test Error Two"); when(clock.time()).thenReturn(timestampOne).thenReturn(timestampTwo); log.record(errorOne); log.record(errorTwo); assertThat(ErrorLogReader.read(buffer, consumer), is(2)); final InOrder inOrder = inOrder(consumer); inOrder.verify(consumer).accept(eq(1), eq(timestampOne), eq(timestampOne), any(String.class)); inOrder.verify(consumer).accept(eq(1), eq(timestampTwo), eq(timestampTwo), any(String.class)); }
public static int saveErrorLog(final PrintStream out, final AtomicBuffer errorBuffer) { final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"); final ErrorConsumer errorConsumer = (count, firstTimestamp, lastTimestamp, ex) -> out.format( "***%n%d observations from %s to %s for:%n %s%n", count, dateFormat.format(new Date(firstTimestamp)), dateFormat.format(new Date(lastTimestamp)), ex); final int distinctErrorCount = ErrorLogReader.read(errorBuffer, errorConsumer); out.format("%n%d distinct errors observed.%n", distinctErrorCount); return distinctErrorCount; }
errorLog.record(ex);
@SuppressWarnings("FinalParameters") private static boolean equals(Throwable lhs, Throwable rhs) { while (true) { if (lhs == rhs) { return true; } if (lhs.getClass() == rhs.getClass() && equals(lhs.getStackTrace(), rhs.getStackTrace())) { lhs = lhs.getCause(); rhs = rhs.getCause(); if (null == lhs && null == rhs) { return true; } else if (null != lhs && null != rhs) { continue; } } return false; } }
@Test public void shouldReadSummarisedObservation() { final ErrorConsumer consumer = mock(ErrorConsumer.class); final long timestampOne = 7; final long timestampTwo = 10; final RuntimeException error = new RuntimeException("Test Error"); final StringWriter stringWriter = new StringWriter(); error.printStackTrace(new PrintWriter(stringWriter)); final String errorAsString = stringWriter.toString(); when(clock.time()).thenReturn(timestampOne).thenReturn(timestampTwo); log.record(error); log.record(error); assertThat(ErrorLogReader.read(buffer, consumer), is(1)); verify(consumer).accept(eq(2), eq(timestampOne), eq(timestampTwo), eq(errorAsString)); }
public static void main(final String[] args) { final File cncFile = CommonContext.newDefaultCncFile(); System.out.println("Command `n Control file " + cncFile); final MappedByteBuffer cncByteBuffer = IoUtil.mapExistingFile(cncFile, "cnc"); final DirectBuffer cncMetaDataBuffer = CncFileDescriptor.createMetaDataBuffer(cncByteBuffer); final int cncVersion = cncMetaDataBuffer.getInt(CncFileDescriptor.cncVersionOffset(0)); if (CNC_VERSION != cncVersion) { throw new IllegalStateException( "Aeron CnC version does not match: version=" + cncVersion + " required=" + CNC_VERSION); } final AtomicBuffer buffer = CncFileDescriptor.createErrorLogBuffer(cncByteBuffer, cncMetaDataBuffer); final int distinctErrorCount = ErrorLogReader.read(buffer, ErrorStat::accept); System.out.format("%n%d distinct errors observed.%n", distinctErrorCount); }
@Test public void shouldFailToRecordWhenInsufficientSpace() { final long timestamp = 7; final RuntimeException error = new RuntimeException("Test Error"); when(clock.time()).thenReturn(timestamp); when(buffer.capacity()).thenReturn(32); assertFalse(log.record(error)); } }
@Test public void shouldReadNoExceptionsFromEmptyLog() { final ErrorConsumer consumer = mock(ErrorConsumer.class); assertThat(ErrorLogReader.read(buffer, consumer), is(0)); verifyZeroInteractions(consumer); }
@Test public void shouldRecordFirstObservation() { final long timestamp = 7; final int offset = 0; final RuntimeException error = new RuntimeException("Test Error"); when(clock.time()).thenReturn(timestamp); assertTrue(log.record(error)); final InOrder inOrder = inOrder(buffer); inOrder.verify(buffer).putBytes(eq(offset + ENCODED_ERROR_OFFSET), any(byte[].class)); inOrder.verify(buffer).putLong(offset + FIRST_OBSERVATION_TIMESTAMP_OFFSET, timestamp); inOrder.verify(buffer).putIntOrdered(eq(offset + LENGTH_OFFSET), anyInt()); inOrder.verify(buffer).getAndAddInt(offset + OBSERVATION_COUNT_OFFSET, 1); inOrder.verify(buffer).putLongOrdered(offset + LAST_OBSERVATION_TIMESTAMP_OFFSET, timestamp); }
@Test public void shouldSummariseObservations() { final long timestampOne = 7; final long timestampTwo = 10; final int offset = 0; final RuntimeException error = new RuntimeException("Test Error"); when(clock.time()).thenReturn(timestampOne).thenReturn(timestampTwo); assertTrue(log.record(error)); assertTrue(log.record(error)); final InOrder inOrder = inOrder(buffer); inOrder.verify(buffer).putBytes(eq(offset + ENCODED_ERROR_OFFSET), any(byte[].class)); inOrder.verify(buffer).putLong(offset + FIRST_OBSERVATION_TIMESTAMP_OFFSET, timestampOne); inOrder.verify(buffer).putIntOrdered(eq(offset + LENGTH_OFFSET), anyInt()); inOrder.verify(buffer).getAndAddInt(offset + OBSERVATION_COUNT_OFFSET, 1); inOrder.verify(buffer).putLongOrdered(offset + LAST_OBSERVATION_TIMESTAMP_OFFSET, timestampOne); inOrder.verify(buffer).getAndAddInt(offset + OBSERVATION_COUNT_OFFSET, 1); inOrder.verify(buffer).putLongOrdered(offset + LAST_OBSERVATION_TIMESTAMP_OFFSET, timestampTwo); }