KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > logging > Log4jService


1 /*
2  * JBoss, Home of Professional Open Source
3  * Copyright 2005, JBoss Inc., and individual contributors as indicated
4  * by the @authors tag. See the copyright.txt in the distribution for a
5  * full listing of individual contributors.
6  *
7  * This is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * This software is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this software; if not, write to the Free
19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21  */

22 package org.jboss.logging;
23
24 import java.io.IOException JavaDoc;
25 import java.io.PrintStream JavaDoc;
26 import java.net.MalformedURLException JavaDoc;
27 import java.net.URL JavaDoc;
28 import java.net.URLConnection JavaDoc;
29 import java.util.StringTokenizer JavaDoc;
30 import java.util.Timer JavaDoc;
31 import java.util.TimerTask JavaDoc;
32
33 import javax.management.MBeanServer JavaDoc;
34 import javax.management.MalformedObjectNameException JavaDoc;
35 import javax.management.Notification JavaDoc;
36 import javax.management.ObjectName JavaDoc;
37
38 import org.apache.log4j.Level;
39 import org.apache.log4j.PropertyConfigurator;
40 import org.apache.log4j.helpers.LogLog;
41 import org.apache.log4j.xml.DOMConfigurator;
42 import org.jboss.logging.util.LoggerStream;
43 import org.jboss.logging.util.OnlyOnceErrorHandler;
44 import org.jboss.system.ServiceMBeanSupport;
45 import org.jboss.util.Strings;
46 import org.jboss.util.ThrowableHandler;
47 import org.jboss.util.ThrowableListener;
48 import org.jboss.util.stream.Streams;
49
50 /**
51  * Initializes the Log4j logging framework. Supports XML and standard
52  * configuration file formats. Defaults to using 'log4j.xml' read
53  * from a system resource.
54  *
55  * <p>Sets up a {@link ThrowableListener} to adapt unhandled
56  * throwables to a logger.
57  *
58  * <p>Installs {@link LoggerStream} adapters for <tt>System.out</tt> and
59  * <tt>System.err</tt> to catch and redirect calls to Log4j.
60  *
61  * @jmx:mbean name="jboss.system:type=Log4jService,service=Logging"
62  * extends="org.jboss.system.ServiceMBean"
63  *
64  * @version <tt>$Revision: 58169 $</tt>
65  * @author <a HREF="mailto:phox@galactica.it">Fulco Muriglio</a>
66  * @author <a HREF="mailto:Scott_Stark@displayscape.com">Scott Stark</a>
67  * @author <a HREF="mailto:davidjencks@earthlink.net">David Jencks</a>
68  * @author <a HREF="mailto:jason@planet57.com">Jason Dillon</a>
69  */

