Java tutorial
/******************************************************************************* * Copyright 2011 Krzysztof Otrebski * * Licensed 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 pl.otros.logview.importer; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import pl.otros.logview.LogData; import pl.otros.logview.LogDataCollector; import pl.otros.logview.gui.table.TableColumns; import pl.otros.logview.importer.InitializationException; import pl.otros.logview.importer.LogImporter; import pl.otros.logview.importer.log4jxml.SAXErrorHandler; import pl.otros.logview.importer.log4jxml.UtilLoggingEntityResolver; import pl.otros.logview.parser.ParsingContext; import pl.otros.logview.parser.TableColumnNameSelfDescribable; import pl.otros.logview.pluginable.AbstractPluginableElement; import org.apache.commons.lang.StringUtils; import javax.imageio.ImageIO; import javax.swing.*; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.awt.event.KeyEvent; import java.io.*; import java.util.Date; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; public class UtilLoggingXmlLogImporter extends AbstractPluginableElement implements LogImporter, TableColumnNameSelfDescribable { private static final String DOC_BUILDER = "DOC_BUILDER"; private static final String PARTIAL_EVENT = "PARTIAL_EVENT"; private static final Logger LOGGER = Logger.getLogger(UtilLoggingXmlLogImporter.class.getName()); private static final String NAME = "Improved XMLFormatter"; private Icon icon; private static final String ICON_PATH = "img/java.png"; public UtilLoggingXmlLogImporter() { super(NAME, NAME); } // NOTE: xml section is only handed on first delivery of events // on this first delivery of events, there is no end tag for the log element /** * Document prolog. */ private static final String BEGIN_PART = "<log>"; /** * Document close. */ private static final String END_PART = "</log>"; /** * Document builder. */ // private DocumentBuilder docBuilder; /** * Record end. */ private static final String RECORD_END = "</record>"; private static final String ENCODING = "UTF-8"; @Override public void init(Properties properties) throws InitializationException { try { icon = new ImageIcon(ImageIO.read(this.getClass().getClassLoader().getResourceAsStream(ICON_PATH))); LOGGER.info("icon loaded"); } catch (Exception e) { LOGGER.warning("Error loading icon: " + e.getMessage()); } } @Override public void importLogs(InputStream in, LogDataCollector collector, ParsingContext parsingContext) { StringBuffer sb = new StringBuffer(); try { LineNumberReader bin = new LineNumberReader(new InputStreamReader(in, ENCODING)); String line = null; while ((line = bin.readLine()) != null) { sb.append(line).append("\n"); if (bin.getLineNumber() % 30 == 0) { decodeEvents(sb.toString(), collector, parsingContext); sb.setLength(0); } } } catch (UnsupportedEncodingException e) { LOGGER.severe("Cant load codepage " + e.getMessage()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { decodeEvents(sb.toString(), collector, parsingContext); } } /** * Converts the LoggingEvent data in XML string format into an actual XML Document class instance. * * @param data * XML fragment * @return dom document */ private Document parse(final String data, DocumentBuilder docBuilder) { if (docBuilder == null || data == null) { return null; } Document document = null; try { // we change the system ID to a valid URI so that Crimson won't // complain. Indeed, "log4j.dtd" alone is not a valid URI which // causes Crimson to barf. The Log4jEntityResolver only cares // about the "log4j.dtd" ending. /** * resetting the length of the StringBuffer is dangerous, particularly on some JDK 1.4 impls, there's a known Bug that causes a memory leak */ StringBuffer buf = new StringBuffer(1024); if (!data.startsWith("<?xml")) { buf.append(BEGIN_PART); } buf.append(data); if (!data.endsWith(END_PART)) { buf.append(END_PART); } InputSource inputSource = new InputSource(new StringReader(buf.toString())); document = docBuilder.parse(inputSource); } catch (Exception e) { LOGGER.warning("Problem with creating document: " + e.getMessage()); } return document; } /** * Decodes a String representing a number of events into a Vector of LoggingEvents. * * @param document * to decode events from * @return Vector of LoggingEvents */ public void decodeEvents(final String document, LogDataCollector collector, ParsingContext parsingContext) { if (document != null) { if (document.trim().equals("")) { return; } String newDoc; String newPartialEvent = null; // separate the string into the last portion ending with </record> // (which will be processed) and the partial event which // will be combined and processed in the next section String partialEvent = (String) parsingContext.getCustomConextProperties().get(PARTIAL_EVENT); // if the document does not contain a record end, // append it to the partial event string if (document.lastIndexOf(RECORD_END) == -1) { partialEvent = partialEvent + document; parsingContext.getCustomConextProperties().put(PARTIAL_EVENT, partialEvent); return; } if (document.lastIndexOf(RECORD_END) + RECORD_END.length() < document.length()) { newDoc = document.substring(0, document.lastIndexOf(RECORD_END) + RECORD_END.length()); newPartialEvent = document.substring(document.lastIndexOf(RECORD_END) + RECORD_END.length()); parsingContext.getCustomConextProperties().put(PARTIAL_EVENT, newPartialEvent); } else { newDoc = document; } if (partialEvent != null) { newDoc = partialEvent + newDoc; } partialEvent = newPartialEvent; Document doc = parse(newDoc, (DocumentBuilder) parsingContext.getCustomConextProperties().get(DOC_BUILDER)); if (doc == null) { return; } decodeEvents(doc, collector, parsingContext); } } /** * Given a Document, converts the XML into a Vector of LoggingEvents. * * @param document * XML document * @return Vector of LoggingEvents */ private void decodeEvents(final Document document, LogDataCollector collector, ParsingContext parsingContext) { NodeList eventList = document.getElementsByTagName("record"); for (int eventIndex = 0; eventIndex < eventList.getLength(); eventIndex++) { Node eventNode = eventList.item(eventIndex); Logger logger = null; long timeStamp = 0L; Level level = null; String threadName = null; Object message = null; String className = null; String methodName = null; String exceptionStackTrace = null; // format of date: 2003-05-04T11:04:52 // ignore date or set as a property? using millis in constructor instead NodeList list = eventNode.getChildNodes(); int listLength = list.getLength(); if (listLength == 0) { continue; } for (int y = 0; y < listLength; y++) { Node logEventNode = list.item(y); String tagName = logEventNode.getNodeName(); if (tagName.equalsIgnoreCase("logger")) { logger = Logger.getLogger(getCData(list.item(y))); } else if (tagName.equalsIgnoreCase("millis")) { timeStamp = Long.parseLong(getCData(list.item(y))); } else if (tagName.equalsIgnoreCase("level")) { level = Level.parse(getCData(list.item(y))); } else if (tagName.equalsIgnoreCase("thread")) { threadName = getCData(list.item(y)); } else if (tagName.equalsIgnoreCase("message")) { message = getCData(list.item(y)); } else if (tagName.equalsIgnoreCase("class")) { className = getCData(list.item(y)); } else if (tagName.equalsIgnoreCase("method")) { methodName = getCData(list.item(y)); } else if (tagName.equalsIgnoreCase("exception")) { exceptionStackTrace = getExceptionStackTrace(list.item(y)); } } if (message != null && exceptionStackTrace != null) { message = message + "\n" + exceptionStackTrace; } else if (exceptionStackTrace != null) { message = exceptionStackTrace; } LogData logData = new LogData(); logData.setLevel(level); logData.setClazz(className); logData.setId(parsingContext.getGeneratedIdAndIncrease()); logData.setDate(new Date(timeStamp)); logData.setLoggerName(logger.getName()); logData.setMessage(StringUtils.defaultString(message != null ? message.toString() : "")); logData.setThread(threadName); logData.setMethod(methodName); logData.setLogSource(parsingContext.getLogSource()); collector.add(logData); } } String getExceptionStackTrace(Node eventNode) { StringBuilder sb = new StringBuilder(); NodeList childNodes = eventNode.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node childNode = childNodes.item(i); String tagName = childNode.getNodeName(); if (tagName.equalsIgnoreCase("message")) { sb.append(getCData(childNodes.item(i))); } else if (tagName.equalsIgnoreCase("frame")) { getStackTraceFrame(childNodes.item(i), sb); } } return sb.toString(); } void getStackTraceFrame(Node item, StringBuilder sb) { NodeList childNodes = item.getChildNodes(); String clazz = null; String method = null; String line = null; for (int i = 0; i < childNodes.getLength(); i++) { Node childNode = childNodes.item(i); String tagName = childNode.getNodeName(); if (tagName.equalsIgnoreCase("class")) { clazz = getCData(childNode); } else if (tagName.equalsIgnoreCase("method")) { method = getCData(childNode); } else if (tagName.equalsIgnoreCase("line")) { line = getCData(childNode); } } //default string if clazz does not contain package String fileName = extractFileName(clazz); appendStackFrame(sb, clazz, method, line, fileName); } StringBuilder appendStackFrame(StringBuilder sb, String clazz, String method, String line, String fileName) { sb.append("\n\tat "); sb.append(clazz).append(".").append(method); if (fileName != null) { sb.append("("); sb.append(fileName).append(".java"); if (line != null) { sb.append(":").append(line); } sb.append(")"); } return sb; } String extractFileName(String clazz) { int clazzStart = StringUtils.lastIndexOf(clazz, '.') + 1; clazzStart = Math.max(0, clazzStart); int clazzEnd = StringUtils.indexOf(clazz, '$'); if (clazzEnd < 0) { clazzEnd = clazz.length(); } String fileName = StringUtils.substring(clazz, clazzStart, clazzEnd); return fileName; } /** * Get contents of CDATASection. * * @param n * CDATASection * @return text content of all text or CDATA children of node. */ private String getCData(final Node n) { StringBuffer buf = new StringBuffer(); NodeList nl = n.getChildNodes(); for (int x = 0; x < nl.getLength(); x++) { Node innerNode = nl.item(x); if ((innerNode.getNodeType() == Node.TEXT_NODE) || (innerNode.getNodeType() == Node.CDATA_SECTION_NODE)) { buf.append(innerNode.getNodeValue()); } } return buf.toString(); } @Override public String getKeyStrokeAccelelator() { return "control l"; } @Override public int getMnemonic() { return KeyEvent.VK_L; } @Override public Icon getIcon() { return icon; } @Override public TableColumns[] getTableColumnsToUse() { return TableColumns.JUL_COLUMNS; } @Override public void initParsingContext(ParsingContext parsingContext) { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setValidating(false); try { DocumentBuilder docBuilder = dbf.newDocumentBuilder(); docBuilder.setErrorHandler(new SAXErrorHandler()); docBuilder.setEntityResolver(new UtilLoggingEntityResolver()); parsingContext.getCustomConextProperties().put(DOC_BUILDER, docBuilder); parsingContext.getCustomConextProperties().put(PARTIAL_EVENT, ""); } catch (ParserConfigurationException pce) { System.err.println("Unable to get document builder"); } } @Override public int getApiVersion() { return LOG_IMPORTER_VERSION_1; } }