001: /*
002: * <copyright>
003: *
004: * Copyright 2001-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.util.log.log4j;
028:
029: import java.io.File;
030: import java.io.IOException;
031: import java.io.InputStream;
032: import java.net.MalformedURLException;
033: import java.net.URL;
034: import java.util.Iterator;
035: import java.util.Map;
036: import java.util.Properties;
037:
038: import org.apache.log4j.PropertyConfigurator;
039: import org.apache.log4j.xml.DOMConfigurator;
040: import org.cougaar.bootstrap.SystemProperties;
041: import org.cougaar.util.Configuration;
042: import org.cougaar.util.log.Logger;
043: import org.cougaar.util.log.LoggerController;
044: import org.cougaar.util.log.LoggerFactory;
045:
046: /**
047: * Log4j implementation of LoggerFactory, which is used to
048: * create Logger and LoggerController instances.
049: * <p>
050: * Typically the "requestor" classname is used to identify
051: * loggers. A special "name" is "root", which is used
052: * to specify the root (no-parent) logger.
053: * <p>
054: * To configure Log4J, you may specify a file of Log4J
055: * configuration properties. This may be a Log4J XML format file
056: * (ends with ".xml"), or a standard Java Properties (name=value) format file.
057: * If no file is given, the default logging settings are used
058: * (log to the CONSOLE at WARN level).
059: * You may also set any number of explicit System Properties to
060: * over-ride any settings from a file. Log4J configuration properties
061: * are documented in
062: * <a href="http://jakarta.apache.org/log4j/docs/manual.html">the log4j manual</a>.
063: * <p>
064: * To specify a file of Log4J configuration settings, use the
065: * System Property <code>org.cougaar.util.log.config</code>
066: * (there are several aliases for this property - see below).
067: * This property should be a valid URL, absolute path to a file,
068: * or a filename in $INSTALL/configs/common.
069: * (and if not found, the Default settings are used as if no
070: * no file was given).
071: * <p>
072: * To specify a particular Log4J configuration setting,
073: * prefix the standard Log4J setting with <code>log4j.logger</code> OR
074: * <code>org.cougaar.util.log</code> OR
075: * <code>org.cougaar.core.logging</code>.
076: * For example, to turn on logging to the INFO level for this package,
077: * use: <code>org.cougaar.util.log.log4j.category.org.cougaar.util.log.log4j=INFO</code>.
078: *
079: * @property org.cougaar.util.log.config Specifies a URL where a LoggerFactory configuration
080: * file may be found. The Log4jLoggerFactory inteprets this as a file of log4j properties.
081: * @property org.cougaar.util.log.config.filename Alias for <code>org.cougaar.util.log.config</code>
082: * @property org.cougaar.core.logging.config.filaname Alias for <code>org.cougaar.util.log.config</code>
083: * @property log4j.configuration Alias for <code>org.cougaar.util.log.config</code>
084: *
085: * @property org.cougaar.core.logging.* Over-ride or set a particular Log4J configuration setting (the *)
086: * @property org.cougaar.util.log.* Over-ride or set a particular Log4J configuration setting (the *)
087: * @property log4j.logger.* Over-ride or set a particular Log4J configuration setting (the *)
088: * Properties not pointing to a Config File are stripped of their
089: * prefix (one of the above 3) and passed to the
090: * logger configuration. These properties override any
091: * properties defined in the (optional)
092: * logging config file.
093: */
094: public class Log4jLoggerFactory extends LoggerFactory {
095: public static final String PREFIX = "org.cougaar.core.logging.";
096: public static final String LOG4JPREFIX = "log4j.logger.";
097: public static final String FILE_NAME_PROPERTY = PREFIX
098: + "config.filename";
099: public static final String LOG4JCONF = "log4j.configuration";
100:
101: /**
102: * Default configuration only prints WARN or higher
103: * statements.
104: * <p>
105: * A client with name "x.y.z" that calls:<pre>
106: * log.warn("test message");
107: * </pre>
108: * will generate a standard-output message similar to:<pre>
109: * 2002-03-08 22:26:08,980 WARN [z] - test message
110: * </pre>.
111: * <p>
112: * See the log4j docs for further details.
113: */
114: private static final String[][] DEFAULT_PROPS = {
115: { "log4j.rootCategory", "WARN,A1" },
116: { "log4j.appender.A1", "org.apache.log4j.ConsoleAppender" },
117: { "log4j.appender.A1.layout",
118: "org.apache.log4j.PatternLayout" },
119: { "log4j.appender.A1.layout.ConversionPattern",
120: "%d{ISO8601} %-5p [%c{1}] - %m%n" }, };
121:
122: private static final Properties DEFAULT_PROPERTIES;
123:
124: static {
125: Properties p = new Properties();
126: for (int i = 0, n = DEFAULT_PROPS.length; i < n; i++) {
127: p.put(DEFAULT_PROPS[i][0], DEFAULT_PROPS[i][1]);
128: }
129: DEFAULT_PROPERTIES = p;
130: }
131:
132: // Name of file to read configuration from
133: private String configFileName = null;
134:
135: public Log4jLoggerFactory() {
136: // Save any errors for logging once Log4J configured
137: Throwable err = null;
138:
139: // Get the logging properties config file from a System Property, if any
140: configFileName = Log4jLoggerFactory.getConfigFileName();
141: // System.out.println("Got Log4J config filename: " + configFileName);
142:
143: if (configFileName == null) {
144: // No file of properties
145: // Gather Default Props
146: // Overlay with System Props
147: // Configure
148: //System.out.println("Using default properties");
149: overlaySystemPropsAndConfigure(DEFAULT_PROPERTIES);
150: } else {
151: // Canonicalize / pseudo-ConfigFinder to get file
152: URL cu = findURLFromName(configFileName);
153: // if can't find it use above no-file approach
154: if (cu == null) {
155: System.err.println("Couldn't find Log4J config file "
156: + configFileName + ". Using defaults.");
157: overlaySystemPropsAndConfigure(DEFAULT_PROPERTIES);
158: } else {
159: //System.out.println("Configuring from URL " + cu);
160: // Got a config file to parse and configure from
161: if (configFileName.endsWith(".xml")) {
162: //System.out.println("Using DOMConfigurator");
163: // Configure with DOMConfigurator
164: DOMConfigurator.configure(cu);
165: // Try overlaying with System Props
166: overlaySystemPropsAndConfigure(new Properties());
167: org.apache.log4j.Logger
168: .getLogger(Log4jLoggerFactory.class)
169: .info(
170: "Configured Log4J logging using DOMConfigurator");
171: } else {
172: //System.out.println("Reading in from props file");
173: Properties props = new Properties();
174: try {
175: // Read in Props from file
176: InputStream is = cu.openStream();
177: if (is != null) {
178: props.load(is);
179: is.close();
180: }
181: } catch (IOException ioe) {
182: // Save the error for later logging once Log4J configured
183: err = ioe;
184: }
185: // Overlay with System Props
186: // Configure
187: overlaySystemPropsAndConfigure(props);
188: } // end of block to configure from props file
189: // Log URL where config file found here.
190: org.apache.log4j.Logger.getLogger(
191: Log4jLoggerFactory.class).info(
192: "Configured logging from file found at URL: "
193: + cu);
194:
195: // Log any error from above
196: if (err != null)
197: org.apache.log4j.Logger
198: .getLogger(Log4jLoggerFactory.class)
199: .error(
200: "Failed to configure logging from file. Defaults used.",
201: err);
202:
203: } // end of block to handl xml or props files
204: } // end of block where got a file name
205: }
206:
207: /**
208: * Search the various possible System Properties for
209: * the log4j configuration file name
210: * Search takes first set property, searching:
211: * ocu.log.config (LF_CONFIG_PROP)
212: * ocu.log.config.filename (LF_PREFIX + "config.filename")
213: * occ.logging.config.filename (FILE_NAME_PROPERTY)
214: * log4j.configuration (LOG4JCONF)
215: * @return String pointing to a file to configure Log4J from (possibly null)
216: **/
217: public static final String getConfigFileName() {
218: String configFileName = SystemProperties
219: .getProperty(LF_CONFIG_PROP);
220: if (configFileName == null)
221: configFileName = SystemProperties.getProperty(LF_PREFIX
222: + ".config.filename");
223: if (configFileName == null)
224: configFileName = SystemProperties
225: .getProperty(FILE_NAME_PROPERTY);
226: if (configFileName == null)
227: configFileName = SystemProperties.getProperty(LOG4JCONF);
228: return configFileName;
229: }
230:
231: // Do a pseudo-ConfigFinder search for a file
232: // HOWEVER: In this case, search only 2 ways.
233: // 1: Treat the filename as a URL or Absolute Path.
234: // If we can't open a URL connection to that (it doesn't exist)
235: // then,
236: // 2: Look for a file of that name in $INSTALL/configs/common
237: // return URL to the file or NULL if not found
238: // See org.cougaar.util.Configuration
239: private URL findURLFromName(String fileName) {
240: URL url = null;
241:
242: // First try the fileName as a URL or absolute path
243: try {
244: url = Configuration.urlify(fileName);
245: if (url != null) {
246: //System.out.println("File arg was URLable: " + url);
247: InputStream is = url.openStream();
248: if (is == null) {
249: // Couldn't open it. Set url to null to indicate failure
250: url = null;
251: } else {
252: is.close();
253: //System.out.println("Found " + url);
254: }
255: }
256: } catch (MalformedURLException mue) {
257: url = null;
258: } catch (IOException ioe) {
259: url = null;
260: }
261:
262: // If the url is null then that didn't work
263: // Try looking for it in configs/common
264: if (url == null) {
265: //System.out.println("Filename arg as URL wouldn't open.");
266: try {
267: // This call interprets the COUGAAR_INSTALL_PATH in and creates a URL from this
268: URL base = Configuration.urlify(Configuration
269: .resolveValue("$INSTALL/configs/common"));
270: if (base != null) {
271: //System.out.println("URLified configs/common: " + base);
272: url = new URL(base, fileName);
273: InputStream is = url.openStream();
274: if (is == null) {
275: url = null;
276: } else {
277: is.close();
278: //System.out.println("Found in configs/common: " + url);
279: }
280: }
281: } catch (MalformedURLException mue) {
282: //System.out.println("Got malformed URL: " + mue);
283: url = null;
284: } catch (IOException ioe) {
285: url = null;
286: }
287: }
288: //System.out.println("Result of looking for " + fileName + " is the url: " + url);
289:
290: // Return whatever URL we came up with - possible null
291: return url;
292: }
293:
294: // Input a properties hashmap (possibly empty)
295: // Overlay properties from System Properties
296: // Such overlaid properties may start with
297: // org.cougaar.core.logging (which will be trimmed off)
298: // org.cougaar.util.log (which will be trimmed off)
299: // or log4j.logger (which will NOT)
300: // Then call PropertyConfigurator.configure with this map
301: // Note that if Log4J has been previously configured using the
302: // DOMConfigurator, these overlays may have little or no effect.
303: private void overlaySystemPropsAndConfigure(Properties props) {
304: if (props == null)
305: props = new Properties();
306:
307: // Track input props for later info logging
308: int inputProps = props.size();
309:
310: // Get properties from SystemProperties
311: Properties spProps = new Properties();
312: spProps.putAll(SystemProperties
313: .getSystemPropertiesWithPrefix(PREFIX));
314: spProps.putAll(SystemProperties
315: .getSystemPropertiesWithPrefix(LOG4JPREFIX));
316: // Allow SystemProps to specify as ocu.log.*?
317: spProps.putAll(SystemProperties
318: .getSystemPropertiesWithPrefix(LF_PREFIX));
319:
320: for (Iterator it = spProps.keySet().iterator(); it.hasNext();) {
321: String name = (String) it.next();
322: //System.out.println("Handling property " + name);
323:
324: // Exclude the properties that point to a config file
325: if (name.equals(FILE_NAME_PROPERTY)) {
326: continue;
327: }
328:
329: if (name.equals(LF_CONFIG_PROP)
330: || name.equals(LF_PREFIX + ".config.filename")) {
331: continue;
332: }
333:
334: String value = spProps.getProperty(name);
335: // Strip the prefixes off
336: if (name.indexOf(PREFIX) != -1)
337: name = name.substring(PREFIX.length());
338:
339: // Note that you must add one for the "."
340: if (name.indexOf(LF_PREFIX) != -1)
341: name = name.substring(LF_PREFIX.length() + 1);
342:
343: // Look for props that end with .File to check that dirs exist
344: // Ugly, but the Log4J configurator refuses to try to create the directory
345: if (name.endsWith(".File")) {
346: if (value == null)
347: continue;
348:
349: File log = new File(value);
350: //System.out.println("Using log file " + log);
351: File parentdir = log.getParentFile();
352: //System.out.println("Using parent dir " + parentdir);
353: if (parentdir != null
354: && ((!parentdir.exists() && !parentdir.mkdirs()) || !parentdir
355: .canWrite())) {
356: System.err
357: .println("Log4JLoggerFactory: Couldn't create / write to directory for File appender "
358: + name + " for file " + value);
359: System.err
360: .println(" -- Will skip that logging property.");
361: // can't write to the dir denoted. Skip it
362: continue;
363: } else {
364: //System.out.println("Dir is OK");
365: }
366: }
367:
368: // System.out.println("Adding to list of system props: " + name + "=" + value);
369:
370: // Add this new prop to that from the info - over-riding
371: // any previous value for that property
372: props.put(name, value);
373: } // end of loop over SystemProperties props to overlay
374:
375: // For debug: Print out logging properties
376: // for (Iterator it = props.keySet().iterator(); it.hasNext(); ) {
377: // String name = (String) it.next();
378: // String value = props.getProperty(name);
379: // System.out.println(name + "=" + value);
380: // }
381:
382: // Have Log4J configure itself from the collected properties.
383: PropertyConfigurator.configure(props);
384:
385: org.apache.log4j.Logger
386: .getLogger(Log4jLoggerFactory.class)
387: .info(
388: "Configured logging from "
389: + spProps.size()
390: + " System Properties"
391: + (inputProps > 0 ? (" overlaid on "
392: + inputProps
393: + " Properties from " + configFileName)
394: : ""));
395: }
396:
397: // ugh. bashing of static structure... sigh.
398: public void configure(Properties props) {
399: PropertyConfigurator.configure(props);
400: }
401:
402: // ugh. bashing of static structure... sigh.
403: public void configure(Map m) {
404: Properties p = new Properties();
405: p.putAll(m);
406: PropertyConfigurator.configure(p);
407: }
408:
409: /*
410: // rely on the base class now
411: public Logger createLogger(Object requestor) {
412: return new LoggerImpl(requestor);
413: }
414: */
415:
416: /** called by the default implementation of createLogger to do the dirty work.
417: * Should always create a new instance.
418: **/
419: protected Logger instantiateLogger(Object requestor) {
420: return new LoggerImpl(requestor);
421: }
422:
423: public LoggerController createLoggerController(String requestor) {
424: (new Throwable()).printStackTrace();
425: return new LoggerControllerImpl(requestor);
426: }
427: }
|