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 <[email protected]> Authored: Thu Apr 14 12:00:42 2016 -0500 Committer: Yuki Morishita <[email protected]> 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())); + } + } + +}
