| /******************************************************************************* |
| * Copyright (c) 2004, 2007 Boeing. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Boeing - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.osee.ote.core.log.record; |
| |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.logging.Level; |
| import java.util.logging.LogRecord; |
| import java.util.regex.Pattern; |
| |
| import javax.xml.stream.XMLStreamException; |
| import javax.xml.stream.XMLStreamWriter; |
| |
| import org.codehaus.jackson.annotate.JsonIgnore; |
| import org.codehaus.jackson.annotate.JsonProperty; |
| import org.codehaus.jackson.annotate.JsonSubTypes; |
| import org.codehaus.jackson.annotate.JsonTypeInfo; |
| import org.eclipse.osee.framework.jdk.core.persistence.Xmlizable; |
| import org.eclipse.osee.framework.jdk.core.persistence.XmlizableStream; |
| import org.eclipse.osee.framework.jdk.core.util.xml.Jaxp; |
| import org.eclipse.osee.framework.jdk.core.util.xml.XMLStreamWriterUtil; |
| import org.eclipse.osee.framework.logging.OseeLog; |
| import org.eclipse.osee.ote.core.environment.TestEnvironment; |
| import org.eclipse.osee.ote.core.environment.interfaces.ITestEnvironmentAccessor; |
| import org.eclipse.osee.ote.message.log.record.MessageJarConfigrecord; |
| import org.eclipse.osee.ote.properties.OtePropertiesCore; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| |
| /** |
| * @author Michael A. Winston |
| */ |
| public abstract class TestRecord extends LogRecord implements Xmlizable, XmlizableStream { |
| private static final long serialVersionUID = 2663140700880844240L; |
| private static final ArrayList<Pattern> stacktraceExcludes = new ArrayList<>(32); |
| private static final ArrayList<Pattern> stacktraceIncludes = new ArrayList<>(32); |
| private static boolean filterTheStacktrace = true; |
| private static boolean locationLogginOn = true; |
| private List<LogRecord> children = new ArrayList<>(); |
| |
| public static void setLocationLoggingOn(boolean on) { |
| locationLogginOn = on; |
| } |
| |
| public static boolean getLocationLoggingOn() { |
| return locationLogginOn; |
| } |
| |
| static { |
| filterTheStacktrace = OtePropertiesCore.noStacktraceFilter.getValue() == null; |
| stacktraceExcludes.add(Pattern.compile("org\\.eclipse\\.osee\\..*")); |
| } |
| |
| private final ITestEnvironmentAccessor source; |
| private long timeStamp; |
| private final boolean printTimeStamp; |
| private Throwable throwable; |
| |
| /** |
| * TestRecord Constructor. This is an abstract class so this constructor is |
| * called via the super() call from the extended class. This sets the |
| * source, the logging level, the log message and whether a timestamp should |
| * be included. |
| * |
| * @param source |
| * The object requesting the logging. |
| * @param level |
| * The logging level. |
| * @param msg |
| * The log message. |
| * @param timeStamp |
| * <b>True </b> to include timestamp, <b>False </b> if not. |
| */ |
| public TestRecord(ITestEnvironmentAccessor source, Level level, String msg, boolean timeStamp) { |
| super(level, msg); |
| this.throwable = new Throwable(); |
| this.printTimeStamp = timeStamp; |
| this.source = source; |
| if (this.printTimeStamp) { |
| if (source != null) { |
| this.timeStamp = source.getEnvTime(); |
| } else { |
| this.timeStamp = System.currentTimeMillis(); |
| try { |
| throw new Exception("source was null"); |
| } catch (Exception e) { |
| OseeLog.log(TestEnvironment.class, Level.SEVERE, e.getMessage(), e); |
| } |
| } |
| } |
| } |
| |
| @JsonIgnore |
| public void setStackTrace(Throwable throwable) { |
| this.throwable = throwable; |
| } |
| |
| private Element calc(Document doc) { |
| StackTraceElement[] stackElements = this.throwable.getStackTrace(); |
| Element locationElement = doc.createElement("Location"); |
| locationElement.setAttribute("id", Integer.toString(locationElement.hashCode())); |
| for (StackTraceElement stackElement : stackElements) { |
| addElement(doc, stackElement, locationElement); |
| } |
| return locationElement; |
| } |
| |
| private void calc(XMLStreamWriter writer) throws XMLStreamException { |
| StackTraceElement[] stackElements = this.throwable.getStackTrace(); |
| writer.writeStartElement("Location"); |
| writer.writeAttribute("id", Integer.toString(stackElements.hashCode())); |
| for (StackTraceElement stackElement : stackElements) { |
| addElement(writer, stackElement); |
| } |
| } |
| |
| private void addElement(XMLStreamWriter writer, StackTraceElement stackElement) throws XMLStreamException { |
| if (filterTheStacktrace) { |
| final String className = stackElement.getClassName(); |
| for (Pattern includes : stacktraceIncludes) { |
| if (includes.matcher(className).matches()) { |
| writer.writeEmptyElement("Stacktrace"); |
| writer.writeAttribute("source", stackElement.getClassName()); |
| writer.writeAttribute("line", Integer.toString(stackElement.getLineNumber())); |
| // writer.writeEndElement(); |
| return; |
| } |
| } |
| for (Pattern excludes : stacktraceExcludes) { |
| if (excludes.matcher(className).matches()) { |
| return; |
| } |
| } |
| } |
| writer.writeEmptyElement("Stacktrace"); |
| writer.writeAttribute("source", stackElement.getClassName()); |
| writer.writeAttribute("line", Integer.toString(stackElement.getLineNumber())); |
| // writer.writeEndElement(); |
| } |
| |
| private void addElement(Document doc, StackTraceElement stackElement, Element locationElement) { |
| if (filterTheStacktrace) { |
| final String className = stackElement.getClassName(); |
| for (Pattern includes : stacktraceIncludes) { |
| if (includes.matcher(className).matches()) { |
| Element stackTrace = doc.createElement("Stacktrace"); |
| stackTrace.setAttribute("source", stackElement.getClassName()); |
| stackTrace.setAttribute("line", Integer.toString(stackElement.getLineNumber())); |
| locationElement.appendChild(stackTrace); |
| return; |
| } |
| } |
| for (Pattern excludes : stacktraceExcludes) { |
| if (excludes.matcher(className).matches()) { |
| return; |
| } |
| } |
| } |
| Element stackTrace = doc.createElement("Stacktrace"); |
| stackTrace.setAttribute("source", stackElement.getClassName()); |
| stackTrace.setAttribute("line", Integer.toString(stackElement.getLineNumber())); |
| locationElement.appendChild(stackTrace); |
| } |
| |
| /** |
| * Converts log element to XML format. |
| * |
| * @return xml formated element. |
| */ |
| @Override |
| public Element toXml(Document doc) { |
| Element recordElement = doc.createElement(getLevel().getName()); |
| if (TestRecord.getLocationLoggingOn()) { |
| recordElement.appendChild(getLocation(doc)); |
| } |
| recordElement.appendChild(Jaxp.createElement(doc, "Message", getMessage())); |
| return recordElement; |
| } |
| |
| @Override |
| public void toXml(XMLStreamWriter writer) throws XMLStreamException { |
| writer.writeStartElement("OteLog"); |
| writer.writeAttribute("Level", getLevel().getName()); |
| writeMessage(writer); |
| writeLocationCheckLocationLoggingOn(writer); |
| writer.writeEndElement(); |
| } |
| |
| public Object getSource() { |
| return source; |
| } |
| |
| /** |
| * @return Elements location. |
| */ |
| protected Element getLocation(Document doc) { |
| Element locationElement = calc(doc); |
| if (this.printTimeStamp) { |
| locationElement.appendChild(Jaxp.createElement(doc, "Time", Long.toString(timeStamp))); |
| } |
| return locationElement; |
| } |
| |
| protected void writeLocation(XMLStreamWriter writer) throws XMLStreamException { |
| calc(writer); |
| writeTime(writer); |
| writer.writeEndElement(); |
| } |
| |
| protected void writeLocationCheckLocationLoggingOn(XMLStreamWriter writer) throws XMLStreamException { |
| if (TestRecord.getLocationLoggingOn()) { |
| writeLocation(writer); |
| } |
| } |
| |
| protected void writeTime(XMLStreamWriter writer) throws XMLStreamException { |
| if (this.printTimeStamp) { |
| writer.writeStartElement("Time"); |
| writer.writeCharacters(Long.toString(timeStamp)); |
| writer.writeEndElement(); |
| } |
| } |
| |
| protected void writeMessage(XMLStreamWriter writer) throws XMLStreamException { |
| writeElement(writer, "Message", getMessage()); |
| } |
| |
| protected void writeElement(XMLStreamWriter writer, String elementName, String characterData) throws XMLStreamException { |
| XMLStreamWriterUtil.writeElement(writer, elementName, characterData); |
| } |
| |
| @JsonProperty |
| public List<String> getLocation() { |
| List<String> result = new ArrayList<>(); |
| if (TestRecord.getLocationLoggingOn()) { |
| StackTraceElement[] stackTrace = this.throwable.getStackTrace(); |
| result.addAll(filterStackTrace(stackTrace)); |
| } |
| if (result.isEmpty()) { |
| return null; |
| } else { |
| return result; |
| } |
| } |
| |
| private List<String> filterStackTrace(StackTraceElement[] stackTrace) { |
| // include everything if not filtered, otherwise ... |
| // default is to include unless explicitly excluded, but only if not |
| // explicitly included ... yuck |
| List<String> result = new ArrayList<>(); |
| for (StackTraceElement stackElement : stackTrace) { |
| final String className = stackElement.getClassName(); |
| if (!filterTheStacktrace || included(className) || !excluded(className)) { |
| result.add(stackElement.getClassName() + ":" + stackElement.getLineNumber()); |
| } |
| } |
| return result; |
| } |
| |
| private boolean excluded(String className) { |
| for (Pattern exclude : stacktraceExcludes) { |
| if (exclude.matcher(className).matches()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private boolean included(String className) { |
| for (Pattern include : stacktraceIncludes) { |
| if (include.matcher(className).matches()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @JsonProperty |
| public Long getTimeStamp() { |
| if (this.printTimeStamp) { |
| return timeStamp; |
| } else { |
| return null; |
| } |
| } |
| |
| @Override |
| @JsonProperty |
| public String getMessage() { |
| return nonEmptyString(super.getMessage()); |
| } |
| |
| protected String nonEmptyString(final String subject) { |
| if (subject != null && subject.trim().length() > 0) { |
| return subject; |
| } else { |
| return null; |
| } |
| } |
| |
| |
| protected <T> List<T> nonEmptyList(final List<T> subject) { |
| return (List<T>) nonEmptyCollection(subject); |
| } |
| |
| protected <T> Collection<T> nonEmptyCollection(final Collection<T> subject) { |
| if (subject.isEmpty()) { |
| return null; |
| } else { |
| return subject; |
| } |
| } |
| |
| // we want LogRecord to record its level in the JSON, but not derivatives, but we must |
| // preserve default behavior for other things, like the log handler |
| @JsonIgnore |
| @Override |
| public Level getLevel() { |
| return super.getLevel(); |
| } |
| |
| public void addChildRecord(final LogRecord record) { |
| children.add(record); |
| } |
| |
| @JsonProperty |
| public List<LogRecord> getChildRecords() { |
| return nonEmptyList(children); |
| } |
| } |