Repository: cassandra Updated Branches: refs/heads/cassandra-2.1 725b9b1f7 -> 6c6b7e40c refs/heads/cassandra-2.2 c08aaf69b -> d7195060c refs/heads/trunk 90033b921 -> c58b11bc7
Support long name output from nodetool commands patch by yukim; reviewed by carlyeks for CASSANDRA-7950 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/b6661ac5 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/b6661ac5 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/b6661ac5 Branch: refs/heads/trunk Commit: b6661ac52c5f8b2fe9640defbc7d86fad693fdf5 Parents: 90033b9 Author: Yuki Morishita <[email protected]> Authored: Wed Jan 20 10:51:56 2016 -0600 Committer: Yuki Morishita <[email protected]> Committed: Wed Feb 10 17:23:05 2016 -0600 ---------------------------------------------------------------------- CHANGES.txt | 1 + .../tools/nodetool/CompactionHistory.java | 92 +++++++-------- .../tools/nodetool/CompactionStats.java | 53 ++------- .../cassandra/tools/nodetool/ListSnapshots.java | 13 ++- .../tools/nodetool/formatter/TableBuilder.java | 103 +++++++++++++++++ .../nodetool/formatter/TableBuilderTest.java | 114 +++++++++++++++++++ 6 files changed, 284 insertions(+), 92 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/b6661ac5/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 551d147..3825165 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.4 + * Support long name output for nodetool commands (CASSANDRA-7950) * Encrypted hints (CASSANDRA-11040) * SASI index options validation (CASSANDRA-11136) * Optimize disk seek using min/max column name meta data when the LIMIT clause is used http://git-wip-us.apache.org/repos/asf/cassandra/blob/b6661ac5/src/java/org/apache/cassandra/tools/nodetool/CompactionHistory.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/tools/nodetool/CompactionHistory.java b/src/java/org/apache/cassandra/tools/nodetool/CompactionHistory.java index 1348d05..40c6887 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/CompactionHistory.java +++ b/src/java/org/apache/cassandra/tools/nodetool/CompactionHistory.java @@ -30,6 +30,7 @@ import io.airlift.command.Command; import org.apache.cassandra.tools.NodeProbe; import org.apache.cassandra.tools.NodeTool.NodeToolCmd; +import org.apache.cassandra.tools.nodetool.formatter.TableBuilder; import static com.google.common.collect.Iterables.toArray; @@ -48,9 +49,9 @@ public class CompactionHistory extends NodeToolCmd return; } - String format = "%-41s%-19s%-29s%-26s%-15s%-15s%s%n"; + TableBuilder table = new TableBuilder(); List<String> indexNames = tabularData.getTabularType().getIndexNames(); - System.out.printf(format, toArray(indexNames, Object.class)); + table.add(toArray(indexNames, String.class)); Set<?> values = tabularData.keySet(); List<CompactionHistoryRow> chr = new ArrayList<>(); @@ -69,53 +70,54 @@ public class CompactionHistory extends NodeToolCmd Collections.sort(chr); for (CompactionHistoryRow eachChc : chr) { - System.out.printf(format, eachChc.getAllAsArray()); + table.add(eachChc.getAllAsArray()); } + table.printTo(System.out); } -} - -/** - * Allows the Compaction History output to be ordered by 'compactedAt' - that is the - * time at which compaction finished. - */ -class CompactionHistoryRow implements Comparable<CompactionHistoryRow> -{ - private final String id; - private final String ksName; - private final String cfName; - private final long compactedAt; - private final long bytesIn; - private final long bytesOut; - private final String rowMerged; - CompactionHistoryRow(String id, String ksName, String cfName, long compactedAt, long bytesIn, long bytesOut, String rowMerged) + /** + * Allows the Compaction History output to be ordered by 'compactedAt' - that is the + * time at which compaction finished. + */ + private static class CompactionHistoryRow implements Comparable<CompactionHistoryRow> { - this.id = id; - this.ksName = ksName; - this.cfName = cfName; - this.compactedAt = compactedAt; - this.bytesIn = bytesIn; - this.bytesOut = bytesOut; - this.rowMerged = rowMerged; - } + private final String id; + private final String ksName; + private final String cfName; + private final long compactedAt; + private final long bytesIn; + private final long bytesOut; + private final String rowMerged; - public int compareTo(CompactionHistoryRow chc) - { - return Long.signum(chc.compactedAt - this.compactedAt); - } + CompactionHistoryRow(String id, String ksName, String cfName, long compactedAt, long bytesIn, long bytesOut, String rowMerged) + { + this.id = id; + this.ksName = ksName; + this.cfName = cfName; + this.compactedAt = compactedAt; + this.bytesIn = bytesIn; + this.bytesOut = bytesOut; + this.rowMerged = rowMerged; + } - public Object[] getAllAsArray() - { - Object[] obj = new Object[7]; - obj[0] = this.id; - obj[1] = this.ksName; - obj[2] = this.cfName; - Instant instant = Instant.ofEpochMilli(this.compactedAt); - LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); - obj[3] = ldt.toString(); - obj[4] = this.bytesIn; - obj[5] = this.bytesOut; - obj[6] = this.rowMerged; - return obj; + public int compareTo(CompactionHistoryRow chc) + { + return Long.signum(chc.compactedAt - this.compactedAt); + } + + public String[] getAllAsArray() + { + String[] obj = new String[7]; + obj[0] = this.id; + obj[1] = this.ksName; + obj[2] = this.cfName; + Instant instant = Instant.ofEpochMilli(this.compactedAt); + LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + obj[3] = ldt.toString(); + obj[4] = Long.toString(this.bytesIn); + obj[5] = Long.toString(this.bytesOut); + obj[6] = this.rowMerged; + return obj; + } } -} \ No newline at end of file +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/b6661ac5/src/java/org/apache/cassandra/tools/nodetool/CompactionStats.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/tools/nodetool/CompactionStats.java b/src/java/org/apache/cassandra/tools/nodetool/CompactionStats.java index b950a1c..bc1f85c 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/CompactionStats.java +++ b/src/java/org/apache/cassandra/tools/nodetool/CompactionStats.java @@ -17,22 +17,22 @@ */ package org.apache.cassandra.tools.nodetool; -import static java.lang.String.format; -import io.airlift.command.Command; -import io.airlift.command.Option; - import java.text.DecimalFormat; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import io.airlift.command.Command; +import io.airlift.command.Option; + import org.apache.cassandra.db.compaction.CompactionManagerMBean; import org.apache.cassandra.db.compaction.OperationType; import org.apache.cassandra.io.util.FileUtils; import org.apache.cassandra.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool; import org.apache.cassandra.tools.NodeTool.NodeToolCmd; +import org.apache.cassandra.tools.nodetool.formatter.TableBuilder; + +import static java.lang.String.format; @Command(name = "compactionstats", description = "Print statistics on compactions") public class CompactionStats extends NodeToolCmd @@ -54,7 +54,7 @@ public class CompactionStats extends NodeToolCmd for (Entry<String, Integer> tableEntry : ksEntry.getValue().entrySet()) numTotalPendingTask += tableEntry.getValue(); } - System.out.println("pending tasks: "+numTotalPendingTask); + System.out.println("pending tasks: " + numTotalPendingTask); for (Entry<String, Map<String, Integer>> ksEntry : pendingTaskNumberByTable.entrySet()) { String ksName = ksEntry.getKey(); @@ -63,26 +63,17 @@ public class CompactionStats extends NodeToolCmd String tableName = tableEntry.getKey(); int pendingTaskCount = tableEntry.getValue(); - StringBuilder builder = new StringBuilder(); - builder.append("- "); - builder.append(ksName); - builder.append('.'); - builder.append(tableName); - builder.append(": "); - builder.append(pendingTaskCount); - System.out.println(builder.toString()); + System.out.println("- " + ksName + '.' + tableName + ": " + pendingTaskCount); } } System.out.println(); long remainingBytes = 0; + TableBuilder table = new TableBuilder(); List<Map<String, String>> compactions = cm.getCompactions(); if (!compactions.isEmpty()) { int compactionThroughput = probe.getCompactionThroughput(); - List<String[]> lines = new ArrayList<>(); - int[] columnSizes = new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }; - - addLine(lines, columnSizes, "id", "compaction type", "keyspace", "table", "completed", "total", "unit", "progress"); + table.add("id", "compaction type", "keyspace", "table", "completed", "total", "unit", "progress"); for (Map<String, String> c : compactions) { long total = Long.parseLong(c.get("total")); @@ -95,24 +86,11 @@ public class CompactionStats extends NodeToolCmd String unit = c.get("unit"); String percentComplete = total == 0 ? "n/a" : new DecimalFormat("0.00").format((double) completed / total * 100) + "%"; String id = c.get("compactionId"); - addLine(lines, columnSizes, id, taskType, keyspace, columnFamily, completedStr, totalStr, unit, percentComplete); + table.add(id, taskType, keyspace, columnFamily, completedStr, totalStr, unit, percentComplete); if (taskType.equals(OperationType.COMPACTION.toString())) remainingBytes += total - completed; } - - StringBuilder buffer = new StringBuilder(); - for (int columnSize : columnSizes) { - buffer.append("%"); - buffer.append(columnSize + 3); - buffer.append("s"); - } - buffer.append("%n"); - String format = buffer.toString(); - - for (String[] line : lines) - { - System.out.printf(format, line[0], line[1], line[2], line[3], line[4], line[5], line[6], line[7]); - } + table.printTo(System.out); String remainingTime = "n/a"; if (compactionThroughput != 0) @@ -123,11 +101,4 @@ public class CompactionStats extends NodeToolCmd System.out.printf("%25s%10s%n", "Active compaction remaining time : ", remainingTime); } } - - private void addLine(List<String[]> lines, int[] columnSizes, String... columns) { - lines.add(columns); - for (int i = 0; i < columns.length; i++) { - columnSizes[i] = Math.max(columnSizes[i], columns[i].length()); - } - } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cassandra/blob/b6661ac5/src/java/org/apache/cassandra/tools/nodetool/ListSnapshots.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/tools/nodetool/ListSnapshots.java b/src/java/org/apache/cassandra/tools/nodetool/ListSnapshots.java index ee7bf34..1b3065b 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/ListSnapshots.java +++ b/src/java/org/apache/cassandra/tools/nodetool/ListSnapshots.java @@ -17,17 +17,17 @@ */ package org.apache.cassandra.tools.nodetool; -import io.airlift.command.Command; - import java.util.List; import java.util.Map; import java.util.Set; - import javax.management.openmbean.TabularData; +import io.airlift.command.Command; + import org.apache.cassandra.io.util.FileUtils; import org.apache.cassandra.tools.NodeProbe; import org.apache.cassandra.tools.NodeTool.NodeToolCmd; +import org.apache.cassandra.tools.nodetool.formatter.TableBuilder; @Command(name = "listsnapshots", description = "Lists all the snapshots along with the size on disk and true size.") public class ListSnapshots extends NodeToolCmd @@ -47,10 +47,10 @@ public class ListSnapshots extends NodeToolCmd } final long trueSnapshotsSize = probe.trueSnapshotsSize(); - final String format = "%-20s%-29s%-29s%-19s%-19s%n"; + TableBuilder table = new TableBuilder(); // display column names only once final List<String> indexNames = snapshotDetails.entrySet().iterator().next().getValue().getTabularType().getIndexNames(); - System.out.printf(format, (Object[]) indexNames.toArray(new String[indexNames.size()])); + table.add(indexNames.toArray(new String[indexNames.size()])); for (final Map.Entry<String, TabularData> snapshotDetail : snapshotDetails.entrySet()) { @@ -58,9 +58,10 @@ public class ListSnapshots extends NodeToolCmd for (Object eachValue : values) { final List<?> value = (List<?>) eachValue; - System.out.printf(format, value.toArray(new Object[value.size()])); + table.add(value.toArray(new String[value.size()])); } } + table.printTo(System.out); System.out.println("\nTotal TrueDiskSpaceUsed: " + FileUtils.stringifyFileSize(trueSnapshotsSize) + "\n"); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/b6661ac5/src/java/org/apache/cassandra/tools/nodetool/formatter/TableBuilder.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/tools/nodetool/formatter/TableBuilder.java b/src/java/org/apache/cassandra/tools/nodetool/formatter/TableBuilder.java new file mode 100644 index 0000000..a56e52e --- /dev/null +++ b/src/java/org/apache/cassandra/tools/nodetool/formatter/TableBuilder.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.cassandra.tools.nodetool.formatter; + +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import javax.annotation.Nonnull; + +/** + * Build and print table. + * + * usage: + * <pre> + * {@code + * TableBuilder table = new TableBuilder(); + * for (String[] row : data) + * { + * table.add(row); + * } + * table.print(System.out); + * } + * </pre> + */ +public class TableBuilder +{ + // column delimiter char + private final char columnDelimiter; + + private int[] maximumColumnWidth; + private final List<String[]> rows = new ArrayList<>(); + + public TableBuilder() + { + this(' '); + } + + public TableBuilder(char columnDelimiter) + { + this.columnDelimiter = columnDelimiter; + } + + public void add(@Nonnull String... row) + { + Objects.requireNonNull(row); + + if (rows.isEmpty()) + { + maximumColumnWidth = new int[row.length]; + } + + // expand max column widths if given row has more columns + if (row.length > maximumColumnWidth.length) + { + int[] tmp = new int[row.length]; + System.arraycopy(maximumColumnWidth, 0, tmp, 0, maximumColumnWidth.length); + maximumColumnWidth = tmp; + } + // calculate maximum column width + int i = 0; + for (String col : row) + { + maximumColumnWidth[i] = Math.max(maximumColumnWidth[i], col != null ? col.length() : 1); + i++; + } + rows.add(row); + } + + public void printTo(PrintStream out) + { + if (rows.isEmpty()) + return; + + for (String[] row : rows) + { + for (int i = 0; i < maximumColumnWidth.length; i++) + { + String col = i < row.length ? row[i] : ""; + out.print(String.format("%-" + maximumColumnWidth[i] + 's', col != null ? col : "")); + if (i < maximumColumnWidth.length - 1) + out.print(columnDelimiter); + } + out.println(); + } + } +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/b6661ac5/test/unit/org/apache/cassandra/tools/nodetool/formatter/TableBuilderTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/tools/nodetool/formatter/TableBuilderTest.java b/test/unit/org/apache/cassandra/tools/nodetool/formatter/TableBuilderTest.java new file mode 100644 index 0000000..9782b5b --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/formatter/TableBuilderTest.java @@ -0,0 +1,114 @@ +/* + * 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.formatter; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class TableBuilderTest +{ + @Test + public void testEmptyRow() + { + TableBuilder table = new TableBuilder(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (PrintStream out = new PrintStream(baos)) + { + table.printTo(out); + } + assertEquals("", baos.toString()); + } + + @Test + public void testOneRow() + { + TableBuilder table = new TableBuilder(); + + table.add("a", "bb", "ccc"); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (PrintStream out = new PrintStream(baos)) + { + table.printTo(out); + } + assertEquals(String.format("a bb ccc%n"), baos.toString()); + } + + @Test + public void testRows() + { + TableBuilder table = new TableBuilder(); + table.add("a", "bb", "ccc"); + table.add("aaa", "bb", "c"); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (PrintStream out = new PrintStream(baos)) + { + table.printTo(out); + } + assertEquals(String.format("a bb ccc%naaa bb c %n"), baos.toString()); + } + + @Test + public void testNullColumn() + { + TableBuilder table = new TableBuilder(); + table.add("a", "b", "c"); + table.add("a", null, "c"); + table.add("a", null, null); + table.add(null, "b", "c"); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (PrintStream out = new PrintStream(baos)) + { + table.printTo(out); + } + assertEquals(String.format("a b c%na c%na %n b c%n"), baos.toString()); + } + + @Test + public void testRowsOfDifferentSize() + { + TableBuilder table = new TableBuilder(); + table.add("a", "b", "c"); + table.add("a", "b", "c", "d", "e"); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (PrintStream out = new PrintStream(baos)) + { + table.printTo(out); + } + assertEquals(baos.toString(), String.format("a b c %na b c d e%n"), baos.toString()); + } + + @Test + public void testDelimiter() + { + TableBuilder table = new TableBuilder('\t'); + + table.add("a", "bb", "ccc"); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (PrintStream out = new PrintStream(baos)) + { + table.printTo(out); + } + assertEquals(String.format("a\tbb\tccc%n"), baos.toString()); + } +} \ No newline at end of file
