FileDocCategorySizeDatePackage
XmlLogger.javaAPI DocApache Ant 1.7017944Wed Dec 13 06:16:18 GMT 2006org.apache.tools.ant

XmlLogger

public class XmlLogger extends Object implements BuildLogger
Generates a file in the current directory with an XML description of what happened during a build. The default filename is "log.xml", but this can be overridden with the property XmlLogger.file. This implementation assumes in its sanity checking that only one thread runs a particular target/task at a time. This is enforced by the way that parallel builds and antcalls are done - and indeed all but the simplest of tasks could run into problems if executed in parallel.
see
Project#addBuildListener(BuildListener)

Fields Summary
private int
msgOutputLevel
private PrintStream
outStream
private static DocumentBuilder
builder
DocumentBuilder to use when creating the document to start with.
private static final String
BUILD_TAG
XML element name for a build.
private static final String
TARGET_TAG
XML element name for a target.
private static final String
TASK_TAG
XML element name for a task.
private static final String
MESSAGE_TAG
XML element name for a message.
private static final String
NAME_ATTR
XML attribute name for a name.
private static final String
TIME_ATTR
XML attribute name for a time.
private static final String
PRIORITY_ATTR
XML attribute name for a message priority.
private static final String
LOCATION_ATTR
XML attribute name for a file location.
private static final String
ERROR_ATTR
XML attribute name for an error description.
private static final String
STACKTRACE_TAG
XML element name for a stack trace.
private Document
doc
The complete log document for this build.
private Hashtable
tasks
Mapping for when tasks started (Task to TimedElement).
private Hashtable
targets
Mapping for when targets started (Task to TimedElement).
private Hashtable
threadStacks
Mapping of threads to stacks of elements (Thread to Stack of TimedElement).
private TimedElement
buildElement
When the build started.
Constructors Summary
public XmlLogger()
Constructs a new BuildListener that logs build events to an XML file.

    
Methods Summary
public voidbuildFinished(BuildEvent event)
Fired when the build finishes, this adds the time taken and any error stacktrace to the build element and writes the document to disk.

param
event An event with any relevant extra information. Will not be null.

        long totalTime = System.currentTimeMillis() - buildElement.startTime;
        buildElement.element.setAttribute(TIME_ATTR,
                DefaultLogger.formatTime(totalTime));

        if (event.getException() != null) {
            buildElement.element.setAttribute(ERROR_ATTR,
                    event.getException().toString());
            // print the stacktrace in the build file it is always useful...
            // better have too much info than not enough.
            Throwable t = event.getException();
            Text errText = doc.createCDATASection(StringUtils.getStackTrace(t));
            Element stacktrace = doc.createElement(STACKTRACE_TAG);
            stacktrace.appendChild(errText);
            buildElement.element.appendChild(stacktrace);
        }

        String outFilename = event.getProject().getProperty("XmlLogger.file");
        if (outFilename == null) {
            outFilename = "log.xml";
        }
        String xslUri
                = event.getProject().getProperty("ant.XmlLogger.stylesheet.uri");
        if (xslUri == null) {
            xslUri = "log.xsl";
        }
        Writer out = null;
        try {
            // specify output in UTF8 otherwise accented characters will blow
            // up everything
            OutputStream stream = outStream;
            if (stream == null) {
                stream = new FileOutputStream(outFilename);
            }
            out = new OutputStreamWriter(stream, "UTF8");
            out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
            if (xslUri.length() > 0) {
                out.write("<?xml-stylesheet type=\"text/xsl\" href=\""
                        + xslUri + "\"?>\n\n");
            }
            (new DOMElementWriter()).write(buildElement.element, out, 0, "\t");
            out.flush();
        } catch (IOException exc) {
            throw new BuildException("Unable to write log file", exc);
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    // ignore
                }
            }
        }
        buildElement = null;
    
public voidbuildStarted(BuildEvent event)
Fired when the build starts, this builds the top-level element for the document and remembers the time of the start of the build.

param
event Ignored.

        buildElement = new TimedElement();
        buildElement.startTime = System.currentTimeMillis();
        buildElement.element = doc.createElement(BUILD_TAG);
    
