private boolean readFromLogAndVerify(RandomAccessFile randomAccessFile, String blobId, IndexValue indexValue,
      Map<Long, Long> coveredRanges) throws Exception {
    final Timer.Context context = metrics.readFromLogAndVerifyTimeMs.time();
    long offset = indexValue.getOffset().getOffset();
    try {
      DumpDataHelper.LogBlobRecordInfo logBlobRecordInfo =
          DumpDataHelper.readSingleRecordFromLog(randomAccessFile, offset, clusterMap, currentTimeInMs, metrics);
      if (coveredRanges != null) {
        coveredRanges.put(offset, offset + logBlobRecordInfo.totalRecordSize);
      }
      compareIndexValueToLogEntry(blobId, indexValue, logBlobRecordInfo);
      if (!logBlobRecordInfo.isDeleted) {
        logger.trace("{}", logBlobRecordInfo.messageHeader + "\n " + logBlobRecordInfo.blobId.getID() + "\n"
            + logBlobRecordInfo.blobEncryptionKey + "\n" + logBlobRecordInfo.blobProperty + "\n"
            + logBlobRecordInfo.userMetadata + "\n" + logBlobRecordInfo.blobDataOutput);
      } else {
        logger.trace("{}", logBlobRecordInfo.messageHeader + "\n " + logBlobRecordInfo.blobId.getID() + "\n"
            + logBlobRecordInfo.deleteMsg);
      }
      return true;
    } catch (IllegalArgumentException e) {
      metrics.logDeserializationError.inc();
      logger.error("Illegal arg exception thrown at  {}, while reading blob starting at offset {} with exception: ",
          randomAccessFile.getChannel().position(), offset, e);
    } catch (MessageFormatException e) {
      metrics.logDeserializationError.inc();
      logger.error("MessageFormat exception thrown at  {} while reading blob starting at offset {} with exception: ",
          randomAccessFile.getChannel().position(), offset, e);
    } catch (EOFException e) {
      metrics.endOfFileOnDumpLogError.inc();
      logger.error("EOFException thrown at {} ", randomAccessFile.getChannel().position(), e);
    } catch (Exception e) {
      metrics.unknownErrorOnDumpLog.inc();
      logger.error("Unknown exception thrown {} ", e.getMessage(), e);
    } finally {
      context.stop();
    }
    return false;
  }
Dumps a single record from the log at a given offset and verifies for corresponding values in index
@param randomAccessFile the {@link RandomAccessFile} referring to log file that needs to be parsed
@param blobId the blobId which that is expected to be matched for the record present at
              <code>offset</code>
@param indexValue the {@link IndexValue} that needs to be compared against
@param coveredRanges a {@link Map} of startOffset to endOffset of ranges covered by records in the log
@throws IOException
ambry-tools/src/main/java/com/github/ambry/store/DumpDataTool.java