001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant;
020:
021: import java.io.BufferedReader;
022: import java.io.IOException;
023: import java.io.PrintStream;
024: import java.io.StringReader;
025:
026: import org.apache.tools.ant.util.DateUtils;
027: import org.apache.tools.ant.util.StringUtils;
028:
029: /**
030: * Writes build events to a PrintStream. Currently, it
031: * only writes which targets are being executed, and
032: * any messages that get logged.
033: *
034: */
035: public class DefaultLogger implements BuildLogger {
036: /**
037: * Size of left-hand column for right-justified task name.
038: * @see #messageLogged(BuildEvent)
039: */
040: public static final int LEFT_COLUMN_SIZE = 12;
041:
042: // CheckStyle:VisibilityModifier OFF - bc
043: /** PrintStream to write non-error messages to */
044: protected PrintStream out;
045:
046: /** PrintStream to write error messages to */
047: protected PrintStream err;
048:
049: /** Lowest level of message to write out */
050: protected int msgOutputLevel = Project.MSG_ERR;
051:
052: /** Time of the start of the build */
053: private long startTime = System.currentTimeMillis();
054:
055: // CheckStyle:ConstantNameCheck OFF - bc
056: /** Line separator */
057: protected static final String lSep = StringUtils.LINE_SEP;
058: // CheckStyle:ConstantNameCheck ON
059:
060: /** Whether or not to use emacs-style output */
061: protected boolean emacsMode = false;
062:
063: // CheckStyle:VisibilityModifier ON
064:
065: /**
066: * Sole constructor.
067: */
068: public DefaultLogger() {
069: }
070:
071: /**
072: * Sets the highest level of message this logger should respond to.
073: *
074: * Only messages with a message level lower than or equal to the
075: * given level should be written to the log.
076: * <p>
077: * Constants for the message levels are in the
078: * {@link Project Project} class. The order of the levels, from least
079: * to most verbose, is <code>MSG_ERR</code>, <code>MSG_WARN</code>,
080: * <code>MSG_INFO</code>, <code>MSG_VERBOSE</code>,
081: * <code>MSG_DEBUG</code>.
082: * <p>
083: * The default message level for DefaultLogger is Project.MSG_ERR.
084: *
085: * @param level the logging level for the logger.
086: */
087: public void setMessageOutputLevel(int level) {
088: this .msgOutputLevel = level;
089: }
090:
091: /**
092: * Sets the output stream to which this logger is to send its output.
093: *
094: * @param output The output stream for the logger.
095: * Must not be <code>null</code>.
096: */
097: public void setOutputPrintStream(PrintStream output) {
098: this .out = new PrintStream(output, true);
099: }
100:
101: /**
102: * Sets the output stream to which this logger is to send error messages.
103: *
104: * @param err The error stream for the logger.
105: * Must not be <code>null</code>.
106: */
107: public void setErrorPrintStream(PrintStream err) {
108: this .err = new PrintStream(err, true);
109: }
110:
111: /**
112: * Sets this logger to produce emacs (and other editor) friendly output.
113: *
114: * @param emacsMode <code>true</code> if output is to be unadorned so that
115: * emacs and other editors can parse files names, etc.
116: */
117: public void setEmacsMode(boolean emacsMode) {
118: this .emacsMode = emacsMode;
119: }
120:
121: /**
122: * Responds to a build being started by just remembering the current time.
123: *
124: * @param event Ignored.
125: */
126: public void buildStarted(BuildEvent event) {
127: startTime = System.currentTimeMillis();
128: }
129:
130: /**
131: * Prints whether the build succeeded or failed,
132: * any errors the occurred during the build, and
133: * how long the build took.
134: *
135: * @param event An event with any relevant extra information.
136: * Must not be <code>null</code>.
137: */
138: public void buildFinished(BuildEvent event) {
139: Throwable error = event.getException();
140: StringBuffer message = new StringBuffer();
141: if (error == null) {
142: message.append(StringUtils.LINE_SEP);
143: message.append(getBuildSuccessfulMessage());
144: } else {
145: message.append(StringUtils.LINE_SEP);
146: message.append(getBuildFailedMessage());
147: message.append(StringUtils.LINE_SEP);
148:
149: if (Project.MSG_VERBOSE <= msgOutputLevel
150: || !(error instanceof BuildException)) {
151: message.append(StringUtils.getStackTrace(error));
152: } else {
153: message.append(error.toString()).append(lSep);
154: }
155: }
156: message.append(StringUtils.LINE_SEP);
157: message.append("Total time: ");
158: message.append(formatTime(System.currentTimeMillis()
159: - startTime));
160:
161: String msg = message.toString();
162: if (error == null) {
163: printMessage(msg, out, Project.MSG_VERBOSE);
164: } else {
165: printMessage(msg, err, Project.MSG_ERR);
166: }
167: log(msg);
168: }
169:
170: /**
171: * This is an override point: the message that indicates whether a build failed.
172: * Subclasses can change/enhance the message.
173: * @return The classic "BUILD FAILED"
174: */
175: protected String getBuildFailedMessage() {
176: return "BUILD FAILED";
177: }
178:
179: /**
180: * This is an override point: the message that indicates that a build succeeded.
181: * Subclasses can change/enhance the message.
182: * @return The classic "BUILD SUCCESSFUL"
183: */
184: protected String getBuildSuccessfulMessage() {
185: return "BUILD SUCCESSFUL";
186: }
187:
188: /**
189: * Logs a message to say that the target has started if this
190: * logger allows information-level messages.
191: *
192: * @param event An event with any relevant extra information.
193: * Must not be <code>null</code>.
194: */
195: public void targetStarted(BuildEvent event) {
196: if (Project.MSG_INFO <= msgOutputLevel
197: && !event.getTarget().getName().equals("")) {
198: String msg = StringUtils.LINE_SEP
199: + event.getTarget().getName() + ":";
200: printMessage(msg, out, event.getPriority());
201: log(msg);
202: }
203: }
204:
205: /**
206: * No-op implementation.
207: *
208: * @param event Ignored.
209: */
210: public void targetFinished(BuildEvent event) {
211: }
212:
213: /**
214: * No-op implementation.
215: *
216: * @param event Ignored.
217: */
218: public void taskStarted(BuildEvent event) {
219: }
220:
221: /**
222: * No-op implementation.
223: *
224: * @param event Ignored.
225: */
226: public void taskFinished(BuildEvent event) {
227: }
228:
229: /**
230: * Logs a message, if the priority is suitable.
231: * In non-emacs mode, task level messages are prefixed by the
232: * task name which is right-justified.
233: *
234: * @param event A BuildEvent containing message information.
235: * Must not be <code>null</code>.
236: */
237: public void messageLogged(BuildEvent event) {
238: int priority = event.getPriority();
239: // Filter out messages based on priority
240: if (priority <= msgOutputLevel) {
241:
242: StringBuffer message = new StringBuffer();
243: if (event.getTask() != null && !emacsMode) {
244: // Print out the name of the task if we're in one
245: String name = event.getTask().getTaskName();
246: String label = "[" + name + "] ";
247: int size = LEFT_COLUMN_SIZE - label.length();
248: StringBuffer tmp = new StringBuffer();
249: for (int i = 0; i < size; i++) {
250: tmp.append(" ");
251: }
252: tmp.append(label);
253: label = tmp.toString();
254:
255: try {
256: BufferedReader r = new BufferedReader(
257: new StringReader(event.getMessage()));
258: String line = r.readLine();
259: boolean first = true;
260: do {
261: if (first) {
262: if (line == null) {
263: message.append(label);
264: break;
265: }
266: } else {
267: message.append(StringUtils.LINE_SEP);
268: }
269: first = false;
270: message.append(label).append(line);
271: line = r.readLine();
272: } while (line != null);
273: } catch (IOException e) {
274: // shouldn't be possible
275: message.append(label).append(event.getMessage());
276: }
277: } else {
278: message.append(event.getMessage());
279: }
280: Throwable ex = event.getException();
281: if (Project.MSG_DEBUG <= msgOutputLevel && ex != null) {
282: message.append(StringUtils.getStackTrace(ex));
283: }
284:
285: String msg = message.toString();
286: if (priority != Project.MSG_ERR) {
287: printMessage(msg, out, priority);
288: } else {
289: printMessage(msg, err, priority);
290: }
291: log(msg);
292: }
293: }
294:
295: /**
296: * Convenience method to format a specified length of time.
297: *
298: * @param millis Length of time to format, in milliseconds.
299: *
300: * @return the time as a formatted string.
301: *
302: * @see DateUtils#formatElapsedTime(long)
303: */
304: protected static String formatTime(final long millis) {
305: return DateUtils.formatElapsedTime(millis);
306: }
307:
308: /**
309: * Prints a message to a PrintStream.
310: *
311: * @param message The message to print.
312: * Should not be <code>null</code>.
313: * @param stream A PrintStream to print the message to.
314: * Must not be <code>null</code>.
315: * @param priority The priority of the message.
316: * (Ignored in this implementation.)
317: */
318: protected void printMessage(final String message,
319: final PrintStream stream, final int priority) {
320: stream.println(message);
321: }
322:
323: /**
324: * Empty implementation which allows subclasses to receive the
325: * same output that is generated here.
326: *
327: * @param message Message being logged. Should not be <code>null</code>.
328: */
329: protected void log(String message) {
330: }
331: }
|