private static javax.xml.parsers.DocumentBuildergetDocumentBuilder()
Returns a default DocumentBuilder instance or throws an ExceptionInInitializerError if it can't be created.

return
a default DocumentBuilder instance.


                            
        
        try {
            return DocumentBuilderFactory.newInstance().newDocumentBuilder();
        } catch (Exception exc) {
            throw new ExceptionInInitializerError(exc);
        }
    
private java.util.StackgetStack()
Returns the stack of timed elements for the current thread.

return
the stack of timed elements for the current thread

        Stack threadStack = (Stack) threadStacks.get(Thread.currentThread());
        if (threadStack == null) {
            threadStack = new Stack();
            threadStacks.put(Thread.currentThread(), threadStack);
        }
        /* For debugging purposes uncomment:
        org.w3c.dom.Comment s = doc.createComment("stack=" + threadStack);
        buildElement.element.appendChild(s);
         */
        return threadStack;
    
private org.apache.tools.ant.XmlLogger$TimedElementgetTaskElement(Task task)
Get the TimedElement associated with a task. Where the task is not found directly, search for unknown elements which may be hiding the real task

        TimedElement element = (TimedElement) tasks.get(task);
        if (element != null) {
            return element;
        }

        for (Enumeration e = tasks.keys(); e.hasMoreElements();) {
            Task key = (Task) e.nextElement();
            if (key instanceof UnknownElement) {
                if (((UnknownElement) key).getTask() == task) {
                    return (TimedElement) tasks.get(key);
                }
            }
        }

        return null;
    
public voidmessageLogged(BuildEvent event)
Fired when a message is logged, this adds a message element to the most appropriate parent element (task, target or build) and records the priority and text of the message.

param
event An event with any relevant extra information. Will not be null.

        int priority = event.getPriority();
        if (priority > msgOutputLevel) {
            return;
        }
        Element messageElement = doc.createElement(MESSAGE_TAG);

        String name = "debug";
        switch (event.getPriority()) {
            case Project.MSG_ERR:
                name = "error";
                break;
            case Project.MSG_WARN:
                name = "warn";
                break;
            case Project.MSG_INFO:
                name = "info";
                break;
            default:
                name = "debug";
                break;
        }
        messageElement.setAttribute(PRIORITY_ATTR, name);

        Throwable ex = event.getException();
        if (Project.MSG_DEBUG <= msgOutputLevel && ex != null) {
            Text errText = doc.createCDATASection(StringUtils.getStackTrace(ex));
            Element stacktrace = doc.createElement(STACKTRACE_TAG);
            stacktrace.appendChild(errText);
            buildElement.element.appendChild(stacktrace);
        }
        Text messageText = doc.createCDATASection(event.getMessage());
        messageElement.appendChild(messageText);

        TimedElement parentElement = null;

        Task task = event.getTask();

        Target target = event.getTarget();
        if (task != null) {
            parentElement = getTaskElement(task);
        }
        if (parentElement == null && target != null) {
            parentElement = (TimedElement) targets.get(target);
        }

        /*
        if (parentElement == null) {
            Stack threadStack
                    = (Stack) threadStacks.get(Thread.currentThread());
            if (threadStack != null) {
                if (!threadStack.empty()) {
                    parentElement = (TimedElement) threadStack.peek();
                }
            }
        }
        */

        if (parentElement != null) {
            parentElement.element.appendChild(messageElement);
        } else {
            buildElement.element.appendChild(messageElement);
        }
    
public voidsetEmacsMode(boolean emacsMode)
Ignore emacs mode, as it has no meaning in XML format

param
emacsMode true if logger should produce emacs compatible output

    
public voidsetErrorPrintStream(java.io.PrintStream err)
Ignore error print stream. All output will be written to either the XML log file or the PrintStream provided to setOutputPrintStream

param
err the stream we are going to ignore.

    
public voidsetMessageOutputLevel(int level)
Set the logging level when using this as a Logger

