/* * Lilith - a log event viewer. * Copyright (C) 2007-2014 Joern Huxhorn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* * Copyright 2007-2014 Joern Huxhorn * * 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 de.huxhorn.lilith.log4j.xml; import de.huxhorn.lilith.data.logging.ExtendedStackTraceElement; import de.huxhorn.lilith.data.logging.LoggingEvent; import de.huxhorn.lilith.data.logging.Message; import de.huxhorn.lilith.data.logging.ThreadInfo; import de.huxhorn.lilith.data.logging.ThrowableInfo; import de.huxhorn.lilith.data.logging.ThrowableInfoParser; import de.huxhorn.sulky.stax.GenericStreamReader; import de.huxhorn.sulky.stax.StaxUtilities; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; public class LoggingEventReader implements GenericStreamReader<LoggingEvent>, LoggingEventSchemaConstants { private static final String NEWLINE = "\n"; public LoggingEventReader() { } public LoggingEvent read(XMLStreamReader reader) throws XMLStreamException { LoggingEvent result = null; String rootNamespace = NAMESPACE_URI; int type = reader.getEventType(); if(XMLStreamConstants.START_DOCUMENT == type) { do { reader.next(); type = reader.getEventType(); } while(type != XMLStreamConstants.START_ELEMENT); rootNamespace = null; } if(XMLStreamConstants.START_ELEMENT == type && LOGGING_EVENT_NODE.equals(reader.getLocalName())) { result = new LoggingEvent(); result.setLogger(StaxUtilities.readAttributeValue(reader, NAMESPACE_URI, LOGGER_ATTRIBUTE)); String levelStr = StaxUtilities.readAttributeValue(reader, NAMESPACE_URI, LEVEL_ATTRIBUTE); if("FATAL".equals(levelStr)) { levelStr = "ERROR"; } try { result.setLevel(LoggingEvent.Level.valueOf(levelStr)); } catch(IllegalArgumentException ex) { // ignore } String threadName = StaxUtilities.readAttributeValue(reader, NAMESPACE_URI, THREAD_NAME_ATTRIBUTE); Long threadId = null; try { String threadIdStr = StaxUtilities.readAttributeValue(reader, NAMESPACE_URI, THREAD_ID_ATTRIBUTE); if(threadIdStr != null) { threadId = Long.valueOf(threadIdStr); } } catch(NumberFormatException ex) { // ignore } String threadGroupName = StaxUtilities .readAttributeValue(reader, NAMESPACE_URI, THREAD_GROUP_NAME_ATTRIBUTE); Long threadGroupId = null; try { String idStr = StaxUtilities.readAttributeValue(reader, NAMESPACE_URI, THREAD_GROUP_ID_ATTRIBUTE); if(idStr != null) { threadGroupId = Long.valueOf(idStr); } } catch(NumberFormatException ex) { // ignore } if(threadName != null || threadId != null || threadGroupId != null || threadGroupName != null) { result.setThreadInfo(new ThreadInfo(threadId, threadName, threadGroupId, threadGroupName)); } String timeStamp = StaxUtilities.readAttributeValue(reader, NAMESPACE_URI, TIMESTAMP_ATTRIBUTE); try { result.setTimeStamp(Long.parseLong(timeStamp)); } catch(NumberFormatException e) { // ignore } reader.nextTag(); String messagePattern = StaxUtilities.readSimpleTextNodeIfAvailable(reader, null, MESSAGE_NODE); if(messagePattern != null) { result.setMessage(new Message(messagePattern)); } result.setNdc(readNdc(reader)); result.setThrowable(readThrowable(reader)); result.setCallStack(readLocationInfo(reader)); result.setMdc(readMdc(reader)); return result; } return result; } private Map<String, String> readMdc(XMLStreamReader reader) throws XMLStreamException { int type = reader.getEventType(); if(XMLStreamConstants.START_ELEMENT == type && PROPERTIES_NODE.equals(reader.getLocalName())) { Map<String, String> mdc = new HashMap<>(); reader.nextTag(); for(;;) { MdcEntry entry = readMdcEntry(reader); if(entry == null) { break; } mdc.put(entry.key, entry.value); } reader.require(XMLStreamConstants.END_ELEMENT, null, PROPERTIES_NODE); reader.nextTag(); return mdc; } return null; } private MdcEntry readMdcEntry(XMLStreamReader reader) throws XMLStreamException { int type = reader.getEventType(); if(XMLStreamConstants.START_ELEMENT == type && DATA_NODE.equals(reader.getLocalName())) { MdcEntry entry = new MdcEntry(); entry.key = StaxUtilities.readAttributeValue(reader, NAMESPACE_URI, NAME_ATTRIBUTE); entry.value = StaxUtilities.readAttributeValue(reader, NAMESPACE_URI, VALUE_ATTRIBUTE); reader.nextTag(); reader.require(XMLStreamConstants.END_ELEMENT, null, DATA_NODE); reader.nextTag(); return entry; } return null; } private ExtendedStackTraceElement[] readLocationInfo(XMLStreamReader reader) throws XMLStreamException { // <log4j:locationInfo class="de.huxhorn.lilith.sandbox.Log4jSandbox$InnerClass" method="execute" file="Log4jSandbox.java" line="18"/> int type = reader.getEventType(); if(XMLStreamConstants.START_ELEMENT == type && LOCATION_INFO_NODE.equals(reader.getLocalName())) { String className = StaxUtilities.readAttributeValue(reader, NAMESPACE_URI, CLASS_ATTRIBUTE); String methodName = StaxUtilities.readAttributeValue(reader, NAMESPACE_URI, METHOD_ATTRIBUTE); String fileName = StaxUtilities.readAttributeValue(reader, NAMESPACE_URI, FILE_ATTRIBUTE); String lineStr = StaxUtilities.readAttributeValue(reader, NAMESPACE_URI, LINE_ATTRIBUTE); int line = -1; if(lineStr != null) { try { line = Integer.parseInt(lineStr); } catch(NumberFormatException ex) { // ignore } } ExtendedStackTraceElement ste = new ExtendedStackTraceElement(className, methodName, fileName, line); reader.nextTag(); reader.require(XMLStreamConstants.END_ELEMENT, null, LOCATION_INFO_NODE); reader.nextTag(); return new ExtendedStackTraceElement[]{ste}; } return null; } private ThrowableInfo readThrowable(XMLStreamReader reader) throws XMLStreamException { String throwableString = StaxUtilities.readSimpleTextNodeIfAvailable(reader, null, THROWABLE_NODE); if(throwableString != null) { StringTokenizer tok = new StringTokenizer(throwableString, NEWLINE, true); List<String> lines = new ArrayList<>(); boolean wasNewline=false; while(tok.hasMoreTokens()) { String current = tok.nextToken(); if(NEWLINE.equals(current)) { if(wasNewline) { // support empty lines lines.add(""); wasNewline=false; } else { wasNewline=true; } } else { wasNewline=false; lines.add(current); } } return ThrowableInfoParser.parse(lines); } return null; } private Message[] readNdc(XMLStreamReader reader) throws XMLStreamException { String ndcString = StaxUtilities.readSimpleTextNodeIfAvailable(reader, null, NDC_NODE); if(ndcString == null) { return null; } ArrayList<Message> ndcs = new ArrayList<>(); StringTokenizer tok = new StringTokenizer(ndcString, " ", false); // *sigh* while(tok.hasMoreTokens()) { ndcs.add(new Message(tok.nextToken())); } return ndcs.toArray(new Message[ndcs.size()]); } private static class MdcEntry { public String key; public String value; } }