package org.jboss.logging;
import java.io.PrintStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.MalformedURLException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.StringTokenizer;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.MalformedObjectNameException;
import org.apache.log4j.Level;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.xml.DOMConfigurator;
import org.jboss.logging.util.LoggerStream;
import org.jboss.logging.util.OnlyOnceErrorHandler;
import org.jboss.system.ServiceMBeanSupport;
import org.jboss.util.ThrowableHandler;
import org.jboss.util.ThrowableListener;
import org.jboss.util.Strings;
import org.jboss.util.stream.Streams;
public class Log4jService
extends ServiceMBeanSupport
implements Log4jServiceMBean
{
public static final String DEFAULT_URL =
System.getProperty(Log4jService.class.getName() + ".configURL", "resource:log4j.xml");
public static final boolean CATCH_SYSTEM_OUT =
getBoolean(Log4jService.class.getName() + ".catchSystemOut", true);
public static final boolean CATCH_SYSTEM_ERR =
getBoolean(Log4jService.class.getName() + ".catchSystemErr", true);
private static boolean getBoolean(String name, boolean defaultValue)
{
String value = System.getProperty(name, null);
if (value == null)
return defaultValue;
return new Boolean(value).booleanValue();
}
private URL configURL;
private int refreshPeriod;
private ThrowableListenerLoggingAdapter throwableAdapter;
private PrintStream out;
private PrintStream err;
private boolean catchSystemOut = CATCH_SYSTEM_OUT;
private boolean catchSystemErr = CATCH_SYSTEM_ERR;
private boolean log4jQuietMode = true;
private Timer timer = new Timer(true);
private URLWatchTimerTask timerTask;
private boolean initialized;
public Log4jService() throws MalformedURLException
{
this(DEFAULT_URL, 60);
}
public Log4jService(final URL url)
{
this(url, 60);
}
public Log4jService(final String url) throws MalformedURLException
{
this(Strings.toURL(url), 60);
}
public Log4jService(final String url, final int refreshPeriod)
throws MalformedURLException
{
this(Strings.toURL(url), refreshPeriod);
}
public Log4jService(final URL url, final int refreshPeriod)
{
this.configURL = url;
this.refreshPeriod = refreshPeriod;
}
public void setCatchSystemOut(final boolean flag)
{
this.catchSystemOut = flag;
}
public boolean getCatchSystemOut()
{
return catchSystemOut;
}
public void setCatchSystemErr(final boolean flag)
{
this.catchSystemErr = flag;
}
public boolean getCatchSystemErr()
{
return catchSystemErr;
}
public boolean getLog4jQuietMode()
{
return log4jQuietMode;
}
public void setLog4jQuietMode(boolean flag)
{
this.log4jQuietMode = flag;
}
public int getRefreshPeriod()
{
return refreshPeriod;
}
public void setRefreshPeriod(final int refreshPeriod)
{
this.refreshPeriod = refreshPeriod;
}
public URL getConfigurationURL()
{
return configURL;
}
public void setConfigurationURL(final URL url)
{
this.configURL = url;
}
public void setLoggerLevel(final String name, final String levelName)
{
org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(name.trim());
Level level = XLevel.toLevel(levelName.trim());
logger.setLevel(level);
log.info("Level set to " + level + " for " + name);
}
public void setLoggerLevels(final String list, final String levelName)
{
StringTokenizer stok = new StringTokenizer(list, ",");
while (stok.hasMoreTokens()) {
String name = stok.nextToken();
setLoggerLevel(name, levelName);
}
}
public String getLoggerLevel(final String name)
{
org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(name);
Level level = logger.getLevel();
if (level != null)
return level.toString();
return null;
}
public void reconfigure() throws IOException
{
if (timerTask == null)
throw new IllegalStateException("Service stopped or not started");
timerTask.reconfigure();
}
public void reconfigure(final String url) throws IOException, MalformedURLException
{
setConfigurationURL(Strings.toURL(url));
reconfigure();
}
private void installSystemAdapters()
{
org.apache.log4j.Logger logger;
if (catchSystemOut)
{
logger = org.apache.log4j.Logger.getLogger("STDOUT");
out = System.out;
System.setOut(new LoggerStream(logger, Level.INFO, out));
log.debug("Installed System.out adapter");
}
if (catchSystemErr)
{
logger = org.apache.log4j.Logger.getLogger("STDERR");
err = System.err;
OnlyOnceErrorHandler.setOutput(err);
System.setErr(new LoggerStream(logger, Level.ERROR, err));
log.debug("Installed System.err adapter");
}
}
private void uninstallSystemAdapters()
{
if (out != null)
{
System.out.flush();
System.setOut(out);
log.debug("Removed System.out adapter");
out = null;
}
if (err != null)
{
System.err.flush();
System.setErr(err);
log.debug("Removed System.err adapter");
err = null;
}
}
protected ObjectName getObjectName(MBeanServer server, ObjectName name)
throws MalformedObjectNameException
{
return name == null ? OBJECT_NAME : name;
}
private void setup() throws Exception
{
if (initialized) return;
timerTask = new URLWatchTimerTask();
timerTask.run();
timer.schedule(timerTask, 1000 * refreshPeriod, 1000 * refreshPeriod);
org.apache.log4j.Logger.getRootLogger();
throwableAdapter = new ThrowableListenerLoggingAdapter();
ThrowableHandler.addThrowableListener(throwableAdapter);
log.debug("Added ThrowableListener: " + throwableAdapter);
initialized = true;
}
protected void createService() throws Exception
{
setup();
}
protected void startService() throws Exception
{
setup();
}
protected void stopService() throws Exception
{
timerTask.cancel();
timerTask = null;
ThrowableHandler.removeThrowableListener(throwableAdapter);
throwableAdapter = null;
uninstallSystemAdapters();
initialized = false;
}
private static class ThrowableListenerLoggingAdapter
implements ThrowableListener
{
private Logger log = Logger.getLogger(ThrowableListenerLoggingAdapter.class);
public void onThrowable(int type, Throwable t)
{
switch (type)
{
default:
case ThrowableHandler.Type.ERROR:
log.error("Unhandled Throwable", t);
break;
case ThrowableHandler.Type.WARNING:
log.warn("Unhandled Throwable", t);
break;
case ThrowableHandler.Type.UNKNOWN:
log.trace("Ynhandled Throwable; status is unknown", t);
break;
}
}
}
private class URLWatchTimerTask
extends TimerTask
{
private Logger log = Logger.getLogger(URLWatchTimerTask.class);
private long lastConfigured = -1;
public void run()
{
log.trace("Checking if configuration changed");
boolean trace = log.isTraceEnabled();
try
{
URLConnection conn = configURL.openConnection();
if (trace)
log.trace("connection: " + conn);
long lastModified = conn.getLastModified();
if (trace)
{
log.trace("last configured: " + lastConfigured);
log.trace("last modified: " + lastModified);
}
if (lastConfigured < lastModified)
{
reconfigure(conn);
}
}
catch (Exception e)
{
log.warn("Failed to check URL: " + configURL, e);
}
}
public void reconfigure() throws IOException
{
URLConnection conn = configURL.openConnection();
reconfigure(conn);
}
private void reconfigure(final URLConnection conn)
{
log.info("Configuring from URL: " + configURL);
boolean xml = false;
boolean trace = log.isTraceEnabled();
String contentType = conn.getContentType();
if (trace)
log.trace("content type: " + contentType);
if (contentType == null)
{
String filename = configURL.getFile().toLowerCase();
if (trace) log.trace("filename: " + filename);
xml = filename.endsWith(".xml");
}
else
{
xml = contentType.equalsIgnoreCase("text/xml");
xml |= contentType.equalsIgnoreCase("application/xml");
}
if (trace)
log.trace("reconfiguring; xml=" + xml);
if (trace)
{
try
{
java.io.InputStream is = conn.getInputStream();
Streams.copy(is, System.out);
}
catch (Exception e)
{
log.error("Failed to dump config", e);
}
}
uninstallSystemAdapters();
if (xml)
{
DOMConfigurator.configure(configURL);
}
else
{
PropertyConfigurator.configure(configURL);
}
LogLog.setQuietMode(log4jQuietMode);
installSystemAdapters();
lastConfigured = System.currentTimeMillis();
}
}
}