This is an automated email from the ASF dual-hosted git repository.

sergeychugunov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 782449d  IGNITE-13687 Improvement of human-readable format of WAL 
records (StandaloneWalRecordsIterator). Fix code style - Fixes #8441.
782449d is described below

commit 782449d118aeb8e1d51b6cb70237b70cbd421f88
Author: a-polyakov <polyakov.alexandr.alexandrov...@gmail.com>
AuthorDate: Tue Jan 26 15:58:11 2021 +0300

    IGNITE-13687 Improvement of human-readable format of WAL records 
(StandaloneWalRecordsIterator). Fix code style - Fixes #8441.
    
    Signed-off-by: Sergey Chugunov <sergey.chugu...@gmail.com>
---
 .../org/apache/ignite/binary/BinaryIdMapper.java   |   2 +-
 .../apache/ignite/binary/BinaryObjectBuilder.java  |   2 +-
 .../configuration/IgniteReflectionFactory.java     |   4 +-
 .../db/wal/reader/IgniteWalReaderTest.java         |   7 +-
 .../ignite/development/utils/DataEntryWrapper.java | 120 ++++-
 .../development/utils/IgniteWalConverter.java      | 183 ++++---
 .../utils/IgniteWalConverterArguments.java         | 491 +++++++++++++++++++
 .../utils/MetastoreDataRecordWrapper.java          |  13 +-
 .../development/utils/DevUtilsTestSuite.java       |   2 +
 .../utils/IgniteWalConverterArgumentsTest.java     | 411 ++++++++++++++++
 .../utils/IgniteWalConverterSensitiveDataTest.java |  69 +--
 .../development/utils/IgniteWalConverterTest.java  | 530 +++++++++++++++++++++
 .../apache/ignite/development/utils/Person.java    |  65 +++
 .../apache/ignite/development/utils/PersonEx.java  |  79 +++
 .../{DevUtilsTestSuite.java => PersonKey.java}     |  46 +-
 15 files changed, 1859 insertions(+), 165 deletions(-)

