Merge remote-tracking branch 'origin/fluo-425'
diff --git a/docs/applications.md b/docs/applications.md
index 7471a3d..5685381 100644
--- a/docs/applications.md
+++ b/docs/applications.md
@@ -143,9 +143,10 @@
 
 | Logger               | Level | Information                                                                                        |
 |----------------------|-------|----------------------------------------------------------------------------------------------------|
-| `fluo.tx`            | TRACE | Provides detailed information about what transactions read and wrote                               |
-| `fluo.tx.summary`    | TRACE | Provides a one line summary about each transaction executed                                        |
-| `fluo.tx.collisions` | TRACE | Provides details about what data was involved When a transaction collides with another transaction |
+| fluo.tx            | TRACE | Provides detailed information about what transactions read and wrote                               |
+| fluo.tx.summary    | TRACE | Provides a one line summary about each transaction executed                                        |
+| fluo.tx.collisions | TRACE | Provides details about what data was involved When a transaction collides with another transaction |
+| fluo.tx.scan       | TRACE | Provides logging for each cell read by a scan.  Scan summary logged at `fluo.tx` level.  This allows suppression of `fluo.tx.scan` while still seeing summary. |
 
 Below is an example log after setting `fluo.tx` to TRACE. The number following `txid: ` is the
 transactions start timestamp from the Oracle.
diff --git a/modules/api/src/main/java/org/apache/fluo/api/config/SimpleConfiguration.java b/modules/api/src/main/java/org/apache/fluo/api/config/SimpleConfiguration.java
index a92dc1d..9877b18 100644
--- a/modules/api/src/main/java/org/apache/fluo/api/config/SimpleConfiguration.java
+++ b/modules/api/src/main/java/org/apache/fluo/api/config/SimpleConfiguration.java
@@ -18,8 +18,6 @@
 import java.io.File;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.Collections;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Map.Entry;
