cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From yu...@apache.org
Subject cassandra git commit: Support json/yaml output in noetool tablestats
Date Mon, 18 Apr 2016 19:02:54 GMT
Repository: cassandra
Updated Branches:
  refs/heads/trunk ba926ff6d -> 5805a76ca


Support json/yaml output in noetool tablestats

patch by Shogo Hoshii; reviewed by yukim for CASSANDRA-5977


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/5805a76c
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5805a76c
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5805a76c

Branch: refs/heads/trunk
Commit: 5805a76ca092648146ba71af744c8bce357bdc39
Parents: ba926ff
Author: Shogo Hoshii <shoshii@yahoo-corp.jp>
Authored: Thu Apr 14 12:00:42 2016 -0500
Committer: Yuki Morishita <yukim@apache.org>
Committed: Mon Apr 18 14:00:20 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../cassandra/tools/nodetool/TableStats.java    | 230 ++++++++-----------
 .../tools/nodetool/stats/StatsHolder.java       | 104 +++++++++
 .../tools/nodetool/stats/StatsKeyspace.java     |  78 +++++++
 .../tools/nodetool/stats/StatsPrinter.java      |  25 ++
 .../tools/nodetool/stats/StatsTable.java        |  65 ++++++
 .../tools/nodetool/stats/TableStatsPrinter.java | 149 ++++++++++++
 7 files changed, 521 insertions(+), 131 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/5805a76c/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 7f284fd..7a77cd4 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.6
+ * Support json/yaml output in noetool tablestats (CASSANDRA-5977)
  * (stress) Add datacenter option to -node options (CASSANDRA-11591)
  * Fix handling of empty slices (CASSANDRA-11513)
  * Make number of cores used by cqlsh COPY visible to testing code (CASSANDRA-11437)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5805a76c/src/java/org/apache/cassandra/tools/nodetool/TableStats.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tools/nodetool/TableStats.java b/src/java/org/apache/cassandra/tools/nodetool/TableStats.java
index 3188c7e..01cd1c3 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/TableStats.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/TableStats.java
@@ -17,20 +17,23 @@
  */
 package org.apache.cassandra.tools.nodetool;
 
-import io.airlift.command.Arguments;
-import io.airlift.command.Command;
-import io.airlift.command.Option;
-
 import java.util.*;
 import javax.management.InstanceNotFoundException;
 
 import com.google.common.collect.ArrayListMultimap;
+import io.airlift.command.Arguments;
+import io.airlift.command.Command;
+import io.airlift.command.Option;
 
 import org.apache.cassandra.db.ColumnFamilyStoreMBean;
 import org.apache.cassandra.io.util.FileUtils;
 import org.apache.cassandra.metrics.CassandraMetricsRegistry;
 import org.apache.cassandra.tools.NodeProbe;
 import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.stats.StatsHolder;
+import org.apache.cassandra.tools.nodetool.stats.StatsKeyspace;
+import org.apache.cassandra.tools.nodetool.stats.StatsTable;
+import org.apache.cassandra.tools.nodetool.stats.TableStatsPrinter;
 
 @Command(name = "tablestats", description = "Print statistics on tables")
 public class TableStats extends NodeToolCmd
@@ -46,14 +49,24 @@ public class TableStats extends NodeToolCmd
             description = "Display bytes in human readable form, i.e. KiB, MiB, GiB, TiB")
     private boolean humanReadable = false;
 
