001: /*
002: This software is OSI Certified Open Source Software.
003: OSI Certified is a certification mark of the Open Source Initiative.
004:
005: The license (Mozilla version 1.0) can be read at the MMBase site.
006: See http://www.MMBase.org/license
007:
008: */
009:
010: package org.mmbase.util.logging.log4j;
011:
012: import org.mmbase.util.logging.Logger;
013: import org.mmbase.util.logging.Level;
014: import org.mmbase.util.logging.Logging;
015:
016: import org.mmbase.util.ResourceWatcher;
017: import org.mmbase.util.ResourceLoader;
018:
019: import org.apache.log4j.xml.DOMConfigurator;
020:
021: import java.io.*;
022:
023: import java.io.PrintStream;
024: import java.io.File;
025:
026: /**
027: * This Logger implementation extends the Logger class from the log4j
028: * project (version >= 1.2). It has the following extra functionality.
029: *
030: * First of all it uses the LoggerLevel class for Level, and so
031: * has two extra priorities, namely 'trace' and 'service'.
032: *
033: * Further it instantiates one object of itself, named `STDERR' to
034: * which stderr will be redirected. Normally this will happen with
035: * priority `info' but Exceptions will get priorty `fatal'.
036: *
037: * It also has a static member method `configure', which calls the
038: * configure of DOMConfigurator, in this way log4j classes are used
039: * only here, and the rest of MMBase can use only `Logger'.
040: *
041: * @author Michiel Meeuwissen
042: */
043:
044: public final class Log4jImpl extends org.apache.log4j.Logger implements
045: Logger {
046: // class is final, perhaps then its methods can be inlined when compiled with -O?
047:
048: // It's enough to instantiate a factory once and for all.
049: private final static org.apache.log4j.spi.LoggerRepository repository = new LoggerRepository(
050: getRootLogger());
051: private static Logger log = Logging
052: .getLoggerInstance(Log4jImpl.class);
053:
054: private static final String classname = Log4jImpl.class.getName();
055:
056: private static ResourceWatcher configWatcher;
057:
058: /**
059: * Constructor, like the constructor of {@link org.apache.log4j.Logger}.
060: */
061: protected Log4jImpl(String name) {
062: super (name);
063: }
064:
065: /**
066: * As getLogger, but cast to MMBase Logger already. And the possible
067: * ClassCastException is caught.
068: */
069: public static Log4jImpl getLoggerInstance(String name) {
070: try {
071: return (Log4jImpl) repository.getLogger(name);
072: } catch (ClassCastException e) {
073: Log4jImpl root = (Log4jImpl) getRootLogger(); // make it log on root, and log a huge error, that something is wrong.
074: root
075: .error("ClassCastException, probably you've forgotten a class attribute in your configuration file. It must say class=\""
076: + Log4jImpl.class.getName() + "\"");
077: return root;
078: }
079: }
080:
081: /**
082: * Calls the configure method of DOMConfigurator, and redirect
083: * standard error to STDERR category. It also starts up the
084: * FileWatcher. The 'configureAndWatch' method of DOMConfigurator
085: * used to be used, but it is not feasible anymore because
086: * 1. cannot give the repository then. 2. Cannot log the happening
087: * on normal way.
088: *
089: * @param s A string to the xml-configuration file. Can be
090: * absolute, or relative to the Logging configuration file.
091: **/
092:
093: public static void configure(String s) {
094:
095: log.info("logging configurationfile : " + s);
096:
097: ResourceLoader rl = Logging.getResourceLoader();
098:
099: log.info("using " + rl + " for resolving " + s + " -> "
100: + rl.getResource(s));
101: configWatcher = new ResourceWatcher(rl) {
102: public void onChange(String s) {
103: doConfigure(resourceLoader.getResourceAsStream(s));
104: }
105: };
106:
107: configWatcher.clear();
108: configWatcher.add(s);
109:
110: doConfigure(rl.getResourceAsStream(s));
111:
112: configWatcher.setDelay(10 * 1000); // check every 10 secs if config changed
113: configWatcher.start();
114: log = getLoggerInstance(Log4jImpl.class.getName());
115:
116: Log4jImpl err = getLoggerInstance("STDERR");
117: // a trick: if the level of STDERR is FATAL, then stderr will not be captured at all.
118: if (err.getLevel() != Log4jLevel.FATAL) {
119: log
120: .service("Redirecting stderr to MMBase logging (If you don't like this, then put the STDER logger to 'fatal')");
121: System.setErr(new LoggerStream(err));
122: }
123: }
124:
125: protected static void doConfigure(InputStream i) {
126: DOMConfigurator domConfigurator = new DOMConfigurator();
127: domConfigurator.doConfigure(i, repository);
128: }
129:
130: /**
131: * Performs the actual parsing of the log4j configuration file and handles the errors
132: */
133: protected static void doConfigure(File f) {
134: log.info("Parsing " + f.getAbsolutePath());
135: try {
136: doConfigure(new FileInputStream(f));
137: } catch (java.io.FileNotFoundException e) {
138: log.error("Could not find " + f + " to configure logging: "
139: + e.toString());
140: }
141:
142: }
143:
144: public void setLevel(Level p) {
145: switch (p.toInt()) {
146: case Level.TRACE_INT:
147: setLevel(Log4jLevel.TRACE);
148: break;
149: case Level.DEBUG_INT:
150: setLevel(Log4jLevel.DEBUG);
151: break;
152: case Level.SERVICE_INT:
153: setLevel(Log4jLevel.SERVICE);
154: break;
155: case Level.INFO_INT:
156: setLevel(Log4jLevel.INFO);
157: break;
158: case Level.WARN_INT:
159: setLevel(Log4jLevel.WARN);
160: break;
161: case Level.ERROR_INT:
162: setLevel(Log4jLevel.ERROR);
163: break;
164: case Level.FATAL_INT:
165: setLevel(Log4jLevel.FATAL);
166: break;
167: case Level.OFF_INT:
168: setLevel(Log4jLevel.OFF);
169: break;
170: default:
171: break;
172: }
173: }
174:
175: /**
176: * This method overrides {@link org.apache.log4j.Logger#getInstance} by supplying
177: * its own factory type as a parameter.
178: */
179: public static org.apache.log4j.Category getInstance(String name) {
180: return getLogger(name);
181: }
182:
183: public static org.apache.log4j.Logger getLogger(String name) {
184: return repository.getLogger(name);
185: }
186:
187: /**
188: * A new logging method that takes the TRACE priority.
189: */
190: public final void trace(Object message) {
191: // disable is defined in Category
192: if (repository.isDisabled(Log4jLevel.TRACE_INT)) {
193: return;
194: }
195: if (Log4jLevel.TRACE.isGreaterOrEqual(this .getEffectiveLevel()))
196: //callAppenders(new LoggingEvent(classname, this, Log4jLevel.TRACE, message, null));
197: forcedLog(classname, Log4jLevel.TRACE, message, null);
198: }
199:
200: public final void trace(Object message, Throwable t) {
201: // disable is defined in Category
202: if (repository.isDisabled(Log4jLevel.TRACE_INT)) {
203: return;
204: }
205: if (Log4jLevel.TRACE.isGreaterOrEqual(this .getEffectiveLevel()))
206: //callAppenders(new LoggingEvent(classname, this, Log4jLevel.TRACE, message, null));
207: forcedLog(classname, Log4jLevel.TRACE, message, t);
208: }
209:
210: /**
211: * A new logging method that takes the SERVICE priority.
212: */
213: public final void service(Object message) {
214: // disable is defined in Category
215: if (repository.isDisabled(Log4jLevel.SERVICE_INT)) {
216: return;
217: }
218: if (Log4jLevel.SERVICE.isGreaterOrEqual(this
219: .getEffectiveLevel()))
220: //callAppenders(new LoggingEvent(classname, this, Log4jLevel.SERVICE, message, null));
221: forcedLog(classname, Log4jLevel.SERVICE, message, null);
222: }
223:
224: public final void service(Object message, Throwable t) {
225: // disable is defined in Category
226: if (repository.isDisabled(Log4jLevel.SERVICE_INT)) {
227: return;
228: }
229: if (Log4jLevel.SERVICE.isGreaterOrEqual(this
230: .getEffectiveLevel()))
231: //callAppenders(new LoggingEvent(classname, this, Log4jLevel.SERVICE, message, null));
232: forcedLog(classname, Log4jLevel.SERVICE, message, t);
233: }
234:
235: public final boolean isServiceEnabled() {
236: if (repository.isDisabled(Log4jLevel.SERVICE_INT))
237: return false;
238: return Log4jLevel.SERVICE.isGreaterOrEqual(this
239: .getEffectiveLevel());
240: }
241:
242: public final boolean isTraceEnabled() {
243: if (repository.isDisabled(Log4jLevel.TRACE_INT))
244: return false;
245: return Log4jLevel.TRACE.isGreaterOrEqual(this
246: .getEffectiveLevel());
247: }
248:
249: public static void shutdown() {
250: Log4jImpl err = getLoggerInstance("STDERR");
251: if (err.getLevel() != Log4jLevel.FATAL) {
252: log.service("System stderr now going to stdout");
253: System.setErr(System.out);
254: }
255: repository.shutdown();
256: }
257:
258: /**
259: * Catches stderr and sends it also to the log file (with category `stderr').
260: *
261: * In this way, things producing standard output, such as uncatch
262: * exceptions, will at least appear in the log-file.
263: *
264: **/
265:
266: private static class LoggerStream extends PrintStream {
267:
268: private Logger log;
269:
270: private int checkCount = 0; // needed to avoid infinite
271:
272: // recursion in some errorneos situations.
273:
274: LoggerStream(Log4jImpl l) throws IllegalArgumentException {
275: super (System.out);
276: if (l == null) {
277: throw new IllegalArgumentException("logger == null");
278: }
279: log = l;
280: }
281:
282: private LoggerStream() {
283: // do not use.
284: super (System.out);
285: }
286:
287: // simply overriding all methods that possibly could be used (forgotten some still)
288: public void print(char[] s) {
289: log.warn(new String(s));
290: }
291:
292: public void print(String s) {
293: log.warn(s);
294: }
295:
296: public void print(Object s) {
297: log.warn(s.toString());
298: }
299:
300: public void println(char[] s) {
301: log.warn(new String(s));
302: }
303:
304: public void println(String s) {
305: // if something goes wrong log4j write to standard error
306: // we don't want to go in an infinite loop then, if LoggerStream is stderr too.
307: if (checkCount > 0) {
308: System.out.println(s);
309: } else {
310: checkCount++;
311: log.trace("6");
312: log.warn(s);
313: checkCount--;
314: }
315: }
316:
317: public void println(Object s) {
318: // it seems that exception are written to log in this way, so we can check
319: // if s is an exception, in which case we want to log with FATAL.
320: if (Exception.class.isAssignableFrom(s.getClass())) {
321: log.fatal(s.toString()); // uncaught exception, that's a fatal error
322: } else {
323: log.warn(s.toString());
324: }
325: }
326:
327: }
328:
329: }
|