/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.tools.ant.listener; import java.io.PrintStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogConfigurationException; import org.apache.commons.logging.LogFactory; import org.apache.tools.ant.BuildEvent; import org.apache.tools.ant.BuildListener; import org.apache.tools.ant.BuildLogger; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; import org.apache.tools.ant.UnknownElement; /** * Jakarta Commons Logging listener. * Note: do not use the SimpleLog as your logger implementation as it * causes an infinite loop since it writes to System.err, which Ant traps * and reroutes to the logger/listener layer. * * The following names are used for the log: * org.apache.tools.ant.Project.PROJECT_NAME - for project events * org.apache.tools.ant.Target.TARGET_NAME - for target events * TASK_CLASS_NAME.TARGET_NAME - for events in individual targets. * * In all target and project names we replace "." and " " with "-". * * TODO: we should use the advanced context logging features (and expose them * in c-l first :-) * TODO: this is _very_ inefficient. Switching the out and tracking the logs * can be optimized a lot - but may require few more changes to the core. * * @since Ant 1.5 */ public class CommonsLoggingListener implements BuildListener, BuildLogger { /** Indicates if the listener was initialized. */ private boolean initialized = false; private LogFactory logFactory; /** * name of the category under which target events are logged */ public static final String TARGET_LOG = "org.apache.tools.ant.Target"; /** * name of the category under which project events are logged */ public static final String PROJECT_LOG = "org.apache.tools.ant.Project"; /** * Construct the listener and make sure that a LogFactory * can be obtained. */ public CommonsLoggingListener() { } private Log getLog(String cat, String suffix) { if (suffix != null) { suffix = suffix.replace('.', '-'); suffix = suffix.replace(' ', '-'); cat = cat + "." + suffix; } final PrintStream tmpOut = System.out; final PrintStream tmpErr = System.err; System.setOut(out); System.setErr(err); if (!initialized) { try { logFactory = LogFactory.getFactory(); } catch (final LogConfigurationException e) { e.printStackTrace(System.err); return null; } } initialized = true; final Log log = logFactory.getInstance(cat); System.setOut(tmpOut); System.setErr(tmpErr); return log; } /** {@inheritDoc}. */ @Override public void buildStarted(final BuildEvent event) { final String categoryString = PROJECT_LOG; final Log log = getLog(categoryString, null); if (initialized) { realLog(log, "Build started.", Project.MSG_INFO, null); } } /** {@inheritDoc}. */ @Override public void buildFinished(final BuildEvent event) { if (initialized) { final String categoryString = PROJECT_LOG; final Log log = getLog(categoryString, event.getProject().getName()); if (event.getException() == null) { realLog(log, "Build finished.", Project.MSG_INFO, null); } else { realLog(log, "Build finished with error.", Project.MSG_ERR, event.getException()); } } } /** * @see BuildListener#targetStarted */ /** {@inheritDoc}. */ @Override public void targetStarted(final BuildEvent event) { if (initialized) { final Log log = getLog(TARGET_LOG, event.getTarget().getName()); // Since task log category includes target, we don't really // need this message realLog(log, "Start: " + event.getTarget().getName(), Project.MSG_VERBOSE, null); } } /** * @see BuildListener#targetFinished */ /** {@inheritDoc}. */ @Override public void targetFinished(final BuildEvent event) { if (initialized) { final String targetName = event.getTarget().getName(); final Log log = getLog(TARGET_LOG, event.getTarget().getName()); if (event.getException() == null) { realLog(log, "Target end: " + targetName, Project.MSG_DEBUG, null); } else { realLog(log, "Target \"" + targetName + "\" finished with error.", Project.MSG_ERR, event.getException()); } } } /** * @see BuildListener#taskStarted */ /** {@inheritDoc}. */ @Override public void taskStarted(final BuildEvent event) { if (initialized) { final Task task = event.getTask(); Object real = task; if (task instanceof UnknownElement) { final Object realObj = ((UnknownElement) task).getTask(); if (realObj != null) { real = realObj; } } final Log log = getLog(real.getClass().getName(), null); if (log.isTraceEnabled()) { realLog(log, "Task \"" + task.getTaskName() + "\" started ", Project.MSG_VERBOSE, null); } } } /** * @see BuildListener#taskFinished */ /** {@inheritDoc}. */ @Override public void taskFinished(final BuildEvent event) { if (initialized) { final Task task = event.getTask(); Object real = task; if (task instanceof UnknownElement) { final Object realObj = ((UnknownElement) task).getTask(); if (realObj != null) { real = realObj; } } final Log log = getLog(real.getClass().getName(), null); if (event.getException() == null) { if (log.isTraceEnabled()) { realLog(log, "Task \"" + task.getTaskName() + "\" finished.", Project.MSG_VERBOSE, null); } } else { realLog(log, "Task \"" + task.getTaskName() + "\" finished with error.", Project.MSG_ERR, event.getException()); } } } /** * @see BuildListener#messageLogged */ /** {@inheritDoc}. */ @Override public void messageLogged(final BuildEvent event) { if (initialized) { Object categoryObject = event.getTask(); String categoryString; String categoryDetail = null; if (categoryObject == null) { categoryObject = event.getTarget(); if (categoryObject == null) { categoryString = PROJECT_LOG; categoryDetail = event.getProject().getName(); } else { categoryString = TARGET_LOG; categoryDetail = event.getTarget().getName(); } } else { // It's a task - append the target if (event.getTarget() != null) { categoryString = categoryObject.getClass().getName(); categoryDetail = event.getTarget().getName(); } else { categoryString = categoryObject.getClass().getName(); } } final Log log = getLog(categoryString, categoryDetail); final int priority = event.getPriority(); final String message = event.getMessage(); realLog(log, message, priority , null); } } private void realLog(final Log log, final String message, final int priority, final Throwable t) { final PrintStream tmpOut = System.out; final PrintStream tmpErr = System.err; System.setOut(out); System.setErr(err); switch (priority) { case Project.MSG_ERR: if (t == null) { log.error(message); } else { log.error(message, t); } break; case Project.MSG_WARN: if (t == null) { log.warn(message); } else { log.warn(message, t); } break; case Project.MSG_INFO: if (t == null) { log.info(message); } else { log.info(message, t); } break; case Project.MSG_VERBOSE: log.debug(message); break; case Project.MSG_DEBUG: log.debug(message); break; default: log.error(message); break; } System.setOut(tmpOut); System.setErr(tmpErr); } // CheckStyle:VisibilityModifier OFF - bc PrintStream out = System.out; PrintStream err = System.err; // CheckStyle:VisibilityModifier ON /** * Set the the output level. * This is not used, the logger config is used instead. * @param level ignored */ @Override public void setMessageOutputLevel(final int level) { // Use the logger config } /** * Set the output print stream. * @param output the output stream */ @Override public void setOutputPrintStream(final PrintStream output) { this.out = output; } /** * Set emacs mode. * This is ignored. * @param emacsMode ignored */ @Override public void setEmacsMode(final boolean emacsMode) { // Doesn't make sense for c-l. Use the logger config } /** * Set the error print stream. * @param err the error stream */ @Override public void setErrorPrintStream(final PrintStream err) { this.err = err; } }