70 public class Log4jService
71    extends ServiceMBeanSupport
72    implements Log4jServiceMBean
73 {
74    /**
75     * The default url for the configuration file. Reads the value
76     * from the system property <tt>org.jboss.logging.Log4jService.configURL</tt>
77     * or if that is not set defaults to <tt>resource:log4j.xml</tt>.
78     */

79    public static final String JavaDoc DEFAULT_URL =
80       System.getProperty(Log4jService.class.getName() + ".configURL", "resource:log4j.xml");
81
82    /**
83     * Default flag to enable/disable cacthing System.out. Reads the value
84     * from the system property <tt>org.jboss.logging.Log4jService.catchSystemOut</tt>
85     * or if not set defaults to <tt>true</tt>.
86     */

87    public static final boolean CATCH_SYSTEM_OUT =
88       getBoolean(Log4jService.class.getName() + ".catchSystemOut", true);
89
90    /**
91     * Default flag to enable/disable cacthing System.err. Reads the value
92     * from the system property <tt>org.jboss.logging.Log4jService.catchSystemErr</tt>
93     * or if not set defaults to <tt>true</tt>.
94     */

95    public static final boolean CATCH_SYSTEM_ERR =
96       getBoolean(Log4jService.class.getName() + ".catchSystemErr", true);
97
98    /** Helper to get boolean value from system property or use default if not set. */
99    private static boolean getBoolean(String JavaDoc name, boolean defaultValue)
100    {
101       String JavaDoc value = System.getProperty(name, null);
102       if (value == null)
103          return defaultValue;
104       return new Boolean JavaDoc(value).booleanValue();
105    }
106    
107    /** The URL to the configuration file. */
108    private URL JavaDoc configURL;
109
110    /** The time in seconds between checking for new config. */
111    private int refreshPeriod;
112    
113    private ThrowableListenerLoggingAdapter throwableAdapter;
114
115    /** The previous value of System.out. */
116    private PrintStream JavaDoc out;
117
118    /** The previous value of System.err. */
119    private PrintStream JavaDoc err;
120
121    /**
122     * Flag to enable/disable adapting <tt>System.out</tt> to the
123     * <tt>STDOUT</tt> logger.
124     */

125    private boolean catchSystemOut = CATCH_SYSTEM_OUT;
126
127    /**
128     * Flag to enable/disable adapting <tt>System.out</tt> to the
129     * <tt>STDERR</tt> logger.
130     */

131    private boolean catchSystemErr = CATCH_SYSTEM_ERR;
132
133    /** The org.apache.log4j.helpers.LogLog.setQuietMode flag setting */
134    private boolean log4jQuietMode = true;
135
136    /** The URL watch timer (in daemon mode). */
137    private Timer JavaDoc timer;
138
139    /** The specialized timer task to watch our config file. */
140    private URLWatchTimerTask timerTask;
141
142    /**
143     * A flag to enable start/stop to work as expected,
144     * but still use create to init early.
145     */

146    private boolean initialized;
147   
148    /**
149     * Uses defaults.
150     *
151     * @jmx:managed-constructor
152     *
153     * @throws MalformedURLException Could not create URL from default (propbably
154     * a problem with overridden properties).
155     */

156    public Log4jService() throws MalformedURLException JavaDoc
157    {
158       this(DEFAULT_URL, 60);
159    }
160    
161    /**
162     * @jmx:managed-constructor
163     *
164     * @param url The configuration URL.
165     */

166    public Log4jService(final URL JavaDoc url)
167    {
168       this(url, 60);
169    }
170
171    /**
172     * @jmx:managed-constructor
173     *
174     * @param url The configuration URL.
175     */

176    public Log4jService(final String JavaDoc url) throws MalformedURLException JavaDoc
177    {
178       this(Strings.toURL(url), 60);
179    }
180    
181    /**
182     * @jmx:managed-constructor
183     *
184     * @param url The configuration URL.
185     * @param refreshPeriod The refreshPeriod in seconds to wait between each check.
186     */

187    public Log4jService(final String JavaDoc url, final int refreshPeriod)
188       throws MalformedURLException JavaDoc
189    {
190       this(Strings.toURL(url), refreshPeriod);
191    }
192
193    /**
194     * @jmx:managed-constructor
195     *
196     * @param url The configuration URL.
197     * @param refreshPeriod The refreshPeriod in seconds to wait between each check.
198     */

199    public Log4jService(final URL JavaDoc url, final int refreshPeriod)
200    {
201       this.configURL = url;
202       this.refreshPeriod = refreshPeriod;
203    }
204
205    /**
206     * Set the catch <tt>System.out</tt> flag.
207     *
208     * @jmx:managed-attribute
209     *
210     * @param flag True to enable, false to disable.
211     */

212    public void setCatchSystemOut(final boolean flag)
213    {
214       this.catchSystemOut = flag;
215    }
216
217    /**
218     * Get the catch <tt>System.out</tt> flag.
219     *
220     * @jmx:managed-attribute
221     *
222     * @return True if enabled, false if disabled.
223     */

224    public boolean getCatchSystemOut()
225    {
226       return catchSystemOut;
227    }
228
229    /**
230     * Set the catch <tt>System.err</tt> flag.
231     *
232     * @jmx:managed-attribute
233     *
234     * @param flag True to enable, false to disable.
235     */

236    public void setCatchSystemErr(final boolean flag)
237    {
238       this.catchSystemErr = flag;
239    }
240
241    /**
242     * Get the catch <tt>System.err</tt> flag.
243     *
244     * @jmx:managed-attribute
245     *
246     * @return True if enabled, false if disabled.
247     */

248    public boolean getCatchSystemErr()
249    {
250       return catchSystemErr;
251    }
252
253    /**
254     * Get the org.apache.log4j.helpers.LogLog.setQuietMode flag
255     *
256     * @jmx:managed-attribute
257     *
258     * @return True if enabled, false if disabled.
259     */

260    public boolean getLog4jQuietMode()
261    {
262       return log4jQuietMode;
263    }
264    /**
265     * Set the org.apache.log4j.helpers.LogLog.setQuietMode flag
266     *
267     * @jmx:managed-attribute
268     *
269     * @return True if enabled, false if disabled.
270     */

271    public void setLog4jQuietMode(boolean flag)
272    {
273       this.log4jQuietMode = flag;
274    }
275
276    /**
277     * Get the refresh period.
278     *
279     * @jmx:managed-attribute
280     */

281    public int getRefreshPeriod()
282    {
283       return refreshPeriod;
284    }
285
286    /**
287     * Set the refresh period.
288     *
289     * @jmx:managed-attribute
290     */

291    public void setRefreshPeriod(final int refreshPeriod)
292    {
293       this.refreshPeriod = refreshPeriod;
294    }
295    
296    /**
297     * Get the Log4j configuration URL.
298     *
299     * @jmx:managed-attribute
300     */

301    public URL JavaDoc getConfigurationURL()
302    {
303       return configURL;
304    }
305    
306    /**
307     * Set the Log4j configuration URL.
308     *
309     * @jmx:managed-attribute
310     */

311    public void setConfigurationURL(final URL JavaDoc url)
312    {
313       this.configURL = url;
314    }
315
316    /**
317     * Sets the level for a logger of the give name.
318     *
319     * <p>Values are trimmed before used.
320     *
321     * @jmx:managed-operation
322     *
323     * @param name The name of the logger to change level
324     * @param levelName The name of the level to change the logger to.
325     */

326    public void setLoggerLevel(final String JavaDoc name, final String JavaDoc levelName)
327    {
328       org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(name.trim());
329       Level level = XLevel.toLevel(levelName.trim());
330
331       logger.setLevel(level);
332       log.info("Level set to " + level + " for " + name);
333    }
334
335    /**
336     * Sets the levels of each logger specified by the given comma
337     * seperated list of logger names.
338     *
339     * @jmx:managed-operation
340     *
341     * @see #setLoggerLevel
342     *
343     * @param list A comma seperated list of logger names.
344     * @param levelName The name of the level to change the logger to.
345     */

346    public void setLoggerLevels(final String JavaDoc list, final String JavaDoc levelName)
347    {
348       StringTokenizer JavaDoc stok = new StringTokenizer JavaDoc(list, ",");
349       
350       while (stok.hasMoreTokens()) {
351          String JavaDoc name = stok.nextToken();
352          setLoggerLevel(name, levelName);
353       }
354    }
355    
356    /**
357     * Gets the level of the logger of the give name.
358     *
359     * @jmx:managed-operation
360     *
361     * @param name The name of the logger to inspect.
362     */

363    public String JavaDoc getLoggerLevel(final String JavaDoc name)
364    {
365       org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(name);
366       Level level = logger.getLevel();
367
368       if (level != null)
369          return level.toString();
370
371       return null;
372    }
373
374    /**
375     * Force the logging system to reconfigure.
376     *
377     * @jmx:managed-operation
378     */

379    public void reconfigure() throws IOException JavaDoc
380    {
381       if (timerTask == null)
382          throw new IllegalStateException JavaDoc("Service stopped or not started");
383       
384       timerTask.reconfigure();
385    }
386
387    /**
388     * Hack to reconfigure and change the URL. This is needed until we
389     * have a JMX HTML Adapter that can use PropertyEditor to coerce.
390     *
391     * @jmx:managed-operation
392     *
393     * @param url The new configuration url
394     */

395    public void reconfigure(final String JavaDoc url) throws IOException JavaDoc, MalformedURLException JavaDoc
396    {
397       setConfigurationURL(Strings.toURL(url));
398       reconfigure();
399    }
400    
401    private void installSystemAdapters()
402    {
403       org.apache.log4j.Logger logger;
404
405       // Install catchers
406
if (catchSystemOut)
407       {
408          logger = org.apache.log4j.Logger.getLogger("STDOUT");
409          out = System.out;
410          System.setOut(new LoggerStream(logger, Level.INFO, out));
411          log.debug("Installed System.out adapter");
412       }
413       
414       if (catchSystemErr)
415       {
416          logger = org.apache.log4j.Logger.getLogger("STDERR");
417          err = System.err;
418          OnlyOnceErrorHandler.setOutput(err);
419          System.setErr(new LoggerStream(logger, Level.ERROR, err));
420          log.debug("Installed System.err adapter");
421       }
422    }
423
424    private void uninstallSystemAdapters()
425    {
426       // Remove System adapters
427
if (out != null)
428       {
429          System.out.flush();
430          System.setOut(out);
431          log.debug("Removed System.out adapter");
432          out = null;
433       }
434       
435       if (err != null)
436       {
437          System.err.flush();
438          System.setErr(err);
439          log.debug("Removed System.err adapter");
440          err = null;
441       }
442    }
443
444
445    ///////////////////////////////////////////////////////////////////////////
446
// Concrete Service Overrides //
447
///////////////////////////////////////////////////////////////////////////
448

449    protected ObjectName JavaDoc getObjectName(MBeanServer JavaDoc server, ObjectName JavaDoc name)
450       throws MalformedObjectNameException JavaDoc
451    {
452       return name == null ? OBJECT_NAME : name;
453    }
454
455    private void setup() throws Exception JavaDoc
456    {
457       if (initialized) return;
458       
459       timerTask = new URLWatchTimerTask();
460       timerTask.run();
461       timer = new Timer JavaDoc("Timer-" + getName(), true);
462       timer.schedule(timerTask, 1000 * refreshPeriod, 1000 * refreshPeriod);
463
464       // Make sure the root Logger has loaded
465
org.apache.log4j.Logger.getRootLogger();
466       
467       // Install listener for unhandled throwables to turn them into log messages
468
throwableAdapter = new ThrowableListenerLoggingAdapter();
469       ThrowableHandler.addThrowableListener(throwableAdapter);
470       log.debug("Added ThrowableListener: " + throwableAdapter);
471
472       initialized = true;
473    }
474
475    protected void createService() throws Exception JavaDoc
476    {
477       setup();
478    }
479
480    protected void startService() throws Exception JavaDoc
481    {
482       setup();
483    }
484    
485    protected void stopService() throws Exception JavaDoc
486    {
487       timer.cancel();
488       timer = null;
489       timerTask.cancel();
490       timerTask = null;
491       
492       // Remove throwable adapter
493
ThrowableHandler.removeThrowableListener(throwableAdapter);
494       throwableAdapter = null;
495
496       uninstallSystemAdapters();
497
498       // allow start to re-initalize
499
initialized = false;
500    }
501
502    protected void emitReconfigureNotification()
503    {
504       // emit a reconfigure notification with the configURL
505
Notification JavaDoc n = new Notification JavaDoc(RECONFIGURE_NOTIFICATION_TYPE,
506             this, getNextNotificationSequenceNumber(), "Log4j subsystem reconfigured");
507       n.setUserData(configURL);
508       
509       super.sendNotification(n);
510    }
511    
512    ///////////////////////////////////////////////////////////////////////////
513
// ThrowableListener Adapter //
514
///////////////////////////////////////////////////////////////////////////
515

516    /**
517     * Adapts ThrowableHandler to the Loggger interface. Using nested
518     * class instead of anoynmous class for better logger naming.
519     */

520    private static class ThrowableListenerLoggingAdapter
521       implements ThrowableListener
522    {
523       private Logger log = Logger.getLogger(ThrowableListenerLoggingAdapter.class);
524       
525       public void onThrowable(int type, Throwable JavaDoc t)
526       {
527          switch (type)
528          {
529              default:
530                 // if type is not valid then make it any error
531

532              case ThrowableHandler.Type.ERROR:
533                 log.error("Unhandled Throwable", t);
534                 break;
535              
536              case ThrowableHandler.Type.WARNING:
537                 log.warn("Unhandled Throwable", t);
538                 break;
539              
540              case ThrowableHandler.Type.UNKNOWN:
541                 // these could be red-herrings, so log them as trace
542
log.trace("Ynhandled Throwable; status is unknown", t);
543                 break;
544          }
545       }
546    }
547
548
549    ///////////////////////////////////////////////////////////////////////////
550
// URL Watching Timer Task //
551
///////////////////////////////////////////////////////////////////////////
552

553    /**
554     * A timer task to check when a URL changes (based on
555     * last modified time) and reconfigure Log4j.
556     */

557    private class URLWatchTimerTask
558       extends TimerTask JavaDoc
559    {
560       private Logger log = Logger.getLogger(URLWatchTimerTask.class);
561
562       private long lastConfigured = -1;
563
564       public void run()
565       {
566          log.trace("Checking if configuration changed");
567
568          boolean trace = log.isTraceEnabled();
569          
570          try
571          {
572             URLConnection JavaDoc conn = configURL.openConnection();
573             if (trace)
574                log.trace("connection: " + conn);
575
576             long lastModified = conn.getLastModified();
577             if (trace)
578             {
579                log.trace("last configured: " + lastConfigured);
580                log.trace("last modified: " + lastModified);
581             }
582
583             if (lastConfigured < lastModified)
584             {
585                reconfigure(conn);
586             }
587          }
588          catch (Exception JavaDoc e)
589          {
590             log.warn("Failed to check URL: " + configURL, e);
591          }
592       }
593
594       public void reconfigure() throws IOException JavaDoc
595       {
596          URLConnection JavaDoc conn = configURL.openConnection();
597          reconfigure(conn);
598       }
599
600       private void reconfigure(final URLConnection JavaDoc conn)
601       {
602          log.debug("Configuring from URL: " + configURL);
603          
604          boolean xml = false;
605          boolean trace = log.isTraceEnabled();
606          
607          // check if the url is xml
608
String JavaDoc contentType = conn.getContentType();
609          if (trace)
610             log.trace("content type: " + contentType);
611          
612          if (contentType == null)
613          {
614             String JavaDoc filename = configURL.getFile().toLowerCase();
615             if (trace) log.trace("filename: " + filename);
616
617             xml = filename.endsWith(".xml");
618          }
619          else
620          {
621             xml = contentType.equalsIgnoreCase("text/xml");
622             xml |= contentType.equalsIgnoreCase("application/xml");
623          }
624          if (trace)
625             log.trace("reconfiguring; xml=" + xml);
626
627          // Dump our config if trace is enabled
628
if (trace)
629          {
630             try
631             {
632                java.io.InputStream JavaDoc is = conn.getInputStream();
633                Streams.copy(is, System.out);
634             }
635             catch (Exception JavaDoc e)
636             {
637                log.error("Failed to dump config", e);
638             }
639          }
640
641          // need to uninstall adapters to avoid problems
642
uninstallSystemAdapters();
643
644          if (xml)
645          {
646             DOMConfigurator.configure(configURL);
647          }
648          else
649          {
650             PropertyConfigurator.configure(configURL);
651          }
652
653          /* Set the LogLog.QuietMode. As of log4j1.2.8 this needs to be set to
654          avoid deadlock on exception at the appender level. See bug#696819.
655          */

656          LogLog.setQuietMode(log4jQuietMode);
657
658          // but make sure they get reinstalled again
659
installSystemAdapters();
660
661          // and then remember when we were last changed
662
lastConfigured = System.currentTimeMillis();
663          
664          // notify other mbeans that might be interested
665
emitReconfigureNotification();
666       }
667    }
668 }
669
Popular Tags