diff --git 
a/modules/core/src/main/java/org/apache/ignite/binary/BinaryIdMapper.java 
b/modules/core/src/main/java/org/apache/ignite/binary/BinaryIdMapper.java
index dac5c44..03c0886 100644
--- a/modules/core/src/main/java/org/apache/ignite/binary/BinaryIdMapper.java
+++ b/modules/core/src/main/java/org/apache/ignite/binary/BinaryIdMapper.java
@@ -50,7 +50,7 @@ public interface BinaryIdMapper {
      *
      * @param typeId Type ID.
      * @param fieldName Field name. Filed anme is a result of {@link 
BinaryNameMapper#fieldName(String)} call for an
-     *        initial filed name.
+     *        initial field name.
      * @return Field ID.
      * @see BinaryNameMapper#fieldName(String)
      */
diff --git 
a/modules/core/src/main/java/org/apache/ignite/binary/BinaryObjectBuilder.java 
b/modules/core/src/main/java/org/apache/ignite/binary/BinaryObjectBuilder.java
index 15bd799..78740da 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/binary/BinaryObjectBuilder.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/binary/BinaryObjectBuilder.java
@@ -83,7 +83,7 @@ public interface BinaryObjectBuilder {
      * Collections and maps returned from this method are modifiable.
      *
      * @param name Field name.
-     * @return Filed value.
+     * @return Field value.
      */
     public <T> T getField(String name);
 
diff --git 
a/modules/core/src/main/java/org/apache/ignite/configuration/IgniteReflectionFactory.java
 
b/modules/core/src/main/java/org/apache/ignite/configuration/IgniteReflectionFactory.java
index 85b86c3..0e64f91 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/configuration/IgniteReflectionFactory.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/configuration/IgniteReflectionFactory.java
@@ -164,7 +164,7 @@ public class IgniteReflectionFactory<T> implements 
Factory<T> {
 
     /**
      * Gets a map of properties. Map contains entries of component class field 
name
-     * to value of the filed which will be used as initial value.
+     * to value of the field which will be used as initial value.
      *
      * @return Properties.
      */
@@ -174,7 +174,7 @@ public class IgniteReflectionFactory<T> implements 
Factory<T> {
 
     /**
      * Sets a map of properties. Map contains entries of component class field 
name
-     * to a value of the filed which will be used as initial value.
+     * to a value of the field which will be used as initial value.
      *
      * @param props Properties.
      */
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/reader/IgniteWalReaderTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/reader/IgniteWalReaderTest.java
index 4a7f2d0..76188f5 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/reader/IgniteWalReaderTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/reader/IgniteWalReaderTest.java
@@ -618,7 +618,7 @@ public class IgniteWalReaderTest extends 
GridCommonAbstractTest {
 
         TestStringContainerToBePrinted val = new 
TestStringContainerToBePrinted(search2);
 
-        ctrlStringsToSearch.add(val.toString()); //will validate original 
toString() was called
+        ctrlStringsToSearch.add("v = [ " + val.getClass().getSimpleName() + 
"{data='" + search2 + "'}]"); //will validate original toString() was called
         ctrlStringsForBinaryObjSearch.add(search2);
 
         addlCache.put("SearchValue", val);
@@ -626,7 +626,8 @@ public class IgniteWalReaderTest extends 
GridCommonAbstractTest {
         String search3 = "SomeTestStringContainerToBePrintedLongLine2";
 
         TestStringContainerToBePrinted key = new 
TestStringContainerToBePrinted(search3);
-        ctrlStringsToSearch.add(key.toString()); //will validate original 
toString() was called
+
+        ctrlStringsToSearch.add("k = " + key.getClass().getSimpleName() + 
"{data='" + search3 + "'}"); //will validate original toString() was called
         ctrlStringsForBinaryObjSearch.add(search3); //validate only string 
itself
 
         addlCache.put(key, "SearchKey");
@@ -676,8 +677,10 @@ public class IgniteWalReaderTest extends 
GridCommonAbstractTest {
 
             for (Iterator<String> iter = ctrlStringsToSearch.iterator(); 
iter.hasNext(); ) {
                 final String next = iter.next();
+
                 if (strRepresentation.contains(next)) {
                     iter.remove();
+
                     break;
                 }
             }
diff --git 
a/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/DataEntryWrapper.java
 
b/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/DataEntryWrapper.java
index f96620a..49edc6c 100644
--- 
a/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/DataEntryWrapper.java
+++ 
b/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/DataEntryWrapper.java
@@ -17,23 +17,31 @@
 
 package org.apache.ignite.development.utils;
 
+import java.util.Base64;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.binary.BinaryObject;
 import org.apache.ignite.internal.pagemem.wal.record.DataEntry;
-import org.apache.ignite.internal.pagemem.wal.record.UnwrappedDataEntry;
+import org.apache.ignite.internal.pagemem.wal.record.UnwrapDataEntry;
+import org.apache.ignite.internal.processors.cache.CacheObject;
+import org.apache.ignite.internal.processors.cache.CacheObjectValueContext;
+import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.SB;
 import org.jetbrains.annotations.Nullable;
 
-import static java.lang.String.valueOf;
-import static java.util.Objects.isNull;
 import static org.apache.ignite.development.utils.ProcessSensitiveData.HASH;
+import static org.apache.ignite.development.utils.ProcessSensitiveData.HIDE;
 import static org.apache.ignite.development.utils.ProcessSensitiveData.MD5;
-import static 
org.apache.ignite.development.utils.ProcessSensitiveDataUtils.md5;
 
 /**
  * Wrapper {@link DataEntry} for sensitive data output.
  */
 class DataEntryWrapper extends DataEntry {
-    /** Unwrapped DataEntry. */
-    @Nullable private final UnwrappedDataEntry unwrappedDataEntry;
+    /**
+     * Source DataEntry.
+     */
+    @Nullable private final DataEntry source;
 
     /** Strategy for the processing of sensitive data. */
     private final ProcessSensitiveData sensitiveData;
@@ -60,30 +68,100 @@ class DataEntryWrapper extends DataEntry {
             dataEntry.partitionCounter()
         );
 
-        this.sensitiveData = sensitiveData;
+        this.source = dataEntry;
 
-        this.unwrappedDataEntry = 
UnwrappedDataEntry.class.isInstance(dataEntry) ?
-            (UnwrappedDataEntry) dataEntry : null;
+        this.sensitiveData = sensitiveData;
     }
 
     /** {@inheritDoc} */
     @Override public String toString() {
-        if (isNull(unwrappedDataEntry))
-            return super.toString();
+        final String keyStr;
+        final String valueStr;
+        if (source instanceof UnwrapDataEntry) {
+            final UnwrapDataEntry unwrappedDataEntry = 
(UnwrapDataEntry)this.source;
+
+            keyStr = toString(unwrappedDataEntry.unwrappedKey(), 
this.source.key());
 
-        Object key = unwrappedDataEntry.unwrappedKey();
-        Object value = unwrappedDataEntry.unwrappedValue();
+            valueStr = toString(unwrappedDataEntry.unwrappedValue(), 
this.source.value());
+        }
+        else {
+            keyStr = toString(null, this.source.key());
 
-        if (HASH == sensitiveData) {
-            key = valueOf(key).hashCode();
-            value = valueOf(value).hashCode();
+            valueStr = toString(null, this.source.value());
         }
-        else if (MD5 == sensitiveData) {
-            key = md5(valueOf(key));
-            value = md5(valueOf(value));
+
+        return new SB(this.source.getClass().getSimpleName())
+            .a("[k = ").a(keyStr)
+            .a(", v = [").a(valueStr).a("]")
+            .a(", super = [").a(S.toString(DataEntry.class, source)).a("]]")
+            .toString();
+    }
+
+    /**
+     * Returns a string representation of the entry key or entry value.
+     *
+     * @param value unwrappedKey or unwrappedValue
+     * @param co    key or value
+     * @return String presentation of the entry key or entry value depends on 
{@code isValue}.
+     */
+    public String toString(Object value, CacheObject co) {
+        String str;
+        if (sensitiveData == HIDE)
+            return "";
+
+        if (sensitiveData == HASH)
+            if (value != null)
+                return Integer.toString(value.hashCode());
+            else
+                return Integer.toString(co.hashCode());
+
+        if (value instanceof String)
+            str = (String)value;
+        else if (value instanceof BinaryObject)
+            str = value.toString();
+        else if (value != null)
+            str = toStringRecursive(value.getClass(), value);
+        else if (co instanceof BinaryObject)
+            str = co.toString();
+        else
+            str = null;
+
+        if (str == null || str.isEmpty()) {
+
+            try {
+                CacheObjectValueContext ctx = null;
+                try {
+                    ctx = IgniteUtils.field(source, "cacheObjValCtx");
+                }
+                catch (Exception e) {
+                    throw new IgniteException(e);
+                }
+                str = Base64.getEncoder().encodeToString(co.valueBytes(ctx));
+            }
+            catch (IgniteCheckedException e) {
+                throw new IgniteException(e);
+            }
         }
 
-        return new SB().a(unwrappedDataEntry.getClass().getSimpleName())
-            .a("[k = ").a(key).a(", v = [ ").a(value).a("], super = 
[").a(super.toString()).a("]]").toString();
+        if (sensitiveData == MD5)
+            str = ProcessSensitiveDataUtils.md5(str);
+
+        return str;
+    }
+
+    /**
+     * Produces auto-generated output of string presentation for given object 
(given the whole hierarchy).
+     *
+     * @param cls Declaration class of the object.
+     * @param obj Object to get a string presentation for.
+     * @return String presentation of the given object.
+     */
+    public static String toStringRecursive(Class cls, Object obj) {
+        String result = null;
+
+        if (cls != Object.class)
+            result = S.toString(cls, obj, 
toStringRecursive(cls.getSuperclass(), obj));
+
+        return result;
     }
 }
diff --git 
a/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/IgniteWalConverter.java
 
b/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/IgniteWalConverter.java
index c4f8b5b..e0d55c7 100644
--- 
a/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/IgniteWalConverter.java
+++ 
b/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/IgniteWalConverter.java
@@ -17,152 +17,187 @@
 
 package org.apache.ignite.development.utils;
 
-import java.io.File;
+import java.io.PrintStream;
+import java.util.ArrayList;
 import java.util.List;
+import org.apache.ignite.IgniteSystemProperties;
 import org.apache.ignite.internal.pagemem.wal.WALIterator;
 import org.apache.ignite.internal.pagemem.wal.record.DataEntry;
 import org.apache.ignite.internal.pagemem.wal.record.DataRecord;
 import org.apache.ignite.internal.pagemem.wal.record.MetastoreDataRecord;
+import org.apache.ignite.internal.pagemem.wal.record.TimeStampRecord;
 import org.apache.ignite.internal.pagemem.wal.record.WALRecord;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
-import 
org.apache.ignite.internal.processors.cache.persistence.wal.FileWriteAheadLogManager;
+import 
org.apache.ignite.internal.processors.cache.persistence.wal.FileDescriptor;
 import org.apache.ignite.internal.processors.cache.persistence.wal.WALPointer;
 import 
org.apache.ignite.internal.processors.cache.persistence.wal.reader.IgniteWalIteratorFactory;
+import 
org.apache.ignite.internal.processors.cache.persistence.wal.serializer.RecordV1Serializer;
 import 
org.apache.ignite.internal.processors.query.h2.database.io.H2ExtrasInnerIO;
 import 
org.apache.ignite.internal.processors.query.h2.database.io.H2ExtrasLeafIO;
 import org.apache.ignite.internal.processors.query.h2.database.io.H2InnerIO;
 import org.apache.ignite.internal.processors.query.h2.database.io.H2LeafIO;
 import 
org.apache.ignite.internal.processors.query.h2.database.io.H2MvccInnerIO;
 import org.apache.ignite.internal.processors.query.h2.database.io.H2MvccLeafIO;
+import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.lang.IgniteBiTuple;
-import org.apache.ignite.logger.NullLogger;
-import org.jetbrains.annotations.Nullable;
-
-import static java.util.stream.Collectors.toList;
-import static 
org.apache.ignite.IgniteSystemProperties.IGNITE_TO_STRING_INCLUDE_SENSITIVE;
-import static org.apache.ignite.IgniteSystemProperties.getBoolean;
-import static org.apache.ignite.IgniteSystemProperties.getEnum;
-import static org.apache.ignite.development.utils.ProcessSensitiveData.HIDE;
-import static org.apache.ignite.development.utils.ProcessSensitiveData.SHOW;
 
 /**
  * Print WAL log data in human-readable form.
  */
 public class IgniteWalConverter {
     /**
-     * System property for printing {@link WALRecord}. By default, {@code 
false}.
-     */
-    static final String PRINT_RECORDS = "PRINT_RECORDS";
-
-    /**
-     * System property for printing {@link WalStat}. By default, {@code true}.
+     * @param args Args.
+     * @throws Exception If failed.
      */
-    static final String PRINT_STAT = "PRINT_STAT";
+    public static void main(String[] args) {
+        final IgniteWalConverterArguments parameters = 
IgniteWalConverterArguments.parse(System.out, args);
 
-    /**
-     * System property for setting {@link ProcessSensitiveData strategy} of 
output sensitive data.
-     * By default, {@link ProcessSensitiveData#SHOW}.
-     */
-    static final String SENSITIVE_DATA = "SENSITIVE_DATA";
+        if (parameters != null)
+            convert(System.out, parameters);
+    }
 
     /**
-     * @param args Args.
-     * @throws Exception If failed.
+     * Write to out WAL log data in human-readable form.
+     *
+     * @param out        Receiver of result.
+     * @param params Parameters.
      */
-    public static void main(String[] args) throws Exception {
-        if (args.length < 2)
-            throw new IllegalArgumentException("\nYou need to provide:\n" +
-                    "\t1. Size of pages, which was selected for file store 
(1024, 2048, 4096, etc).\n" +
-                    "\t2. Path to dir with wal files.\n" +
-                    "\t3. (Optional) Path to dir with archive wal files.");
-
+    public static void convert(final PrintStream out, final 
IgniteWalConverterArguments params) {
         PageIO.registerH2(H2InnerIO.VERSIONS, H2LeafIO.VERSIONS, 
H2MvccInnerIO.VERSIONS, H2MvccLeafIO.VERSIONS);
         H2ExtrasInnerIO.register();
         H2ExtrasLeafIO.register();
 
-        boolean printRecords = getBoolean(PRINT_RECORDS, false); //TODO read 
them from argumetns
-        boolean printStat = getBoolean(PRINT_STAT, true); //TODO read them 
from argumetns
-        ProcessSensitiveData sensitiveData = getEnum(SENSITIVE_DATA, SHOW); 
//TODO read them from argumetns
+        
System.setProperty(IgniteSystemProperties.IGNITE_TO_STRING_INCLUDE_SENSITIVE,
+            Boolean.toString(params.getProcessSensitiveData() == 
ProcessSensitiveData.HIDE));
+
+        System.setProperty(IgniteSystemProperties.IGNITE_PDS_SKIP_CRC, 
Boolean.toString(params.isSkipCrc()));
+        RecordV1Serializer.skipCrc = params.isSkipCrc();
 
-        if (printRecords && HIDE == sensitiveData)
-            System.setProperty(IGNITE_TO_STRING_INCLUDE_SENSITIVE, 
Boolean.FALSE.toString());
+        System.setProperty(IgniteSystemProperties.IGNITE_TO_STRING_MAX_LENGTH, 
String.valueOf(Integer.MAX_VALUE));
 
-        final IgniteWalIteratorFactory factory = new 
IgniteWalIteratorFactory(new NullLogger());
+        final WalStat stat = params.isPrintStat() ? new WalStat() : null;
 
-        final File walWorkDirWithConsistentId = new File(args[1]);
+        IgniteWalIteratorFactory.IteratorParametersBuilder 
iteratorParametersBuilder = new 
IgniteWalIteratorFactory.IteratorParametersBuilder()
+            .pageSize(params.getPageSize())
+            .binaryMetadataFileStoreDir(params.getBinaryMetadataFileStoreDir())
+            
.marshallerMappingFileStoreDir(params.getMarshallerMappingFileStoreDir())
+            .keepBinary(params.isKeepBinary());
 
-        final File[] workFiles = 
walWorkDirWithConsistentId.listFiles(FileWriteAheadLogManager.WAL_SEGMENT_FILE_FILTER);
+        if (params.getWalDir() != null)
+            iteratorParametersBuilder.filesOrDirs(params.getWalDir());
 
-        if (workFiles == null)
-            throw new IllegalArgumentException("No .wal files in dir: " + 
args[1]);
+        if (params.getWalArchiveDir() != null)
+            iteratorParametersBuilder.filesOrDirs(params.getWalArchiveDir());
 
-        @Nullable final WalStat stat = printStat ? new WalStat() : null;
+        final IgniteWalIteratorFactory factory = new 
IgniteWalIteratorFactory();
 
-        IgniteWalIteratorFactory.IteratorParametersBuilder 
iteratorParametersBuilder =
-                new 
IgniteWalIteratorFactory.IteratorParametersBuilder().filesOrDirs(workFiles)
-                    .pageSize(Integer.parseInt(args[0]));
+        boolean printAlways = F.isEmpty(params.getRecordTypes());
 
         try (WALIterator stIt = factory.iterator(iteratorParametersBuilder)) {
+            String currentWalPath = null;
+
             while (stIt.hasNextX()) {
+                final String currentRecordWalPath = 
getCurrentWalFilePath(stIt);
+
+                if (currentWalPath == null || 
!currentWalPath.equals(currentRecordWalPath)) {
+                    out.println("File: " + currentRecordWalPath);
+
+                    currentWalPath = currentRecordWalPath;
+                }
+
                 IgniteBiTuple<WALPointer, WALRecord> next = stIt.nextX();
 
                 final WALPointer pointer = next.get1();
+
                 final WALRecord record = next.get2();
 
                 if (stat != null)
                     stat.registerRecord(record, pointer, true);
 
-                if (printRecords)
-                    System.out.println("[W] " + toString(record, 
sensitiveData));
+                if (printAlways || 
params.getRecordTypes().contains(record.type())) {
+                    boolean print = true;
+
+                    if (record instanceof TimeStampRecord)
+                        print = withinTimeRange((TimeStampRecord) record, 
params.getFromTime(), params.getToTime());
+
+                    final String recordStr = toString(record, 
params.getProcessSensitiveData());
+
+                    if (print && (F.isEmpty(params.getRecordContainsText()) || 
recordStr.contains(params.getRecordContainsText())))
+                        out.println(recordStr);
+                }
             }
         }
+        catch (Exception e) {
+            e.printStackTrace(out);
+        }
 
-        if (args.length >= 3) {
-            final File walArchiveDirWithConsistentId = new File(args[2]);
+        if (stat != null)
+            out.println("Statistic collected:\n" + stat.toString());
+    }
 
-            try (WALIterator stIt = 
factory.iterator(walArchiveDirWithConsistentId)) {
-                while (stIt.hasNextX()) {
-                    IgniteBiTuple<WALPointer, WALRecord> next = stIt.nextX();
+    /**
+     * Checks if provided TimeStampRecord is within time range.
+     *
+     * @param rec Record.
+     * @param fromTime Lower bound for timestamp.
+     * @param toTime Upper bound for timestamp;
+     * @return {@code True} if timestamp is within range.
+     */
+    private static boolean withinTimeRange(TimeStampRecord rec, Long fromTime, 
Long toTime) {
+        if (fromTime != null && rec.timestamp() < fromTime)
+            return false;
 
-                    final WALPointer pointer = next.get1();
-                    final WALRecord record = next.get2();
+        if (toTime != null && rec.timestamp() > toTime)
+            return false;
 
-                    if (stat != null)
-                        stat.registerRecord(record, pointer, false);
+        return true;
+    }
 
-                    if (printRecords)
-                        System.out.println("[A] " + toString(record, 
sensitiveData));
-                }
-            }
-        }
+    /**
+     * Get current wal file path, used in {@code WALIterator}
+     *
+     * @param it WALIterator.
+     * @return Current wal file path.
+     */
+    private static String getCurrentWalFilePath(WALIterator it) {
+        String result = null;
 
-        System.err.flush();
+        try {
+            final Integer curIdx = IgniteUtils.field(it, "curIdx");
 
-        if (stat != null)
-            System.out.println("Statistic collected:\n" + stat.toString());
+            final List<FileDescriptor> walFileDescriptors = 
IgniteUtils.field(it, "walFileDescriptors");
+
+            if (curIdx != null && walFileDescriptors != null && 
!walFileDescriptors.isEmpty())
+                result = walFileDescriptors.get(curIdx).getAbsolutePath();
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return result;
     }
 
     /**
      * Converting {@link WALRecord} to a string with sensitive data.
      *
-     * @param walRecord Instance of {@link WALRecord}.
+     * @param walRecord     Instance of {@link WALRecord}.
      * @param sensitiveData Strategy for processing of sensitive data.
      * @return String representation of {@link WALRecord}.
      */
     private static String toString(WALRecord walRecord, ProcessSensitiveData 
sensitiveData) {
-        if (SHOW == sensitiveData || HIDE == sensitiveData)
-            return walRecord.toString();
+        if (walRecord instanceof DataRecord) {
+            final DataRecord dataRecord = (DataRecord)walRecord;
 
-        if (MetastoreDataRecord.class.isInstance(walRecord))
-            walRecord = new 
MetastoreDataRecordWrapper((MetastoreDataRecord)walRecord, sensitiveData);
-        else if (DataRecord.class.isInstance(walRecord)) {
-            DataRecord dataRecord = (DataRecord)walRecord;
+            final List<DataEntry> entryWrappers = new 
ArrayList<>(dataRecord.writeEntries().size());
 
-            List<DataEntry> entryWrappers = dataRecord.writeEntries().stream()
-                .map(dataEntry -> new DataEntryWrapper(dataEntry, 
sensitiveData)).collect(toList());
+            for (DataEntry dataEntry : dataRecord.writeEntries())
+                entryWrappers.add(new DataEntryWrapper(dataEntry, 
sensitiveData));
 
             dataRecord.setWriteEntries(entryWrappers);
         }
+        else if (walRecord instanceof MetastoreDataRecord)
+            walRecord = new 
MetastoreDataRecordWrapper((MetastoreDataRecord)walRecord, sensitiveData);
 
         return walRecord.toString();
     }
diff --git 
a/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/IgniteWalConverterArguments.java
 
b/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/IgniteWalConverterArguments.java
new file mode 100644
index 0000000..af3bfb8
--- /dev/null
+++ 
b/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/IgniteWalConverterArguments.java
@@ -0,0 +1,491 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.development.utils;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import org.apache.ignite.internal.pagemem.wal.record.WALRecord;
+import org.apache.ignite.internal.util.typedef.F;
+
+/**
+ * Parameters for IgniteWalConverter with parsed and validated.
+ */
+public class IgniteWalConverterArguments {
+    /** */
+    private static final String WAL_DIR = "walDir";
+
+    /** */
+    private static final String WAL_ARCHIVE_DIR = "walArchiveDir";
+
+    /** */
+    private static final String PAGE_SIZE = "pageSize";
+
+    /** */
+    private static final String BINARY_METADATA_FILE_STORE_DIR = 
"binaryMetadataFileStoreDir";
+
+    /** */
+    private static final String MARSHALLER_MAPPING_FILE_STORE_DIR = 
"marshallerMappingFileStoreDir";
+
+    /** */
+    private static final String KEEP_BINARY = "keepBinary";
+
+    /** */
+    private static final String RECORD_TYPES = "recordTypes";
+
+    /** */
+    private static final String WAL_TIME_FROM_MILLIS = "walTimeFromMillis";
+
+    /** */
+    private static final String WAL_TIME_TO_MILLIS = "walTimeToMillis";
+
+    /** */
+    private static final String RECORD_CONTAINS_TEXT = "recordContainsText";
+
+    /** */
+    private static final String PROCESS_SENSITIVE_DATA = 
"processSensitiveData";
+
+    /** */
+    private static final String PRINT_STAT = "printStat";
+
+    /** */
+    private static final String SKIP_CRC = "skipCrc";
+
+    /** Path to dir with wal files. */
+    private final File walDir;
+
+    /** Path to dir with archive wal files. */
+    private final File walArchiveDir;
+
+    /** Size of pages, which was selected for file store (1024, 2048, 4096, 
etc). */
+    private final int pageSize;
+
+    /** Path to binary metadata dir. */
+    private final File binaryMetadataFileStoreDir;
+
+    /** Path to marshaller dir. */
+    private final File marshallerMappingFileStoreDir;
+
+    /** Keep binary flag. */
+    private final boolean keepBinary;
+
+    /** WAL record types (TX_RECORD, DATA_RECORD, etc). */
+    private final Set<WALRecord.RecordType> recordTypes;
+
+    /** The start time interval for the record time in milliseconds. */
+    private final Long fromTime;
+
+    /** The end time interval for the record time in milliseconds. */
+    private final Long toTime;
+
+    /** Filter by substring in the WAL record. */
+    private final String recordContainsText;
+
+    /** Strategy for the processing of sensitive data (SHOW, HIDE, HASH, MD5). 
*/
+    private final ProcessSensitiveData processSensitiveData;
+
+    /** Write summary statistics for WAL */
+    private final boolean printStat;
+
+    /** Skip CRC calculation/check flag */
+    private final boolean skipCrc;
+
+    /**
+     * @param walDir                        Path to dir with wal files.
+     * @param walArchiveDir                 Path to dir with archive wal files.
+     * @param pageSize                      Size of pages, which was selected 
for file store (1024, 2048, 4096, etc).
+     * @param binaryMetadataFileStoreDir    Path to binary metadata dir.
+     * @param marshallerMappingFileStoreDir Path to marshaller dir.
+     * @param keepBinary                    Keep binary flag.
+     * @param recordTypes                   WAL record types (TX_RECORD, 
DATA_RECORD, etc).
+     * @param fromTime                      The start time interval for the 
record time in milliseconds.
+     * @param toTime                        The end time interval for the 
record time in milliseconds.
+     * @param recordContainsText            Filter by substring in the WAL 
record.
+     * @param processSensitiveData          Strategy for the processing of 
sensitive data (SHOW, HIDE, HASH, MD5).
+     * @param printStat                     Write summary statistics for WAL.
+     * @param skipCrc                       Skip CRC calculation/check flag.
+     */
+    public IgniteWalConverterArguments(File walDir, File walArchiveDir, int 
pageSize,
+        File binaryMetadataFileStoreDir, File marshallerMappingFileStoreDir, 
boolean keepBinary,
+        Set<WALRecord.RecordType> recordTypes, Long fromTime, Long toTime, 
String recordContainsText,
+        ProcessSensitiveData processSensitiveData,
+        boolean printStat, boolean skipCrc) {
+        this.walDir = walDir;
+        this.walArchiveDir = walArchiveDir;
+        this.pageSize = pageSize;
+        this.binaryMetadataFileStoreDir = binaryMetadataFileStoreDir;
+        this.marshallerMappingFileStoreDir = marshallerMappingFileStoreDir;
+        this.keepBinary = keepBinary;
+        this.recordTypes = recordTypes;
+        this.fromTime = fromTime;
+        this.toTime = toTime;
+        this.recordContainsText = recordContainsText;
+        this.processSensitiveData = processSensitiveData;
+        this.printStat = printStat;
+        this.skipCrc = skipCrc;
+    }
+
+    /**
+     * Path to dir with wal files.
+     *
+     * @return walDir
+     */
+    public File getWalDir() {
+        return walDir;
+    }
+
+    /**
+     * Path to dir with archive wal files.
+     *
+     * @return walArchiveDir
+     */
+    public File getWalArchiveDir() {
+        return walArchiveDir;
+    }
+
+    /**
+     * Size of pages, which was selected for file store (1024, 2048, 4096, 
etc).
+     *
+     * @return pageSize
+     */
+    public int getPageSize() {
+        return pageSize;
+    }
+
+    /**
+     * Path to binary metadata dir.
+     *
+     * @return binaryMetadataFileStoreD
+     */
+    public File getBinaryMetadataFileStoreDir() {
+        return binaryMetadataFileStoreDir;
+    }
+
+    /**
+     * Path to marshaller dir.
+     *
+     * @return marshallerMappingFileStoreD
+     */
+    public File getMarshallerMappingFileStoreDir() {
+        return marshallerMappingFileStoreDir;
+    }
+
+    /**
+     * Keep binary flag.
+     *
+     * @return keepBina
+     */
+    public boolean isKeepBinary() {
+        return keepBinary;
+    }
+
+    /**
+     * WAL record types (TX_RECORD, DATA_RECORD, etc).
+     *
+     * @return recordTypes
+     */
+    public Set<WALRecord.RecordType> getRecordTypes() {
+        return recordTypes;
+    }
+
+    /**
+     * The start time interval for the record time in milliseconds.
+     *
+     * @return fromTime
+     */
+    public Long getFromTime() {
+        return fromTime;
+    }
+
+    /**
+     * The end time interval for the record time in milliseconds.
+     *
+     * @return toTime
+     */
+    public Long getToTime() {
+        return toTime;
+    }
+
+    /**
+     * Filter by substring in the WAL record.
+     *
+     * @return recordContainsText
+     */
+    public String getRecordContainsText() {
+        return recordContainsText;
+    }
+
+    /**
+     * Strategy for the processing of sensitive data (SHOW, HIDE, HASH, MD5).
+     *
+     * @return processSensitiveData
+     */
+    public ProcessSensitiveData getProcessSensitiveData() {
+        return processSensitiveData;
+    }
+
+    /**
+     * Write summary statistics for WAL.
+     *
+     * @return printStat
+     */
+    public boolean isPrintStat() {
+        return printStat;
+    }
+
+    /**
+     * Skip CRC calculation/check flag.
+     *
+     * @return skipCrc
+     */
+    public boolean isSkipCrc() {
+        return skipCrc;
+    }
+
+    /**
+     * Parse command line arguments and return filled 
IgniteWalConverterArguments
+     *
+     * @param args Command line arguments.
+     * @return IgniteWalConverterArguments.
+     */
+    public static IgniteWalConverterArguments parse(final PrintStream out, 
String args[]) {
+        if (args == null || args.length < 1) {
+            out.println("Print WAL log data in human-readable form.");
+            out.println("You need to provide:");
+            out.println("    walDir                           Path to dir with 
wal files.");
+            out.println("    walArchiveDir                    Path to dir with 
archive wal files. walDir or walArchiveDir must be specified.");
+            out.println("    pageSize                         Size of pages, 
which was selected for file store (1024, 2048, 4096, etc). Default 4096.");
+            out.println("    binaryMetadataFileStoreDir       (Optional) Path 
to binary meta.");
+            out.println("    marshallerMappingFileStoreDir    (Optional) Path 
to marshaller dir.");
+            out.println("    keepBinary                       Keep binary 
flag. Default true.");
+            out.println("    recordTypes                      (Optional) 
Comma-separated WAL record types (TX_RECORD, DATA_RECORD, etc). Default all.");
+            out.println("    walTimeFromMillis                (Optional) The 
start time interval for the record time in milliseconds.");
+            out.println("    walTimeToMillis                  (Optional) The 
end time interval for the record time in milliseconds.");
+            out.println("    recordContainsText               (Optional) 
Filter by substring in the WAL record.");
+            out.println("    processSensitiveData             (Optional) 
Strategy for the processing of sensitive data (SHOW, HIDE, HASH, MD5). Default 
SHOW.");
+            out.println("    printStat                        Write summary 
statistics for WAL. Default false.");
+            out.println("    skipCrc                          Skip CRC 
calculation/check flag. Default false.");
+            out.println("For example:");
+            out.println("    walDir=/work/db/wal");
+            out.println("    walArchiveDir=/work/db/wal_archive");
+            out.println("    pageSize=4096");
+            out.println("    
binaryMetadataFileStoreDir=/work/db/nodeId-consistentId");
+            out.println("    
marshallerMappingFileStoreDir=/work/db/marshaller");
+            out.println("    keepBinary=true");
+            out.println("    recordTypes=DataRecord,TxRecord");
+            out.println("    walTimeFromMillis=1575158400000");
+            out.println("    walTimeToMillis=1577836740999");
+            out.println("    recordContainsText=search string");
+            out.println("    processSensitiveData=SHOW");
+            out.println("    skipCrc=true");
+            return null;
+        }
+
+        File walDir = null;
+        File walArchiveDir = null;
+        int pageSize = 4096;
+        File binaryMetadataFileStoreDir = null;
+        File marshallerMappingFileStoreDir = null;
+        boolean keepBinary = true;
+        final Set<WALRecord.RecordType> recordTypes = new HashSet<>();
+        Long fromTime = null;
+        Long toTime = null;
+        String recordContainsText = null;
+        ProcessSensitiveData processSensitiveData = ProcessSensitiveData.SHOW;
+        boolean printStat = false;
+        boolean skipCrc = false;
+
+        for (String arg : args) {
+            if (arg.startsWith(WAL_DIR + "=")) {
+                final String walPath = arg.substring(WAL_DIR.length() + 1);
+
+                walDir = new File(walPath);
+
+                if (!walDir.exists())
+                    throw new IllegalArgumentException("Incorrect path to dir 
with wal files: " + walPath);
+            }
+            else if (arg.startsWith(WAL_ARCHIVE_DIR + "=")) {
+                final String walArchivePath = 
arg.substring(WAL_ARCHIVE_DIR.length() + 1);
+
+                walArchiveDir = new File(walArchivePath);
+
+                if (!walArchiveDir.exists())
+                    throw new IllegalArgumentException("Incorrect path to dir 
with archive wal files: " + walArchivePath);
+            }
+            else if (arg.startsWith(PAGE_SIZE + "=")) {
+                final String pageSizeStr = arg.substring(PAGE_SIZE.length() + 
1);
+
+                try {
+                    pageSize = Integer.parseInt(pageSizeStr);
+                }
+                catch (Exception e) {
+                    throw new IllegalArgumentException("Incorrect page size. 
Error parse: " + pageSizeStr);
+                }
+            }
+            else if (arg.startsWith(BINARY_METADATA_FILE_STORE_DIR + "=")) {
+                final String binaryMetadataFileStorePath = 
arg.substring(BINARY_METADATA_FILE_STORE_DIR.length() + 1);
+
+                binaryMetadataFileStoreDir = new 
File(binaryMetadataFileStorePath);
+
+                if (!binaryMetadataFileStoreDir.isDirectory())
+                    throw new IllegalArgumentException("Incorrect path to dir 
with binary meta files: " + binaryMetadataFileStorePath);
+            }
+            else if (arg.startsWith(MARSHALLER_MAPPING_FILE_STORE_DIR + "=")) {
+                final String marshallerMappingFileStorePath = 
arg.substring(MARSHALLER_MAPPING_FILE_STORE_DIR.length() + 1);
+
+                marshallerMappingFileStoreDir = new 
File(marshallerMappingFileStorePath);
+
+                if (!marshallerMappingFileStoreDir.isDirectory())
+                    throw new IllegalArgumentException("Incorrect path to dir 
with marshaller files: " + marshallerMappingFileStorePath);
+            }
+            else if (arg.startsWith(KEEP_BINARY + "=")) {
+                keepBinary = parseBoolean(KEEP_BINARY, 
arg.substring(KEEP_BINARY.length() + 1));
+            }
+            else if (arg.startsWith(RECORD_TYPES + "=")) {
+                final String recordTypesStr = 
arg.substring(RECORD_TYPES.length() + 1);
+
+                final String[] recordTypesStrArray = recordTypesStr.split(",");
+
+                final SortedSet<String> unknownRecordTypes = new TreeSet<>();
+
+                for (String recordTypeStr : recordTypesStrArray) {
+                    try {
+                        
recordTypes.add(WALRecord.RecordType.valueOf(recordTypeStr));
+                    }
+                    catch (Exception e) {
+                        unknownRecordTypes.add(recordTypeStr);
+                    }
+                }
+
+                if (!unknownRecordTypes.isEmpty())
+                    throw new IllegalArgumentException("Unknown record types: 
" + unknownRecordTypes +
+                        ". Supported record types: " + 
Arrays.toString(WALRecord.RecordType.values()));
+            }
+            else if (arg.startsWith(WAL_TIME_FROM_MILLIS + "=")) {
+                final String fromTimeStr = 
arg.substring(WAL_TIME_FROM_MILLIS.length() + 1);
+
+                try {
+                    fromTime = Long.parseLong(fromTimeStr);
+                }
+                catch (Exception e) {
+                    throw new IllegalArgumentException("Incorrect 
walTimeFromMillis. Error parse: " + fromTimeStr);
+                }
+            }
+            else if (arg.startsWith(WAL_TIME_TO_MILLIS + "=")) {
+                final String toTimeStr = 
arg.substring(WAL_TIME_TO_MILLIS.length() + 1);
+
+                try {
+                    toTime = Long.parseLong(toTimeStr);
+                }
+                catch (Exception e) {
+                    throw new IllegalArgumentException("Incorrect 
walTimeToMillis. Error parse: " + toTimeStr);
+                }
+            }
+            else if (arg.startsWith(RECORD_CONTAINS_TEXT + "=")) {
+                recordContainsText = 
arg.substring(RECORD_CONTAINS_TEXT.length() + 1);
+            }
+            else if (arg.startsWith(PROCESS_SENSITIVE_DATA + "=")) {
+                final String processSensitiveDataStr = 
arg.substring(PROCESS_SENSITIVE_DATA.length() + 1);
+                try {
+                    processSensitiveData = 
ProcessSensitiveData.valueOf(processSensitiveDataStr);
+                }
+                catch (Exception e) {
+                    throw new IllegalArgumentException("Unknown 
processSensitiveData: " + processSensitiveDataStr +
+                        ". Supported: " + 
Arrays.toString(ProcessSensitiveData.values()));
+                }
+            }
+            else if (arg.startsWith(PRINT_STAT + "=")) {
+                printStat = parseBoolean(PRINT_STAT, 
arg.substring(PRINT_STAT.length() + 1));
+            }
+            else if (arg.startsWith(SKIP_CRC + "=")) {
+                skipCrc = parseBoolean(SKIP_CRC, 
arg.substring(SKIP_CRC.length() + 1));
+            }
+        }
+
+        if (walDir == null && walArchiveDir == null)
+            throw new IllegalArgumentException("The paths to the WAL files are 
not specified.");
+
+        out.println("Program arguments:");
+
+        if (walDir != null)
+            out.printf("\t%s = %s\n", WAL_DIR, walDir.getAbsolutePath());
+
+        if (walArchiveDir != null)
+            out.printf("\t%s = %s\n", WAL_ARCHIVE_DIR, 
walArchiveDir.getAbsolutePath());
+
+        out.printf("\t%s = %d\n", PAGE_SIZE, pageSize);
+
+        if (binaryMetadataFileStoreDir != null)
+            out.printf("\t%s = %s\n", BINARY_METADATA_FILE_STORE_DIR, 
binaryMetadataFileStoreDir);
+
+        if (marshallerMappingFileStoreDir != null)
+            out.printf("\t%s = %s\n", MARSHALLER_MAPPING_FILE_STORE_DIR, 
marshallerMappingFileStoreDir);
+
+        out.printf("\t%s = %s\n", KEEP_BINARY, keepBinary);
+
+        if (!F.isEmpty(recordTypes))
+            out.printf("\t%s = %s\n", RECORD_TYPES, recordTypes);
+
+        if (fromTime != null)
+            out.printf("\t%s = %s\n", WAL_TIME_FROM_MILLIS, new 
Date(fromTime));
+
+        if (toTime != null)
+            out.printf("\t%s = %s\n", WAL_TIME_TO_MILLIS, new Date(toTime));
+
+        if (recordContainsText != null)
+            out.printf("\t%s = %s\n", RECORD_CONTAINS_TEXT, 
recordContainsText);
+
+        out.printf("\t%s = %b\n", PRINT_STAT, printStat);
+
+        out.printf("\t%s = %b\n", SKIP_CRC, skipCrc);
+
+        return new IgniteWalConverterArguments(walDir, walArchiveDir, pageSize,
+            binaryMetadataFileStoreDir, marshallerMappingFileStoreDir,
+            keepBinary, recordTypes, fromTime, toTime, recordContainsText, 
processSensitiveData, printStat, skipCrc);
+    }
+
+    /**
+     * Parses the string argument as a boolean.  The {@code boolean}
+     * returned represents the value {@code true} if the string argument
+     * is not {@code null} and is equal, ignoring case, to the string
+     * {@code "true"}, returned value {@code false} if the string argument
+     * is not {@code null} and is equal, ignoring case, to the string
+     * {@code "false"}, else throw IllegalArgumentException<p>
+     *
+     * @param name parameter name of boolean type.
+     * @param value the {@code String} containing the boolean representation 
to be parsed.
+     * @return the boolean represented by the string argument
+     *
+     */
+    private static boolean parseBoolean(String name, String value) {
+        if (value == null)
+            throw new IllegalArgumentException("Null value passed for flag " + 
name);
+
+        if (value.equalsIgnoreCase(Boolean.TRUE.toString()))
+            return true;
+        else if (value.equalsIgnoreCase(Boolean.FALSE.toString()))
+            return false;
+        else
+            throw new IllegalArgumentException("Incorrect flag " + name + ", 
valid value: true or false. Error parse: " + value);
+    }
+}
diff --git 
a/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/MetastoreDataRecordWrapper.java
 
b/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/MetastoreDataRecordWrapper.java
index f32c8f4..f76a99a 100644
--- 
a/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/MetastoreDataRecordWrapper.java
+++ 
b/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/MetastoreDataRecordWrapper.java
@@ -23,6 +23,7 @@ import 
org.apache.ignite.internal.pagemem.wal.record.MetastoreDataRecord;
 import static java.lang.String.valueOf;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.apache.ignite.development.utils.ProcessSensitiveData.HASH;
+import static org.apache.ignite.development.utils.ProcessSensitiveData.HIDE;
 import static org.apache.ignite.development.utils.ProcessSensitiveData.MD5;
 import static 
org.apache.ignite.development.utils.ProcessSensitiveDataUtils.md5;
 
@@ -38,11 +39,13 @@ class MetastoreDataRecordWrapper extends 
MetastoreDataRecord {
      */
     public MetastoreDataRecordWrapper(MetastoreDataRecord metastoreRecord, 
ProcessSensitiveData sensitiveData) {
         super(
-            HASH == sensitiveData ? valueOf(metastoreRecord.key().hashCode()) :
-                MD5 == sensitiveData ? md5(metastoreRecord.key()) : 
metastoreRecord.key(),
-            HASH == sensitiveData ? 
valueOf(Arrays.hashCode(metastoreRecord.value())).getBytes(UTF_8) :
-                MD5 == sensitiveData ? 
md5(Arrays.toString(metastoreRecord.value())).getBytes(UTF_8) :
-                    metastoreRecord.value()
+            HIDE == sensitiveData ? "" :
+                HASH == sensitiveData ? 
valueOf(metastoreRecord.key().hashCode()) :
+                    MD5 == sensitiveData ? md5(metastoreRecord.key()) : 
metastoreRecord.key(),
+            HIDE == sensitiveData ? new byte[0] :
+                HASH == sensitiveData ? 
valueOf(Arrays.hashCode(metastoreRecord.value())).getBytes(UTF_8) :
+                    MD5 == sensitiveData ? 
md5(Arrays.toString(metastoreRecord.value())).getBytes(UTF_8) :
+                        metastoreRecord.value()
         );
 
         size(metastoreRecord.size());
diff --git 
a/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/DevUtilsTestSuite.java
 
b/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/DevUtilsTestSuite.java
index 5f424d9..919fb9b 100644
--- 
a/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/DevUtilsTestSuite.java
+++ 
b/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/DevUtilsTestSuite.java
@@ -25,6 +25,8 @@ import org.junit.runners.Suite;
  */
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
+    IgniteWalConverterTest.class,
+    IgniteWalConverterArgumentsTest.class,
     IgniteWalConverterSensitiveDataTest.class
 })
 public class DevUtilsTestSuite {
diff --git 
a/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/IgniteWalConverterArgumentsTest.java
 
b/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/IgniteWalConverterArgumentsTest.java
new file mode 100644
index 0000000..84098fb
--- /dev/null
+++ 
b/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/IgniteWalConverterArgumentsTest.java
@@ -0,0 +1,411 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.development.utils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.pagemem.wal.record.WALRecord;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test for IgniteWalConverterArguments
+ */
+public class IgniteWalConverterArgumentsTest extends GridCommonAbstractTest {
+    /**
+     *
+     */
+    public IgniteWalConverterArgumentsTest() {
+        super(false);
+    }
+
+    /**
+     * View help
+     * <ul>
+     *     <li>Read wal with out params</li>
+     * </ul>
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testViewHelp() throws Exception {
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        final IgniteWalConverterArguments parseArgs = 
IgniteWalConverterArguments.parse(new PrintStream(out), null);
+
+        Assert.assertNull(parseArgs);
+
+        final String help = out.toString();
+
+        Assert.assertTrue(help.startsWith("Print WAL log data in 
human-readable form."));
+
+        for (final Field field : 
IgniteWalConverterArguments.class.getDeclaredFields()) {
+            if (Modifier.isStatic(field.getModifiers())
+                && Modifier.isStatic(field.getModifiers())
+                && field.getType() == String.class) {
+                field.setAccessible(true);
+
+                final String arg = (String)field.get(null);
+
+                Assert.assertTrue(help.contains("    " + arg + " "));
+            }
+        }
+    }
+
+    /**
+     * Checking whether fields "walDir" or "walArchiveDir" are mandatory.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testRequiredWalDir() throws Exception {
+        GridTestUtils.assertThrows(log, () -> {
+            IgniteWalConverterArguments.parse(System.out, new String[] 
{"pageSize=4096"});
+        }, IgniteException.class, "The paths to the WAL files are not 
specified.");
+    }
+
+    /**
+     * Checking whether field "walDir" are incorrect.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIncorrectWalDir() throws Exception {
+        GridTestUtils.assertThrows(log, () -> {
+            IgniteWalConverterArguments.parse(System.out, new String[] 
{"walDir=non_existing_path"});
+        }, IgniteException.class, "Incorrect path to dir with wal files: 
non_existing_path");
+    }
+
+    /**
+     * Checking whether field "walArchiveDir" are incorrect.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIncorrectWalArchiveDir() throws Exception {
+        GridTestUtils.assertThrows(log, () -> {
+            IgniteWalConverterArguments.parse(System.out, new String[] 
{"walArchiveDir=non_existing_path"});
+        }, IgniteException.class, "Incorrect path to dir with archive wal 
files: non_existing_path");
+    }
+
+    /**
+     * Checking whether field "pageSize" are incorrect.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIncorrectPageSize() throws Exception {
+        GridTestUtils.assertThrows(log, () -> {
+            final File wal = File.createTempFile("wal", "");
+            wal.deleteOnExit();
+
+            final String[] args = {
+                "walDir=" + wal.getAbsolutePath(),
+                "pageSize=not_integer"
+            };
+
+            IgniteWalConverterArguments.parse(System.out, args);
+        }, IgniteException.class, "Incorrect page size. Error parse: 
not_integer");
+    }
+
+    /**
+     * Checking whether field "binaryMetadataFileStoreDir" are incorrect.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIncorrectBinaryMetadataFileStoreDir() throws Exception {
+        GridTestUtils.assertThrows(log, () -> {
+            final File wal = File.createTempFile("wal", "");
+            wal.deleteOnExit();
+
+            final String[] args = {
+                "walDir=" + wal.getAbsolutePath(),
+                "binaryMetadataFileStoreDir=non_existing_path"
+            };
+
+            IgniteWalConverterArguments.parse(System.out, args);
+        }, IgniteException.class, "Incorrect path to dir with binary meta 
files: non_existing_path");
+    }
+
+    /**
+     * Checking whether field "marshallerMappingFileStoreDir" are incorrect.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIncorrectMarshallerMappingFileStoreDir() throws Exception {
+        GridTestUtils.assertThrows(log, () -> {
+            final File wal = File.createTempFile("wal", "");
+            wal.deleteOnExit();
+
+            final String[] args = {
+                "walDir=" + wal.getAbsolutePath(),
+                "marshallerMappingFileStoreDir=non_existing_path"
+            };
+
+            IgniteWalConverterArguments.parse(System.out, args);
+        }, IgniteException.class, "Incorrect path to dir with marshaller 
files: non_existing_path");
+    }
+
+    /**
+     * Checking whether field "keepBinary" are incorrect.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIncorrectKeepBinary() throws Exception {
+        GridTestUtils.assertThrows(log, () -> {
+            final File wal = File.createTempFile("wal", "");
+            wal.deleteOnExit();
+
+            final String[] args = {
+                "walDir=" + wal.getAbsolutePath(),
+                "keepBinary=not_boolean"
+            };
+
+            IgniteWalConverterArguments.parse(System.out, args);
+        }, IgniteException.class, "Incorrect flag keepBinary, valid value: 
true or false. Error parse: not_boolean");
+    }
+
+    /**
+     * Checking whether field "recordTypes" are incorrect.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIncorrectRecordTypes() throws Exception {
+        GridTestUtils.assertThrows(log, () -> {
+            final File wal = File.createTempFile("wal", "");
+            wal.deleteOnExit();
+
+            final String[] args = {
+                "walDir=" + wal.getAbsolutePath(),
+                "recordTypes=not_exist"
+            };
+
+            IgniteWalConverterArguments.parse(System.out, args);
+        }, IgniteException.class, "Unknown record types: [not_exist].");
+    }
+
+    /**
+     * Checking whether field "recordTypes" are incorrect several value.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIncorrectSeveralRecordTypes() throws Exception {
+        GridTestUtils.assertThrows(log, () -> {
+            final File wal = File.createTempFile("wal", "");
+            wal.deleteOnExit();
+
+            final String[] args = {
+                "walDir=" + wal.getAbsolutePath(),
+                "recordTypes=not_exist1,not_exist2"
+            };
+
+            IgniteWalConverterArguments.parse(System.out, args);
+        }, IgniteException.class, "Unknown record types: [not_exist1, 
not_exist2].");
+    }
+
+    /**
+     * Checking whether field "walTimeFromMillis" are incorrect.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIncorrectWalTimeFromMillis() throws Exception {
+        GridTestUtils.assertThrows(log, () -> {
+            final File wal = File.createTempFile("wal", "");
+            wal.deleteOnExit();
+
+            final String[] args = {
+                "walDir=" + wal.getAbsolutePath(),
+                "walTimeFromMillis=not_long"
+            };
+
+            IgniteWalConverterArguments.parse(System.out, args);
+        }, IgniteException.class, "Incorrect walTimeFromMillis. Error parse: 
not_long");
+    }
+
+    /**
+     * Checking whether field "walTimeToMillis" are incorrect.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIncorrectWalTimeToMillis() throws Exception {
+        GridTestUtils.assertThrows(log, () -> {
+            final File wal = File.createTempFile("wal", "");
+            wal.deleteOnExit();
+
+            final String[] args = {
+                "walDir=" + wal.getAbsolutePath(),
+                "walTimeToMillis=not_long"
+            };
+
+            IgniteWalConverterArguments.parse(System.out, args);
+        }, IgniteException.class, "Incorrect walTimeToMillis. Error parse: 
not_long");
+    }
+
+    /**
+     * Checking whether field "processSensitiveData" are incorrect.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIncorrectProcessSensitiveData() throws Exception {
+        GridTestUtils.assertThrows(log, () -> {
+            final File wal = File.createTempFile("wal", "");
+            wal.deleteOnExit();
+
+            final String[] args = {
+                "walDir=" + wal.getAbsolutePath(),
+                "processSensitiveData=unknown"
+            };
+
+            IgniteWalConverterArguments.parse(System.out, args);
+        }, IgniteException.class, "Unknown processSensitiveData: unknown. 
Supported: ");
+    }
+
+    /**
+     * Checking whether field "printStat" are incorrect.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIncorrectPrintStat() throws Exception {
+        GridTestUtils.assertThrows(log, () -> {
+            final File wal = File.createTempFile("wal", "");
+            wal.deleteOnExit();
+
+            final String[] args = {
+                "walDir=" + wal.getAbsolutePath(),
+                "printStat=not_boolean"
+            };
+
+            IgniteWalConverterArguments.parse(System.out, args);
+        }, IgniteException.class, "Incorrect flag printStat, valid value: true 
or false. Error parse: not_boolean");
+    }
+
+    /**
+     * Checking whether field "skipCrc" are incorrect.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIncorrectSkipCrc() throws Exception {
+        GridTestUtils.assertThrows(log, () -> {
+            final File wal = File.createTempFile("wal", "");
+            wal.deleteOnExit();
+
+            final String[] args = {
+                "walDir=" + wal.getAbsolutePath(),
+                "skipCrc=not_boolean"
+            };
+
+            IgniteWalConverterArguments.parse(System.out, args);
+        }, IgniteException.class, "Incorrect flag skipCrc, valid value: true 
or false. Error parse: not_boolean");
+    }
+
+    /**
+     * Checking default value.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testDefault() throws IOException {
+        final File wal = File.createTempFile("wal", "");
+        wal.deleteOnExit();
+
+        final String[] args = {
+            "walDir=" + wal.getAbsolutePath()
+        };
+
+        final IgniteWalConverterArguments parseArgs = 
IgniteWalConverterArguments.parse(System.out, args);
+
+        Assert.assertEquals(4096, parseArgs.getPageSize());
+        Assert.assertNull(parseArgs.getBinaryMetadataFileStoreDir());
+        Assert.assertNull(parseArgs.getMarshallerMappingFileStoreDir());
+        Assert.assertTrue(parseArgs.isKeepBinary());
+        Assert.assertTrue(parseArgs.getRecordTypes().isEmpty());
+        Assert.assertNull(parseArgs.getFromTime());
+        Assert.assertNull(parseArgs.getToTime());
+        Assert.assertNull(parseArgs.getRecordContainsText());
+        Assert.assertEquals(ProcessSensitiveData.SHOW, 
parseArgs.getProcessSensitiveData());
+        Assert.assertFalse(parseArgs.isPrintStat());
+        Assert.assertFalse(parseArgs.isSkipCrc());
+    }
+
+    /**
+     * Checking all value set.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testParse() throws IOException {
+        final File wal = File.createTempFile("wal", "");
+        wal.deleteOnExit();
+
+        final File walArchive = File.createTempFile("wal_archive", "");
+        walArchive.deleteOnExit();
+
+        final File binaryMetadataDir = new 
File(System.getProperty("java.io.tmpdir"));
+
+        final File marshallerDir = binaryMetadataDir;
+
+        final String[] args = {
+            "walDir=" + wal.getAbsolutePath(),
+            "walArchiveDir=" + walArchive.getAbsolutePath(),
+            "pageSize=2048",
+            "binaryMetadataFileStoreDir=" + 
binaryMetadataDir.getAbsolutePath(),
+            "marshallerMappingFileStoreDir=" + marshallerDir.getAbsolutePath(),
+            "keepBinary=false",
+            "recordTypes=DATA_RECORD,TX_RECORD",
+            "walTimeFromMillis=1575158400000",
+            "walTimeToMillis=1577836740999",
+            "recordContainsText=search string",
+            "processSensitiveData=MD5",
+            "printStat=true",
+            "skipCrc=true"};
+
+        final IgniteWalConverterArguments parseArgs = 
IgniteWalConverterArguments.parse(System.out, args);
+        Assert.assertEquals(wal, parseArgs.getWalDir());
+        Assert.assertEquals(walArchive, parseArgs.getWalArchiveDir());
+        Assert.assertEquals(2048, parseArgs.getPageSize());
+        Assert.assertEquals(binaryMetadataDir, 
parseArgs.getBinaryMetadataFileStoreDir());
+        Assert.assertEquals(marshallerDir, 
parseArgs.getMarshallerMappingFileStoreDir());
+        Assert.assertFalse(parseArgs.isKeepBinary());
+        
Assert.assertTrue(parseArgs.getRecordTypes().contains(WALRecord.RecordType.DATA_RECORD));
+        
Assert.assertTrue(parseArgs.getRecordTypes().contains(WALRecord.RecordType.TX_RECORD));
+        Assert.assertEquals(1575158400000L, (long)parseArgs.getFromTime());
+        Assert.assertEquals(1577836740999L, (long)parseArgs.getToTime());
+        Assert.assertEquals("search string", 
parseArgs.getRecordContainsText());
+        Assert.assertEquals(ProcessSensitiveData.MD5, 
parseArgs.getProcessSensitiveData());
+        Assert.assertTrue(parseArgs.isPrintStat());
+        Assert.assertTrue(parseArgs.isSkipCrc());
+    }
+}
diff --git 
a/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/IgniteWalConverterSensitiveDataTest.java
 
b/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/IgniteWalConverterSensitiveDataTest.java
index e2b2dd4..bb4cab9 100644
--- 
a/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/IgniteWalConverterSensitiveDataTest.java
+++ 
b/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/IgniteWalConverterSensitiveDataTest.java
@@ -45,21 +45,16 @@ import 
org.apache.ignite.internal.processors.cache.GridCacheOperation;
 import org.apache.ignite.internal.processors.cache.KeyCacheObjectImpl;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
 import org.apache.ignite.internal.util.typedef.internal.CU;
-import org.apache.ignite.testframework.junits.WithSystemProperty;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.apache.ignite.transactions.Transaction;
 import org.junit.Test;
 
 import static java.lang.String.valueOf;
 import static java.lang.System.setOut;
-import static java.lang.System.setProperty;
 import static java.util.Arrays.asList;
 import static java.util.Objects.requireNonNull;
 import static java.util.function.Function.identity;
-import static 
org.apache.ignite.IgniteSystemProperties.IGNITE_TO_STRING_INCLUDE_SENSITIVE;
 import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
-import static 
org.apache.ignite.development.utils.IgniteWalConverter.PRINT_RECORDS;
-import static 
org.apache.ignite.development.utils.IgniteWalConverter.SENSITIVE_DATA;
 import static org.apache.ignite.testframework.GridTestUtils.assertContains;
 import static org.apache.ignite.testframework.GridTestUtils.assertNotContains;
 import static 
org.apache.ignite.testframework.wal.record.RecordUtils.isIncludeIntoLog;
@@ -155,7 +150,7 @@ public class IgniteWalConverterSensitiveDataTest extends 
GridCommonAbstractTest
     @Override protected void afterTest() throws Exception {
         super.afterTest();
 
-        log.info("Test output for " + currentTestMethod());
+        log.info("Test output for " + getName());
         log.info("----------------------------------------");
 
         setOut(sysOut);
@@ -189,42 +184,13 @@ public class IgniteWalConverterSensitiveDataTest extends 
GridCommonAbstractTest
     }
 
     /**
-     * Test checks that by default {@link WALRecord} will not be output without
-     * system option {@link IgniteWalConverter#PRINT_RECORDS}.
-     *
-     * @throws Exception If failed.
-     */
-    @Test
-    public void testNotPrintRecordsByDefault() throws Exception {
-        exeWithCheck(false, false, identity());
-    }
-
-    /**
      * Test checks that by default sensitive data is displayed.
      *
      * @throws Exception If failed.
      */
     @Test
-    @WithSystemProperty(key = PRINT_RECORDS, value = "true")
     public void testShowSensitiveDataByDefault() throws Exception {
-        exeWithCheck(true, true, identity());
-    }
-
-    /**
-     * Test checks that sensitive data is displayed.
-     *
-     * @throws Exception If failed.
-     */
-    @Test
-    @WithSystemProperty(key = PRINT_RECORDS, value = "true")
-    @WithSystemProperty(key = SENSITIVE_DATA, value = "SHOW")
-    public void testShowSensitiveData() throws Exception {
-        exeWithCheck(true, true, identity());
-
-        setProperty(SENSITIVE_DATA, currentTestMethod().getName());
-        resetTestOut();
-
-        exeWithCheck(true, true, identity());
+        exeWithCheck(null, true, true, identity());
     }
 
     /**
@@ -233,11 +199,8 @@ public class IgniteWalConverterSensitiveDataTest extends 
GridCommonAbstractTest
      * @throws Exception If failed.
      */
     @Test
-    @WithSystemProperty(key = PRINT_RECORDS, value = "true")
-    @WithSystemProperty(key = SENSITIVE_DATA, value = "HIDE")
-    @WithSystemProperty(key = IGNITE_TO_STRING_INCLUDE_SENSITIVE, value = 
"true")
     public void testHideSensitiveData() throws Exception {
-        exeWithCheck(false, false, identity());
+        exeWithCheck(ProcessSensitiveData.HIDE, false, false, identity());
     }
 
     /**
@@ -246,10 +209,8 @@ public class IgniteWalConverterSensitiveDataTest extends 
GridCommonAbstractTest
      * @throws Exception If failed.
      */
     @Test
-    @WithSystemProperty(key = PRINT_RECORDS, value = "true")
-    @WithSystemProperty(key = SENSITIVE_DATA, value = "HASH")
     public void testHashSensitiveData() throws Exception {
-        exeWithCheck(true, false, s -> valueOf(s.hashCode()));
+        exeWithCheck(ProcessSensitiveData.HASH, true, false, s -> 
valueOf(s.hashCode()));
     }
 
     /**
@@ -258,21 +219,21 @@ public class IgniteWalConverterSensitiveDataTest extends 
GridCommonAbstractTest
      * @throws Exception If failed.
      */
     @Test
-    @WithSystemProperty(key = PRINT_RECORDS, value = "true")
-    @WithSystemProperty(key = SENSITIVE_DATA, value = "MD5")
     public void testMd5HashSensitiveData() throws Exception {
-        exeWithCheck(true, false, ProcessSensitiveDataUtils::md5);
+        exeWithCheck(ProcessSensitiveData.MD5, true, false, 
ProcessSensitiveDataUtils::md5);
     }
 
     /**
      * Executing {@link IgniteWalConverter} with checking the content of its 
output.
      *
-     * @param containsData Contains or not elements {@link #sensitiveValues} 
in utility output.
-     * @param containsPrefix Contains or not {@link 
#SENSITIVE_DATA_VALUE_PREFIX} in utility output.
-     * @param converter Converting elements {@link #sensitiveValues} for 
checking in utility output.
+     * @param processSensitiveData Strategy for the processing of sensitive 
data.
+     * @param containsData         Contains or not elements {@link 
#sensitiveValues} in utility output.
+     * @param containsPrefix       Contains or not {@link 
#SENSITIVE_DATA_VALUE_PREFIX} in utility output.
+     * @param converter            Converting elements {@link 
#sensitiveValues} for checking in utility output.
      * @throws Exception If failed.
      */
     private void exeWithCheck(
+        ProcessSensitiveData processSensitiveData,
         boolean containsData,
         boolean containsPrefix,
         Function<String, String> converter
@@ -281,7 +242,13 @@ public class IgniteWalConverterSensitiveDataTest extends 
GridCommonAbstractTest
 
         injectTestSystemOut();
 
-        IgniteWalConverter.main(new String[] {valueOf(pageSize), walDirPath});
+        List<String> args = new ArrayList<>();
+        args.add("pageSize=" + pageSize);
+        args.add("walDir=" + walDirPath);
+        if (processSensitiveData != null)
+            args.add("processSensitiveData=" + processSensitiveData.name());
+
+        IgniteWalConverter.main(args.toArray(new String[args.size()]));
 
         String testOutStr = testOut.toString();
 
@@ -373,7 +340,7 @@ public class IgniteWalConverterSensitiveDataTest extends 
GridCommonAbstractTest
          * Constructor.
          *
          * @param orgId Organization id.
-         * @param name Organization name.
+         * @param name  Organization name.
          */
         Person(int orgId, String name) {
             this.orgId = orgId;
diff --git 
a/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/IgniteWalConverterTest.java
 
b/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/IgniteWalConverterTest.java
new file mode 100644
index 0000000..1e38dbd
--- /dev/null
+++ 
b/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/IgniteWalConverterTest.java
@@ -0,0 +1,530 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.development.utils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.PrintStream;
+import java.io.RandomAccessFile;
+import java.util.Base64;
+import java.util.LinkedList;
+import java.util.List;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteSystemProperties;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.WALMode;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.pagemem.wal.record.WALRecord;
+import 
org.apache.ignite.internal.processors.cache.persistence.wal.serializer.RecordV1Serializer;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+/**
+ * Test for IgniteWalConverter
+ */
+public class IgniteWalConverterTest extends GridCommonAbstractTest {
+    /** */
+    public static final String PERSON_NAME_PREFIX = "Name ";
+
+    /** Flag "skip CRC calculation" in system property save before test and 
restore after. */
+    private String beforeIgnitePdsSkipCrc;
+
+    /** Flag "skip CRC calculation" in RecordV1Serializer save before test and 
restore after. */
+    private boolean beforeSkipCrc;
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        beforeIgnitePdsSkipCrc = 
System.getProperty(IgniteSystemProperties.IGNITE_PDS_SKIP_CRC);
+        beforeSkipCrc = RecordV1Serializer.skipCrc;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        super.beforeTest();
+
+        cleanPersistenceDir();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        stopAllGrids(true);
+
+        cleanPersistenceDir();
+
+        super.afterTest();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        if (beforeIgnitePdsSkipCrc != null)
+            System.setProperty(IgniteSystemProperties.IGNITE_PDS_SKIP_CRC, 
beforeIgnitePdsSkipCrc);
+        else
+            System.clearProperty(IgniteSystemProperties.IGNITE_PDS_SKIP_CRC);
+
+        RecordV1Serializer.skipCrc = beforeSkipCrc;
+
+        super.afterTestsStopped();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String 
igniteInstanceName) throws Exception {
+        final IgniteConfiguration igniteConfiguration = 
super.getConfiguration(igniteInstanceName);
+
+        
igniteConfiguration.setDataStorageConfiguration(getDataStorageConfiguration());
+
+        final CacheConfiguration cacheConfiguration = new 
CacheConfiguration<>()
+            .setName(DEFAULT_CACHE_NAME)
+            .setCacheMode(CacheMode.PARTITIONED)
+            .setBackups(0)
+            .setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL)
+            .setIndexedTypes(PersonKey.class, Person.class);
+
+        igniteConfiguration.setCacheConfiguration(cacheConfiguration);
+
+        return igniteConfiguration;
+    }
+
+    /** @return DataStorageConfiguration. */
+    private DataStorageConfiguration getDataStorageConfiguration() {
+        final DataStorageConfiguration dataStorageConfiguration = new 
DataStorageConfiguration()
+            .setWalSegmentSize(4 * 1024 * 1024)
+            .setWalMode(WALMode.LOG_ONLY)
+            .setCheckpointFrequency(1000)
+            .setWalCompactionEnabled(true)
+            .setDefaultDataRegionConfiguration(getDataRegionConfiguration());
+
+        return dataStorageConfiguration;
+    }
+
+    /** @return DataRegionConfiguration. */
+    private DataRegionConfiguration getDataRegionConfiguration() {
+        final DataRegionConfiguration dataRegionConfiguration = new 
DataRegionConfiguration()
+            .setPersistenceEnabled(true)
+            .setMaxSize(100L * 1024 * 1024);
+
+        return dataRegionConfiguration;
+    }
+
+    /**
+     * Checking utility IgniteWalConverter
+     * <ul>
+     *     <li>Start node</li>
+     *     <li>Create cache with <a 
href="https://apacheignite.readme.io/docs/indexes#section-registering-indexed-types";>Registering
 Indexed Types</a></li>
+     *     <li>Put several entity</li>
+     *     <li>Stop node</li>
+     *     <li>Read wal with specifying binaryMetadata</li>
+     *     <li>Check that the output contains all DataRecord with previously 
added entities</li>
+     * </ul>
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIgniteWalConverter() throws Exception {
+        final List<Person> list = new LinkedList<>();
+
+        final String nodeFolder = createWal(list);
+
+        final ByteArrayOutputStream outByte = new ByteArrayOutputStream();
+
+        final PrintStream out = new PrintStream(outByte);
+
+        final IgniteWalConverterArguments arg = new 
IgniteWalConverterArguments(
+            U.resolveWorkDirectory(U.defaultWorkDirectory(), 
DataStorageConfiguration.DFLT_WAL_PATH, false),
+            U.resolveWorkDirectory(U.defaultWorkDirectory(), 
DataStorageConfiguration.DFLT_WAL_ARCHIVE_PATH, false),
+            DataStorageConfiguration.DFLT_PAGE_SIZE,
+            new File(U.resolveWorkDirectory(U.defaultWorkDirectory(), 
DataStorageConfiguration.DFLT_BINARY_METADATA_PATH, false), nodeFolder),
+            U.resolveWorkDirectory(U.defaultWorkDirectory(), 
DataStorageConfiguration.DFLT_MARSHALLER_PATH, false),
+            false,
+            null,
+            null, null, null, null, true,true
+        );
+
+        IgniteWalConverter.convert(out, arg);
+
+        final String result = outByte.toString();
+
+        int index = 0;
+
+        for (Person person : list) {
+            boolean find = false;
+
+            index = result.indexOf("DataRecord", index);
+
+            if (index > 0) {
+                index = result.indexOf("PersonKey", index + 10);
+
+                if (index > 0) {
+                    index = result.indexOf("id=" + person.getId(), index + 9);
+
+                    if (index > 0) {
+                        index = result.indexOf("name=" + person.getName(), 
index + 4);
+
+                        find = index > 0;
+                    }
+                }
+            }
+
+            assertTrue("DataRecord for Person(id=" + person.getId() + ") not 
found", find);
+        }
+    }
+
+    /**
+     * Checking utility IgniteWalConverter with out binary_meta
+     * <ul>
+     *     <li>Start node</li>
+     *     <li>Create cache with <a 
href="https://apacheignite.readme.io/docs/indexes#section-registering-indexed-types";>Registering
 Indexed Types</a></li>
+     *     <li>Put several entity</li>
+     *     <li>Stop node</li>
+     *     <li>Read wal with <b>out</b> specifying binaryMetadata</li>
+     *     <li>Check that the output contains all DataRecord and in DataRecord 
not empty key and value</li>
+     * </ul>
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIgniteWalConverterWithOutBinaryMeta() throws Exception {
+        final List<Person> list = new LinkedList<>();
+
+        createWal(list);
+
+        final ByteArrayOutputStream outByte = new ByteArrayOutputStream();
+
+        final PrintStream out = new PrintStream(outByte);
+
+        final IgniteWalConverterArguments arg = new 
IgniteWalConverterArguments(
+            U.resolveWorkDirectory(U.defaultWorkDirectory(), 
DataStorageConfiguration.DFLT_WAL_PATH, false),
+            U.resolveWorkDirectory(U.defaultWorkDirectory(), 
DataStorageConfiguration.DFLT_WAL_ARCHIVE_PATH, false),
+            DataStorageConfiguration.DFLT_PAGE_SIZE,
+            null,
+            null,
+            false,
+            null,
+            null, null, null, null, true,true
+        );
+
+        IgniteWalConverter.convert(out, arg);
+
+        final String result = outByte.toString();
+
+        int index = 0;
+
+        for (Person person : list) {
+            boolean find = false;
+
+            index = result.indexOf("DataRecord", index);
+
+            if (index > 0) {
+                index = result.indexOf(" v = [", index + 10);
+
+                if (index > 0) {
+                    int start = index + 6;
+
+                    index = result.indexOf("]", start);
+
+                    if (index > 0) {
+                        final String value = result.substring(start, index);
+
+                        find = new 
String(Base64.getDecoder().decode(value)).contains(person.getName());
+                    }
+                }
+            }
+
+            assertTrue("DataRecord for Person(id=" + person.getId() + ") not 
found", find);
+        }
+    }
+
+    /**
+     * Checking utility IgniteWalConverter on broken WAL
+     * <ul>
+     *     <li>Start node</li>
+     *     <li>Create cache with <a 
href="https://apacheignite.readme.io/docs/indexes#section-registering-indexed-types";>Registering
 Indexed Types</a></li>
+     *     <li>Put several entity</li>
+     *     <li>Stop node</li>
+     *     <li>Change byte in DataRecord value</li>
+     *     <li>Read wal</li>
+     *     <li>Check one error when reading WAL</li>
+     * </ul>
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIgniteWalConverterWithBrokenWal() throws Exception {
+        final List<Person> list = new LinkedList<>();
+
+        final String nodeFolder = createWal(list);
+
+        final File walDir = U.resolveWorkDirectory(U.defaultWorkDirectory(), 
DataStorageConfiguration.DFLT_WAL_PATH, false);
+
+        final File wal = new File(walDir, nodeFolder + File.separator + 
"0000000000000000.wal");
+
+        try (RandomAccessFile raf = new RandomAccessFile(wal, "rw")) {
+            raf.seek(RecordV1Serializer.HEADER_RECORD_SIZE); // HeaderRecord
+
+            byte findByte[] = (PERSON_NAME_PREFIX + 0).getBytes();
+
+            boolean find = false;
+
+            while (!find) {
+                int recordTypeIndex = raf.read();
+
+                if (recordTypeIndex > 0) {
+                    recordTypeIndex--;
+
+                    final long idx = raf.readLong();
+
+                    final int fileOff = Integer.reverseBytes(raf.readInt());
+
+                    final int len = Integer.reverseBytes(raf.readInt());
+
+                    if (recordTypeIndex == 
WALRecord.RecordType.DATA_RECORD.index()) {
+                        int i = 0;
+
+                        int b;
+
+                        while (!find && (b = raf.read()) >= 0) {
+                            if (findByte[i] == b) {
+                                i++;
+
+                                if (i == findByte.length)
+                                    find = true;
+                            }
+                            else
+                                i = 0;
+                        }
+                        if (find) {
+                            raf.seek(raf.getFilePointer() - 1);
+
+                            raf.write(' ');
+                        }
+                    }
+
+                    raf.seek(fileOff + len);
+                }
+            }
+        }
+
+        final ByteArrayOutputStream outByte = new ByteArrayOutputStream();
+
+        final PrintStream out = new PrintStream(outByte);
+
+        final IgniteWalConverterArguments arg = new 
IgniteWalConverterArguments(
+            walDir,
+            U.resolveWorkDirectory(U.defaultWorkDirectory(), 
DataStorageConfiguration.DFLT_WAL_ARCHIVE_PATH, false),
+            DataStorageConfiguration.DFLT_PAGE_SIZE,
+            new File(U.resolveWorkDirectory(U.defaultWorkDirectory(), 
DataStorageConfiguration.DFLT_BINARY_METADATA_PATH, false), nodeFolder),
+            U.resolveWorkDirectory(U.defaultWorkDirectory(), 
DataStorageConfiguration.DFLT_MARSHALLER_PATH, false),
+            false,
+            null,
+            null, null, null, null, true,true
+        );
+
+        IgniteWalConverter.convert(out, arg);
+
+        final String result = outByte.toString();
+
+        int index = 0;
+
+        int countErrorRead = 0;
+
+        for (Person person : list) {
+            boolean find = false;
+
+            index = result.indexOf("DataRecord", index);
+
+            if (index > 0) {
+                index = result.indexOf("PersonKey", index + 10);
+
+                if (index > 0) {
+                    index = result.indexOf("id=" + person.getId(), index + 9);
+
+                    if (index > 0) {
+                        index = result.indexOf("name=" + person.getName(), 
index + 4);
+
+                        find = index > 0;
+                    }
+                }
+            }
+
+            if (!find)
+                countErrorRead++;
+        }
+        assertEquals(1, countErrorRead);
+    }
+
+    /**
+     * Checking utility IgniteWalConverter on unreadable WAL
+     * <ul>
+     *     <li>Start node</li>
+     *     <li>Create cache with <a 
href="https://apacheignite.readme.io/docs/indexes#section-registering-indexed-types";>Registering
 Indexed Types</a></li>
+     *     <li>Put several entity</li>
+     *     <li>Stop node</li>
+     *     <li>Change byte RecordType in second DataRecord</li>
+     *     <li>Read wal</li>
+     *     <li>Check contains one DataRecord in output before error when 
reading WAL</li>
+     * </ul>
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIgniteWalConverterWithUnreadableWal() throws Exception {
+        final List<Person> list = new LinkedList<>();
+
+        final String nodeFolder = createWal(list);
+
+        final File walDir = U.resolveWorkDirectory(U.defaultWorkDirectory(), 
DataStorageConfiguration.DFLT_WAL_PATH, false);
+
+        final File wal = new File(walDir, nodeFolder + File.separator + 
"0000000000000000.wal");
+
+        try (RandomAccessFile raf = new RandomAccessFile(wal, "rw")) {
+            raf.seek(RecordV1Serializer.HEADER_RECORD_SIZE); // HeaderRecord
+
+            int find = 0;
+
+            while (find < 2) {
+                int recordTypeIndex = raf.read();
+
+                if (recordTypeIndex > 0) {
+                    recordTypeIndex--;
+
+                    if (recordTypeIndex == 
WALRecord.RecordType.DATA_RECORD.index()) {
+                        find++;
+
+                        if (find == 2) {
+                            raf.seek(raf.getFilePointer() - 1);
+
+                            raf.write(Byte.MAX_VALUE);
+                        }
+                    }
+
+                    final long idx = raf.readLong();
+
+                    final int fileOff = Integer.reverseBytes(raf.readInt());
+
+                    final int len = Integer.reverseBytes(raf.readInt());
+
+                    raf.seek(fileOff + len);
+                }
+            }
+        }
+
+        final ByteArrayOutputStream outByte = new ByteArrayOutputStream();
+
+        final PrintStream out = new PrintStream(outByte);
+
+        final IgniteWalConverterArguments arg = new 
IgniteWalConverterArguments(
+            walDir,
+            U.resolveWorkDirectory(U.defaultWorkDirectory(), 
DataStorageConfiguration.DFLT_WAL_ARCHIVE_PATH, false),
+            DataStorageConfiguration.DFLT_PAGE_SIZE,
+            new File(U.resolveWorkDirectory(U.defaultWorkDirectory(), 
DataStorageConfiguration.DFLT_BINARY_METADATA_PATH, false), nodeFolder),
+            U.resolveWorkDirectory(U.defaultWorkDirectory(), 
DataStorageConfiguration.DFLT_MARSHALLER_PATH, false),
+            false,
+            null,
+            null, null, null, null, true,true
+        );
+
+        IgniteWalConverter.convert(out, arg);
+
+        final String result = outByte.toString();
+
+        int index = 0;
+
+        int countErrorRead = 0;
+
+        for (Person person : list) {
+            boolean find = false;
+
+            index = result.indexOf("DataRecord", index);
+
+            if (index > 0) {
+                index = result.indexOf("PersonKey", index + 10);
+
+                if (index > 0) {
+                    index = result.indexOf("id=" + person.getId(), index + 9);
+
+                    if (index > 0) {
+                        index = 
result.indexOf(person.getClass().getSimpleName(), index + 4);
+
+                        if (index > 0) {
+                            index = result.indexOf("id=" + person.getId(), 
index + person.getClass().getSimpleName().length());
+
+                            if (index > 0) {
+                                index = result.indexOf("name=" + 
person.getName(), index + 4);
+
+                                find = index > 0;
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (!find)
+                countErrorRead++;
+        }
+
+        assertEquals(9, countErrorRead);
+    }
+
+    /**
+     * Common part
+     * <ul>
+     *    <li>Start node</li>
+     *    <li>Create cache with <a 
href="https://apacheignite.readme.io/docs/indexes#section-registering-indexed-types";>Registering
 Indexed Types</a></li>
+     *    <li>Put several entity</li>
+     * </ul>
+     *
+     * @param list Returns entities that have been added.
+     * @return Node folder name.
+     * @throws Exception
+     */
+    private String createWal(List<Person> list) throws Exception {
+        String nodeFolder;
+
+        try (final IgniteEx node = startGrid(0)) {
+            node.cluster().active(true);
+
+            nodeFolder = 
node.context().pdsFolderResolver().resolveFolders().folderName();
+
+            final IgniteCache<PersonKey, Person> cache = 
node.cache(DEFAULT_CACHE_NAME);
+
+            for (int i = 0; i < 10; i++) {
+                final PersonKey key = new PersonKey(i);
+
+                final Person value;
+
+                if (i % 2 == 0)
+                    value = new Person(i, PERSON_NAME_PREFIX + i);
+                else
+                    value = new PersonEx(i, PERSON_NAME_PREFIX + i, 
"Additional information " + i, "Description " + i);
+
+                cache.put(key, value);
+
+                list.add(value);
+            }
+        }
+
+        return nodeFolder;
+    }
+}
diff --git 
a/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/Person.java
 
b/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/Person.java
new file mode 100644
index 0000000..67c104b
--- /dev/null
+++ 
b/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/Person.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.development.utils;
+
+import java.util.Objects;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+
+/**
+ * A person entity used for the tests.
+ */
+public class Person {
+    /** Id. */
+    private final Integer id;
+
+    /** Name. */
+    @QuerySqlField
+    private final String name;
+
+    /** Constructor. */
+    public Person(Integer id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    /** @return id. */
+    public Integer getId() {
+        return id;
+    }
+
+    /** @return name. */
+    public String getName() {
+        return name;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        return Objects.hash(id, name);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object obj) {
+        if (!(obj instanceof Person))
+            return false;
+
+        Person other = (Person)obj;
+
+        return Objects.equals(id, other.id) &&
+            Objects.equals(name, other.name);
+    }
+}
diff --git 
a/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/PersonEx.java
 
b/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/PersonEx.java
new file mode 100644
index 0000000..f03dc30
--- /dev/null
+++ 
b/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/PersonEx.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.development.utils;
+
+import java.util.Objects;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+
+/**
+ * A person entity used for the tests.
+ */
+public class PersonEx extends Person {
+    /**
+     * Additional information.
+     */
+    @QuerySqlField
+    private final String info;
+
+    /**
+     * Description - not declared as SQL field.
+     */
+    private final String description;
+
+    /**
+     * Constructor.
+     */
+    public PersonEx(Integer id, String name, String info, String description) {
+        super(id, name);
+        this.info = info;
+        this.description = description;
+    }
+
+    /**
+     * @return Additional information.
+     */
+    public String getInfo() {
+        return info;
+    }
+
+    /**
+     * @return Description.
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override public int hashCode() {
+        return Objects.hash(super.hashCode(), info, description);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override public boolean equals(Object obj) {
+        if (!(obj instanceof PersonEx))
+            return false;
+
+        PersonEx other = (PersonEx)obj;
+
+        return super.equals(other) && Objects.equals(info, other.info) && 
Objects.equals(description, other.description);
+    }
+}
diff --git 
a/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/DevUtilsTestSuite.java
 
b/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/PersonKey.java
similarity index 52%
copy from 
modules/dev-utils/src/test/java/org/apache/ignite/development/utils/DevUtilsTestSuite.java
copy to 
modules/dev-utils/src/test/java/org/apache/ignite/development/utils/PersonKey.java
index 5f424d9..a5f82b7 100644
--- 
a/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/DevUtilsTestSuite.java
+++ 
b/modules/dev-utils/src/test/java/org/apache/ignite/development/utils/PersonKey.java
@@ -17,15 +17,45 @@
 
 package org.apache.ignite.development.utils;
 
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
+import java.util.Objects;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
 
 /**
- * Test suite for dev utils.
+ * A person entity used for the tests.
  */
-@RunWith(Suite.class)
-@Suite.SuiteClasses({
-    IgniteWalConverterSensitiveDataTest.class
-})
-public class DevUtilsTestSuite {
+public class PersonKey {
+    /**
+     * Id.
+     */
+    @QuerySqlField(index = true)
+    private final Integer id;
+
+    /**
+     * Constructor.
+     */
+    public PersonKey(Integer id) {
+        this.id = id;
+    }
+
+    /**
+     * @return id.
+     */
+    public Integer getId() {
+        return id;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object obj) {
+        if (!(obj instanceof PersonKey))
+            return false;
+
+        PersonKey other = (PersonKey)obj;
+
+        return Objects.equals(other.id, id);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        return Objects.hash(id);
+    }
 }

Reply via email to