You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bookkeeper.apache.org by si...@apache.org on 2018/12/12 01:23:12 UTC

[bookkeeper] branch master updated: [STATS] [DOC] Add @StatsDoc annotation for bookkeeper client stats

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

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


The following commit(s) were added to refs/heads/master by this push:
     new f4bbc21  [STATS] [DOC] Add @StatsDoc annotation for bookkeeper client stats
f4bbc21 is described below

commit f4bbc21d39a8af5947fafecb2a52febf8b478b9e
Author: Sijie Guo <gu...@gmail.com>
AuthorDate: Wed Dec 12 09:23:07 2018 +0800

    [STATS] [DOC] Add @StatsDoc annotation for bookkeeper client stats
    
    
    
    Descriptions of the changes in this PR:
    
    *Motivation*
    
    As part of [BP-36](https://github.com/apache/bookkeeper/issues/1785), this PR is to document bookkeeper client stats.
    
    *Changes*
    
    - convert bookkeeper client stats to use StatsDoc for documenting metrics
    
    Master Issue: #1785
    
    
    
    
    Reviewers: Enrico Olivelli <eo...@gmail.com>
    
    This closes #1878 from sijie/client_stats
---
 .../bookkeeper/client/BookKeeperClientStats.java   | 117 +---------
 .../bookkeeper/client/BookieWatcherImpl.java       |  16 ++
 .../client/LedgerFragmentReplicator.java           |  35 ++-
 .../RackawareEnsemblePlacementPolicyImpl.java      |  28 ++-
 .../client/impl/BookKeeperClientStatsImpl.java     | 259 +++++++++++++++++++++
 .../bookkeeper/proto/PerChannelBookieClient.java   |  97 ++++++++
 6 files changed, 430 insertions(+), 122 deletions(-)

diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeperClientStats.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeperClientStats.java
index 2fa6753..8b358b6 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeperClientStats.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeperClientStats.java
@@ -21,6 +21,7 @@
 
 package org.apache.bookkeeper.client;
 
+import org.apache.bookkeeper.client.impl.BookKeeperClientStatsImpl;
 import org.apache.bookkeeper.stats.Counter;
 import org.apache.bookkeeper.stats.Gauge;
 import org.apache.bookkeeper.stats.OpStatsLogger;
@@ -30,6 +31,8 @@ import org.apache.bookkeeper.stats.StatsLogger;
  * List of constants for defining client stats names.
  */
 public interface BookKeeperClientStats {
+    String CATEGORY_CLIENT = "client";
+
     String CLIENT_SCOPE = "bookkeeper_client";
 
     // Metadata Operations
@@ -114,119 +117,7 @@ public interface BookKeeperClientStats {
     void registerPendingAddsGauge(Gauge<Integer> gauge);
 
     static BookKeeperClientStats newInstance(StatsLogger stats) {
-        OpStatsLogger createOpLogger = stats.getOpStatsLogger(CREATE_OP);
-        OpStatsLogger deleteOpLogger = stats.getOpStatsLogger(DELETE_OP);
-        OpStatsLogger openOpLogger = stats.getOpStatsLogger(OPEN_OP);
-        OpStatsLogger recoverOpLogger = stats.getOpStatsLogger(RECOVER_OP);
-        OpStatsLogger readOpLogger = stats.getOpStatsLogger(READ_OP);
-        Counter readOpDmCounter = stats.getCounter(READ_OP_DM);
-        OpStatsLogger readLacAndEntryOpLogger = stats.getOpStatsLogger(READ_LAST_CONFIRMED_AND_ENTRY);
-        OpStatsLogger readLacAndEntryRespLogger = stats.getOpStatsLogger(READ_LAST_CONFIRMED_AND_ENTRY_RESPONSE);
-        OpStatsLogger addOpLogger = stats.getOpStatsLogger(ADD_OP);
-        OpStatsLogger forceOpLogger = stats.getOpStatsLogger(FORCE_OP);
-        Counter addOpUrCounter = stats.getCounter(ADD_OP_UR);
-        OpStatsLogger writeLacOpLogger = stats.getOpStatsLogger(WRITE_LAC_OP);
-        OpStatsLogger readLacOpLogger = stats.getOpStatsLogger(READ_LAC_OP);
-        OpStatsLogger recoverAddEntriesStats = stats.getOpStatsLogger(LEDGER_RECOVER_ADD_ENTRIES);
-        OpStatsLogger recoverReadEntriesStats = stats.getOpStatsLogger(LEDGER_RECOVER_READ_ENTRIES);
-
-        Counter ensembleChangeCounter = stats.getCounter(ENSEMBLE_CHANGES);
-        Counter lacUpdateHitsCounter = stats.getCounter(LAC_UPDATE_HITS);
-        Counter lacUpdateMissesCounter = stats.getCounter(LAC_UPDATE_MISSES);
-        OpStatsLogger clientChannelWriteWaitStats = stats.getOpStatsLogger(CLIENT_CHANNEL_WRITE_WAIT);
-
-        Counter speculativeReadCounter = stats.getCounter(SPECULATIVE_READ_COUNT);
-
-        return new BookKeeperClientStats() {
-            @Override
-            public OpStatsLogger getCreateOpLogger() {
-                return createOpLogger;
-            }
-            @Override
-            public OpStatsLogger getOpenOpLogger() {
-                return openOpLogger;
-            }
-            @Override
-            public OpStatsLogger getDeleteOpLogger() {
-                return deleteOpLogger;
-            }
-            @Override
-            public OpStatsLogger getRecoverOpLogger() {
-                return recoverOpLogger;
-            }
-            @Override
-            public OpStatsLogger getReadOpLogger() {
-                return readOpLogger;
-            }
-            @Override
-            public OpStatsLogger getReadLacAndEntryOpLogger() {
-                return readLacAndEntryOpLogger;
-            }
-            @Override
-            public OpStatsLogger getReadLacAndEntryRespLogger() {
-                return readLacAndEntryRespLogger;
-            }
-            @Override
-            public OpStatsLogger getAddOpLogger() {
-                return addOpLogger;
-            }
-            @Override
-            public OpStatsLogger getForceOpLogger() {
-                return forceOpLogger;
-            }
-            @Override
-            public OpStatsLogger getWriteLacOpLogger() {
-                return writeLacOpLogger;
-            }
-            @Override
-            public OpStatsLogger getReadLacOpLogger() {
-                return readLacOpLogger;
-            }
-            @Override
-            public OpStatsLogger getRecoverAddCountLogger() {
-                return recoverAddEntriesStats;
-            }
-            @Override
-            public OpStatsLogger getRecoverReadCountLogger() {
-                return recoverReadEntriesStats;
-            }
-            @Override
-            public Counter getReadOpDmCounter() {
-                return readOpDmCounter;
-            }
-            @Override
-            public Counter getAddOpUrCounter() {
-                return addOpUrCounter;
-            }
-            @Override
-            public Counter getSpeculativeReadCounter() {
-                return speculativeReadCounter;
-            }
-            @Override
-            public Counter getEnsembleChangeCounter() {
-                return ensembleChangeCounter;
-            }
-            @Override
-            public Counter getLacUpdateHitsCounter() {
-                return lacUpdateHitsCounter;
-            }
-            @Override
-            public Counter getLacUpdateMissesCounter() {
-                return lacUpdateMissesCounter;
-            }
-            @Override
-            public OpStatsLogger getClientChannelWriteWaitLogger() {
-                return clientChannelWriteWaitStats;
-            }
-            @Override
-            public Counter getEnsembleBookieDistributionCounter(String bookie) {
-                return stats.getCounter(LEDGER_ENSEMBLE_BOOKIE_DISTRIBUTION + "-" + bookie);
-            }
-            @Override
-            public void registerPendingAddsGauge(Gauge<Integer> gauge) {
-                stats.registerGauge(PENDING_ADDS, gauge);
-            }
-        };
+        return new BookKeeperClientStatsImpl(stats);
     }
 
 }
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookieWatcherImpl.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookieWatcherImpl.java
index 2941a41..8b14b77 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookieWatcherImpl.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookieWatcherImpl.java
@@ -19,6 +19,8 @@ package org.apache.bookkeeper.client;
 
 import static org.apache.bookkeeper.bookie.BookKeeperServerStats.NEW_ENSEMBLE_TIME;
 import static org.apache.bookkeeper.bookie.BookKeeperServerStats.REPLACE_BOOKIE_TIME;
+import static org.apache.bookkeeper.bookie.BookKeeperServerStats.WATCHER_SCOPE;
+import static org.apache.bookkeeper.client.BookKeeperClientStats.CREATE_OP;
 
 import com.google.common.cache.Cache;
 import com.google.common.cache.CacheBuilder;
@@ -43,6 +45,7 @@ import org.apache.bookkeeper.discover.RegistrationClient;
 import org.apache.bookkeeper.net.BookieSocketAddress;
 import org.apache.bookkeeper.stats.OpStatsLogger;
 import org.apache.bookkeeper.stats.StatsLogger;
+import org.apache.bookkeeper.stats.annotations.StatsDoc;
 
 /**
  * This class is responsible for maintaining a consistent view of what bookies
@@ -51,6 +54,10 @@ import org.apache.bookkeeper.stats.StatsLogger;
  * replacement
  *
  */
+@StatsDoc(
+    name = WATCHER_SCOPE,
+    help = "Bookie watcher related stats"
+)
 @Slf4j
 class BookieWatcherImpl implements BookieWatcher {
 
@@ -70,7 +77,16 @@ class BookieWatcherImpl implements BookieWatcher {
     private final ClientConfiguration conf;
     private final RegistrationClient registrationClient;
     private final EnsemblePlacementPolicy placementPolicy;
+    @StatsDoc(
+        name = NEW_ENSEMBLE_TIME,
+        help = "operation stats of new ensembles",
+        parent = CREATE_OP
+    )
     private final OpStatsLogger newEnsembleTimer;
+    @StatsDoc(
+        name = REPLACE_BOOKIE_TIME,
+        help = "operation stats of replacing bookie in an ensemble"
+    )
     private final OpStatsLogger replaceBookieTimer;
 
     // Bookies that will not be preferred to be chosen in a new ensemble
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 28929cf..765dec0 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
@@ -20,6 +20,11 @@
 package org.apache.bookkeeper.client;
 
 import static org.apache.bookkeeper.client.LedgerHandle.INVALID_ENTRY_ID;
+import static org.apache.bookkeeper.replication.ReplicationStats.NUM_BYTES_READ;
+import static org.apache.bookkeeper.replication.ReplicationStats.NUM_BYTES_WRITTEN;
+import static org.apache.bookkeeper.replication.ReplicationStats.NUM_ENTRIES_READ;
+import static org.apache.bookkeeper.replication.ReplicationStats.NUM_ENTRIES_WRITTEN;
+import static org.apache.bookkeeper.replication.ReplicationStats.REPLICATION_WORKER_SCOPE;
 
 import io.netty.buffer.Unpooled;
 
@@ -41,11 +46,11 @@ import org.apache.bookkeeper.net.BookieSocketAddress;
 import org.apache.bookkeeper.proto.BookieProtocol;
 import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.MultiCallback;
 import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteCallback;
-import org.apache.bookkeeper.replication.ReplicationStats;
 import org.apache.bookkeeper.stats.Counter;
 import org.apache.bookkeeper.stats.NullStatsLogger;
 import org.apache.bookkeeper.stats.OpStatsLogger;
 import org.apache.bookkeeper.stats.StatsLogger;
+import org.apache.bookkeeper.stats.annotations.StatsDoc;
 import org.apache.bookkeeper.util.ByteBufList;
 import org.apache.zookeeper.AsyncCallback;
 import org.slf4j.Logger;
@@ -55,23 +60,43 @@ import org.slf4j.LoggerFactory;
  * This is the helper class for replicating the fragments from one bookie to
  * another.
  */
+@StatsDoc(
+    name = REPLICATION_WORKER_SCOPE,
+    help = "Ledger fragment replicator related stats"
+)
 public class LedgerFragmentReplicator {
 
     // BookKeeper instance
     private BookKeeper bkc;
     private StatsLogger statsLogger;
+    @StatsDoc(
+        name = NUM_ENTRIES_READ,
+        help = "Number of entries read by the replicator"
+    )
     private final Counter numEntriesRead;
+    @StatsDoc(
+        name = NUM_BYTES_READ,
+        help = "The distribution of size of entries read by the replicator"
+    )
     private final OpStatsLogger numBytesRead;
+    @StatsDoc(
+        name = NUM_ENTRIES_WRITTEN,
+        help = "Number of entries written by the replicator"
+    )
     private final Counter numEntriesWritten;
+    @StatsDoc(
+        name = NUM_BYTES_WRITTEN,
+        help = "The distribution of size of entries written by the replicator"
+    )
     private final OpStatsLogger numBytesWritten;
 
     public LedgerFragmentReplicator(BookKeeper bkc, StatsLogger statsLogger) {
         this.bkc = bkc;
         this.statsLogger = statsLogger;
-        numEntriesRead = this.statsLogger.getCounter(ReplicationStats.NUM_ENTRIES_READ);
-        numBytesRead = this.statsLogger.getOpStatsLogger(ReplicationStats.NUM_BYTES_READ);
-        numEntriesWritten = this.statsLogger.getCounter(ReplicationStats.NUM_ENTRIES_WRITTEN);
-        numBytesWritten = this.statsLogger.getOpStatsLogger(ReplicationStats.NUM_BYTES_WRITTEN);
+        numEntriesRead = this.statsLogger.getCounter(NUM_ENTRIES_READ);
+        numBytesRead = this.statsLogger.getOpStatsLogger(NUM_BYTES_READ);
+        numEntriesWritten = this.statsLogger.getCounter(NUM_ENTRIES_WRITTEN);
+        numBytesWritten = this.statsLogger.getOpStatsLogger(NUM_BYTES_WRITTEN);
     }
 
     public LedgerFragmentReplicator(BookKeeper bkc) {
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/RackawareEnsemblePlacementPolicyImpl.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/RackawareEnsemblePlacementPolicyImpl.java
index 578d49e..7578c41 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/RackawareEnsemblePlacementPolicyImpl.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/RackawareEnsemblePlacementPolicyImpl.java
@@ -18,6 +18,10 @@
 package org.apache.bookkeeper.client;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.bookkeeper.bookie.BookKeeperServerStats.BOOKIES_JOINED;
+import static org.apache.bookkeeper.bookie.BookKeeperServerStats.BOOKIES_LEFT;
+import static org.apache.bookkeeper.client.BookKeeperClientStats.CLIENT_SCOPE;
+import static org.apache.bookkeeper.client.BookKeeperClientStats.READ_REQUESTS_REORDERED;
 import static org.apache.bookkeeper.client.RegionAwareEnsemblePlacementPolicy.UNKNOWN_REGION;
 
 import com.google.common.cache.Cache;
@@ -44,7 +48,6 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.function.Supplier;
 
-import org.apache.bookkeeper.bookie.BookKeeperServerStats;
 import org.apache.bookkeeper.client.BKException.BKNotEnoughBookiesException;
 import org.apache.bookkeeper.client.BookieInfoReader.BookieInfo;
 import org.apache.bookkeeper.client.WeightedRandomSelection.WeightedObject;
@@ -63,6 +66,7 @@ import org.apache.bookkeeper.net.ScriptBasedMapping;
 import org.apache.bookkeeper.net.StabilizeNetworkTopology;
 import org.apache.bookkeeper.stats.OpStatsLogger;
 import org.apache.bookkeeper.stats.StatsLogger;
+import org.apache.bookkeeper.stats.annotations.StatsDoc;
 import org.apache.commons.collections4.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -72,6 +76,10 @@ import org.slf4j.LoggerFactory;
  *
  * <p>Make most of the class and methods as protected, so it could be extended to implement other algorithms.
  */
+@StatsDoc(
+    name = CLIENT_SCOPE,
+    help = "BookKeeper client stats"
+)
 public class RackawareEnsemblePlacementPolicyImpl extends TopologyAwareEnsemblePlacementPolicy {
 
     static final Logger LOG = LoggerFactory.getLogger(RackawareEnsemblePlacementPolicyImpl.class);
@@ -204,8 +212,20 @@ public class RackawareEnsemblePlacementPolicyImpl extends TopologyAwareEnsembleP
     // looks like these only assigned in the same thread as constructor, immediately after constructor;
     // no need to make volatile
     protected StatsLogger statsLogger = null;
+    @StatsDoc(
+        name = BOOKIES_JOINED,
+        help = "The distribution of number of bookies joined the cluster on each network topology change"
+    )
     protected OpStatsLogger bookiesJoinedCounter = null;
+    @StatsDoc(
+        name = BOOKIES_LEFT,
+        help = "The distribution of number of bookies left the cluster on each network topology change"
+    )
     protected OpStatsLogger bookiesLeftCounter = null;
+    @StatsDoc(
+        name = READ_REQUESTS_REORDERED,
+        help = "The distribution of number of bookies reordered on each read request"
+    )
     protected OpStatsLogger readReorderedCounter = null;
 
     private String defaultRack = NetworkTopology.DEFAULT_RACK;
@@ -244,9 +264,9 @@ public class RackawareEnsemblePlacementPolicyImpl extends TopologyAwareEnsembleP
                                                               StatsLogger statsLogger) {
         checkNotNull(statsLogger, "statsLogger should not be null, use NullStatsLogger instead.");
         this.statsLogger = statsLogger;
-        this.bookiesJoinedCounter = statsLogger.getOpStatsLogger(BookKeeperServerStats.BOOKIES_JOINED);
-        this.bookiesLeftCounter = statsLogger.getOpStatsLogger(BookKeeperServerStats.BOOKIES_LEFT);
-        this.readReorderedCounter = statsLogger.getOpStatsLogger(BookKeeperClientStats.READ_REQUESTS_REORDERED);
+        this.bookiesJoinedCounter = statsLogger.getOpStatsLogger(BOOKIES_JOINED);
+        this.bookiesLeftCounter = statsLogger.getOpStatsLogger(BOOKIES_LEFT);
+        this.readReorderedCounter = statsLogger.getOpStatsLogger(READ_REQUESTS_REORDERED);
         this.reorderReadsRandom = reorderReadsRandom;
         this.stabilizePeriodSeconds = stabilizePeriodSeconds;
         this.reorderThresholdPendingRequests = reorderThresholdPendingRequests;
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/impl/BookKeeperClientStatsImpl.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/impl/BookKeeperClientStatsImpl.java
new file mode 100644
index 0000000..a29621a
--- /dev/null
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/impl/BookKeeperClientStatsImpl.java
@@ -0,0 +1,259 @@
+/*
+ * 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.client.impl;
+
+import static org.apache.bookkeeper.client.BookKeeperClientStats.CATEGORY_CLIENT;
+import static org.apache.bookkeeper.client.BookKeeperClientStats.CLIENT_SCOPE;
+
+import org.apache.bookkeeper.client.BookKeeperClientStats;
+import org.apache.bookkeeper.stats.Counter;
+import org.apache.bookkeeper.stats.Gauge;
+import org.apache.bookkeeper.stats.OpStatsLogger;
+import org.apache.bookkeeper.stats.StatsLogger;
+import org.apache.bookkeeper.stats.annotations.StatsDoc;
+
+/**
+ * The default implementation of {@link BookKeeperClientStats}.
+ */
+@StatsDoc(
+    name = CLIENT_SCOPE,
+    category = CATEGORY_CLIENT,
+    help = "BookKeeper client stats"
+)
+public class BookKeeperClientStatsImpl implements BookKeeperClientStats {
+    private final StatsLogger stats;
+    @StatsDoc(
+        name = CREATE_OP,
+        help = "operation stats of creating ledgers"
+    )
+    private final OpStatsLogger createOpLogger;
+    @StatsDoc(
+        name = DELETE_OP,
+        help = "operation stats of deleting ledgers"
+    )
+    private final OpStatsLogger deleteOpLogger;
+    @StatsDoc(
+        name = OPEN_OP,
+        help = "operation stats of opening ledgers"
+    )
+    private final OpStatsLogger openOpLogger;
+    @StatsDoc(
+        name = RECOVER_OP,
+        help = "operation stats of recovering ledgers"
+    )
+    private final OpStatsLogger recoverOpLogger;
+    @StatsDoc(
+        name = READ_OP,
+        help = "operation stats of reading entries requests"
+    )
+    private final OpStatsLogger readOpLogger;
+    @StatsDoc(
+        name = READ_OP_DM,
+        help = "the number of read entries hitting DigestMismatch errors"
+    )
+    private final Counter readOpDmCounter;
+    @StatsDoc(
+        name = READ_LAST_CONFIRMED_AND_ENTRY,
+        help = "operation stats of read_last_confirmed_and_entry requests"
+    )
+    private final OpStatsLogger readLacAndEntryOpLogger;
+    @StatsDoc(
+        name = READ_LAST_CONFIRMED_AND_ENTRY_RESPONSE,
+        help = "operation stats of read_last_confirmed_and_entry responses"
+    )
+    private final OpStatsLogger readLacAndEntryRespLogger;
+    @StatsDoc(
+        name = ADD_OP,
+        help = "operation stats of adding entries requests"
+    )
+    private final OpStatsLogger addOpLogger;
+    @StatsDoc(
+        name = FORCE_OP,
+        help = "operation stats of force requests"
+    )
+    private final OpStatsLogger forceOpLogger;
+    @StatsDoc(
+        name = ADD_OP_UR,
+        help = "the number of add entries under replication"
+    )
+    private final Counter addOpUrCounter;
+    @StatsDoc(
+        name = WRITE_LAC_OP,
+        help = "operation stats of write_lac requests"
+    )
+    private final OpStatsLogger writeLacOpLogger;
+    @StatsDoc(
+        name = READ_LAC_OP,
+        help = "operation stats of read_lac requests"
+    )
+    private final OpStatsLogger readLacOpLogger;
+    @StatsDoc(
+        name = LEDGER_RECOVER_ADD_ENTRIES,
+        help = "the distribution of entries written in ledger recovery requests"
+    )
+    private final OpStatsLogger recoverAddEntriesStats;
+    @StatsDoc(
+        name = LEDGER_RECOVER_READ_ENTRIES,
+        help = "the distribution of entries read in ledger recovery requests"
+    )
+    private final OpStatsLogger recoverReadEntriesStats;
+
+    @StatsDoc(
+        name = ENSEMBLE_CHANGES,
+        help = "The number of ensemble changes"
+    )
+    private final Counter ensembleChangeCounter;
+    @StatsDoc(
+        name = LAC_UPDATE_HITS,
+        help = "The number of successful lac updates on piggybacked responses"
+    )
+    private final Counter lacUpdateHitsCounter;
+    @StatsDoc(
+        name = LAC_UPDATE_MISSES,
+        help = "The number of unsuccessful lac updates on piggybacked responses"
+    )
+    private final Counter lacUpdateMissesCounter;
+    @StatsDoc(
+        name = CLIENT_CHANNEL_WRITE_WAIT,
+        help = " The latency distribution of waiting time on channel being writable"
+    )
+    private final OpStatsLogger clientChannelWriteWaitStats;
+    @StatsDoc(
+        name = SPECULATIVE_READ_COUNT,
+        help = "The number of speculative read requests"
+    )
+    private final Counter speculativeReadCounter;
+
+
+    public BookKeeperClientStatsImpl(StatsLogger stats) {
+        this.stats = stats;
+        this.createOpLogger = stats.getOpStatsLogger(CREATE_OP);
+        this.deleteOpLogger = stats.getOpStatsLogger(DELETE_OP);
+        this.openOpLogger = stats.getOpStatsLogger(OPEN_OP);
+        this.recoverOpLogger = stats.getOpStatsLogger(RECOVER_OP);
+        this.readOpLogger = stats.getOpStatsLogger(READ_OP);
+        this.readOpDmCounter = stats.getCounter(READ_OP_DM);
+        this.readLacAndEntryOpLogger = stats.getOpStatsLogger(READ_LAST_CONFIRMED_AND_ENTRY);
+        this.readLacAndEntryRespLogger = stats.getOpStatsLogger(READ_LAST_CONFIRMED_AND_ENTRY_RESPONSE);
+        this.addOpLogger = stats.getOpStatsLogger(ADD_OP);
+        this.forceOpLogger = stats.getOpStatsLogger(FORCE_OP);
+        this.addOpUrCounter = stats.getCounter(ADD_OP_UR);
+        this.writeLacOpLogger = stats.getOpStatsLogger(WRITE_LAC_OP);
+        this.readLacOpLogger = stats.getOpStatsLogger(READ_LAC_OP);
+        this.recoverAddEntriesStats = stats.getOpStatsLogger(LEDGER_RECOVER_ADD_ENTRIES);
+        this.recoverReadEntriesStats = stats.getOpStatsLogger(LEDGER_RECOVER_READ_ENTRIES);
+
+        this.ensembleChangeCounter = stats.getCounter(ENSEMBLE_CHANGES);
+        this.lacUpdateHitsCounter = stats.getCounter(LAC_UPDATE_HITS);
+        this.lacUpdateMissesCounter = stats.getCounter(LAC_UPDATE_MISSES);
+        this.clientChannelWriteWaitStats = stats.getOpStatsLogger(CLIENT_CHANNEL_WRITE_WAIT);
+
+        speculativeReadCounter = stats.getCounter(SPECULATIVE_READ_COUNT);
+    }
+
+    @Override
+    public OpStatsLogger getCreateOpLogger() {
+        return createOpLogger;
+    }
+    @Override
+    public OpStatsLogger getOpenOpLogger() {
+        return openOpLogger;
+    }
+    @Override
+    public OpStatsLogger getDeleteOpLogger() {
+        return deleteOpLogger;
+    }
+    @Override
+    public OpStatsLogger getRecoverOpLogger() {
+        return recoverOpLogger;
+    }
+    @Override
+    public OpStatsLogger getReadOpLogger() {
+        return readOpLogger;
+    }
+    @Override
+    public OpStatsLogger getReadLacAndEntryOpLogger() {
+        return readLacAndEntryOpLogger;
+    }
+    @Override
+    public OpStatsLogger getReadLacAndEntryRespLogger() {
+        return readLacAndEntryRespLogger;
+    }
+    @Override
+    public OpStatsLogger getAddOpLogger() {
+        return addOpLogger;
+    }
+    @Override
+    public OpStatsLogger getForceOpLogger() {
+        return forceOpLogger;
+    }
+    @Override
+    public OpStatsLogger getWriteLacOpLogger() {
+        return writeLacOpLogger;
+    }
+    @Override
+    public OpStatsLogger getReadLacOpLogger() {
+        return readLacOpLogger;
+    }
+    @Override
+    public OpStatsLogger getRecoverAddCountLogger() {
+        return recoverAddEntriesStats;
+    }
+    @Override
+    public OpStatsLogger getRecoverReadCountLogger() {
+        return recoverReadEntriesStats;
+    }
+    @Override
+    public Counter getReadOpDmCounter() {
+        return readOpDmCounter;
+    }
+    @Override
+    public Counter getAddOpUrCounter() {
+        return addOpUrCounter;
+    }
+    @Override
+    public Counter getSpeculativeReadCounter() {
+        return speculativeReadCounter;
+    }
+    @Override
+    public Counter getEnsembleChangeCounter() {
+        return ensembleChangeCounter;
+    }
+    @Override
+    public Counter getLacUpdateHitsCounter() {
+        return lacUpdateHitsCounter;
+    }
+    @Override
+    public Counter getLacUpdateMissesCounter() {
+        return lacUpdateMissesCounter;
+    }
+    @Override
+    public OpStatsLogger getClientChannelWriteWaitLogger() {
+        return clientChannelWriteWaitStats;
+    }
+    @Override
+    public Counter getEnsembleBookieDistributionCounter(String bookie) {
+        return stats.getCounter(LEDGER_ENSEMBLE_BOOKIE_DISTRIBUTION + "-" + bookie);
+    }
+    @Override
+    public void registerPendingAddsGauge(Gauge<Integer> gauge) {
+        stats.registerGauge(PENDING_ADDS, gauge);
+    }
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java
index 917f6f3..6839afc 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java
@@ -125,6 +125,7 @@ import org.apache.bookkeeper.stats.Counter;
 import org.apache.bookkeeper.stats.NullStatsLogger;
 import org.apache.bookkeeper.stats.OpStatsLogger;
 import org.apache.bookkeeper.stats.StatsLogger;
+import org.apache.bookkeeper.stats.annotations.StatsDoc;
 import org.apache.bookkeeper.tls.SecurityException;
 import org.apache.bookkeeper.tls.SecurityHandlerFactory;
 import org.apache.bookkeeper.tls.SecurityHandlerFactory.NodeType;
@@ -142,6 +143,10 @@ import org.slf4j.MDC;
  * This class manages all details of connection to a particular bookie. It also
  * has reconnect logic if a connection to a bookie fails.
  */
+@StatsDoc(
+    name = BookKeeperClientStats.CHANNEL_SCOPE,
+    help = "Per channel bookie client stats"
+)
 @Sharable
 public class PerChannelBookieClient extends ChannelInboundHandlerAdapter {
 
@@ -177,29 +182,121 @@ public class PerChannelBookieClient extends ChannelInboundHandlerAdapter {
         new SynchronizedHashMultiMap<>();
 
     private final StatsLogger statsLogger;
+    @StatsDoc(
+        name = BookKeeperClientStats.CHANNEL_READ_OP,
+        help = "channel stats of read entries requests"
+    )
     private final OpStatsLogger readEntryOpLogger;
+    @StatsDoc(
+        name = BookKeeperClientStats.CHANNEL_TIMEOUT_READ,
+        help = "timeout stats of read entries requests"
+    )
     private final OpStatsLogger readTimeoutOpLogger;
+    @StatsDoc(
+        name = BookKeeperClientStats.CHANNEL_ADD_OP,
+        help = "channel stats of add entries requests"
+    )
     private final OpStatsLogger addEntryOpLogger;
+    @StatsDoc(
+        name = BookKeeperClientStats.CHANNEL_WRITE_LAC_OP,
+        help = "channel stats of write_lac requests"
+    )
     private final OpStatsLogger writeLacOpLogger;
+    @StatsDoc(
+        name = BookKeeperClientStats.CHANNEL_FORCE_OP,
+        help = "channel stats of force requests"
+    )
     private final OpStatsLogger forceLedgerOpLogger;
+    @StatsDoc(
+        name = BookKeeperClientStats.CHANNEL_READ_LAC_OP,
+        help = "channel stats of read_lac requests"
+    )
     private final OpStatsLogger readLacOpLogger;
+    @StatsDoc(
+        name = BookKeeperClientStats.CHANNEL_TIMEOUT_ADD,
+        help = "timeout stats of add entries requests"
+    )
     private final OpStatsLogger addTimeoutOpLogger;
+    @StatsDoc(
+        name = BookKeeperClientStats.CHANNEL_TIMEOUT_WRITE_LAC,
+        help = "timeout stats of write_lac requests"
+    )
     private final OpStatsLogger writeLacTimeoutOpLogger;
+    @StatsDoc(
+        name = BookKeeperClientStats.CHANNEL_TIMEOUT_FORCE,
+        help = "timeout stats of force requests"
+    )
     private final OpStatsLogger forceLedgerTimeoutOpLogger;
+    @StatsDoc(
+        name = BookKeeperClientStats.CHANNEL_TIMEOUT_READ_LAC,
+        help = "timeout stats of read_lac requests"
+    )
     private final OpStatsLogger readLacTimeoutOpLogger;
+    @StatsDoc(
+        name = BookKeeperClientStats.GET_BOOKIE_INFO_OP,
+        help = "channel stats of get_bookie_info requests"
+    )
     private final OpStatsLogger getBookieInfoOpLogger;
+    @StatsDoc(
+        name = BookKeeperClientStats.TIMEOUT_GET_BOOKIE_INFO,
+        help = "timeout stats of get_bookie_info requests"
+    )
     private final OpStatsLogger getBookieInfoTimeoutOpLogger;
+    @StatsDoc(
+        name = BookKeeperClientStats.CHANNEL_START_TLS_OP,
+        help = "channel stats of start_tls requests"
+    )
     private final OpStatsLogger startTLSOpLogger;
+    @StatsDoc(
+        name = BookKeeperClientStats.CHANNEL_TIMEOUT_START_TLS_OP,
+        help = "timeout stats of start_tls requests"
+    )
     private final OpStatsLogger startTLSTimeoutOpLogger;
+    @StatsDoc(
+        name = BookKeeperClientStats.CLIENT_CONNECT_TIMER,
+        help = "channel stats of connect requests"
+    )
     private final OpStatsLogger connectTimer;
+    @StatsDoc(
+        name = BookKeeperClientStats.NETTY_EXCEPTION_CNT,
+        help = "the number of exceptions received from this channel"
+    )
     private final Counter exceptionCounter;
+    @StatsDoc(
+        name = BookKeeperClientStats.ADD_OP_OUTSTANDING,
+        help = "the number of outstanding add_entry requests"
+    )
     private final Counter addEntryOutstanding;
+    @StatsDoc(
+        name = BookKeeperClientStats.READ_OP_OUTSTANDING,
+        help = "the number of outstanding add_entry requests"
+    )
     private final Counter readEntryOutstanding;
     /* collect stats on all Ops that flows through netty pipeline */
+    @StatsDoc(
+        name = BookKeeperClientStats.NETTY_OPS,
+        help = "channel stats for all operations flowing through netty pipeline"
+    )
     private final OpStatsLogger nettyOpLogger;
+    @StatsDoc(
+        name = BookKeeperClientStats.ACTIVE_NON_TLS_CHANNEL_COUNTER,
+        help = "the number of active non-tls channels"
+    )
     private final Counter activeNonTlsChannelCounter;
+    @StatsDoc(
+        name = BookKeeperClientStats.ACTIVE_TLS_CHANNEL_COUNTER,
+        help = "the number of active tls channels"
+    )
     private final Counter activeTlsChannelCounter;
+    @StatsDoc(
+        name = BookKeeperClientStats.FAILED_CONNECTION_COUNTER,
+        help = "the number of failed connections"
+    )
     private final Counter failedConnectionCounter;
+    @StatsDoc(
+        name = BookKeeperClientStats.FAILED_TLS_HANDSHAKE_COUNTER,
+        help = "the number of failed tls handshakes"
+    )
     private final Counter failedTlsHandshakeCounter;
 
     private final boolean useV2WireProtocol;