1 package org.apache.helix.tools;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.beans.Introspector;
23 import java.beans.PropertyDescriptor;
24 import java.io.BufferedWriter;
25 import java.io.ByteArrayInputStream;
26 import java.io.EOFException;
27 import java.io.File;
28 import java.io.FileInputStream;
29 import java.io.FileNotFoundException;
30 import java.io.FileWriter;
31 import java.io.IOException;
32 import java.text.DateFormat;
33 import java.util.HashMap;
34 import java.util.LinkedList;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.zip.Adler32;
38 import java.util.zip.Checksum;
39
40 import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
41
42 import org.apache.jute.BinaryInputArchive;
43 import org.apache.jute.InputArchive;
44 import org.apache.jute.Record;
45 import org.apache.log4j.Logger;
46 import org.apache.zookeeper.KeeperException.NoNodeException;
47 import org.apache.zookeeper.ZooDefs.OpCode;
48 import org.apache.zookeeper.data.Stat;
49 import org.apache.zookeeper.server.DataNode;
50 import org.apache.zookeeper.server.DataTree;
51 import org.apache.zookeeper.server.persistence.FileHeader;
52 import org.apache.zookeeper.server.persistence.FileSnap;
53 import org.apache.zookeeper.server.persistence.FileTxnLog;
54 import org.apache.zookeeper.server.util.SerializeUtils;
55 import org.apache.zookeeper.txn.TxnHeader;
56
57 public class ZKLogFormatter
58 {
59 private static final Logger LOG = Logger.getLogger(ZKLogFormatter.class);
60 private static DateFormat dateTimeInstance = DateFormat.getDateTimeInstance(
61 DateFormat.SHORT, DateFormat.LONG);
62 private static HexBinaryAdapter adapter = new HexBinaryAdapter();
63 private static String fieldDelim = ":";
64 private static String fieldSep = " ";
65
66 static BufferedWriter bw = null;
67
68
69
70 public static void main(String[] args) throws Exception
71 {
72 if (args.length != 2 && args.length != 3)
73 {
74 System.err.println("USAGE: LogFormatter <log|snapshot> log_file");
75 System.exit(2);
76 }
77
78 if (args.length == 3)
79 {
80 bw = new BufferedWriter(new FileWriter(new File(args[2])));
81 }
82
83 if (args[0].equals("log"))
84 {
85 readTransactionLog(args[1]);
86 } else if (args[0].equals("snapshot"))
87 {
88 readSnapshotLog(args[1]);
89 }
90
91 if (bw != null)
92 {
93 bw.close();
94 }
95 }
96
97 private static void readSnapshotLog(String snapshotPath) throws Exception
98 {
99 FileInputStream fis = new FileInputStream(snapshotPath);
100 BinaryInputArchive ia = BinaryInputArchive.getArchive(fis);
101 Map<Long, Integer> sessions = new HashMap<Long, Integer>();
102 DataTree dt = new DataTree();
103 FileHeader header = new FileHeader();
104 header.deserialize(ia, "fileheader");
105 if (header.getMagic() != FileSnap.SNAP_MAGIC)
106 {
107 throw new IOException("mismatching magic headers " + header.getMagic()
108 + " != " + FileSnap.SNAP_MAGIC);
109 }
110 SerializeUtils.deserializeSnapshot(dt, ia, sessions);
111
112 if (bw != null)
113 {
114 bw.write(sessions.toString());
115 bw.newLine();
116 } else
117 {
118 System.out.println(sessions);
119 }
120 traverse(dt, 1, "/");
121
122 }
123
124
125
126
127 private static void traverse(DataTree dt, int startId, String startPath) throws Exception
128 {
129 LinkedList<Pair> queue = new LinkedList<Pair>();
130 queue.add(new Pair(startPath, startId));
131 while (!queue.isEmpty())
132 {
133 Pair pair = queue.removeFirst();
134 String path = pair._path;
135 DataNode head = dt.getNode(path);
136 Stat stat = new Stat();
137 byte[] data = null;
138 try
139 {
140 data = dt.getData(path, stat, null);
141 } catch (NoNodeException e)
142 {
143 e.printStackTrace();
144 }
145
146 format(startId, pair, head, data);
147 Set<String> children = head.getChildren();
148 if (children != null)
149 {
150 for (String child : children)
151 {
152 String childPath;
153 if (path.endsWith("/"))
154 {
155 childPath = path + child;
156 } else
157 {
158 childPath = path + "/" + child;
159 }
160 queue.add(new Pair(childPath, startId));
161 }
162 }
163 startId = startId + 1;
164 }
165
166 }
167
168 static class Pair
169 {
170
171 private final String _path;
172 private final int _parentId;
173
174 public Pair(String path, int parentId)
175 {
176 _path = path;
177 _parentId = parentId;
178 }
179
180 }
181
182 private static void format(int id, Pair pair, DataNode head, byte[] data) throws Exception
183 {
184 String dataStr = "";
185 if (data != null)
186 {
187 dataStr = new String(data).replaceAll("[\\s]+", "");
188 }
189 StringBuffer sb = new StringBuffer();
190
191 sb.append("id").append(fieldDelim).append(id).append(fieldSep);
192 sb.append("parent").append(fieldDelim).append(pair._parentId).append(fieldSep);
193 sb.append("path").append(fieldDelim).append(pair._path).append(fieldSep);
194 sb.append("session").append(fieldDelim).append("0x" +Long.toHexString(head.stat.getEphemeralOwner())).append(fieldSep);
195 sb.append("czxid").append(fieldDelim).append("0x" +Long.toHexString(head.stat.getCzxid())).append(fieldSep);
196 sb.append("ctime").append(fieldDelim).append(head.stat.getCtime()).append(fieldSep);
197 sb.append("mtime").append(fieldDelim).append(head.stat.getMtime()).append(fieldSep);
198 sb.append("cmzxid").append(fieldDelim).append("0x" +Long.toHexString(head.stat.getMzxid())).append(fieldSep);
199 sb.append("pzxid").append(fieldDelim).append("0x" +Long.toHexString(head.stat.getPzxid())).append(fieldSep);
200 sb.append("aversion").append(fieldDelim).append(head.stat.getAversion()).append(fieldSep);
201 sb.append("cversion").append(fieldDelim).append(head.stat.getCversion()).append(fieldSep);
202 sb.append("version").append(fieldDelim).append(head.stat.getVersion()).append(fieldSep);
203 sb.append("data").append(fieldDelim).append(dataStr).append(fieldSep);
204
205
206 if (bw != null)
207 {
208 bw.write(sb.toString());
209 bw.newLine();
210 } else
211 {
212 System.out.println(sb);
213 }
214
215 }
216
217 private static void readTransactionLog(String logfilepath)
218 throws FileNotFoundException, IOException, EOFException
219 {
220 FileInputStream fis = new FileInputStream(logfilepath);
221 BinaryInputArchive logStream = BinaryInputArchive.getArchive(fis);
222 FileHeader fhdr = new FileHeader();
223 fhdr.deserialize(logStream, "fileheader");
224
225 if (fhdr.getMagic() != FileTxnLog.TXNLOG_MAGIC)
226 {
227 System.err.println("Invalid magic number for " + logfilepath);
228 System.exit(2);
229 }
230
231 if (bw != null)
232 {
233 bw.write("ZooKeeper Transactional Log File with dbid "
234 + fhdr.getDbid() + " txnlog format version " + fhdr.getVersion());
235 bw.newLine();
236 } else
237 {
238 System.out.println("ZooKeeper Transactional Log File with dbid "
239 + fhdr.getDbid() + " txnlog format version " + fhdr.getVersion());
240 }
241
242
243 int count = 0;
244 while (true)
245 {
246 long crcValue;
247 byte[] bytes;
248 try
249 {
250 crcValue = logStream.readLong("crcvalue");
251
252 bytes = logStream.readBuffer("txnEntry");
253 } catch (EOFException e)
254 {
255 if (bw != null)
256 {
257 bw.write("EOF reached after " + count + " txns.");
258 bw.newLine();
259 } else
260 {
261 System.out.println("EOF reached after " + count + " txns.");
262 }
263
264 break;
265 }
266 if (bytes.length == 0)
267 {
268
269
270 if (bw != null)
271 {
272 bw.write("EOF reached after " + count + " txns.");
273 bw.newLine();
274 } else
275 {
276 System.out.println("EOF reached after " + count + " txns.");
277 }
278
279 return;
280 }
281 Checksum crc = new Adler32();
282 crc.update(bytes, 0, bytes.length);
283 if (crcValue != crc.getValue())
284 {
285 throw new IOException("CRC doesn't match " + crcValue + " vs "
286 + crc.getValue());
287 }
288 InputArchive iab = BinaryInputArchive
289 .getArchive(new ByteArrayInputStream(bytes));
290 TxnHeader hdr = new TxnHeader();
291 Record txn = SerializeUtils.deserializeTxn(iab, hdr);
292 if (bw != null)
293 {
294 bw.write(formatTransaction(hdr, txn));
295 bw.newLine();
296 } else
297 {
298 System.out.println(formatTransaction(hdr, txn));
299 }
300
301 if (logStream.readByte("EOR") != 'B')
302 {
303 LOG.error("Last transaction was partial.");
304 throw new EOFException("Last transaction was partial.");
305 }
306 count++;
307 }
308 }
309
310 static String op2String(int op)
311 {
312 switch (op)
313 {
314 case OpCode.notification:
315 return "notification";
316 case OpCode.create:
317 return "create";
318 case OpCode.delete:
319 return "delete";
320 case OpCode.exists:
321 return "exists";
322 case OpCode.getData:
323 return "getDate";
324 case OpCode.setData:
325 return "setData";
326 case OpCode.getACL:
327 return "getACL";
328 case OpCode.setACL:
329 return "setACL";
330 case OpCode.getChildren:
331 return "getChildren";
332 case OpCode.getChildren2:
333 return "getChildren2";
334 case OpCode.ping:
335 return "ping";
336 case OpCode.createSession:
337 return "createSession";
338 case OpCode.closeSession:
339 return "closeSession";
340 case OpCode.error:
341 return "error";
342 default:
343 return "unknown " + op;
344 }
345 }
346
347 private static String formatTransaction(TxnHeader header, Record txn)
348 {
349 StringBuilder sb = new StringBuilder();
350
351 sb.append("time").append(fieldDelim).append(header.getTime());
352 sb.append(fieldSep).append("session").append(fieldDelim).append("0x")
353 .append(Long.toHexString(header.getClientId()));
354 sb.append(fieldSep).append("cxid").append(fieldDelim).append("0x")
355 .append(Long.toHexString(header.getCxid()));
356 sb.append(fieldSep).append("zxid").append(fieldDelim).append("0x")
357 .append(Long.toHexString(header.getZxid()));
358 sb.append(fieldSep).append("type").append(fieldDelim)
359 .append(op2String(header.getType()));
360 if (txn != null)
361 {
362 try
363 {
364 byte[] data = null;
365 for (PropertyDescriptor pd : Introspector.getBeanInfo(txn.getClass())
366 .getPropertyDescriptors())
367 {
368 if (pd.getName().equalsIgnoreCase("data"))
369 {
370 data = (byte[]) pd.getReadMethod().invoke(txn);
371 continue;
372 }
373 if (pd.getReadMethod() != null && !"class".equals(pd.getName()))
374 {
375 sb.append(fieldSep)
376 .append(pd.getDisplayName())
377 .append(fieldDelim)
378 .append(
379 pd.getReadMethod().invoke(txn).toString()
380 .replaceAll("[\\s]+", ""));
381 }
382 }
383 if (data != null)
384 {
385 sb.append(fieldSep).append("data").append(fieldDelim)
386 .append(new String(data).replaceAll("[\\s]+", ""));
387 }
388 } catch (Exception e)
389 {
390 LOG.error(
391 "Error while retrieving bean property values for " + txn.getClass(),
392 e);
393 }
394 }
395
396 return sb.toString();
397 }
398
399 }