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
018package org.apache.log4j.lf5;
019
020import java.awt.Toolkit;
021
022import org.apache.log4j.AppenderSkeleton;
023import org.apache.log4j.lf5.viewer.LogBrokerMonitor;
024import org.apache.log4j.spi.LocationInfo;
025import org.apache.log4j.spi.LoggingEvent;
026
027/**
028 * <code>LF5Appender</code> logs events to a swing based logging
029 * console. The swing console supports turning categories on and off,
030 * multiple detail level views, as well as full text searching and many
031 * other capabilties.
032 *
033 * @author Brent Sprecher
034 */
035
036// Contributed by ThoughtWorks Inc.
037
038public class LF5Appender extends AppenderSkeleton {
039  //--------------------------------------------------------------------------
040  // Constants:
041  //--------------------------------------------------------------------------
042
043  //--------------------------------------------------------------------------
044  // Protected Variables:
045  //--------------------------------------------------------------------------
046
047  protected LogBrokerMonitor _logMonitor;
048  protected static LogBrokerMonitor _defaultLogMonitor;
049  protected static AppenderFinalizer _finalizer;
050
051  //--------------------------------------------------------------------------
052  // Private Variables:
053  //--------------------------------------------------------------------------
054
055  //--------------------------------------------------------------------------
056  // Constructors:
057  //--------------------------------------------------------------------------
058
059  /**
060   * Constructs a <code>LF5Appender</code> using the default instance of
061   * the <code>LogBrokerMonitor</code>. This constructor should <bold>always
062   * </bold> be  preferred over the
063   * <code>LF5Appender(LogBrokerMonitor monitor)</code>
064   * constructor, unless you need to spawn additional log monitoring
065   * windows.
066   */
067  public LF5Appender() {
068    this(getDefaultInstance());
069  }
070
071  /**
072   * Constructs a <code>LF5Appender<code> using an instance of
073   * a <code>LogBrokerMonitor<code> supplied by the user. This
074   * constructor should only be used when you need to spawn
075   * additional log monitoring windows.
076   *
077   * @param monitor An instance of a <code>LogBrokerMonitor<code>
078   * created by the user.
079   */
080  public LF5Appender(LogBrokerMonitor monitor) {
081
082    if (monitor != null) {
083      _logMonitor = monitor;
084    }
085  }
086
087  //--------------------------------------------------------------------------
088  // Public Methods:
089  //--------------------------------------------------------------------------
090
091  /**
092   * Appends a <code>LoggingEvent</code> record to the
093   * <code>LF5Appender</code>.
094   * @param event The <code>LoggingEvent</code>
095   * to be appended.
096   */
097  public void append(LoggingEvent event) {
098    // Retrieve the information from the log4j LoggingEvent.
099    String category = event.getLoggerName();
100    String logMessage = event.getRenderedMessage();
101    String nestedDiagnosticContext = event.getNDC();
102    String threadDescription = event.getThreadName();
103    String level = event.getLevel().toString();
104    long time = event.timeStamp;
105    LocationInfo locationInfo = event.getLocationInformation();
106
107    // Add the logging event information to a LogRecord
108    Log4JLogRecord record = new Log4JLogRecord();
109
110    record.setCategory(category);
111    record.setMessage(logMessage);
112    record.setLocation(locationInfo.fullInfo);
113    record.setMillis(time);
114    record.setThreadDescription(threadDescription);
115
116    if (nestedDiagnosticContext != null) {
117      record.setNDC(nestedDiagnosticContext);
118    } else {
119      record.setNDC("");
120    }
121
122    if (event.getThrowableInformation() != null) {
123      record.setThrownStackTrace(event.getThrowableInformation());
124    }
125
126    try {
127      record.setLevel(LogLevel.valueOf(level));
128    } catch (LogLevelFormatException e) {
129      // If the priority level doesn't match one of the predefined
130      // log levels, then set the level to warning.
131      record.setLevel(LogLevel.WARN);
132    }
133
134    if (_logMonitor != null) {
135      _logMonitor.addMessage(record);
136    }
137  }
138
139  /**
140   * This method is an empty implementation of the close() method inherited
141   * from the <code>org.apache.log4j.Appender</code> interface.
142   */
143  public void close() {
144  }
145
146  /**
147   * Returns a value that indicates whether this appender requires a
148   * <code>Layout</code>. This method always returns false.
149   * No layout is required for the <code>LF5Appender</code>.
150   */
151  public boolean requiresLayout() {
152    return false;
153  }
154
155  /**
156   * This method is used to set the property that controls whether
157   * the <code>LogBrokerMonitor</code> is hidden or closed when a user
158   * exits
159   * the monitor. By default, the <code>LogBrokerMonitor</code> will hide
160   * itself when the log window is exited, and the swing thread will
161   * continue to run in the background. If this property is
162   * set to true, the <code>LogBrokerMonitor</code> will call System.exit(0)
163   * and will shut down swing thread and the virtual machine.
164   *
165   * @param callSystemExitOnClose A boolean value indicating whether
166   * to call System.exit(0) when closing the log window.
167   */
168  public void setCallSystemExitOnClose(boolean callSystemExitOnClose) {
169    _logMonitor.setCallSystemExitOnClose(callSystemExitOnClose);
170  }
171
172  /**
173   * The equals method compares two LF5Appenders and determines whether
174   * they are equal. Two <code>Appenders</code> will be considered equal
175   * if, and only if, they both contain references to the same <code>
176   * LogBrokerMonitor</code>.
177   *
178   * @param compareTo A boolean value indicating whether
179   * the two LF5Appenders are equal.
180   */
181  public boolean equals(LF5Appender compareTo) {
182    // If both reference the same LogBrokerMonitor, they are equal.
183    return _logMonitor == compareTo.getLogBrokerMonitor();
184  }
185
186  public LogBrokerMonitor getLogBrokerMonitor() {
187    return _logMonitor;
188  }
189
190  public static void main(String[] args) {
191    new LF5Appender();
192  }
193
194  public void setMaxNumberOfRecords(int maxNumberOfRecords) {
195    _defaultLogMonitor.setMaxNumberOfLogRecords(maxNumberOfRecords);
196  }
197  //--------------------------------------------------------------------------
198  // Protected Methods:
199  //--------------------------------------------------------------------------
200
201  /**
202   * @return The default instance of the <code>LogBrokerMonitor</code>.
203   */
204  protected static synchronized LogBrokerMonitor getDefaultInstance() {
205    if (_defaultLogMonitor == null) {
206      try {
207        _defaultLogMonitor =
208            new LogBrokerMonitor(LogLevel.getLog4JLevels());
209        _finalizer = new AppenderFinalizer(_defaultLogMonitor);
210
211        _defaultLogMonitor.setFrameSize(getDefaultMonitorWidth(),
212            getDefaultMonitorHeight());
213        _defaultLogMonitor.setFontSize(12);
214        _defaultLogMonitor.show();
215
216      } catch (SecurityException e) {
217        _defaultLogMonitor = null;
218      }
219    }
220
221    return _defaultLogMonitor;
222  }
223
224  /**
225   * @return the screen width from Toolkit.getScreenSize()
226   * if possible, otherwise returns 800
227   * @see java.awt.Toolkit
228   */
229  protected static int getScreenWidth() {
230    try {
231      return Toolkit.getDefaultToolkit().getScreenSize().width;
232    } catch (Throwable t) {
233      return 800;
234    }
235  }
236
237  /**
238   * @return the screen height from Toolkit.getScreenSize()
239   * if possible, otherwise returns 600
240   * @see java.awt.Toolkit
241   */
242  protected static int getScreenHeight() {
243    try {
244      return Toolkit.getDefaultToolkit().getScreenSize().height;
245    } catch (Throwable t) {
246      return 600;
247    }
248  }
249
250  protected static int getDefaultMonitorWidth() {
251    return (3 * getScreenWidth()) / 4;
252  }
253
254  protected static int getDefaultMonitorHeight() {
255    return (3 * getScreenHeight()) / 4;
256  }
257  //--------------------------------------------------------------------------
258  // Private Methods:
259  //--------------------------------------------------------------------------
260
261
262  //--------------------------------------------------------------------------
263  // Nested Top-Level Classes or Interfaces:
264  //--------------------------------------------------------------------------
265
266}