+    @Option(title = "format",
+            name = {"-F", "--format"},
+            description = "Output format (json, yaml)")
+    private String outputFormat = "";
+
     @Override
     public void execute(NodeProbe probe)
     {
+        if (!outputFormat.isEmpty() && !"json".equals(outputFormat) && !"yaml".equals(outputFormat))
+        {
+            throw new IllegalArgumentException("arguments for -F are json,yaml only.");
+        }
+
         TableStats.OptionFilter filter = new OptionFilter(ignore, tableNames);
         ArrayListMultimap<String, ColumnFamilyStoreMBean> selectedTableMbeans = ArrayListMultimap.create();
-        Map<String, KeyspaceStats> keyspaceStats = new HashMap<>();
+        Map<String, StatsKeyspace> keyspaceStats = new HashMap<>();
 
-        // get a list of column family stores
+        // get a list of table stores
         Iterator<Map.Entry<String, ColumnFamilyStoreMBean>> tableMBeans = probe.getColumnFamilyStoreMBeanProxies();
 
         while (tableMBeans.hasNext())
@@ -64,10 +77,10 @@ public class TableStats extends NodeToolCmd
 
             if (filter.isKeyspaceIncluded(keyspaceName))
             {
-                KeyspaceStats stats = keyspaceStats.get(keyspaceName);
+                StatsKeyspace stats = keyspaceStats.get(keyspaceName);
                 if (stats == null)
                 {
-                    stats = new KeyspaceStats(probe, keyspaceName);
+                    stats = new StatsKeyspace(probe, keyspaceName);
                     keyspaceStats.put(keyspaceName, stats);
                 }
                 stats.add(tableProxy);
@@ -81,51 +94,35 @@ public class TableStats extends NodeToolCmd
         filter.verifyKeyspaces(probe.getKeyspaces());
         filter.verifyTables();
 
-        // print out the table statistics
+        // get metrics of keyspace
+        StatsHolder holder = new StatsHolder();
         for (Map.Entry<String, Collection<ColumnFamilyStoreMBean>> entry : selectedTableMbeans.asMap().entrySet())
         {
             String keyspaceName = entry.getKey();
             Collection<ColumnFamilyStoreMBean> tables = entry.getValue();
+            StatsKeyspace statsKeyspace = keyspaceStats.get(keyspaceName);
 
-            System.out.println("Keyspace: " + keyspaceName);
-            KeyspaceStats stats = keyspaceStats.get(keyspaceName);
-
-            System.out.println("\tRead Count: " + stats.readCount);
-            System.out.println("\tRead Latency: " + String.format("%s", stats.readLatency())
+ " ms.");
-            System.out.println("\tWrite Count: " + stats.writeCount);
-            System.out.println("\tWrite Latency: " + String.format("%s", stats.writeLatency())
+ " ms.");
-            System.out.println("\tPending Flushes: " + stats.pendingFlushes);
-
-            // print out column family statistics for this keyspace
+            // get metrics of table statistics for this keyspace
             for (ColumnFamilyStoreMBean table : tables)
             {
                 String tableName = table.getTableName();
-                if (tableName.contains("."))
-                    System.out.println("\t\tTable (index): " + tableName);
-                else
-                    System.out.println("\t\tTable: " + tableName);
-
-                System.out.println("\t\tSSTable count: " + probe.getColumnFamilyMetric(keyspaceName,
tableName, "LiveSSTableCount"));
-
+                StatsTable statsTable = new StatsTable();
+                statsTable.name = tableName;
+                statsTable.isIndex = tableName.contains(".");
+                statsTable.sstableCount = probe.getColumnFamilyMetric(keyspaceName, tableName,
"LiveSSTableCount");
                 int[] leveledSStables = table.getSSTableCountPerLevel();
                 if (leveledSStables != null)
                 {
-                    System.out.print("\t\tSSTables in each level: [");
+                    statsTable.isLeveledSstable = true;
+
                     for (int level = 0; level < leveledSStables.length; level++)
                     {
                         int count = leveledSStables[level];
-                        System.out.print(count);
                         long maxCount = 4L; // for L0
                         if (level > 0)
                             maxCount = (long) Math.pow(10, level);
-                        //  show max threshold for level when exceeded
-                        if (count > maxCount)
-                            System.out.print("/" + maxCount);
-
-                        if (level < leveledSStables.length - 1)
-                            System.out.print(", ");
-                        else
-                            System.out.println("]");
+                        // show max threshold for level when exceeded
+                        statsTable.sstablesInEachLevel.add(count + ((count > maxCount)
? "/" + maxCount : ""));
                     }
                 }
 
@@ -133,7 +130,6 @@ public class TableStats extends NodeToolCmd
                 Long bloomFilterOffHeapSize = null;
                 Long indexSummaryOffHeapSize = null;
                 Long compressionMetadataOffHeapSize = null;
-
                 Long offHeapSize = null;
 
                 try
@@ -142,7 +138,6 @@ public class TableStats extends NodeToolCmd
                     bloomFilterOffHeapSize = (Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "BloomFilterOffHeapMemoryUsed");
                     indexSummaryOffHeapSize = (Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "IndexSummaryOffHeapMemoryUsed");
                     compressionMetadataOffHeapSize = (Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "CompressionMetadataOffHeapMemoryUsed");
-
                     offHeapSize = memtableOffHeapSize + bloomFilterOffHeapSize + indexSummaryOffHeapSize
+ compressionMetadataOffHeapSize;
                 }
                 catch (RuntimeException e)
@@ -152,52 +147,76 @@ public class TableStats extends NodeToolCmd
                         throw e;
                 }
 
-                System.out.println("\t\tSpace used (live): " + format((Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "LiveDiskSpaceUsed"), humanReadable));
-                System.out.println("\t\tSpace used (total): " + format((Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "TotalDiskSpaceUsed"), humanReadable));
-                System.out.println("\t\tSpace used by snapshots (total): " + format((Long)
probe.getColumnFamilyMetric(keyspaceName, tableName, "SnapshotsSize"), humanReadable));
+                statsTable.spaceUsedLive = format((Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "LiveDiskSpaceUsed"), humanReadable);
+                statsTable.spaceUsedTotal = format((Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "TotalDiskSpaceUsed"), humanReadable);
+                statsTable.spaceUsedBySnapshotsTotal = format((Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "SnapshotsSize"), humanReadable);
                 if (offHeapSize != null)
-                    System.out.println("\t\tOff heap memory used (total): " + format(offHeapSize,
humanReadable));
-                System.out.println("\t\tSSTable Compression Ratio: " + probe.getColumnFamilyMetric(keyspaceName,
tableName, "CompressionRatio"));
-                System.out.println("\t\tNumber of keys (estimate): " + probe.getColumnFamilyMetric(keyspaceName,
tableName, "EstimatedPartitionCount"));
-                System.out.println("\t\tMemtable cell count: " + probe.getColumnFamilyMetric(keyspaceName,
tableName, "MemtableColumnsCount"));
-                System.out.println("\t\tMemtable data size: " + format((Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "MemtableLiveDataSize"), humanReadable));
+                {
+                    statsTable.offHeapUsed = true;
+                    statsTable.offHeapMemoryUsedTotal = format(offHeapSize, humanReadable);
+
+                }
+                statsTable.sstableCompressionRatio = probe.getColumnFamilyMetric(keyspaceName,
tableName, "CompressionRatio");
+                statsTable.numberOfKeysEstimate = probe.getColumnFamilyMetric(keyspaceName,
tableName, "EstimatedPartitionCount");
+                statsTable.memtableCellCount = probe.getColumnFamilyMetric(keyspaceName,
tableName, "MemtableColumnsCount");
+                statsTable.memtableDataSize = format((Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "MemtableLiveDataSize"), humanReadable);
                 if (memtableOffHeapSize != null)
-                    System.out.println("\t\tMemtable off heap memory used: " + format(memtableOffHeapSize,
humanReadable));
-                System.out.println("\t\tMemtable switch count: " + probe.getColumnFamilyMetric(keyspaceName,
tableName, "MemtableSwitchCount"));
-                System.out.println("\t\tLocal read count: " + ((CassandraMetricsRegistry.JmxTimerMBean)
probe.getColumnFamilyMetric(keyspaceName, tableName, "ReadLatency")).getCount());
+                {
+                    statsTable.memtableOffHeapUsed = true;
+                    statsTable.memtableOffHeapMemoryUsed = format(memtableOffHeapSize, humanReadable);
+                }
+                statsTable.memtableSwitchCount = probe.getColumnFamilyMetric(keyspaceName,
tableName, "MemtableSwitchCount");
+                statsTable.localReadCount = ((CassandraMetricsRegistry.JmxTimerMBean) probe.getColumnFamilyMetric(keyspaceName,
tableName, "ReadLatency")).getCount();
+
                 double localReadLatency = ((CassandraMetricsRegistry.JmxTimerMBean) probe.getColumnFamilyMetric(keyspaceName,
tableName, "ReadLatency")).getMean() / 1000;
                 double localRLatency = localReadLatency > 0 ? localReadLatency : Double.NaN;
-                System.out.printf("\t\tLocal read latency: %01.3f ms%n", localRLatency);
-                System.out.println("\t\tLocal write count: " + ((CassandraMetricsRegistry.JmxTimerMBean)
probe.getColumnFamilyMetric(keyspaceName, tableName, "WriteLatency")).getCount());
+                statsTable.localReadLatencyMs = localRLatency;
+                statsTable.localWriteCount = ((CassandraMetricsRegistry.JmxTimerMBean) probe.getColumnFamilyMetric(keyspaceName,
tableName, "WriteLatency")).getCount();
+
                 double localWriteLatency = ((CassandraMetricsRegistry.JmxTimerMBean) probe.getColumnFamilyMetric(keyspaceName,
tableName, "WriteLatency")).getMean() / 1000;
                 double localWLatency = localWriteLatency > 0 ? localWriteLatency : Double.NaN;
-                System.out.printf("\t\tLocal write latency: %01.3f ms%n", localWLatency);
-                System.out.println("\t\tPending flushes: " + probe.getColumnFamilyMetric(keyspaceName,
tableName, "PendingFlushes"));
-                System.out.println("\t\tBloom filter false positives: " + probe.getColumnFamilyMetric(keyspaceName,
tableName, "BloomFilterFalsePositives"));
-                System.out.printf("\t\tBloom filter false ratio: %s%n", String.format("%01.5f",
probe.getColumnFamilyMetric(keyspaceName, tableName, "RecentBloomFilterFalseRatio")));
-                System.out.println("\t\tBloom filter space used: " + format((Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "BloomFilterDiskSpaceUsed"), humanReadable));
+                statsTable.localWriteLatencyMs = localWLatency;
+                statsTable.pendingFlushes = probe.getColumnFamilyMetric(keyspaceName, tableName,
"PendingFlushes");
+
+                statsTable.bloomFilterFalsePositives = probe.getColumnFamilyMetric(keyspaceName,
tableName, "BloomFilterFalsePositives");
+                statsTable.bloomFilterFalseRatio = probe.getColumnFamilyMetric(keyspaceName,
tableName, "RecentBloomFilterFalseRatio");
+                statsTable.bloomFilterSpaceUsed = format((Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "BloomFilterDiskSpaceUsed"), humanReadable);
+
                 if (bloomFilterOffHeapSize != null)
-                    System.out.println("\t\tBloom filter off heap memory used: " + format(bloomFilterOffHeapSize,
humanReadable));
+                {
+                    statsTable.bloomFilterOffHeapUsed = true;
+                    statsTable.bloomFilterOffHeapMemoryUsed = format(bloomFilterOffHeapSize,
humanReadable);
+                }
+
                 if (indexSummaryOffHeapSize != null)
-                    System.out.println("\t\tIndex summary off heap memory used: " + format(indexSummaryOffHeapSize,
humanReadable));
+                {
+                    statsTable.indexSummaryOffHeapUsed = true;
+                    statsTable.indexSummaryOffHeapMemoryUsed = format(indexSummaryOffHeapSize,
humanReadable);
+                }
                 if (compressionMetadataOffHeapSize != null)
-                    System.out.println("\t\tCompression metadata off heap memory used: "
+ format(compressionMetadataOffHeapSize, humanReadable));
+                {
+                    statsTable.compressionMetadataOffHeapUsed = true;
+                    statsTable.compressionMetadataOffHeapMemoryUsed = format(compressionMetadataOffHeapSize,
humanReadable);
+                }
+                statsTable.compactedPartitionMinimumBytes = (Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "MinPartitionSize");
+                statsTable.compactedPartitionMaximumBytes = (Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "MaxPartitionSize");
+                statsTable.compactedPartitionMeanBytes = (Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "MeanPartitionSize");
 
-                System.out.println("\t\tCompacted partition minimum bytes: " + format((Long)
probe.getColumnFamilyMetric(keyspaceName, tableName, "MinPartitionSize"), humanReadable));
-                System.out.println("\t\tCompacted partition maximum bytes: " + format((Long)
probe.getColumnFamilyMetric(keyspaceName, tableName, "MaxPartitionSize"), humanReadable));
-                System.out.println("\t\tCompacted partition mean bytes: " + format((Long)
probe.getColumnFamilyMetric(keyspaceName, tableName, "MeanPartitionSize"), humanReadable));
                 CassandraMetricsRegistry.JmxHistogramMBean histogram = (CassandraMetricsRegistry.JmxHistogramMBean)
probe.getColumnFamilyMetric(keyspaceName, tableName, "LiveScannedHistogram");
-                System.out.println("\t\tAverage live cells per slice (last five minutes):
" + histogram.getMean());
-                System.out.println("\t\tMaximum live cells per slice (last five minutes):
" + histogram.getMax());
-                histogram = (CassandraMetricsRegistry.JmxHistogramMBean) probe.getColumnFamilyMetric(keyspaceName,
tableName, "TombstoneScannedHistogram");
-                System.out.println("\t\tAverage tombstones per slice (last five minutes):
" + histogram.getMean());
-                System.out.println("\t\tMaximum tombstones per slice (last five minutes):
" + histogram.getMax());
-                System.out.println("\t\tDropped Mutations: " + format((Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "DroppedMutations"), humanReadable));
+                statsTable.averageLiveCellsPerSliceLastFiveMinutes = histogram.getMean();
+                statsTable.maximumLiveCellsPerSliceLastFiveMinutes = histogram.getMax();
 
-                System.out.println("");
+                histogram = (CassandraMetricsRegistry.JmxHistogramMBean) probe.getColumnFamilyMetric(keyspaceName,
tableName, "TombstoneScannedHistogram");
+                statsTable.averageTombstonesPerSliceLastFiveMinutes = histogram.getMean();
+                statsTable.maximumTombstonesPerSliceLastFiveMinutes = histogram.getMax();
+                statsTable.droppedMutations = format((Long) probe.getColumnFamilyMetric(keyspaceName,
tableName, "DroppedMutations"), humanReadable);
+                statsKeyspace.tables.add(statsTable);
             }
-            System.out.println("----------------");
+            holder.keyspaces.add(statsKeyspace);
         }
+        // print out the keyspace and table statistics
+        TableStatsPrinter printer = TableStatsPrinter.from(outputFormat);
+        printer.print(holder, System.out);
     }
 
     private String format(long bytes, boolean humanReadable)
@@ -210,13 +229,13 @@ public class TableStats extends NodeToolCmd
      */
     private static class OptionFilter
     {
-        private Map<String, List<String>> filter = new HashMap<>();
-        private Map<String, List<String>> verifier = new HashMap<>(); //
Same as filter initially, but we remove tables every time we've checked them for inclusion
-                                                                      // in isTableIncluded()
so that we detect if those table requested don't exist (verifyTables())
-        private List<String> filterList = new ArrayList<>();
-        private boolean ignoreMode;
+        private final Map<String, List<String>> filter = new HashMap<>();
+        private final Map<String, List<String>> verifier = new HashMap<>();
// Same as filter initially, but we remove tables every time we've checked them for inclusion
+                                                                            // in isTableIncluded()
so that we detect if those table requested don't exist (verifyTables())
+        private final List<String> filterList = new ArrayList<>();
+        private final boolean ignoreMode;
 
-        public OptionFilter(boolean ignoreMode, List<String> filterList)
+        OptionFilter(boolean ignoreMode, List<String> filterList)
         {
             this.filterList.addAll(filterList);
             this.ignoreMode = ignoreMode;
@@ -228,8 +247,8 @@ public class TableStats extends NodeToolCmd
                 // build the map that stores the keyspaces and tables to use
                 if (!filter.containsKey(keyValues[0]))
                 {
-                    filter.put(keyValues[0], new ArrayList<String>());
-                    verifier.put(keyValues[0], new ArrayList<String>());
+                    filter.put(keyValues[0], new ArrayList<>());
+                    verifier.put(keyValues[0], new ArrayList<>());
                 }
 
                 if (keyValues.length == 2)
@@ -253,7 +272,7 @@ public class TableStats extends NodeToolCmd
                 return ignoreMode;
                 // only a keyspace with no tables was supplied
                 // so ignore or include (based on the flag) every column family in specified
keyspace
-            else if (tables.size() == 0)
+            else if (tables.isEmpty())
                 return !ignoreMode;
 
             // keyspace exists, and it contains specific table
@@ -283,59 +302,8 @@ public class TableStats extends NodeToolCmd
         public void verifyTables()
         {
             for (String ks : filter.keySet())
-                if (verifier.get(ks).size() > 0)
+                if (!verifier.get(ks).isEmpty())
                     throw new IllegalArgumentException("Unknown tables: " + verifier.get(ks)
+ " in keyspace: " + ks);
         }
     }
-
-    private static class KeyspaceStats
-    {
-        private final NodeProbe probe;
-        private final String keyspaceName;
-
-        public long readCount;
-        public long writeCount;
-        public int pendingFlushes;
-        private double totalReadTime;
-        private double totalWriteTime;
-
-        public KeyspaceStats(NodeProbe probe, String keyspaceName)
-        {
-            this.probe = probe;
-            this.keyspaceName = keyspaceName;
-        }
-
-        public void add(ColumnFamilyStoreMBean table)
-        {
-            String tableName = table.getTableName();
-            long tableWriteCount = ((CassandraMetricsRegistry.JmxTimerMBean) probe.getColumnFamilyMetric(keyspaceName,
tableName, "WriteLatency")).getCount();
-            long tableReadCount = ((CassandraMetricsRegistry.JmxTimerMBean) probe.getColumnFamilyMetric(keyspaceName,
tableName, "ReadLatency")).getCount();
-
-            if (tableReadCount > 0)
-            {
-                readCount += tableReadCount;
-                totalReadTime += (long) probe.getColumnFamilyMetric(keyspaceName, tableName,
"ReadTotalLatency");
-            }
-            if (tableWriteCount > 0)
-            {
-                writeCount += tableWriteCount;
-                totalWriteTime += (long) probe.getColumnFamilyMetric(keyspaceName, tableName,
"WriteTotalLatency");
-            }
-            pendingFlushes += (long) probe.getColumnFamilyMetric(keyspaceName, tableName,
"PendingFlushes");
-        }
-
-        public double readLatency()
-        {
-            return readCount > 0
-                 ? totalReadTime / readCount / 1000
-                 : Double.NaN;
-        }
-
-        public double writeLatency()
-        {
-            return writeCount > 0
-                 ? totalWriteTime / writeCount / 1000
-                 : Double.NaN;
-        }
-    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5805a76c/src/java/org/apache/cassandra/tools/nodetool/stats/StatsHolder.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tools/nodetool/stats/StatsHolder.java b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsHolder.java
new file mode 100644
index 0000000..96122f4
--- /dev/null
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsHolder.java
@@ -0,0 +1,104 @@
+/*
+ * 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.cassandra.tools.nodetool.stats;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class StatsHolder
+{
+    public List<StatsKeyspace> keyspaces;
+
+    public StatsHolder()
+    {
+        keyspaces = new ArrayList<>();
+    }
+
+    public Map<String, HashMap<String, Object>> convert2Map()
+    {
+        HashMap<String, HashMap<String, Object>> mpRet = new HashMap<>();
+        for (StatsKeyspace keyspace : keyspaces)
+        {
+            // store each keyspace's metrics to map
+            HashMap<String, Object> mpKeyspace = new HashMap<>();
+            mpKeyspace.put("read_latency", keyspace.readLatency());
+            mpKeyspace.put("read_count", keyspace.readCount);
+            mpKeyspace.put("read_latency_ms", keyspace.readLatency());
+            mpKeyspace.put("write_count", keyspace.writeCount);
+            mpKeyspace.put("write_latency_ms", keyspace.writeLatency());
+            mpKeyspace.put("pending_flushes", keyspace.pendingFlushes);
+
+            // store each table's metrics to map
+            List<StatsTable> tables = keyspace.tables;
+            Map<String, Map<String, Object>> mpTables = new HashMap<>();
+            for (StatsTable table : tables)
+            {
+                Map<String, Object> mpTable = new HashMap<>();
+
+                mpTable.put("sstables_in_each_level", table.sstablesInEachLevel);
+                mpTable.put("space_used_live", table.spaceUsedLive);
+                mpTable.put("space_used_total", table.spaceUsedTotal);
+                mpTable.put("space_used_by_snapshots_total", table.spaceUsedBySnapshotsTotal);
+                if (table.offHeapUsed)
+                    mpTable.put("off_heap_memory_used_total", table.offHeapMemoryUsedTotal);
+                mpTable.put("sstable_compression_ratio", table.sstableCompressionRatio);
+                mpTable.put("number_of_keys_estimate", table.numberOfKeysEstimate);
+                mpTable.put("memtable_cell_count", table.memtableCellCount);
+                mpTable.put("memtable_data_size", table.memtableDataSize);
+                if (table.memtableOffHeapUsed)
+                    mpTable.put("memtable_off_heap_memory_used", table.memtableOffHeapMemoryUsed);
+                mpTable.put("memtable_switch_count", table.memtableSwitchCount);
+                mpTable.put("local_read_count", table.localReadCount);
+                mpTable.put("local_read_latency_ms", String.format("%01.3f", table.localReadLatencyMs));
+                mpTable.put("local_write_count", table.localWriteCount);
+                mpTable.put("local_write_latency_ms", String.format("%01.3f", table.localWriteLatencyMs));
+                mpTable.put("pending_flushes", table.pendingFlushes);
+                mpTable.put("bloom_filter_false_positives", table.bloomFilterFalsePositives);
+                mpTable.put("bloom_filter_false_ratio", String.format("%01.5f", table.bloomFilterFalseRatio));
+                mpTable.put("bloom_filter_space_used", table.bloomFilterSpaceUsed);
+                if (table.bloomFilterOffHeapUsed)
+                    mpTable.put("bloom_filter_off_heap_memory_used", table.bloomFilterOffHeapMemoryUsed);
+                if (table.indexSummaryOffHeapUsed)
+                    mpTable.put("index_summary_off_heap_memory_used", table.indexSummaryOffHeapMemoryUsed);
+                if (table.compressionMetadataOffHeapUsed)
+                    mpTable.put("compression_metadata_off_heap_memory_used",
+                                table.compressionMetadataOffHeapMemoryUsed);
+                mpTable.put("compacted_partition_minimum_bytes", table.compactedPartitionMinimumBytes);
+                mpTable.put("compacted_partition_maximum_bytes", table.compactedPartitionMaximumBytes);
+                mpTable.put("compacted_partition_mean_bytes", table.compactedPartitionMeanBytes);
+                mpTable.put("average_live_cells_per_slice_last_five_minutes",
+                            table.averageLiveCellsPerSliceLastFiveMinutes);
+                mpTable.put("maximum_live_cells_per_slice_last_five_minutes",
+                            table.maximumLiveCellsPerSliceLastFiveMinutes);
+                mpTable.put("average_tombstones_per_slice_last_five_minutes",
+                            table.averageTombstonesPerSliceLastFiveMinutes);
+                mpTable.put("maximum_tombstones_per_slice_last_five_minutes",
+                            table.maximumTombstonesPerSliceLastFiveMinutes);
+                mpTable.put("dropped_mutations", table.droppedMutations);
+
+                mpTables.put(table.name, mpTable);
+            }
+            mpKeyspace.put("tables", mpTables);
+            mpRet.put(keyspace.name, mpKeyspace);
+        }
+        return mpRet;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5805a76c/src/java/org/apache/cassandra/tools/nodetool/stats/StatsKeyspace.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tools/nodetool/stats/StatsKeyspace.java b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsKeyspace.java
new file mode 100644
index 0000000..dc15332
--- /dev/null
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsKeyspace.java
@@ -0,0 +1,78 @@
+/*
+ * 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.cassandra.tools.nodetool.stats;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.cassandra.db.ColumnFamilyStoreMBean;
+import org.apache.cassandra.metrics.CassandraMetricsRegistry;
+import org.apache.cassandra.tools.NodeProbe;
+
+public class StatsKeyspace
+{
+    public List<StatsTable> tables = new ArrayList<>();
+    private final NodeProbe probe;
+
+    public String name;
+    public long readCount;
+    public long writeCount;
+    public int pendingFlushes;
+    private double totalReadTime;
+    private double totalWriteTime;
+
+    public StatsKeyspace(NodeProbe probe, String keyspaceName)
+    {
+        this.probe = probe;
+        this.name = keyspaceName;
+    }
+
+    public void add(ColumnFamilyStoreMBean table)
+    {
+        String tableName = table.getTableName();
+        long tableWriteCount = ((CassandraMetricsRegistry.JmxTimerMBean) probe.getColumnFamilyMetric(name,
tableName, "WriteLatency")).getCount();
+        long tableReadCount = ((CassandraMetricsRegistry.JmxTimerMBean) probe.getColumnFamilyMetric(name,
tableName, "ReadLatency")).getCount();
+
+        if (tableReadCount > 0)
+        {
+            readCount += tableReadCount;
+            totalReadTime += (long) probe.getColumnFamilyMetric(name, tableName, "ReadTotalLatency");
+        }
+        if (tableWriteCount > 0)
+        {
+            writeCount += tableWriteCount;
+            totalWriteTime += (long) probe.getColumnFamilyMetric(name, tableName, "WriteTotalLatency");
+        }
+        pendingFlushes += (long) probe.getColumnFamilyMetric(name, tableName, "PendingFlushes");
+    }
+
+    public double readLatency()
+    {
+        return readCount > 0
+               ? totalReadTime / readCount / 1000
+               : Double.NaN;
+    }
+
+    public double writeLatency()
+    {
+        return writeCount > 0
+               ? totalWriteTime / writeCount / 1000
+               : Double.NaN;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5805a76c/src/java/org/apache/cassandra/tools/nodetool/stats/StatsPrinter.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tools/nodetool/stats/StatsPrinter.java b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsPrinter.java
new file mode 100644
index 0000000..2d98781
--- /dev/null
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsPrinter.java
@@ -0,0 +1,25 @@
+/*
+ * 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.cassandra.tools.nodetool.stats;
+
+import java.io.PrintStream;
+
+public interface StatsPrinter<T>
+{
+    void printFormat(T data, PrintStream out);
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5805a76c/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java
new file mode 100644
index 0000000..b707be6
--- /dev/null
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.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.cassandra.tools.nodetool.stats;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class StatsTable
+{
+    public String name;
+    public boolean isIndex;
+    public boolean isLeveledSstable = false;
+    public Object sstableCount;
+    public String spaceUsedLive;
+    public String spaceUsedTotal;
+    public String spaceUsedBySnapshotsTotal;
+    public boolean offHeapUsed = false;
+    public String offHeapMemoryUsedTotal;
+    public Object sstableCompressionRatio;
+    public Object numberOfKeysEstimate;
+    public Object memtableCellCount;
+    public String memtableDataSize;
+    public boolean memtableOffHeapUsed = false;
+    public String memtableOffHeapMemoryUsed;
+    public Object memtableSwitchCount;
+    public long localReadCount;
+    public double localReadLatencyMs;
+    public long localWriteCount;
+    public double localWriteLatencyMs;
+    public Object pendingFlushes;
+    public Object bloomFilterFalsePositives;
+    public Object bloomFilterFalseRatio;
+    public String bloomFilterSpaceUsed;
+    public boolean bloomFilterOffHeapUsed = false;
+    public String bloomFilterOffHeapMemoryUsed;
+    public boolean indexSummaryOffHeapUsed = false;
+    public String indexSummaryOffHeapMemoryUsed;
+    public boolean compressionMetadataOffHeapUsed = false;
+    public String compressionMetadataOffHeapMemoryUsed;
+    public long compactedPartitionMinimumBytes;
+    public long compactedPartitionMaximumBytes;
+    public long compactedPartitionMeanBytes;
+    public double averageLiveCellsPerSliceLastFiveMinutes;
+    public long maximumLiveCellsPerSliceLastFiveMinutes;
+    public double averageTombstonesPerSliceLastFiveMinutes;
+    public long maximumTombstonesPerSliceLastFiveMinutes;
+    public String droppedMutations;
+    public List<String> sstablesInEachLevel = new ArrayList<>();
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5805a76c/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java b/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java
new file mode 100644
index 0000000..2d8b8a3
--- /dev/null
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java
@@ -0,0 +1,149 @@
+/*
+ * 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.cassandra.tools.nodetool.stats;
+
+import java.io.PrintStream;
+import java.util.List;
+
+import org.json.simple.JSONObject;
+import org.yaml.snakeyaml.Yaml;
+
+public enum TableStatsPrinter
+{
+    DEFAULT(new DefaultPrinter()),
+    JSON(new JsonPrinter()),
+    YAML(new YamlPrinter()),;
+
+    private final StatsPrinter<StatsHolder> printer;
+
+    TableStatsPrinter(StatsPrinter<StatsHolder> printer)
+    {
+        this.printer = printer;
+    }
+
+    public void print(StatsHolder stats, PrintStream out)
+    {
+        printer.printFormat(stats, out);
+    }
+
+    public static TableStatsPrinter from(String format)
+    {
+        switch (format)
+        {
+            case "json":
+                return JSON;
+            case "yaml":
+                return YAML;
+            default:
+                return DEFAULT;
+        }
+    }
+
+    private static class DefaultPrinter implements StatsPrinter<StatsHolder>
+    {
+        @Override
+        public void printFormat(StatsHolder data, PrintStream out)
+        {
+            List<StatsKeyspace> keyspaces = data.keyspaces;
+            for (StatsKeyspace keyspace : keyspaces)
+            {
+                // print each keyspace's information
+                out.println("Keyspace : " + keyspace.name);
+                out.println("\tRead Count: " + keyspace.readCount);
+                out.println("\tRead Latency: " + keyspace.readLatency() + " ms.");
+                out.println("\tWrite Count: " + keyspace.writeCount);
+                out.println("\tWrite Latency: " + keyspace.writeLatency() + " ms.");
+                out.println("\tPending Flushes: " + keyspace.pendingFlushes);
+
+                // print each table's information
+                List<StatsTable> tables = keyspace.tables;
+                for (StatsTable table : tables)
+                {
+                    out.println("\t\tTable" + (table.isIndex ? " (index): " + table.name
: ": ") + table.name);
+                    if (table.isLeveledSstable)
+                        out.println("\t\tSSTables in each level: [" + String.join(", ",
+                                                                                  table.sstablesInEachLevel)
+ "]");
+
+                    out.println("\t\tSpace used (live): " + table.spaceUsedLive);
+                    out.println("\t\tSpace used (total): " + table.spaceUsedTotal);
+                    out.println("\t\tSpace used by snapshots (total): " + table.spaceUsedBySnapshotsTotal);
+
+                    if (table.offHeapUsed)
+                        out.println("\t\tOff heap memory used (total): " + table.offHeapMemoryUsedTotal);
+                    out.println("\t\tSSTable Compression Ratio: " + table.sstableCompressionRatio);
+                    out.println("\t\tNumber of keys (estimate): " + table.numberOfKeysEstimate);
+                    out.println("\t\tMemtable cell count: " + table.memtableCellCount);
+                    out.println("\t\tMemtable data size: " + table.memtableDataSize);
+
+                    if (table.memtableOffHeapUsed)
+                        out.println("\t\tMemtable off heap memory used: " + table.memtableOffHeapMemoryUsed);
+                    out.println("\t\tMemtable switch count: " + table.memtableSwitchCount);
+                    out.println("\t\tLocal read count: " + table.localReadCount);
+                    out.printf("\t\tLocal read latency: %01.3f ms%n", table.localReadLatencyMs);
+                    out.println("\t\tLocal write count: " + table.localWriteCount);
+                    out.printf("\t\tLocal write latency: %01.3f ms%n", table.localWriteLatencyMs);
+                    out.println("\t\tPending flushes: " + table.pendingFlushes);
+
+                    out.println("\t\tBloom filter false positives: " + table.bloomFilterFalsePositives);
+                    out.printf("\t\tBloom filter false ratio: %01.5f%n", table.bloomFilterFalseRatio);
+                    out.println("\t\tBloom filter space used: " + table.bloomFilterSpaceUsed);
+
+                    if (table.bloomFilterOffHeapUsed)
+                        out.println("\t\tBloom filter off heap memory used: " + table.bloomFilterOffHeapMemoryUsed);
+                    if (table.indexSummaryOffHeapUsed)
+                        out.println("\t\tIndex summary off heap memory used: " + table.indexSummaryOffHeapMemoryUsed);
+                    if (table.compressionMetadataOffHeapUsed)
+                        out.println("\t\tCompression metadata off heap memory used: " + table.compressionMetadataOffHeapMemoryUsed);
+
+                    out.println("\t\tCompacted partition minimum bytes: " + table.compactedPartitionMinimumBytes);
+                    out.println("\t\tCompacted partition maximum bytes: " + table.compactedPartitionMaximumBytes);
+                    out.println("\t\tCompacted partition mean bytes: " + table.compactedPartitionMeanBytes);
+                    out.println("\t\tAverage live cells per slice (last five minutes): "
+ table.averageLiveCellsPerSliceLastFiveMinutes);
+                    out.println("\t\tMaximum live cells per slice (last five minutes): "
+ table.maximumLiveCellsPerSliceLastFiveMinutes);
+                    out.println("\t\tAverage tombstones per slice (last five minutes): "
+ table.averageTombstonesPerSliceLastFiveMinutes);
+                    out.println("\t\tMaximum tombstones per slice (last five minutes): "
+ table.maximumTombstonesPerSliceLastFiveMinutes);
+                    out.println("\t\tDropped Mutations: " + table.droppedMutations);
+                    out.println("");
+                }
+                out.println("----------------");
+            }
+        }
+    }
+
+    private static class JsonPrinter implements StatsPrinter<StatsHolder>
+    {
+        @Override
+        public void printFormat(StatsHolder data, PrintStream out)
+        {
+            JSONObject json = new JSONObject();
+            json.putAll(data.convert2Map());
+            out.println(json.toString());
+        }
+    }
+
+    private static class YamlPrinter implements StatsPrinter<StatsHolder>
+    {
+        @Override
+        public void printFormat(StatsHolder data, PrintStream out)
+        {
+            Yaml yaml = new Yaml();
+            out.println(yaml.dump(data.convert2Map()));
+        }
+    }
+
+}


Mime
View raw message