Repository: zookeeper Updated Branches: refs/heads/master b58791016 -> 657e2e832
ZOOKEEPER-3142: Extend SnapshotFormatter to dump data in json format Author: Brian Nixon <ni...@fb.com> Reviewers: Michael Han <h...@apache.org> Closes #619 from enixon/extend-sf Project: http://git-wip-us.apache.org/repos/asf/zookeeper/repo Commit: http://git-wip-us.apache.org/repos/asf/zookeeper/commit/657e2e83 Tree: http://git-wip-us.apache.org/repos/asf/zookeeper/tree/657e2e83 Diff: http://git-wip-us.apache.org/repos/asf/zookeeper/diff/657e2e83 Branch: refs/heads/master Commit: 657e2e832ee31738d70d54a046d4254f3ed8267a Parents: b587910 Author: Brian Nixon <ni...@fb.com> Authored: Fri Sep 14 16:09:36 2018 -0700 Committer: Michael Han <h...@apache.org> Committed: Fri Sep 14 16:09:36 2018 -0700 ---------------------------------------------------------------------- build.xml | 2 +- ivy.xml | 3 + .../zookeeper/server/SnapshotFormatter.java | 179 +++++++++++++++---- 3 files changed, 150 insertions(+), 34 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/zookeeper/blob/657e2e83/build.xml ---------------------------------------------------------------------- diff --git a/build.xml b/build.xml index 18eb104..2043e68 100644 --- a/build.xml +++ b/build.xml @@ -59,7 +59,7 @@ xmlns:cs="antlib:com.puppycrawl.tools.checkstyle.ant"> <property name="kerby.version" value="1.1.0"/> <property name="clover.version" value="4.2.1" /> - + <property name="json.version" value="1.1.1"/> <!-- ====================================================== --> <!-- Project properties --> http://git-wip-us.apache.org/repos/asf/zookeeper/blob/657e2e83/ivy.xml ---------------------------------------------------------------------- diff --git a/ivy.xml b/ivy.xml index 3387fca..663216e 100644 --- a/ivy.xml +++ b/ivy.xml @@ -63,6 +63,9 @@ <artifact name="netty" type="jar" conf="default"/> </dependency> + <dependency org="com.googlecode.json-simple" name="json-simple" rev="${json.version}" > + <exclude org="junit" module="junit"/> + </dependency> <dependency org="junit" name="junit" rev="${junit.version}" conf="test->default"/> <dependency org="org.mockito" name="mockito-all" rev="${mockito.version}" conf="test->default"/> http://git-wip-us.apache.org/repos/asf/zookeeper/blob/657e2e83/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java ---------------------------------------------------------------------- diff --git a/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java b/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java index 360ad8d..2a80d89 100644 --- a/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java +++ b/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java @@ -6,9 +6,9 @@ * 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 - * + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> * 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. @@ -20,8 +20,10 @@ package org.apache.zookeeper.server; import java.io.BufferedInputStream; import java.io.FileInputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.Base64; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -34,71 +36,130 @@ import org.apache.jute.InputArchive; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.data.StatPersisted; import org.apache.zookeeper.server.persistence.FileSnap; +import org.apache.zookeeper.server.persistence.Util; +import org.json.simple.JSONValue; + +import static org.apache.zookeeper.server.persistence.FileSnap.SNAPSHOT_FILE_PREFIX; /** * Dump a snapshot file to stdout. + * + * For JSON format, followed https://dev.yorhel.nl/ncdu/jsonfmt */ @InterfaceAudience.Public public class SnapshotFormatter { + // per-znode counter so ncdu treats each as a unique object + private static Integer INODE_IDX = 1000; + /** * USAGE: SnapshotFormatter snapshot_file */ public static void main(String[] args) throws Exception { - if (args.length != 1) { - System.err.println("USAGE: SnapshotFormatter snapshot_file"); + String snapshotFile = null; + boolean dumpData = false; + boolean dumpJson = false; + + int i; + for (i = 0; i < args.length; i++) { + if (args[i].equals("-d")) { + dumpData = true; + } else if (args[i].equals("-json")) { + dumpJson = true; + } else { + snapshotFile = args[i]; + i++; + break; + } + } + if (args.length != i || snapshotFile == null) { + System.err.println("USAGE: SnapshotFormatter [-d|-json] snapshot_file"); + System.err.println(" -d dump the data for each znode"); + System.err.println(" -json dump znode info in json format"); System.exit(ExitCode.INVALID_INVOCATION.getValue()); } - new SnapshotFormatter().run(args[0]); + if (dumpData && dumpJson) { + System.err.println("Cannot specify both data dump (-d) and json mode (-json) in same call"); + System.exit(ExitCode.INVALID_INVOCATION.getValue()); + } + + new SnapshotFormatter().run(snapshotFile, dumpData, dumpJson); } - - public void run(String snapshotFileName) throws IOException { - InputStream is = new CheckedInputStream( + + public void run(String snapshotFileName, boolean dumpData, boolean dumpJson) + throws IOException { + File snapshotFile = new File(snapshotFileName); + try (InputStream is = new CheckedInputStream( new BufferedInputStream(new FileInputStream(snapshotFileName)), - new Adler32()); - InputArchive ia = BinaryInputArchive.getArchive(is); - - FileSnap fileSnap = new FileSnap(null); + new Adler32())) { + InputArchive ia = BinaryInputArchive.getArchive(is); + + FileSnap fileSnap = new FileSnap(null); + + DataTree dataTree = new DataTree(); + Map<Long, Integer> sessions = new HashMap<Long, Integer>(); - DataTree dataTree = new DataTree(); - Map<Long, Integer> sessions = new HashMap<Long, Integer>(); - - fileSnap.deserialize(dataTree, sessions, ia); + fileSnap.deserialize(dataTree, sessions, ia); + long fileNameZxid = Util.getZxidFromName(snapshotFile.getName(), SNAPSHOT_FILE_PREFIX); - printDetails(dataTree, sessions); + if (dumpJson) { + printSnapshotJson(dataTree); + } else { + printDetails(dataTree, sessions, dumpData, fileNameZxid); + } + } } - private void printDetails(DataTree dataTree, Map<Long, Integer> sessions) { - printZnodeDetails(dataTree); + private void printDetails( + DataTree dataTree, Map<Long, Integer> sessions, + boolean dumpData, long fileNameZxid + ) { + long dtZxid = printZnodeDetails(dataTree, dumpData); printSessionDetails(dataTree, sessions); + System.out.println(String.format("----%nLast zxid: 0x%s", Long.toHexString(Math.max(fileNameZxid, dtZxid)))); } - private void printZnodeDetails(DataTree dataTree) { - System.out.println(String.format("ZNode Details (count=%d):", - dataTree.getNodeCount())); - - printZnode(dataTree, "/"); + private long printZnodeDetails(DataTree dataTree, boolean dumpData) { + System.out.println(String.format( + "ZNode Details (count=%d):", + dataTree.getNodeCount() + )); + + final long zxid = printZnode(dataTree, "/", dumpData); System.out.println("----"); + return zxid; } - private void printZnode(DataTree dataTree, String name) { + private long printZnode(DataTree dataTree, String name, boolean dumpData) { System.out.println("----"); DataNode n = dataTree.getNode(name); Set<String> children; - synchronized(n) { // keep findbugs happy + long zxid; + synchronized (n) { // keep findbugs happy System.out.println(name); printStat(n.stat); - if (n.data != null) { - System.out.println(" dataLength = " + n.data.length); + zxid = Math.max(n.stat.getMzxid(), n.stat.getPzxid()); + if (dumpData) { + System.out.println(" data = " + (n.data == null ? "" : + Base64.getEncoder().encodeToString(n.data))); } else { - System.out.println(" no data"); + System.out.println(" dataLength = " + + (n.data == null ? 0 : n.data.length)); } children = n.getChildren(); } - for (String child : children) { - printZnode(dataTree, name + (name.equals("/") ? "" : "/") + child); + if (children != null) { + for (String child : children) { + long cxid = printZnode( + dataTree, + name + (name.equals("/") ? "" : "/") + child, + dumpData + ); + zxid = Math.max(zxid, cxid); + } } + return zxid; } private void printSessionDetails(DataTree dataTree, Map<Long, Integer> sessions) { @@ -106,7 +167,8 @@ public class SnapshotFormatter { for (Map.Entry<Long, Integer> e : sessions.entrySet()) { long sid = e.getKey(); System.out.println(String.format("%#016x, %d, %d", - sid, e.getValue(), dataTree.getEphemerals(sid).size())); + sid, e.getValue(), dataTree.getEphemerals(sid).size() + )); } } @@ -125,4 +187,55 @@ public class SnapshotFormatter { private void printHex(String prefix, long value) { System.out.println(String.format(" %s = %#016x", prefix, value)); } + + private void printSnapshotJson(final DataTree dataTree) { + System.out.printf("[1,0,{\"progname\":\"SnapshotFormatter.java\",\"progver\":\"0.01\",\"timestamp\":%d}", System.currentTimeMillis()); + printZnodeJson(dataTree, "/"); + System.out.print("]"); + } + + private void printZnodeJson(final DataTree dataTree, final String fullPath) { + + final DataNode n = dataTree.getNode(fullPath); + + if (null == n) { + System.err.println("DataTree Node for " + fullPath + " doesn't exist"); + return; + } + + final String name = fullPath.equals("/") ? fullPath : fullPath.substring(fullPath.lastIndexOf( + "/") + 1); + + System.out.print(","); + + int dataLen; + synchronized (n) { // keep findbugs happy + dataLen = (n.data == null) ? 0 : n.data.length; + } + StringBuilder nodeSB = new StringBuilder(); + nodeSB.append("{"); + nodeSB.append("\"name\":\"").append(JSONValue.escape(name)).append("\"").append(","); + nodeSB.append("\"asize\":").append(dataLen).append(","); + nodeSB.append("\"dsize\":").append(dataLen).append(","); + nodeSB.append("\"dev\":").append(0).append(","); + nodeSB.append("\"ino\":").append(++INODE_IDX); + nodeSB.append("}"); + + Set<String> children; + synchronized (n) { // keep findbugs happy + children = n.getChildren(); + } + if (children != null && children.size() > 0) { + System.out.print("[" + nodeSB); + for (String child : children) { + printZnodeJson( + dataTree, + fullPath + (fullPath.equals("/") ? "" : "/") + child + ); + } + System.out.print("]"); + } else { + System.out.print(nodeSB); + } + } }