param
level the logging level - see {@link org.apache.tools.ant.Project#MSG_ERR Project} class for level definitions

        msgOutputLevel = level;
    
public voidsetOutputPrintStream(java.io.PrintStream output)
Set the output stream to which logging output is sent when operating as a logger.

param
output the output PrintStream.

        this.outStream = new PrintStream(output, true);
    
public voidtargetFinished(BuildEvent event)
Fired when a target finishes building, this adds the time taken and any error stacktrace to the appropriate target element in the log.

param
event An event with any relevant extra information. Will not be null.

        Target target = event.getTarget();
        TimedElement targetElement = (TimedElement) targets.get(target);
        if (targetElement != null) {
            long totalTime
                    = System.currentTimeMillis() - targetElement.startTime;
            targetElement.element.setAttribute(TIME_ATTR,
                    DefaultLogger.formatTime(totalTime));

            TimedElement parentElement = null;
            Stack threadStack = getStack();
            if (!threadStack.empty()) {
                TimedElement poppedStack = (TimedElement) threadStack.pop();
                if (poppedStack != targetElement) {
                    throw new RuntimeException("Mismatch - popped element = "
                            + poppedStack
                            + " finished target element = "
                            + targetElement);
                }
                if (!threadStack.empty()) {
                    parentElement = (TimedElement) threadStack.peek();
                }
            }
            if (parentElement == null) {
                buildElement.element.appendChild(targetElement.element);
            } else {
                parentElement.element.appendChild(targetElement.element);
            }
        }
        targets.remove(target);
    
public voidtargetStarted(BuildEvent event)
Fired when a target starts building, this pushes a timed element for the target onto the stack of elements for the current thread, remembering the current time and the name of the target.

param
event An event with any relevant extra information. Will not be null.

        Target target = event.getTarget();
        TimedElement targetElement = new TimedElement();
        targetElement.startTime = System.currentTimeMillis();
        targetElement.element = doc.createElement(TARGET_TAG);
        targetElement.element.setAttribute(NAME_ATTR, target.getName());
        targets.put(target, targetElement);
        getStack().push(targetElement);
    
public voidtaskFinished(BuildEvent event)
Fired when a task finishes building, this adds the time taken and any error stacktrace to the appropriate task element in the log.

param
event An event with any relevant extra information. Will not be null.

        Task task = event.getTask();
        TimedElement taskElement = (TimedElement) tasks.get(task);
        if (taskElement != null) {
            long totalTime = System.currentTimeMillis() - taskElement.startTime;
            taskElement.element.setAttribute(TIME_ATTR,
                    DefaultLogger.formatTime(totalTime));
            Target target = task.getOwningTarget();
            TimedElement targetElement = null;
            if (target != null) {
                targetElement = (TimedElement) targets.get(target);
            }
            if (targetElement == null) {
                buildElement.element.appendChild(taskElement.element);
            } else {
                targetElement.element.appendChild(taskElement.element);
            }
            Stack threadStack = getStack();
            if (!threadStack.empty()) {
                TimedElement poppedStack = (TimedElement) threadStack.pop();
                if (poppedStack != taskElement) {
                    throw new RuntimeException("Mismatch - popped element = "
                            + poppedStack + " finished task element = "
                            + taskElement);
                }
            }
            tasks.remove(task);
        } else {
            throw new RuntimeException("Unknown task " + task + " not in " + tasks);
        }
    
public voidtaskStarted(BuildEvent event)
Fired when a task starts building, this pushes a timed element for the task onto the stack of elements for the current thread, remembering the current time and the name of the task.

param
event An event with any relevant extra information. Will not be null.

        TimedElement taskElement = new TimedElement();
        taskElement.startTime = System.currentTimeMillis();
        taskElement.element = doc.createElement(TASK_TAG);

        Task task = event.getTask();
        String name = event.getTask().getTaskName();
        if (name == null) {
            name = "";
        }
        taskElement.element.setAttribute(NAME_ATTR, name);
        taskElement.element.setAttribute(LOCATION_ATTR,
                event.getTask().getLocation().toString());
        tasks.put(task, taskElement);
        getStack().push(taskElement);