--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2014 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.management.ManagementFactory;
+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.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.
+ * <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 final static String EXCEPTION= "EXCEPTION ";
+ public final static String IGNORED= "IGNORED ";
+
+ /**
+ * Logging Configuration Properties
+ */
+ protected static final Properties __props;
+ /**
+ * The {@link Logger} implementation class name
+ */
+ public static String __logClass;
+ /**
+ * Legacy flag indicating if {@link Logger#ignore(Throwable)} methods produce any output in the {@link Logger}s
+ */
+ public static boolean __ignored;
+
+ /**
+ * Hold loggers only.
+ */
+ private final static ConcurrentMap<String, Logger> __loggers = new ConcurrentHashMap<>();
+
+
+ static
+ {
+ /* Instantiate a default configuration properties (empty)
+ */
+ __props = new Properties();
+
+ AccessController.doPrivileged(new PrivilegedAction<Object>()
+ {
+ 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 = osName.toLowerCase(Locale.ENGLISH).replace(' ','-');
+ 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;
+ }
+ });
+ }
+
+ private static void loadProperties(String resourceName, Properties props)
+ {
+ URL testProps = Loader.getResource(Log.class,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;
+
+ final long uptime=ManagementFactory.getRuntimeMXBean().getUptime();
+
+ try
+ {
+ Class<?> log_class = Loader.loadClass(Log.class, __logClass);
+ if (LOG == null || !LOG.getClass().equals(log_class))
+ {
+ LOG = (Logger)log_class.newInstance();
+ LOG.debug("Logging to {} via {}", LOG, log_class.getName());
+ }
+ }
+ catch(Throwable e)
+ {
+ // Unable to load specified Logger implementation, default to standard logging.
+ initStandardLogging(e);
+ }
+
+ if (LOG!=null)
+ LOG.info(String.format("Logging initialized @%dms",uptime));
+ }
+ }
+
+ private static void initStandardLogging(Throwable e)
+ {
+ Class<?> log_class;
+ if(e != null && __ignored)
+ {
+ e.printStackTrace(System.err);
+ }
+
+ if (LOG == null)
+ {
+ log_class = StdErrLog.class;
+ LOG = new StdErrLog();
+ LOG.debug("Logging to {} via {}", LOG, log_class.getName());
+ }
+ }
+
+ public static Logger getLog()
+ {
+ initialized();
+ return LOG;
+ }
+
+ public static void setLog(Logger log)
+ {
+ Log.LOG = log;
+ }
+
+ /**
+ * 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.
+ * <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.
+ *
+ * @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.
+ * @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 {@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);
+ }
+}