001: /*
002: * Enhydra Java Application Server Project
003: *
004: * The contents of this file are subject to the Enhydra Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License on
007: * the Enhydra web site ( http://www.enhydra.org/ ).
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
011: * the License for the specific terms governing rights and limitations
012: * under the License.
013: *
014: * The Initial Developer of the Enhydra Application Server is Lutris
015: * Technologies, Inc. The Enhydra Application Server and portions created
016: * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
017: * All Rights Reserved.
018: *
019: * Contributor(s):
020: *
021: * $Id: Log4jLogger.java,v 1.3 2007-10-19 10:05:39 sinisa Exp $
022: */
023: package com.lutris.logging;
024:
025: import java.io.File;
026: import java.io.IOException;
027: import java.io.PrintWriter;
028: import java.util.Hashtable;
029:
030: import org.apache.log4j.BasicConfigurator;
031: import org.apache.log4j.ConsoleAppender;
032: import org.apache.log4j.Level;
033: import org.apache.log4j.Logger;
034: import org.apache.log4j.PatternLayout;
035: import org.apache.log4j.RollingFileAppender;
036: import org.apache.log4j.spi.LoggerRepository;
037: import org.apache.log4j.xml.DOMConfigurator;
038:
039: import com.lutris.util.Config;
040: import com.lutris.util.ConfigException;
041:
042: /**
043: * Log4j implementation of the <CODE>Logger</CODE>. This is
044: * general-purpose logging facility. A client that needs additional
045: * functionality can either extend this class or provide there own
046: * implementationm of <CODE>Logger</CODE>. <P>
047: *
048: * Currently this is a bare-bones class that writes INFO and above
049: * levels to stderr and all others to a log file.
050: *
051: * @author Vladimir Puskas
052: * @author Predrag Djoic
053: * @author Sinisa Milosevic
054: * @see com.lutris.logging.Logger
055: * @see com.lutris.logging.LogChannel
056: */
057: public class Log4jLogger extends com.lutris.logging.Logger {
058:
059: /**
060: * Table of level names to level numbers. While level configuration
061: * is local to a facility, a global table is kept assigning numbers
062: * to each level name.
063: */
064: private Hashtable levelNumbers = new Hashtable();
065:
066: /**
067: * Table translating level number to name and the largest entry in the
068: * array that is valid. Will be expanded if needed.
069: */
070: protected String[] levelNames = new String[MAX_STD_LEVEL * 2];
071: protected int numLevels = 0;
072:
073: /**
074: * Table of levels that are to be enabled.
075: * Accessed directly by the channel. If null, ignored.
076: */
077: protected boolean[] enabledLevelFlags = null;
078:
079: /**
080: * Table of levels that are to be written to the log file.
081: * Accessed directly by the channel. If null, ignored.
082: */
083: protected boolean[] logFileLevelFlags = null;
084:
085: /**
086: * Table of levels that are to be written to stderr
087: * Accessed directly by the channel. If null, then
088: * the default behavior of writing serious standard
089: * levels to stderr is in affect.
090: */
091: protected boolean[] stderrLevelFlags = null;
092:
093: /**
094: * Default Log4j configuration file
095: */
096: protected String DEFAULT_LOG_CONFIG_FILE = "log4j.xml";
097:
098: /**
099: * Log file name.
100: */
101: File activeLogFile;
102:
103: /**
104: * Log file writter. Use directly by channels.
105: */
106: PrintWriter logFileStream;
107:
108: /**
109: * Stderr writter. Use directly by channels.
110: */
111: PrintWriter stderrStream;
112:
113: /**
114: * Table of <CODE>StandardLogChannel<CODE> objects, indexed by facility
115: * name.
116: */
117: private Hashtable logChannels = new Hashtable();
118:
119: String defaultSeparatorLine = "";
120: Hashtable appSeparatorLine = new Hashtable();
121:
122: /**
123: * Construct a new logger. Configuration is not done now, to allow
124: * the logger to be created very early.
125: *
126: * @param makeCentral Make this object the central logging object.
127: */
128: public Log4jLogger(boolean makeCentral) {
129: int level;
130:
131: for (level = 0; level <= MAX_STD_LEVEL; level++) {
132: String name = standardLevelNames[level];
133:
134: levelNumbers.put(name, new Integer(level));
135: levelNames[level] = name;
136: }
137: numLevels = level;
138: if (makeCentral) {
139: centralLogger = this ;
140: }
141: }
142:
143: /**
144: * Get maximum level number in a set of level names.
145: *
146: * @param levels String names of levels.
147: * @return The maximum level number
148: */
149: private int getMaxLevel(String[] levels) {
150: int levelNum;
151: int maxLevelNum = 0;
152:
153: for (int idx = 0; idx < levels.length; idx++) {
154: levelNum = getLevel(levels[idx]);
155: if (levelNum > maxLevelNum) {
156: maxLevelNum = levelNum;
157: }
158: }
159: return maxLevelNum;
160: }
161:
162: /**
163: * Generate a boolean array for all of the listed levels, indicating
164: * if they are enabled.
165: *
166: * @param levels String names of levels.
167: * @param maxLevelNum Size to make the array.
168: */
169: private boolean[] getLevelStateArray(String[] levels,
170: int maxLevelNum) {
171: // int levelNum;
172: // Initialize the stated.
173: boolean[] levelNums = new boolean[maxLevelNum + 1];
174:
175: for (int idx = 0; idx < levels.length; idx++) {
176: levelNums[getLevel(levels[idx])] = true;
177: }
178: return levelNums;
179: }
180:
181: /**
182: * Configure the logger. All current configuration is discarded.
183: * This is a simplistic initial implementation that just allows
184: * directing to a single log file or stderr on a level basis.
185: * A more complete interface will be provided in the future.
186: *
187: * @param logFile The log file to write to.
188: * @param fileLevels List of levels that will be written to the file.
189: * @param stderrLevels List of levels that will be written to stderr.
190: * The same level may appear in both lists.
191: * @exception java.io.IOException If an error occurs opening the log file.
192: */
193: public synchronized void configure(String log4jConfFile)
194: throws ConfigException {
195: try {
196: DOMConfigurator.configure(log4jConfFile);
197: } catch (javax.xml.parsers.FactoryConfigurationError fce) {
198: throw new ConfigException("Cannot configure Log4jLogger:",
199: fce);
200: }
201:
202: }
203:
204: /**
205: * This method configures Logger if configure data was not found in config
206: * file (at command line)
207: */
208: static private void configureLogger() {
209: // do the basic configuration:
210: BasicConfigurator.configure();
211: Logger root = Logger.getRootLogger();
212:
213: // remove predefined appenders from basic configuration:
214: root.removeAllAppenders();
215: LoggerRepository repository = root.getLoggerRepository();
216:
217: // set level for log info:
218: repository.setThreshold(Level.INFO);
219: RollingFileAppender multiserverAppender = null;
220: RollingFileAppender accessAppender = null;
221: // strale@uns.ns.ac.yu, 29 nov 2002
222: ConsoleAppender conAppender = null;
223: // set path to log files:
224: String enhydraLogs = System.getProperty("enhydra.home")
225: + File.separator + "logs" + File.separator;
226: String multiserverLogFile = enhydraLogs + "multiserver.log";
227: String accessLogFile = enhydraLogs + "access.log";
228:
229: try {
230:
231: /* create default appenders:
232: * %d(ISO8601) - date in format quick to write
233: * %t - time
234: * %C(1) - full class name (one level) i.e.: com.lutris.Enhydra -> Enhydra
235: * %p - priority of the input
236: * %c - category of the input
237: * %m - massage
238: * %n - new line
239: */
240: multiserverAppender = new RollingFileAppender(
241: new PatternLayout(
242: "%d{ISO8601}: [%t], %C(1) %p, %c: %m%n"),
243: multiserverLogFile);
244: accessAppender = new RollingFileAppender(new PatternLayout(
245: "%m%n"), accessLogFile);
246: conAppender = new ConsoleAppender(new PatternLayout(
247: "%d{ISO8601}: [%t], %C(1) %p, %c: %m%n"));
248: } catch (IOException e) {
249: e.printStackTrace();
250: }
251: root.addAppender(multiserverAppender);
252: // create new logger for with "REQUEST" category that log to access.log:
253: Logger requestLogger = Logger.getLogger("REQUEST");
254:
255: // does not apply root appender to REQUEST logger:
256: requestLogger.setAdditivity(false);
257: requestLogger.addAppender(accessAppender);
258: Logger sysOutLogger = Logger.getLogger("SysOut");
259:
260: sysOutLogger.setAdditivity(false);
261: sysOutLogger.addAppender(conAppender);
262: }
263:
264: /**
265: * Create a log channel.
266: */
267: private synchronized Log4jLogChannel createChannel(String facility) {
268:
269: String sepLine = (String) appSeparatorLine.get(facility);
270: if (sepLine == null)
271: sepLine = defaultSeparatorLine;
272: Log4jLogChannel channel = (Log4jLogChannel) logChannels
273: .get(facility);
274:
275: if (channel == null) {
276: channel = new Log4jLogChannel(facility, Logger
277: .getLogger(facility), sepLine);
278: logChannels.put(facility, channel);
279: }
280:
281: return channel;
282: }
283:
284: /**
285: * Get the log channel object for a facility. For a given facility,
286: * the same object is always returned.
287: *
288: * @param facility Facility the channel is associated with.
289: */
290: public LogChannel getChannel(String facility) {
291:
292: Log4jLogChannel channel = (Log4jLogChannel) logChannels
293: .get(facility);
294:
295: if (channel == null) {
296: // Slow path, synchronized
297: channel = createChannel(facility);
298: }
299: return channel;
300: }
301:
302: /**
303: * Create a log level.
304: */
305: private synchronized Integer createLevel(String level) {
306: Integer intLevel = (Integer) levelNumbers.get(level);
307:
308: if (intLevel == null) {
309: intLevel = new Integer(numLevels);
310: levelNames[numLevels] = level;
311: levelNumbers.put(level, intLevel);
312: numLevels++;
313: }
314: return intLevel;
315: }
316:
317: /**
318: * Convert a symbolic level to an integer identifier,
319: * creating it if it doesn't exist
320: *
321: * @param level Symbolic level that is to be checked.
322: * @return The numeric level identifier
323: */
324: public synchronized int getLevel(String level) {
325: Integer intLevel = (Integer) levelNumbers.get(level);
326:
327: if (intLevel == null) {
328: // Slow path, synchronized
329: intLevel = createLevel(level);
330: }
331: return intLevel.intValue();
332: }
333:
334: /**
335: * Convert an int to a symbolic level name.
336: *
337: * @param level an int level.
338: * @return The String symolic level name or null if there is not one.
339: */
340: public String getLevelName(int level) {
341: if ((level >= 0) && (level < numLevels)) {
342: return levelNames[level];
343: } else {
344: return null;
345: }
346: }
347:
348: /**
349: * Configure Logger with given config section
350: *
351: * @param logConfig containing parameters for configuring logger
352: */
353: public void configure(Config logConfig) throws ConfigException {
354:
355: String fileName = null;
356: File logConfigFile = null;
357: if (logConfig.containsKey("Log4j")) {
358: fileName = logConfig.getString("Log4j");
359: logConfigFile = new File(fileName);
360: if (!logConfigFile.exists()) {
361: fileName = logConfig.getConfigFile().getFile()
362: .getParent()
363: + File.separator + fileName;
364: logConfigFile = new File(fileName);
365: if (!logConfigFile.exists()) {
366: fileName = logConfig.getConfigFile().getFile()
367: .getParent();
368: if (null != fileName) {
369: fileName += File.separator
370: + DEFAULT_LOG_CONFIG_FILE;
371: }
372: }
373: }
374:
375: } else if (null != logConfig.getConfigFile()) {
376: fileName = logConfig.getConfigFile().getFile().getParent();
377: if (null != fileName) {
378: fileName += File.separator + DEFAULT_LOG_CONFIG_FILE;
379: }
380: } else {
381: throw new ConfigException(
382: "Cannot configure logger. Config file is null.");
383: }
384: if (logConfig.containsKey("SeparatorLine")) {
385:
386: String app = logConfig.getString("Server.AppClass");
387: String separator = logConfig.getString("SeparatorLine");
388: int index = app.lastIndexOf(".");
389: String temp = app.substring(index);
390: appSeparatorLine.put(temp.substring(1), separator);
391: }
392: configure(fileName);
393: }
394: }
|