1 17 18 package org.netbeans.lib.uihandler; 19 20 import java.io.ByteArrayInputStream ; 21 import java.io.EOFException ; 22 import java.io.IOException ; 23 import java.io.InputStream ; 24 import java.io.OutputStream ; 25 import java.io.PushbackInputStream ; 26 import java.io.SequenceInputStream ; 27 import java.util.ArrayList ; 28 import java.util.Collections ; 29 import java.util.EnumMap ; 30 import java.util.Enumeration ; 31 import java.util.List ; 32 import java.util.Map ; 33 import java.util.MissingResourceException ; 34 import java.util.ResourceBundle ; 35 import java.util.logging.Formatter ; 36 import java.util.logging.Handler ; 37 import java.util.logging.Level ; 38 import java.util.logging.LogRecord ; 39 import java.util.logging.Logger ; 40 import java.util.logging.XMLFormatter ; 41 import javax.xml.parsers.ParserConfigurationException ; 42 import javax.xml.parsers.SAXParser ; 43 import javax.xml.parsers.SAXParserFactory ; 44 import org.xml.sax.Attributes ; 45 import org.xml.sax.Locator ; 46 import org.xml.sax.SAXException ; 47 import org.xml.sax.SAXParseException ; 48 import org.xml.sax.helpers.DefaultHandler ; 49 50 54 public final class LogRecords { 55 private LogRecords() { 56 } 57 58 private static final Logger LOG = Logger.getLogger(LogRecords.class.getName()); 59 60 private static final Formatter FORMATTER = new XMLFormatter () { 61 public String formatMessage(LogRecord r) { 62 return super.formatMessage(r); 63 } 65 }; 66 67 public static void write(OutputStream os, LogRecord rec) throws IOException { 68 String formated = FORMATTER.format(rec); 69 byte[] arr = formated.getBytes("utf-8"); 70 os.write(arr); 71 } 72 73 public static void scan(InputStream is, Handler h) throws IOException { 74 PushbackInputStream wrap = new PushbackInputStream (is, 32); 75 byte[] arr = new byte[5]; 76 int len = wrap.read(arr); 77 if (len == -1) { 78 return; 79 } 80 wrap.unread(arr, 0, len); 81 82 if (arr[0] == '<' && 83 arr[1] == '?' && 84 arr[2] == 'x' && 85 arr[3] == 'm' && 86 arr[4] == 'l' 87 ) { 88 is = wrap; 89 } else { 90 ByteArrayInputStream header = new ByteArrayInputStream ( 91 "<?xml version='1.0' encoding='UTF-8'?><uigestures version='1.0'>".getBytes() 92 ); 93 ByteArrayInputStream footer = new ByteArrayInputStream ( 94 "</uigestures>".getBytes() 95 ); 96 is = new SequenceInputStream ( 97 new SequenceInputStream (header, wrap), 98 footer 99 ); 100 } 101 102 SAXParserFactory f = SAXParserFactory.newInstance(); 103 f.setValidating(false); 104 SAXParser p; 105 try { 106 f.setFeature("http://apache.org/xml/features/continue-after-fatal-error", true); p = f.newSAXParser(); 108 } catch (ParserConfigurationException ex) { 109 LOG.log(Level.SEVERE, null, ex); 110 throw (IOException )new IOException (ex.getMessage()).initCause(ex); 111 } catch (SAXException ex) { 112 LOG.log(Level.SEVERE, null, ex); 113 throw (IOException )new IOException (ex.getMessage()).initCause(ex); 114 } 115 116 Parser parser = new Parser (h); 117 try { 118 p.parse(is, parser); 119 } catch (SAXException ex) { 120 LOG.log(Level.WARNING, null, ex); 121 throw (IOException )new IOException (ex.getMessage()).initCause(ex); 122 } 123 } 124 125 126 public static LogRecord read(InputStream is) throws IOException { 127 int[] end = new int[1]; 128 byte[] data = readXMLBlock(is, end); 129 if (data == null) { 130 return null; 131 } 132 133 String s = new String (data, 0, end[0]); 134 135 if (s.indexOf("record>") == -1) { Logger.getLogger(LogRecords.class.getName()).info("Skipping: " + s); data = readXMLBlock(is, end); 139 if (data == null) { 140 return null; 141 } 142 s = new String (data, 0, end[0]); 143 } 144 145 s = s.replaceAll("&", "&").replaceAll(">", ">") 146 .replaceAll("<", "<"); 147 148 String millis = content(s, "millis", true); 149 String seq = content(s, "sequence", true); 150 String lev = content(s, "level", true); 151 String thread = content(s, "thread", true); 152 String msg = content(s, "message", true); 153 String key = content(s, "key", false); 154 String catalog = content(s, "catalog", false); 155 156 LogRecord r = new LogRecord (parseLevel(lev), key != null && catalog != null ? key : msg); 157 r.setThreadID(Integer.parseInt(thread)); 158 r.setSequenceNumber(Long.parseLong(seq)); 159 r.setMillis(Long.parseLong(millis)); 160 if (catalog != null && key != null) { 161 r.setResourceBundleName(catalog); 162 if (!"<null>".equals(catalog)) { try { 164 ResourceBundle b = ResourceBundle.getBundle(catalog); 165 b.getObject(key); 166 r.setResourceBundle(b); 168 } catch (MissingResourceException e) { 169 LOG.log(Level.CONFIG, "Cannot find resource bundle for {0} and key {1}", new Object [] { catalog, key }); 170 r.setResourceBundle(new FakeBundle(key, msg)); 171 } 172 } 173 174 int[] paramFrom = new int[1]; 175 List <String > params = new ArrayList <String >(); 176 for (;;) { 177 String p = content(s, "param", false, paramFrom); 178 if (p == null) { 179 break; 180 } 181 params.add(p); 182 } 183 184 r.setParameters(params.toArray()); 185 } 186 187 String exception = content(s, "exception", false); 188 if (exception != null) { 189 FakeException currentEx = new FakeException(null); 190 int[] stackIndex = new int[1]; 191 currentEx.message = content(exception, "message", true, stackIndex); 192 193 if (currentEx.message.equals(r.getMessage())) { 194 r.setMessage(null); 196 } 197 198 for (;;) { 199 String frame = content(exception, "frame", false, stackIndex); 200 if (frame == null) { 201 break; 202 } 203 204 String clazz = content(frame, "class", true); 205 String method = content(frame, "method", false); 206 String line = content(frame, "line", false); 207 LOG.finer("StackTrace " + clazz + "." + method + ":" + line); 208 StackTraceElement elem = new StackTraceElement ( 209 clazz, 210 method, 211 null, 212 line == null ? -1 : Integer.parseInt(line) 213 ); 214 currentEx.trace.add(elem); 215 } 216 r.setThrown(currentEx); 217 } 218 219 return r; 220 } 221 222 static Level parseLevel(String lev) { 223 return "USER".equals(lev) ? Level.SEVERE : Level.parse(lev); 224 } 225 private static String content(String where, String what, boolean fail) throws IOException { 226 return content(where, what, fail, new int[1]); 227 } 228 private static String content(String where, String what, boolean fail, int[] from) throws IOException { 229 int indx = where.indexOf("<" + what + ">", from[0]); 230 if (indx == -1) { 231 if (fail) { 232 throw new IOException ("Not found: <" + what + "> inside of:\n"+ where); } else { 234 return null; 235 } 236 } 237 int begin = indx + what.length() + 2; 238 239 int end = where.indexOf("</" + what + ">", indx); 240 if (indx == -1) { 241 throw new IOException ("Not found: </" + what + "> inside of:\n"+ where); } 243 from[0] = end; 244 245 return where.substring(begin, end); 246 } 247 248 private static byte[] readXMLBlock(InputStream is, int[] len) throws IOException { 249 byte[] arr = new byte[4096 * 12]; 250 int index = 0; 251 252 for (;;) { 253 int ch = is.read(); 254 if (ch == -1) { 255 return null; 256 } 257 258 if (ch == '<') { 259 arr[index++] = '<'; 260 break; 261 } 262 } 263 264 int depth = 0; 265 boolean inTag = true; 266 boolean seenSlash = false; 267 boolean seenQuest = false; 268 int uigestures = 0; 269 for (;;) { 270 if (!inTag && depth == 0) { 271 break; 272 } 273 274 int ch = is.read(); 275 if (ch == -1) { 276 throw new EOFException (); 277 } 278 if (index == arr.length) { 279 throw new EOFException ("Buffer size " + arr.length + " exceeded"); } 281 282 arr[index++] = (byte)ch; 283 284 if (inTag) { 285 switch (uigestures) { 286 case 0: if (ch == 'u') uigestures = 1; else uigestures = 0; break; 287 case 1: if (ch == 'i') uigestures = 2; else uigestures = 0; break; 288 case 2: if (ch == 'g') uigestures = 3; else uigestures = 0; break; 289 case 3: if (ch == 'e') uigestures = 4; else uigestures = 0; break; 290 case 4: if (ch == 's') uigestures = 5; else uigestures = 0; break; 291 case 5: if (ch == 't') uigestures = 6; else uigestures = 0; break; 292 case 6: if (ch == 'u') uigestures = 7; else uigestures = 0; break; 293 case 7: if (ch == 'r') uigestures = 8; else uigestures = 0; break; 294 case 8: if (ch == 'e') uigestures = 9; else uigestures = 0; break; 295 case 9: if (ch == 's') uigestures = 10; else uigestures = 0; break; 296 case 10: } 298 299 if (ch == '?') { 300 seenQuest = true; 301 } else if (ch == '/') { 302 seenSlash = true; 303 } else if (ch == '>') { 304 inTag = false; 305 if (uigestures == 10) { 306 return readXMLBlock(is, len); 308 } 309 if (seenSlash) { 310 depth--; 311 } else if (seenQuest) { 312 } else { 314 depth++; 315 } 316 } 317 } else { 318 if (ch == '<') { 319 inTag = true; 320 seenSlash = false; 321 seenQuest = false; 322 uigestures = 0; 323 } 324 } 325 } 326 len[0] = index; 327 return arr; 328 } 329 330 private static final class Parser extends DefaultHandler { 331 private Handler callback; 332 private static enum Elem { 333 UIGESTURES, RECORD, DATE, MILLIS, SEQUENCE, LEVEL, THREAD, 334 MESSAGE, KEY, PARAM, FRAME, CLASS, METHOD, LOGGER, EXCEPTION, LINE, 335 CATALOG; 336 337 public String parse(Map <Elem,String > values) { 338 String v = values.get(this); 339 return v; 340 } 341 } 342 private Map <Elem,String > values = new EnumMap <Elem,String >(Elem.class); 343 private Elem current; 344 private FakeException currentEx; 345 private List <String > params; 346 private StringBuilder chars = new StringBuilder (); 347 348 public Parser(Handler c) { 349 this.callback = c; 350 } 351 352 353 public void setDocumentLocator(Locator locator) { 354 } 355 356 public void startDocument() throws SAXException { 357 } 358 359 public void endDocument() throws SAXException { 360 callback.flush(); 361 } 362 363 public void startPrefixMapping(String prefix, String uri) throws SAXException { 364 } 365 366 public void endPrefixMapping(String prefix) throws SAXException { 367 } 368 369 public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { 370 if (LOG.isLoggable(Level.FINEST)) { 371 LOG.log(Level.FINEST, "uri: {0} localName: {1} qName: {2} atts: {3}", new Object [] { uri, localName, qName, atts }); 372 } 373 374 try { 375 current = Elem.valueOf(qName.toUpperCase()); 376 if (current == Elem.EXCEPTION) { 377 currentEx = new FakeException(new EnumMap <Elem,String >(values)); 378 } 379 } catch (IllegalArgumentException ex) { 380 LOG.log(Level.FINE, "Uknown tag " + qName, ex); 381 current = null; 382 } 383 chars = new StringBuilder (); 384 } 385 386 public void endElement(String uri, String localName, String qName) throws SAXException { 387 if (current != null) { 388 String v = chars.toString(); 389 values.put(current, v); 390 if (current == Elem.PARAM) { 391 if (params == null) { 392 params = new ArrayList <String >(); 393 } 394 params.add(v); 395 if (params.size() > 500) { 396 LOG.severe("Too long params when reading a record. Deleting few."); for (String p : params) { 398 LOG.fine(p); 399 } 400 params.clear(); 401 } 402 } 403 } 404 current = null; 405 chars = new StringBuilder (); 406 407 if (currentEx != null && currentEx.values != null) { 408 if ("frame".equals(qName)) { String line = Elem.LINE.parse(values); 410 StackTraceElement elem = new StackTraceElement ( 411 Elem.CLASS.parse(values), 412 Elem.METHOD.parse(values), 413 null, 414 line == null ? -1 : Integer.parseInt(line) 415 ); 416 currentEx.trace.add(elem); 417 values.remove(Elem.CLASS); 418 values.remove(Elem.METHOD); 419 values.remove(Elem.LINE); 420 } 421 if ("exception".equals(qName)) { 422 currentEx.message = values.get(Elem.MESSAGE); 423 values = currentEx.values; 424 currentEx.values = null; 425 } 426 return; 427 } 428 429 if ("record".equals(qName)) { String millis = Elem.MILLIS.parse(values); 431 String seq = Elem.SEQUENCE.parse(values); 432 String lev = Elem.LEVEL.parse(values); 433 String thread = Elem.THREAD.parse(values); 434 String msg = Elem.MESSAGE.parse(values); 435 String key = Elem.KEY.parse(values); 436 String catalog = Elem.CATALOG.parse(values); 437 438 LogRecord r = new LogRecord (parseLevel(lev), key != null && catalog != null ? key : msg); 439 r.setThreadID(Integer.parseInt(thread)); 440 r.setSequenceNumber(Long.parseLong(seq)); 441 r.setMillis(Long.parseLong(millis)); 442 r.setResourceBundleName(key); 443 if (catalog != null && key != null) { 444 r.setResourceBundleName(catalog); 445 if (!"<null>".equals(catalog)) { try { 447 ResourceBundle b = ResourceBundle.getBundle(catalog); 448 b.getObject(key); 449 r.setResourceBundle(b); 451 } catch (MissingResourceException e) { 452 LOG.log(Level.CONFIG, "Cannot find resource bundle {0} for key {1}", new Object [] { catalog, key }); 453 r.setResourceBundle(new FakeBundle(key, msg)); 454 } 455 } else { 456 LOG.log(Level.CONFIG, "Cannot find resource bundle <null> for key {1}", key); 457 } 458 if (params != null) { 459 r.setParameters(params.toArray()); 460 } 461 } 462 if (currentEx != null) { 463 r.setThrown(currentEx); 464 } 465 466 callback.publish(r); 467 468 currentEx = null; 469 params = null; 470 values.clear(); 471 } 472 473 } 474 475 public void characters(char[] ch, int start, int length) throws SAXException { 476 chars.append(ch, start, length); 477 } 478 479 public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { 480 } 481 482 public void processingInstruction(String target, String data) throws SAXException { 483 } 484 485 public void skippedEntity(String name) throws SAXException { 486 } 487 488 public void fatalError(SAXParseException e) throws SAXException { 489 LOG.log(Level.FINE, null, e); 490 } 491 492 } 493 494 private static final class FakeBundle extends ResourceBundle { 495 private String key; 496 private String value; 497 498 public FakeBundle(String key, String value) { 499 this.key = key; 500 this.value = value; 501 } 502 503 504 protected Object handleGetObject(String arg0) { 505 if (key.equals(arg0)) { 506 return value; 507 } else { 508 return null; 509 } 510 } 511 512 public Enumeration <String > getKeys() { 513 return Collections.enumeration(Collections.singleton(key)); 514 } 515 } 517 private static final class FakeException extends Exception { 518 final List <StackTraceElement > trace = new ArrayList <StackTraceElement >(); 519 Map <Parser.Elem ,String > values; 520 String message; 521 522 public FakeException(Map <Parser.Elem ,String > values) { 523 this.values = values; 524 } 525 526 public StackTraceElement [] getStackTrace() { 527 return trace.toArray(new StackTraceElement [0]); 528 } 529 530 public String getMessage() { 531 return message; 532 } 533 } } 535 | Popular Tags |