View Javadoc

1   package org.apache.helix.tools;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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     * @param args
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    * Level order traversal
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       // print the node
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     //@formatter:off
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     //@formatter:on
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         // Since we preallocate, we define EOF to be an
269         // empty transaction
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 }