diff --git a/modules/core/src/main/java/org/apache/fluo/core/log/TracingCellScanner.java b/modules/core/src/main/java/org/apache/fluo/core/log/TracingCellScanner.java
new file mode 100644
index 0000000..9d77315
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/fluo/core/log/TracingCellScanner.java
@@ -0,0 +1,51 @@
+/*
+ * 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.fluo.core.log;
+
+import java.util.Iterator;
+
+import com.google.common.collect.Iterators;
+import org.apache.fluo.api.client.scanner.CellScanner;
+import org.apache.fluo.api.config.FluoConfiguration;
+import org.apache.fluo.api.data.RowColumnValue;
+import org.apache.fluo.core.util.Hex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TracingCellScanner implements CellScanner {
+  static final Logger log = LoggerFactory.getLogger(FluoConfiguration.TRANSACTION_PREFIX + ".scan");
+
+  private final CellScanner wrappedScanner;
+  private final long txid;
+  private final String scanId;
+
+  TracingCellScanner(CellScanner wrappedScanner, long txid, String scanId) {
+    this.wrappedScanner = wrappedScanner;
+    this.txid = txid;
+    this.scanId = scanId;
+  }
+
+  @Override
+  public Iterator<RowColumnValue> iterator() {
+    return Iterators.transform(
+        wrappedScanner.iterator(),
+        rcv -> {
+          log.trace("txid: {} scanId: {} next()-> {} {}", txid, scanId,
+              Hex.encNonAscii(rcv.getRowColumn()), Hex.encNonAscii(rcv.getValue()));
+          return rcv;
+        });
+  }
+}
diff --git a/modules/core/src/main/java/org/apache/fluo/core/log/TracingColumnScanner.java b/modules/core/src/main/java/org/apache/fluo/core/log/TracingColumnScanner.java
new file mode 100644
index 0000000..d950a80
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/fluo/core/log/TracingColumnScanner.java
@@ -0,0 +1,64 @@
+/*
+ * 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.fluo.core.log;
+
+import java.util.Iterator;
+
+import com.google.common.collect.Iterators;
+import org.apache.fluo.api.client.scanner.ColumnScanner;
+import org.apache.fluo.api.data.Bytes;
+import org.apache.fluo.api.data.ColumnValue;
+import org.apache.fluo.core.util.Hex;
+import org.slf4j.Logger;
+
+public class TracingColumnScanner implements ColumnScanner {
+
+  static final Logger log = TracingCellScanner.log;
+
+  private final ColumnScanner cs;
+  private final long txid;
+  private final String scanId;
+  private final String encRow;
+
+  TracingColumnScanner(ColumnScanner cs, long txid, String scanId) {
+    this.cs = cs;
+    this.txid = txid;
+    this.scanId = scanId;
+    this.encRow = Hex.encNonAscii(cs.getRow());
+  }
+
+  @Override
+  public Iterator<ColumnValue> iterator() {
+    return Iterators.transform(
+        cs.iterator(),
+        cv -> {
+          log.trace("txid: {} scanId: {} next()-> {} {} {}", txid, scanId, encRow,
+              Hex.encNonAscii(cv.getColumn()), Hex.encNonAscii(cv.getValue()));
+          return cv;
+        });
+  }
+
+  @Override
+  public Bytes getRow() {
+    return cs.getRow();
+  }
+
+  @Override
+  public String getsRow() {
+    return cs.getsRow();
+  }
+
+}
diff --git a/modules/core/src/main/java/org/apache/fluo/core/log/TracingRowScanner.java b/modules/core/src/main/java/org/apache/fluo/core/log/TracingRowScanner.java
new file mode 100644
index 0000000..9796d40
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/fluo/core/log/TracingRowScanner.java
@@ -0,0 +1,42 @@
+/*
+ * 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.fluo.core.log;
+
+import java.util.Iterator;
+
+import com.google.common.collect.Iterators;
+import org.apache.fluo.api.client.scanner.ColumnScanner;
+import org.apache.fluo.api.client.scanner.RowScanner;
+
+public class TracingRowScanner implements RowScanner {
+  private final RowScanner wrappedScanner;
+  private final long txid;
+  private final String scanId;
+
+  TracingRowScanner(RowScanner wrappedScanner, long txid, String scanId) {
+    this.wrappedScanner = wrappedScanner;
+    this.txid = txid;
+    this.scanId = scanId;
+  }
+
+  @Override
+  public Iterator<ColumnScanner> iterator() {
+    return Iterators.transform(wrappedScanner.iterator(), cs -> {
+      return new TracingColumnScanner(cs, txid, scanId);
+    });
+  }
+
+}
diff --git a/modules/core/src/main/java/org/apache/fluo/core/log/TracingScannerBuilder.java b/modules/core/src/main/java/org/apache/fluo/core/log/TracingScannerBuilder.java
new file mode 100644
index 0000000..3964711
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/fluo/core/log/TracingScannerBuilder.java
@@ -0,0 +1,103 @@
+/*
+ * 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.fluo.core.log;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.fluo.api.client.scanner.CellScanner;
+import org.apache.fluo.api.client.scanner.RowScanner;
+import org.apache.fluo.api.client.scanner.RowScannerBuilder;
+import org.apache.fluo.api.client.scanner.ScannerBuilder;
+import org.apache.fluo.api.config.FluoConfiguration;
+import org.apache.fluo.api.data.Column;
+import org.apache.fluo.api.data.Span;
+import org.apache.fluo.core.util.Hex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TracingScannerBuilder implements ScannerBuilder {
+
+  private static final Logger log = LoggerFactory.getLogger(FluoConfiguration.TRANSACTION_PREFIX);
+
+  private static final Span EMPTY_SPAN = new Span();
+
+  private Span span = EMPTY_SPAN;
+  private Collection<Column> columns = Collections.emptyList();
+
+  private final ScannerBuilder wrappedBuilder;
+  private final long txid;
+
+  TracingScannerBuilder(ScannerBuilder wrappedBuilder, long txid) {
+    this.wrappedBuilder = wrappedBuilder;
+    this.txid = txid;
+  }
+
+  @Override
+  public ScannerBuilder over(Span span) {
+    Objects.requireNonNull(span);
+    this.span = span;
+    wrappedBuilder.over(span);
+    return this;
+  }
+
+  @Override
+  public ScannerBuilder fetch(Column... columns) {
+    Objects.requireNonNull(columns);
+    this.columns = ImmutableSet.copyOf(columns);
+    wrappedBuilder.fetch(this.columns);
+    return this;
+  }
+
+  @Override
+  public ScannerBuilder fetch(Collection<Column> columns) {
+    Objects.requireNonNull(columns);
+    this.columns = ImmutableSet.copyOf(columns);
+    wrappedBuilder.fetch(this.columns);
+    return this;
+  }
+
+  @Override
+  public CellScanner build() {
+    String scanId = Integer.toHexString(Math.abs(Objects.hash(span, columns, txid)));
+    log.trace("txid: {} scanId: {} scanner().over({}).fetch({}).build()", txid, scanId,
+        Hex.encNonAscii(span), Hex.encNonAscii(columns));
+    if (TracingCellScanner.log.isTraceEnabled()) {
+      return new TracingCellScanner(wrappedBuilder.build(), txid, scanId);
+    } else {
+      return wrappedBuilder.build();
+    }
+  }
+
+  @Override
+  public RowScannerBuilder byRow() {
+    String scanId = Integer.toHexString(Math.abs(Objects.hash(span, columns, txid)));
+    return new RowScannerBuilder() {
+      @Override
+      public RowScanner build() {
+        log.trace("txid: {} scanId: {} scanner().over({}).fetch({}).byRow().build()", txid, scanId,
+            Hex.encNonAscii(span), Hex.encNonAscii(columns));
+        if (TracingCellScanner.log.isTraceEnabled()) {
+          return new TracingRowScanner(wrappedBuilder.byRow().build(), txid, scanId);
+        } else {
+          return wrappedBuilder.byRow().build();
+        }
+      }
+    };
+  }
+}
diff --git a/modules/core/src/main/java/org/apache/fluo/core/log/TracingTransaction.java b/modules/core/src/main/java/org/apache/fluo/core/log/TracingTransaction.java
index f9298de..db2fefb 100644
--- a/modules/core/src/main/java/org/apache/fluo/core/log/TracingTransaction.java
+++ b/modules/core/src/main/java/org/apache/fluo/core/log/TracingTransaction.java
@@ -156,9 +156,7 @@
 
   @Override
   public ScannerBuilder scanner() {
-    // TODO log something better (see fluo-425)
-    log.trace("txid: {} newScanner()", txid);
-    return tx.scanner();
+    return new TracingScannerBuilder(tx.scanner(), txid);
   }
 
   @Override
diff --git a/modules/core/src/main/java/org/apache/fluo/core/util/Hex.java b/modules/core/src/main/java/org/apache/fluo/core/util/Hex.java
index f9fc85f..d2c1bd7 100644
--- a/modules/core/src/main/java/org/apache/fluo/core/util/Hex.java
+++ b/modules/core/src/main/java/org/apache/fluo/core/util/Hex.java
@@ -16,10 +16,12 @@
 package org.apache.fluo.core.util;
 
 import java.io.ByteArrayOutputStream;
+import java.util.Collection;
 
 import org.apache.fluo.api.data.Bytes;
 import org.apache.fluo.api.data.Column;
 import org.apache.fluo.api.data.RowColumn;
+import org.apache.fluo.api.data.Span;
 import org.apache.fluo.core.impl.Notification;
 
 public class Hex {
@@ -59,6 +61,19 @@
     return sb.toString();
   }
 
+  public static Object encNonAscii(Collection<Column> columns) {
+    StringBuilder sb = new StringBuilder();
+    sb.append("[");
+    String sep = "";
+    for (Column column : columns) {
+      sb.append(sep);
+      encNonAscii(sb, column, " ");
+      sep = ",";
+    }
+    sb.append("]");
+    return sb.toString();
+  }
+
   public static String encNonAscii(RowColumn rc) {
     StringBuilder sb = new StringBuilder();
     encNonAscii(sb, rc, " ");
@@ -79,6 +94,13 @@
     return sb.toString();
   }
 
+  public static String encNonAscii(Span span) {
+    return ((span.isStartInclusive() && !span.getStart().equals(RowColumn.EMPTY)) ? "[" : "(")
+        + (span.getStart().equals(RowColumn.EMPTY) ? "-inf" : encNonAscii(span.getStart())) + ","
+        + (span.getEnd().equals(RowColumn.EMPTY) ? "+inf" : encNonAscii(span.getEnd()))
+        + ((span.isEndInclusive() && !span.getEnd().equals(RowColumn.EMPTY)) ? "]" : ")");
+  }
+
   static byte[] decode(String s) {
 
     // the next best thing to a StringBuilder for bytes
diff --git a/modules/core/src/main/java/org/apache/fluo/core/worker/ObserverContext.java b/modules/core/src/main/java/org/apache/fluo/core/worker/ObserverContext.java
index fadea3e..47d5997 100644
--- a/modules/core/src/main/java/org/apache/fluo/core/worker/ObserverContext.java
+++ b/modules/core/src/main/java/org/apache/fluo/core/worker/ObserverContext.java
@@ -20,7 +20,6 @@
 import org.apache.fluo.api.observer.Observer;
 import org.apache.fluo.core.impl.Environment;
 import org.apache.fluo.core.metrics.DummyMetricsReporter;
-import org.apache.fluo.core.metrics.MetricsReporterImpl;
 
 public class ObserverContext implements Observer.Context {
 
diff --git a/modules/integration/src/test/java/org/apache/fluo/integration/impl/ObserverConfigIT.java b/modules/integration/src/test/java/org/apache/fluo/integration/impl/ObserverConfigIT.java
index a2c624f..bfd8e25 100644
--- a/modules/integration/src/test/java/org/apache/fluo/integration/impl/ObserverConfigIT.java
+++ b/modules/integration/src/test/java/org/apache/fluo/integration/impl/ObserverConfigIT.java
@@ -30,7 +30,6 @@
 import org.apache.fluo.api.metrics.Counter;
 import org.apache.fluo.api.metrics.Meter;
 import org.apache.fluo.api.observer.AbstractObserver;
-import org.apache.fluo.api.metrics.MetricsReporter;
 import org.apache.fluo.api.observer.Observer.NotificationType;
 import org.apache.fluo.integration.ITBaseMini;
 import org.junit.Assert;
diff --git a/modules/integration/src/test/java/org/apache/fluo/integration/log/LogIT.java b/modules/integration/src/test/java/org/apache/fluo/integration/log/LogIT.java
index c2167bc..02a4fd3 100644
--- a/modules/integration/src/test/java/org/apache/fluo/integration/log/LogIT.java
+++ b/modules/integration/src/test/java/org/apache/fluo/integration/log/LogIT.java
@@ -16,19 +16,29 @@
 package org.apache.fluo.integration.log;
 
 import java.io.StringWriter;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import org.apache.fluo.api.client.Loader;
 import org.apache.fluo.api.client.LoaderExecutor;
 import org.apache.fluo.api.client.Snapshot;
 import org.apache.fluo.api.client.Transaction;
 import org.apache.fluo.api.client.TransactionBase;
+import org.apache.fluo.api.client.scanner.CellScanner;
+import org.apache.fluo.api.client.scanner.ColumnScanner;
+import org.apache.fluo.api.client.scanner.RowScanner;
 import org.apache.fluo.api.config.ObserverSpecification;
 import org.apache.fluo.api.data.Bytes;
 import org.apache.fluo.api.data.Column;
+import org.apache.fluo.api.data.ColumnValue;
 import org.apache.fluo.api.data.RowColumn;
+import org.apache.fluo.api.data.RowColumnValue;
+import org.apache.fluo.api.data.Span;
 import org.apache.fluo.api.observer.AbstractObserver;
 import org.apache.fluo.integration.ITBaseMini;
 import org.apache.fluo.integration.TestUtil;
@@ -325,10 +335,19 @@
       logger.addAppender(appender);
 
       try (Snapshot snap = client.newSnapshot()) {
-        snap.gets(Arrays.asList(new RowColumn("r1", c1), new RowColumn("r2", c2)));
-        snap.gets(Arrays.asList("r1", "r2"), ImmutableSet.of(c1));
-        snap.gets("r1", ImmutableSet.of(c1, c2));
-        snap.gets("r1", c1);
+        Map<RowColumn, String> ret1 =
+            snap.gets(Arrays.asList(new RowColumn("r1", c1), new RowColumn("r2", c2)));
+        Assert.assertEquals(
+            ImmutableMap.of(new RowColumn("r1", c1), "v1", new RowColumn("r2", c2), "v4"), ret1);
+        Map<String, Map<Column, String>> ret2 =
+            snap.gets(Arrays.asList("r1", "r2"), ImmutableSet.of(c1));
+        Assert
+            .assertEquals(
+                ImmutableMap.of("r1", ImmutableMap.of(c1, "v1"), "r2", ImmutableMap.of(c1, "v3")),
+                ret2);
+        Map<Column, String> ret3 = snap.gets("r1", ImmutableSet.of(c1, c2));
+        Assert.assertEquals(ImmutableMap.of(c1, "v1", c2, "v2"), ret3);
+        Assert.assertEquals("v1", snap.gets("r1", c1));
       }
 
       miniFluo.waitForObservers();
@@ -412,4 +431,137 @@
     pattern += ".*";
     Assert.assertTrue(origLogMsgs, logMsgs.matches(pattern));
   }
+
+  private void assertEqual(CellScanner cs, RowColumnValue... rcvs) {
+    ArrayList<RowColumnValue> actual = new ArrayList<>();
+    Iterables.addAll(actual, cs);
+    Assert.assertEquals(Arrays.asList(rcvs), actual);
+  }
+
+  private void assertEqual(RowScanner rs, Object... stuff) {
+    int i = 0;
+    for (ColumnScanner cs : rs) {
+      Assert.assertEquals(stuff[i++], cs.getsRow());
+      for (ColumnValue cv : cs) {
+        Assert.assertEquals(stuff[i++], cv.getColumn());
+        Assert.assertEquals(stuff[i++], cv.getsValue());
+      }
+    }
+
+    Assert.assertEquals(stuff.length, i);
+  }
+
+  @Test
+  public void testScanLogging() {
+    Column c1 = new Column("f1", "q1");
+    Column c2 = new Column("f1", "q2");
+
+    try (Transaction tx = client.newTransaction()) {
+      tx.set("r1", c1, "v1");
+      tx.set("r1", c2, "v2");
+      tx.set("r2", c1, "v3");
+      tx.set("r2", c2, "v4");
+      tx.commit();
+    }
+
+    Logger logger = Logger.getLogger("fluo.tx");
+
+    StringWriter writer = new StringWriter();
+    WriterAppender appender =
+        new WriterAppender(new PatternLayout("%d{ISO8601} [%-8c{2}] %-5p: %m%n"), writer);
+
+    Level level = logger.getLevel();
+    boolean additivity = logger.getAdditivity();
+
+    try {
+      logger.setLevel(Level.TRACE);
+      logger.setAdditivity(false);
+      logger.addAppender(appender);
+
+      try (Snapshot snap = client.newSnapshot()) {
+        RowColumnValue rcv1 = new RowColumnValue("r1", c1, "v1");
+        RowColumnValue rcv2 = new RowColumnValue("r1", c2, "v2");
+        RowColumnValue rcv3 = new RowColumnValue("r2", c1, "v3");
+        RowColumnValue rcv4 = new RowColumnValue("r2", c2, "v4");
+
+        CellScanner scanner1 = snap.scanner().build();
+        assertEqual(scanner1, rcv1, rcv2, rcv3, rcv4);
+
+        CellScanner scanner2 = snap.scanner().over(Span.exact("r1")).build();
+        assertEqual(scanner2, rcv1, rcv2);
+
+        CellScanner scanner3 = snap.scanner().over(Span.exact("r1")).fetch(c1).build();
+        assertEqual(scanner3, rcv1);
+
+        CellScanner scanner4 = snap.scanner().fetch(c1).build();
+        assertEqual(scanner4, rcv1, rcv3);
+      }
+
+      try (Snapshot snap = client.newSnapshot()) {
+        RowScanner rowScanner1 = snap.scanner().byRow().build();
+        assertEqual(rowScanner1, "r1", c1, "v1", c2, "v2", "r2", c1, "v3", c2, "v4");
+
+        RowScanner rowScanner2 = snap.scanner().over(Span.exact("r1")).byRow().build();
+        assertEqual(rowScanner2, "r1", c1, "v1", c2, "v2");
+
+        RowScanner rowScanner3 = snap.scanner().over(Span.exact("r1")).fetch(c1).byRow().build();
+        assertEqual(rowScanner3, "r1", c1, "v1");
+
+        RowScanner rowScanner4 = snap.scanner().fetch(c1).byRow().build();
+        assertEqual(rowScanner4, "r1", c1, "v1", "r2", c1, "v3");
+      }
+
+    } finally {
+      logger.removeAppender(appender);
+      logger.setAdditivity(additivity);
+      logger.setLevel(level);
+    }
+
+    String origLogMsgs = writer.toString();
+    String logMsgs = origLogMsgs.replace('\n', ' ');
+
+    String pattern = "";
+
+    pattern += ".*txid: (\\d+) begin\\(\\) thread: \\d+";
+    pattern +=
+        ".*txid: \\1 scanId: ([0-9a-f]+) \\Qscanner().over((-inf,+inf)).fetch([]).build()\\E";
+    pattern += ".*txid: \\1 scanId: \\2 \\Qnext()-> r1 f1 q1  v1\\E";
+    pattern += ".*txid: \\1 scanId: \\2 \\Qnext()-> r1 f1 q2  v2\\E";
+    pattern += ".*txid: \\1 scanId: \\2 \\Qnext()-> r2 f1 q1  v3\\E";
+    pattern += ".*txid: \\1 scanId: \\2 \\Qnext()-> r2 f1 q2  v4\\E";
+    pattern +=
+        ".*txid: \\1 scanId: ([0-9a-f]+) \\Qscanner().over([r1   ,r1\\x00   )).fetch([]).build()\\E";
+    pattern += ".*txid: \\1 scanId: \\3 \\Qnext()-> r1 f1 q1  v1\\E";
+    pattern += ".*txid: \\1 scanId: \\3 \\Qnext()-> r1 f1 q2  v2\\E";
+    pattern +=
+        ".*txid: \\1 scanId: ([0-9a-f]+) \\Qscanner().over([r1   ,r1\\x00   )).fetch([f1 q1 ]).build()\\E";
+    pattern += ".*txid: \\1 scanId: \\4 \\Qnext()-> r1 f1 q1  v1\\E";
+    pattern +=
+        ".*txid: \\1 scanId: ([0-9a-f]+) \\Qscanner().over((-inf,+inf)).fetch([f1 q1 ]).build()\\E";
+    pattern += ".*txid: \\1 scanId: \\5 \\Qnext()-> r1 f1 q1  v1\\E";
+    pattern += ".*txid: \\1 scanId: \\5 \\Qnext()-> r2 f1 q1  v3\\E";
+    pattern += ".*txid: \\1 \\Qclose()\\E";
+    pattern += ".*txid: (\\d+) begin\\(\\) thread: \\d+";
+    pattern +=
+        ".*txid: \\6 scanId: ([0-9a-f]+) \\Qscanner().over((-inf,+inf)).fetch([]).byRow().build()\\E";
+    pattern += ".*txid: \\6 scanId: \\7 \\Qnext()-> r1 f1 q1  v1\\E";
+    pattern += ".*txid: \\6 scanId: \\7 \\Qnext()-> r1 f1 q2  v2\\E";
+    pattern += ".*txid: \\6 scanId: \\7 \\Qnext()-> r2 f1 q1  v3\\E";
+    pattern += ".*txid: \\6 scanId: \\7 \\Qnext()-> r2 f1 q2  v4\\E";
+    pattern +=
+        ".*txid: \\6 scanId: ([0-9a-f]+) \\Qscanner().over([r1   ,r1\\x00   )).fetch([]).byRow().build()\\E";
+    pattern += ".*txid: \\6 scanId: \\8 \\Qnext()-> r1 f1 q1  v1\\E";
+    pattern += ".*txid: \\6 scanId: \\8 \\Qnext()-> r1 f1 q2  v2\\E";
+    pattern +=
+        ".*txid: \\6 scanId: ([0-9a-f]+) \\Qscanner().over([r1   ,r1\\x00   )).fetch([f1 q1 ]).byRow().build()\\E";
+    pattern += ".*txid: \\6 scanId: \\9 \\Qnext()-> r1 f1 q1  v1\\E";
+    pattern +=
+        ".*txid: \\6 scanId: ([0-9a-f]+) \\Qscanner().over((-inf,+inf)).fetch([f1 q1 ]).byRow().build()\\E";
+    pattern += ".*txid: \\6 scanId: \\10 \\Qnext()-> r1 f1 q1  v1\\E";
+    pattern += ".*txid: \\6 scanId: \\10 \\Qnext()-> r2 f1 q1  v3\\E";
+    pattern += ".*txid: \\6 \\Qclose()\\E.*";
+
+    Assert.assertTrue(origLogMsgs, logMsgs.matches(pattern));
+
+  }
 }