sijie closed pull request #868: ISSUE #863: BK client error messages need to be 
more descriptive
URL: https://github.com/apache/bookkeeper/pull/868
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/LedgerDescriptorImpl.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/LedgerDescriptorImpl.java
index c327afe5a..0649bc56a 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/LedgerDescriptorImpl.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/LedgerDescriptorImpl.java
@@ -26,6 +26,7 @@
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.bookkeeper.client.api.BKException;
 import org.apache.bookkeeper.common.util.Watcher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -122,7 +123,10 @@ ByteBuf getExplicitLac() {
         }
         ByteBuf entry = createLedgerFenceEntry(ledgerId);
         journal.logAddEntry(entry, (rc, ledgerId, entryId, addr, ctx) -> {
-            LOG.debug("Record fenced state for ledger {} in journal with rc 
{}", ledgerId, rc);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Record fenced state for ledger {} in journal with 
rc {}",
+                        ledgerId, BKException.codeLogger(rc));
+            }
             if (rc == 0) {
                 fenceEntryPersisted.compareAndSet(false, true);
                 result.set(true);
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeperAdmin.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeperAdmin.java
index 0d6321f80..85e858167 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeperAdmin.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeperAdmin.java
@@ -545,7 +545,7 @@ public void recoverBookieData(final 
Set<BookieSocketAddress> bookiesSrc, boolean
         asyncRecoverBookieData(bookiesSrc, dryrun, skipOpenLedgers, new 
RecoverCallback() {
             @Override
             public void recoverComplete(int rc, Object ctx) {
-                LOG.info("Recover bookie operation completed with rc: " + rc);
+                LOG.info("Recover bookie operation completed with rc: {}", 
BKException.codeLogger(rc));
                 SyncObject syncObj = (SyncObject) ctx;
                 synchronized (syncObj) {
                     syncObj.rc = rc;
@@ -574,7 +574,7 @@ public void recoverBookieData(final long lid,
         SyncObject sync = new SyncObject();
         // Call the async method to recover bookie data.
         asyncRecoverBookieData(lid, bookiesSrc, dryrun, skipOpenLedgers, (rc, 
ctx) -> {
-            LOG.info("Recover bookie for {} completed with rc : {}", lid, rc);
+            LOG.info("Recover bookie for {} completed with rc : {}", lid, 
BKException.codeLogger(rc));
             SyncObject syncObject = (SyncObject) ctx;
             synchronized (syncObject) {
                 syncObject.rc = rc;
@@ -780,7 +780,7 @@ public void openComplete(int newrc, final LedgerHandle 
newlh, Object newctx) {
                     @Override
                     public void processResult(int rc, String path, Object ctx) 
{
                         if (BKException.Code.OK != rc) {
-                            LOG.error("Failed to recover ledger {} : {}", lId, 
rc);
+                            LOG.error("Failed to recover ledger {} : {}", lId, 
BKException.codeLogger(rc));
                         } else {
                             LOG.info("Recovered ledger {}.", lId);
                         }
@@ -789,7 +789,7 @@ public void processResult(int rc, String path, Object ctx) {
                         } catch (InterruptedException ie) {
                             Thread.currentThread().interrupt();
                         } catch (BKException bke) {
-                            LOG.warn("Error on cloing ledger handle for {}.", 
lId);
+                            LOG.warn("Error on closing ledger handle for {}.", 
lId);
                         }
                         finalLedgerIterCb.processResult(rc, path, ctx);
                     }
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookieInfoReader.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookieInfoReader.java
index 66926b468..7147d6c2c 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookieInfoReader.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookieInfoReader.java
@@ -339,7 +339,8 @@ void processReadInfoComplete(int rc, BookieInfo bInfo, 
Object ctx) {
                                 BookieSocketAddress b = (BookieSocketAddress) 
ctx;
                                 if (rc != BKException.Code.OK) {
                                     if (LOG.isErrorEnabled()) {
-                                        LOG.error("Reading bookie info from 
bookie {} failed due to error: {}.", b, rc);
+                                        LOG.error("Reading bookie info from 
bookie {} failed due to {}",
+                                                b, BKException.codeLogger(rc));
                                     }
                                     // We reread bookies missing from the map 
each time, so remove to ensure
                                     // we get to it on the next scan
@@ -413,7 +414,8 @@ public void getBookieInfoComplete(int rc, BookieInfo bInfo, 
Object ctx) {
                             BookieSocketAddress b = (BookieSocketAddress) ctx;
                             if (rc != BKException.Code.OK) {
                                 if (LOG.isErrorEnabled()) {
-                                    LOG.error("Reading bookie info from bookie 
{} failed due to error: {}.", b, rc);
+                                    LOG.error("Reading bookie info from bookie 
{} failed due to {}",
+                                            b, BKException.codeLogger(rc));
                                 }
                             } else {
                                 if (LOG.isDebugEnabled()) {
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerFragmentReplicator.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerFragmentReplicator.java
index b1d2b4408..31d8e21ef 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerFragmentReplicator.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerFragmentReplicator.java
@@ -438,8 +438,8 @@ public String toString() {
                         });
                 return;
             } else if (rc != BKException.Code.OK) {
-                LOG.error("Error updating ledger config metadata for ledgerId "
-                        + lh.getId() + " : " + BKException.getMessage(rc));
+                LOG.error("Error updating ledger config metadata for ledgerId 
{} : {}",
+                        lh.getId(), BKException.codeLogger(rc));
             } else {
                 LOG.info("Updated ZK for ledgerId: (" + lh.getId() + " : "
                         + fragmentStartId
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerHandle.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerHandle.java
index 519f7978a..32eeab812 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerHandle.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerHandle.java
@@ -483,8 +483,8 @@ public void safeOperationComplete(final int rc, Void 
result) {
                                 @Override
                                 public void safeOperationComplete(int newrc, 
LedgerMetadata newMeta) {
                                     if (newrc != BKException.Code.OK) {
-                                        LOG.error("Error reading new metadata 
from ledger {} when closing, code={}",
-                                                ledgerId, newrc);
+                                        LOG.error("Error reading new metadata 
from ledger {} when closing: {}",
+                                                ledgerId, 
BKException.codeLogger(newrc));
                                         cb.closeComplete(rc, 
LedgerHandle.this, ctx);
                                     } else {
                                         metadata.setState(prevState);
@@ -519,7 +519,8 @@ public String toString() {
                                 }
                             });
                         } else if (rc != BKException.Code.OK) {
-                            LOG.error("Error update ledger metadata for ledger 
{} : {}", ledgerId, rc);
+                            LOG.error("Error update ledger metadata for ledger 
{} : {}",
+                                    ledgerId, BKException.codeLogger(rc));
                             cb.closeComplete(rc, LedgerHandle.this, ctx);
                         } else {
                             cb.closeComplete(BKException.Code.OK, 
LedgerHandle.this, ctx);
@@ -1394,7 +1395,7 @@ void handleUnrecoverableErrorDuringAdd(int rc) {
             errorOutPendingAdds(rc);
             return;
         }
-        LOG.error("Closing ledger {} due to error {}", ledgerId, rc);
+        LOG.error("Closing ledger {} due to {}", ledgerId, 
BKException.codeLogger(rc));
         asyncCloseInternal(NoopCloseCallback.instance, null, rc);
     }
 
@@ -1657,14 +1658,15 @@ public String toString() {
         @Override
         public void safeOperationComplete(int newrc, LedgerMetadata newMeta) {
             if (newrc != BKException.Code.OK) {
-                LOG.error("[EnsembleChange-L{}-{}] : error re-reading metadata 
to address ensemble change conflicts,"
-                        + " code=", ledgerId, ensembleChangeIdx, newrc);
+                LOG.error("[EnsembleChange-L{}-{}] : error re-reading metadata 
"
+                                + "to address ensemble change conflicts: {}",
+                        ledgerId, ensembleChangeIdx, 
BKException.codeLogger(newrc));
                 handleUnrecoverableErrorDuringAdd(rc);
             } else {
                 if (!resolveConflict(newMeta)) {
                     LOG.error("[EnsembleChange-L{}-{}] : could not resolve 
ledger metadata conflict"
-                            + " while changing ensemble to: {}, local meta 
data is \n {} \n,"
-                            + " zk meta data is \n {} \n, closing ledger",
+                                    + " while changing ensemble to: {}, local 
meta data is \n {} \n,"
+                                    + " zk meta data is \n {} \n, closing 
ledger",
                             ledgerId, ensembleChangeIdx, 
ensembleInfo.newEnsemble, metadata, newMeta);
                     handleUnrecoverableErrorDuringAdd(rc);
                 }
@@ -1921,7 +1923,7 @@ public String toString() {
                         .setEntryListener(listener)
                         .initiate();
                 } else {
-                    LOG.error("Error writing ledger config {} of ledger {}", 
rc, ledgerId);
+                    LOG.error("Error writing ledger {} config: {}", ledgerId, 
BKException.codeLogger(rc));
                     cb.operationComplete(rc, null);
                 }
             }
@@ -1939,7 +1941,7 @@ public String toString() {
         @Override
         public void closeComplete(int rc, LedgerHandle lh, Object ctx) {
             if (rc != BKException.Code.OK) {
-                LOG.warn("Close failed: " + BKException.getMessage(rc));
+                LOG.warn("Close failed: {}", BKException.codeLogger(rc));
             }
             // noop
         }
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerRecoveryOp.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerRecoveryOp.java
index 4be5fef50..6ea21b61e 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerRecoveryOp.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerRecoveryOp.java
@@ -240,8 +240,8 @@ public void onEntryComplete(int rc, LedgerHandle lh, 
LedgerEntry entry, Object c
     @Override
     public void addComplete(int rc, LedgerHandle lh, long entryId, Object ctx) 
{
         if (rc != BKException.Code.OK) {
-            LOG.error("Failure " + BKException.getMessage(rc) + " while 
writing entry: " + (entryId + 1)
-                      + " ledger: " + lh.ledgerId + " while recovering 
ledger");
+            LOG.error("Failure {} while writing entry: {} while recovering 
ledger: {}",
+                    BKException.codeLogger(rc), entryId + 1, lh.ledgerId);
             if (callbackDone.compareAndSet(false, true)) {
                 // Give up, we can't recover from this error
                 submitCallback(rc);
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/UpdateLedgerOp.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/UpdateLedgerOp.java
index 8199e675f..23d6be2bd 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/UpdateLedgerOp.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/UpdateLedgerOp.java
@@ -218,7 +218,7 @@ public void operationComplete(int rc, LedgerMetadata 
metadata) {
                 return; // this is OK
             } else if (BKException.Code.OK != rc) {
                 // open ledger failed.
-                LOG.error("Get ledger metadata {} failed. Error code {}", 
ledgerId, rc);
+                LOG.error("Get ledger metadata {} failed: {}", ledgerId, 
BKException.codeLogger(rc));
                 future.setException(BKException.create(rc));
                 return;
             }
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/api/BKException.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/api/BKException.java
index 07bcf0fae..825088b0c 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/api/BKException.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/api/BKException.java
@@ -15,6 +15,8 @@
  */
 package org.apache.bookkeeper.client.api;
 
+import java.lang.reflect.Field;
+
 import org.apache.bookkeeper.client.LedgerHandleAdv;
 import org.apache.bookkeeper.common.annotation.InterfaceAudience.Public;
 import org.apache.bookkeeper.common.annotation.InterfaceStability.Unstable;
@@ -29,6 +31,8 @@
 public abstract class BKException extends Exception {
     protected final int code;
 
+    private static final LogMessagePool logMessagePool = new LogMessagePool();
+
     /**
      * Create a new exception.
      *
@@ -52,10 +56,21 @@ public final int getCode() {
         return this.code;
     }
 
+    /**
+     * Returns a lazy error code formatter suitable to pass to log functions.
+     *
+     * @param code the error code value
+     *
+     * @return lazy error code log formatter
+     */
+    public static Object codeLogger(int code) {
+        return logMessagePool.get(code);
+    }
+
     /**
      * Describe an error code.
      *
-     * @param code
+     * @param code the error code value
      *
      * @return the description of the error code
      */
@@ -131,7 +146,7 @@ public static String getMessage(int code) {
     }
 
     /**
-     * Codes which represent the various exceptoin types.
+     * Codes which represent the various exception types.
      */
     public interface Code {
         /** A placer holder (unused). */
@@ -240,4 +255,78 @@ public static String getMessage(int code) {
         int UnexpectedConditionException = -999;
     }
 
+    /**
+     * Code log message pool.
+     */
+    private static class LogMessagePool {
+        private final int minCode;
+        private final String[] pool;
+
+        private LogMessagePool() {
+            Field[] fields = Code.class.getDeclaredFields();
+            this.minCode = minCode(fields);
+            this.pool = new String[-minCode + 2]; // 
UnexpectedConditionException is an outlier
+            initPoolMessages(fields);
+        }
+
+        private int minCode(Field[] fields) {
+            int min = 0;
+            for (Field field : fields) {
+                int code = getFieldInt(field);
+                if (code < min && code > Code.UnexpectedConditionException) {
+                    min = code;
+                }
+            }
+            return min;
+        }
+
+        private void initPoolMessages(Field[] fields) {
+            for (Field field : fields) {
+                int code = getFieldInt(field);
+                int index = poolIndex(code);
+                if (index >= 0) {
+                    pool[index] = String.format("%s: %s", field.getName(), 
getMessage(code));
+                }
+            }
+        }
+
+        private static int getFieldInt(Field field) {
+            try {
+                return field.getInt(null);
+            } catch (IllegalAccessException e) {
+                return -1;
+            }
+        }
+
+        private Object get(int code) {
+            int index = poolIndex(code);
+            String logMessage = index >= 0 ? pool[index] : null;
+            return logMessage != null ? logMessage : new 
UnrecognizedCodeLogFormatter(code);
+        }
+
+        private int poolIndex(int code) {
+            switch (code) {
+            case Code.UnexpectedConditionException:
+                return -minCode + 1;
+            default:
+                return code <= 0 && code >= minCode ? -minCode + code : -1;
+            }
+        }
+
+        /**
+         * Unrecognized code lazy log message formatter.
+         */
+        private static class UnrecognizedCodeLogFormatter {
+            private final int code;
+
+            private UnrecognizedCodeLogFormatter(int code) {
+                this.code = code;
+            }
+
+            @Override
+            public String toString() {
+                return String.format("%d: %s", code, getMessage(code));
+            }
+        }
+    }
 }
diff --git 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/api/BookKeeperApiTest.java
 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/api/BookKeeperApiTest.java
index 562ecbc59..9a3712223 100644
--- 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/api/BookKeeperApiTest.java
+++ 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/api/BookKeeperApiTest.java
@@ -22,14 +22,19 @@
 
 import static com.google.common.base.Charsets.UTF_8;
 import static org.apache.bookkeeper.common.concurrent.FutureUtils.result;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.hasProperty;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 import io.netty.buffer.Unpooled;
 import java.nio.ByteBuffer;
 import java.util.Iterator;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicLong;
 import org.apache.bookkeeper.client.BKException;
 import org.apache.bookkeeper.client.BKException.BKDigestMatchException;
@@ -38,7 +43,10 @@
 import org.apache.bookkeeper.client.BKException.BKNoSuchLedgerExistsException;
 import org.apache.bookkeeper.client.BKException.BKUnauthorizedAccessException;
 import org.apache.bookkeeper.client.MockBookKeeperTestCase;
+import org.apache.bookkeeper.util.LoggerOutput;
+import org.junit.Rule;
 import org.junit.Test;
+import org.slf4j.event.LoggingEvent;
 
 /**
  * Unit tests of classes in this package.
@@ -48,6 +56,9 @@
     private static final byte[] data = "foo".getBytes(UTF_8);
     private static final byte[] password = "password".getBytes(UTF_8);
 
+    @Rule
+    public LoggerOutput loggerOutput = new LoggerOutput();
+
     @Test
     public void testWriteHandle() throws Exception {
         try (WriteHandle writer = result(newCreateLedgerOp()
@@ -235,6 +246,14 @@ public void testOpenLedgerRead() throws Exception {
 
     @Test(expected = BKLedgerFencedException.class)
     public void testOpenLedgerWithRecovery() throws Exception {
+
+        loggerOutput.expect((List<LoggingEvent> logEvents) -> {
+            assertThat(logEvents, hasItem(hasProperty("message",
+                    containsString("due to LedgerFencedException: "
+                            + "Ledger has been fenced off. Some other client 
must have opened it to read")
+            )));
+        });
+
         long lId;
         try (WriteHandle writer = result(newCreateLedgerOp()
             .withAckQuorumSize(1)
@@ -250,10 +269,10 @@ public void testOpenLedgerWithRecovery() throws Exception 
{
 
             // open with fencing
             try (ReadHandle reader = result(newOpenLedgerOp()
-                .withPassword(password)
-                .withRecovery(true)
-                .withLedgerId(lId)
-                .execute())) {
+                    .withPassword(password)
+                    .withRecovery(true)
+                    .withLedgerId(lId)
+                    .execute())) {
                 assertTrue(reader.isClosed());
                 assertEquals(1L, reader.getLastAddConfirmed());
             }
@@ -335,6 +354,22 @@ public void testLedgerEntriesIterable() throws Exception {
         }
     }
 
+    @Test
+    public void testBKExceptionCodeLogger() {
+        assertEquals("OK: No problem", BKException.codeLogger(0).toString());
+        assertEquals("ReadException: Error while reading ledger", 
BKException.codeLogger(-1).toString());
+        assertEquals("IncorrectParameterException: Incorrect parameter input", 
BKException.codeLogger(-14).toString());
+        assertEquals("LedgerFencedException: Ledger has been fenced off. Some 
other client must have opened it to read",
+                BKException.codeLogger(-101).toString());
+        assertEquals("ReplicationException: Errors in replication pipeline", 
BKException.codeLogger(-200).toString());
+
+        assertEquals("UnexpectedConditionException: Unexpected condition", 
BKException.codeLogger(-999).toString());
+
+        assertEquals("1: Unexpected condition", 
BKException.codeLogger(1).toString());
+        assertEquals("123: Unexpected condition", 
BKException.codeLogger(123).toString());
+        assertEquals("-201: Unexpected condition", 
BKException.codeLogger(-201).toString());
+    }
+
     private static void checkEntries(LedgerEntries entries, byte[] data)
         throws InterruptedException, BKException {
         Iterator<LedgerEntry> iterator = entries.iterator();
diff --git 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/util/LoggerOutput.java 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/util/LoggerOutput.java
new file mode 100644
index 000000000..eeb161e9f
--- /dev/null
+++ 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/util/LoggerOutput.java
@@ -0,0 +1,140 @@
+/*
+ *
+ * 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.bookkeeper.util;
+
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import org.apache.log4j.Appender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.mockito.ArgumentCaptor;
+import org.slf4j.Marker;
+import org.slf4j.event.Level;
+import org.slf4j.event.LoggingEvent;
+
+/**
+ * A utility class for testing logger output.
+ */
+public class LoggerOutput implements TestRule {
+
+    private Appender logAppender;
+    private ArgumentCaptor<org.apache.log4j.spi.LoggingEvent> logEventCaptor;
+    private List<Consumer<List<LoggingEvent>>> logEventExpectations = new 
ArrayList<>();
+
+    public void expect(Consumer<List<LoggingEvent>> expectation) {
+        if (logEventCaptor == null) {
+            logEventCaptor = 
ArgumentCaptor.forClass(org.apache.log4j.spi.LoggingEvent.class);
+        }
+        logEventExpectations.add(expectation);
+    }
+
+    @Override
+    public Statement apply(final Statement base, Description description) {
+        return new Statement() {
+
+            @Override
+            public void evaluate() throws Throwable {
+                logAppender = mock(Appender.class);
+                Logger rootLogger = LogManager.getRootLogger();
+                rootLogger.addAppender(logAppender);
+                try {
+                    base.evaluate();
+                    if (!logEventExpectations.isEmpty()) {
+                        verify(logAppender, 
atLeastOnce()).doAppend(logEventCaptor.capture());
+                        List<LoggingEvent> logEvents = 
logEventCaptor.getAllValues().stream()
+                                .map(LoggerOutput::toSlf4j)
+                                .collect(Collectors.toList());
+                        for (Consumer<List<LoggingEvent>> expectation : 
logEventExpectations) {
+                            expectation.accept(logEvents);
+                        }
+                    }
+                } finally {
+                    rootLogger.removeAppender(logAppender);
+                    logEventExpectations.clear();
+                    logEventCaptor = null;
+                }
+            }
+        };
+    }
+
+    private static LoggingEvent toSlf4j(org.apache.log4j.spi.LoggingEvent 
log4jEvent) {
+        return new LoggingEvent() {
+            @Override
+            public Level getLevel() {
+                switch (log4jEvent.getLevel().toString()) {
+                    case "FATAL":
+                    case "ERROR": return Level.ERROR;
+                    case "WARN": return Level.WARN;
+                    case "INFO": return Level.INFO;
+                    case "DEBUG": return Level.DEBUG;
+                    case "TRACE":
+                    case "ALL":
+                    case "OFF":
+                    default: return Level.TRACE;
+                }
+            }
+
+            @Override
+            public Marker getMarker() {
+                return null;
+            }
+
+            @Override
+            public String getLoggerName() {
+                return log4jEvent.getLoggerName();
+            }
+
+            @Override
+            public String getMessage() {
+                return log4jEvent.getRenderedMessage();
+            }
+
+            @Override
+            public String getThreadName() {
+                return log4jEvent.getThreadName();
+            }
+
+            @Override
+            public Object[] getArgumentArray() {
+                return new Object[0];
+            }
+
+            @Override
+            public long getTimeStamp() {
+                return log4jEvent.getTimeStamp();
+            }
+
+            @Override
+            public Throwable getThrowable() {
+                return null;
+            }
+        };
+    }
+}


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

Reply via email to