//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.util.log;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Uptime;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
Logging.
This class provides a static logging interface. If an instance of the
org.slf4j.Logger class is found on the classpath, the static log methods
are directed to a slf4j logger for "org.eclipse.log". Otherwise the logs
are directed to stderr.
The "org.eclipse.jetty.util.log.class" system property can be used
to select a specific logging implementation.
If the system property org.eclipse.jetty.util.log.IGNORED is set,
then ignored exceptions are logged in detail.
See Also: - StdErrLog
- Slf4jLog
/**
* Logging.
* This class provides a static logging interface. If an instance of the
* org.slf4j.Logger class is found on the classpath, the static log methods
* are directed to a slf4j logger for "org.eclipse.log". Otherwise the logs
* are directed to stderr.
* <p>
* The "org.eclipse.jetty.util.log.class" system property can be used
* to select a specific logging implementation.
* <p>
* If the system property org.eclipse.jetty.util.log.IGNORED is set,
* then ignored exceptions are logged in detail.
*
* @see StdErrLog
* @see Slf4jLog
*/
public class Log
{
public static final String EXCEPTION = "EXCEPTION ";
public static final String IGNORED = "IGNORED EXCEPTION ";
Logging Configuration Properties
/**
* Logging Configuration Properties
*/
protected static final Properties __props;
The Logger
implementation class name /**
* The {@link Logger} implementation class name
*/
public static String __logClass;
Legacy flag indicating if Logger.ignore(Throwable)
methods produce any output in the Logger
s /**
* Legacy flag indicating if {@link Logger#ignore(Throwable)} methods produce any output in the {@link Logger}s
*/
public static boolean __ignored;
Hold loggers only.
/**
* Hold loggers only.
*/
private static final ConcurrentMap<String, Logger> __loggers = new ConcurrentHashMap<>();
static
{
/* Instantiate a default configuration properties (empty)
*/
__props = new Properties();
AccessController.doPrivileged(new PrivilegedAction<Object>()
{
@Override
public Object run()
{
/* First see if the jetty-logging.properties object exists in the classpath.
* This is an optional feature used by embedded mode use, and test cases to allow for early
* configuration of the Log class in situations where access to the System.properties are
* either too late or just impossible.
*/
loadProperties("jetty-logging.properties", __props);
/*
* Next see if an OS specific jetty-logging.properties object exists in the classpath.
* This really for setting up test specific logging behavior based on OS.
*/
String osName = System.getProperty("os.name");
// NOTE: cannot use jetty-util's StringUtil as that initializes logging itself.
if (osName != null && osName.length() > 0)
{
osName = StringUtil.replace(osName.toLowerCase(Locale.ENGLISH), ' ', '-');
loadProperties("jetty-logging-" + osName + ".properties", __props);
}
/* Now load the System.properties as-is into the __props, these values will override
* any key conflicts in __props.
*/
@SuppressWarnings("unchecked")
Enumeration<String> systemKeyEnum = (Enumeration<String>)System.getProperties().propertyNames();
while (systemKeyEnum.hasMoreElements())
{
String key = systemKeyEnum.nextElement();
String val = System.getProperty(key);
// protect against application code insertion of non-String values (returned as null)
if (val != null)
{
__props.setProperty(key, val);
}
}
/* Now use the configuration properties to configure the Log statics
*/
__logClass = __props.getProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.Slf4jLog");
__ignored = Boolean.parseBoolean(__props.getProperty("org.eclipse.jetty.util.log.IGNORED", "false"));
return null;
}
});
}
static void loadProperties(String resourceName, Properties props)
{
URL testProps = Loader.getResource(resourceName);
if (testProps != null)
{
try (InputStream in = testProps.openStream())
{
Properties p = new Properties();
p.load(in);
for (Object key : p.keySet())
{
Object value = p.get(key);
if (value != null)
{
props.put(key, value);
}
}
}
catch (IOException e)
{
System.err.println("[WARN] Error loading logging config: " + testProps);
e.printStackTrace(System.err);
}
}
}
private static Logger LOG;
private static boolean __initialized = false;
public static void initialized()
{
synchronized (Log.class)
{
if (__initialized)
return;
__initialized = true;
Boolean announce = Boolean.parseBoolean(__props.getProperty("org.eclipse.jetty.util.log.announce", "true"));
try
{
Class<?> logClass = __logClass == null ? null : Loader.loadClass(Log.class, __logClass);
if (LOG == null || (logClass != null && !LOG.getClass().equals(logClass)))
{
LOG = (Logger)logClass.getDeclaredConstructor().newInstance();
if (announce)
{
LOG.debug("Logging to {} via {}", LOG, logClass.getName());
}
}
}
catch (Throwable e)
{
// Unable to load specified Logger implementation, default to standard logging.
initStandardLogging(e);
}
if (announce && LOG != null)
{
LOG.info(String.format("Logging initialized @%dms to %s", Uptime.getUptime(), LOG.getClass().getName()));
}
}
}
private static void initStandardLogging(Throwable e)
{
Class<?> logClass;
if (e != null && __ignored)
{
e.printStackTrace(System.err);
}
if (LOG == null)
{
logClass = StdErrLog.class;
LOG = new StdErrLog();
boolean announce = Boolean.parseBoolean(__props.getProperty("org.eclipse.jetty.util.log.announce", "true"));
if (announce)
{
LOG.debug("Logging to {} via {}", LOG, logClass.getName());
}
}
}
public static Logger getLog()
{
initialized();
return LOG;
}
Set the root logger.
Note that if any classes have statically obtained their logger instance prior to this call, their Logger will not
be affected by this call.
Params: - log – the root logger implementation to set
/**
* Set the root logger.
* <p>
* Note that if any classes have statically obtained their logger instance prior to this call, their Logger will not
* be affected by this call.
*
* @param log the root logger implementation to set
*/
public static void setLog(Logger log)
{
Log.LOG = log;
__logClass = null;
}
Get the root logger.
Returns: the root logger
/**
* Get the root logger.
*
* @return the root logger
*/
public static Logger getRootLogger()
{
initialized();
return LOG;
}
static boolean isIgnored()
{
return __ignored;
}
Set Log to parent Logger.
If there is a different Log class available from a parent classloader, call getLogger(String)
on it and construct a LoggerLog
instance as this Log's Logger, so that logging is delegated to the parent Log.
This should be used if a webapp is using Log, but wishes the logging to be
directed to the containers log.
If there is not parent Log, then this call is equivalent to
Log.setLog(Log.getLogger(name));
Params: - name – Logger name
/**
* Set Log to parent Logger.
* <p>
* If there is a different Log class available from a parent classloader,
* call {@link #getLogger(String)} on it and construct a {@link LoggerLog} instance
* as this Log's Logger, so that logging is delegated to the parent Log.
* <p>
* This should be used if a webapp is using Log, but wishes the logging to be
* directed to the containers log.
* <p>
* If there is not parent Log, then this call is equivalent to<pre>
* Log.setLog(Log.getLogger(name));
* </pre>
*
* @param name Logger name
*/
public static void setLogToParent(String name)
{
ClassLoader loader = Log.class.getClassLoader();
if (loader != null && loader.getParent() != null)
{
try
{
Class<?> uberlog = loader.getParent().loadClass("org.eclipse.jetty.util.log.Log");
Method getLogger = uberlog.getMethod("getLogger", new Class[]{String.class});
Object logger = getLogger.invoke(null, name);
setLog(new LoggerLog(logger));
}
catch (Exception e)
{
e.printStackTrace();
}
}
else
{
setLog(getLogger(name));
}
}
Obtain a named Logger based on the fully qualified class name.
Params: - clazz – the class to base the Logger name off of
Returns: the Logger with the given name
/**
* Obtain a named Logger based on the fully qualified class name.
*
* @param clazz the class to base the Logger name off of
* @return the Logger with the given name
*/
public static Logger getLogger(Class<?> clazz)
{
return getLogger(clazz.getName());
}
Obtain a named Logger or the default Logger if null is passed.
Params: - name – the Logger name
Returns: the Logger with the given name
/**
* Obtain a named Logger or the default Logger if null is passed.
*
* @param name the Logger name
* @return the Logger with the given name
*/
public static Logger getLogger(String name)
{
initialized();
if (name == null)
return LOG;
Logger logger = __loggers.get(name);
if (logger == null)
logger = LOG.getLogger(name);
return logger;
}
static ConcurrentMap<String, Logger> getMutableLoggers()
{
return __loggers;
}
Get a map of all configured Logger
instances. Returns: a map of all configured Logger
instances
/**
* Get a map of all configured {@link Logger} instances.
*
* @return a map of all configured {@link Logger} instances
*/
@ManagedAttribute("list of all instantiated loggers")
public static Map<String, Logger> getLoggers()
{
return Collections.unmodifiableMap(__loggers);
}
public static Properties getProperties()
{
return __props;
}
}