Commit 0ab536fb authored by Nils Christian Ehmke's avatar Nils Christian Ehmke

Made the import more robust for corrupted monitoring logs

parent 834543eb
......@@ -3,6 +3,7 @@ package kieker.diagnosis.service.data;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
......@@ -35,6 +36,7 @@ import kieker.common.record.flow.trace.operation.AfterOperationEvent;
import kieker.common.record.flow.trace.operation.AfterOperationFailedEvent;
import kieker.common.record.flow.trace.operation.BeforeOperationEvent;
import kieker.common.record.misc.KiekerMetadataRecord;
import kieker.diagnosis.architecture.exception.BusinessException;
import kieker.diagnosis.architecture.monitoring.MonitoringProbe;
final class MonitoringLogImporter {
......@@ -49,6 +51,8 @@ final class MonitoringLogImporter {
private final LongObjectMap<List<MethodCall>> ivReconstructionMap = new LongObjectHashMap<>( );
private final IntObjectMap<String> ivStringMapping = new IntObjectHashMap<>( );
private final IntByteMap ivIgnoredRecordsSizeMap = new IntByteHashMap( );
private boolean ivStreamCorrupt = false;
private Exception ivException = null;
private MonitoringLogService ivMonitoringLogService;
private int ivBeforeOperationEventKey;
......@@ -61,7 +65,7 @@ final class MonitoringLogImporter {
private TimeUnit ivSourceTimeUnit;
public void importMonitoringLog( final File aDirectory, final MonitoringLogService aMonitoringLogService ) throws IOException {
public void importMonitoringLog( final File aDirectory, final MonitoringLogService aMonitoringLogService ) throws IOException, BusinessException {
ivMonitoringLogService = aMonitoringLogService;
final long tin = System.currentTimeMillis( );
......@@ -90,6 +94,10 @@ final class MonitoringLogImporter {
ivMonitoringLogService.setIgnoredRecords( ivIgnoredRecords );
ivMonitoringLogService.setDanglingRecords( ivDanglingRecords );
ivMonitoringLogService.setIncompleteTraces( ivReconstructionMap.size( ) );
if ( ivStreamCorrupt ) {
throw new BusinessException( ivResourceBundle.getString( "errorMessageStreamCorrupt" ), ivException );
}
}
private void clearFields( ) {
......@@ -197,24 +205,30 @@ final class MonitoringLogImporter {
final byte[] binaryContent = Files.readAllBytes( aBinaryFile );
final ByteBuffer byteBuffer = ByteBuffer.wrap( binaryContent );
while ( byteBuffer.hasRemaining( ) ) {
final int recordKey = byteBuffer.getInt( );
skipBytes( (byte) 8, byteBuffer ); // Ignore the logging timestamp
if ( recordKey == ivBeforeOperationEventKey ) {
readBeforeOperationEvent( byteBuffer );
} else if ( recordKey == ivAfterOperationEventKey ) {
readAfterOperationEvent( byteBuffer );
} else if ( recordKey == ivAfterOperationFailedEventKey ) {
readAfterOperationFailedEvent( byteBuffer );
} else if ( recordKey == ivTraceMetadataKey ) {
readTraceMetadata( byteBuffer );
} else if ( recordKey == ivKiekerMetadataRecordKey ) {
readKiekerMetadataRecord( byteBuffer );
} else {
// Expensive case. We have to find out which record we are dealing with and skip it
readUnknownRecord( byteBuffer, recordKey );
try {
while ( byteBuffer.hasRemaining( ) ) {
final int recordKey = byteBuffer.getInt( );
skipBytes( (byte) 8, byteBuffer ); // Ignore the logging timestamp
if ( recordKey == ivBeforeOperationEventKey ) {
readBeforeOperationEvent( byteBuffer );
} else if ( recordKey == ivAfterOperationEventKey ) {
readAfterOperationEvent( byteBuffer );
} else if ( recordKey == ivAfterOperationFailedEventKey ) {
readAfterOperationFailedEvent( byteBuffer );
} else if ( recordKey == ivTraceMetadataKey ) {
readTraceMetadata( byteBuffer );
} else if ( recordKey == ivKiekerMetadataRecordKey ) {
readKiekerMetadataRecord( byteBuffer );
} else {
// Expensive case. We have to find out which record we are dealing with and skip it
readUnknownRecord( byteBuffer, recordKey );
}
}
} catch ( final BufferUnderflowException ex ) {
// The stream is incomplete. We still want to terminate the whole import in a useful manner.
ivStreamCorrupt = true;
ivException = ex;
}
return binaryContent.length;
......
......@@ -7,6 +7,7 @@ import java.util.List;
import com.google.inject.Singleton;
import kieker.diagnosis.architecture.exception.BusinessException;
import kieker.diagnosis.architecture.exception.TechnicalException;
import kieker.diagnosis.architecture.service.ServiceBase;
......@@ -29,7 +30,7 @@ public class MonitoringLogService extends ServiceBase {
super( false );
}
public void importMonitoringLog( final File aDirectory ) {
public void importMonitoringLog( final File aDirectory ) throws BusinessException {
try {
clear( );
......@@ -37,13 +38,22 @@ public class MonitoringLogService extends ServiceBase {
final MonitoringLogImporter importer = new MonitoringLogImporter( );
importer.importMonitoringLog( aDirectory, this );
ivDirectory = aDirectory.getAbsolutePath( );
dataAvailable = true;
setDataAvailable( aDirectory );
} catch ( final BusinessException ex ) {
// A technical exception means, that something went wrong, but that the data is partially available
setDataAvailable( aDirectory );
throw ex;
} catch ( final Exception ex ) {
throw new TechnicalException( getLocalizedString( "errorMessageImportFailed" ), ex );
}
}
private void setDataAvailable( final File aDirectory ) {
ivDirectory = aDirectory.getAbsolutePath( );
dataAvailable = true;
}
private void clear( ) {
dataAvailable = false;
ivTraceRoots.clear( );
......
......@@ -154,8 +154,14 @@ public class MainController extends ControllerBase<MainViewModel> {
// Load the monitoring log
try {
BusinessException exception = null;
final MonitoringLogService monitoringLogService = getService( MonitoringLogService.class );
monitoringLogService.importMonitoringLog( ivDirectory );
try {
monitoringLogService.importMonitoringLog( ivDirectory );
} catch ( final BusinessException ex ) {
// If a business exception occurs, we still want to refresh, but we also want to display the exception.
exception = ex;
}
// Now refresh everything
Platform.runLater( ( ) -> {
......@@ -167,6 +173,10 @@ public class MainController extends ControllerBase<MainViewModel> {
Platform.runLater( ( ) -> {
getViewModel( ).performRefresh( );
} );
if ( exception != null ) {
ExceptionUtil.handleException( exception, getLogger( ).getName( ) );
}
} catch ( final Exception ex ) {
// At this point we have no exception handling. We have to perform this ourselves.
ExceptionUtil.handleException( ex, getLogger( ).getName( ) );
......
errorMessageUnknownRecord=The importer encountered an unknown record with the name '%s'.
\ No newline at end of file
errorMessageUnknownRecord=The importer encountered an unknown record with the name '%s'.
errorMessageStreamCorrupt=The monitoring log files are corrupted. The import may be incomplete.
\ No newline at end of file
errorMessageUnknownRecord=Ein unbekannter Record mit dem Namen '%s' wurde gelesen.
\ No newline at end of file
errorMessageUnknownRecord=Ein unbekannter Record mit dem Namen '%s' wurde gelesen.
errorMessageStreamCorrupt=Die Monitoring Log Dateien sind beschädigt. Der Import könnte unvollständig sein.
\ No newline at end of file
......@@ -37,6 +37,7 @@ import kieker.common.record.system.CPUUtilizationRecord;
import kieker.common.util.registry.IRegistry;
import kieker.common.util.registry.Registry;
import kieker.diagnosis.KiekerTraceDiagnosisModule;
import kieker.diagnosis.architecture.exception.BusinessException;
/**
* Test class for the {@link MonitoringLogService}.
......@@ -65,7 +66,7 @@ public class MonitoringLogServiceTest {
}
@Test
public void testEmptyDirectory( ) {
public void testEmptyDirectory( ) throws BusinessException {
// Import the directory
final File directory = ivTemporaryFolder.getRoot( );
ivService.importMonitoringLog( directory );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment