Home » openjdk-7 » java » util » logging » [javadoc | source]

    1   /*
    2    * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   
   27   package java.util.logging;
   28   
   29   import java.io;
   30   import java.util;
   31   import java.security;
   32   import java.lang.ref.ReferenceQueue;
   33   import java.lang.ref.WeakReference;
   34   import java.beans.PropertyChangeListener;
   35   import java.beans.PropertyChangeSupport;
   36   import java.net.URL;
   37   import sun.security.action.GetPropertyAction;
   38   
   39   /**
   40    * There is a single global LogManager object that is used to
   41    * maintain a set of shared state about Loggers and log services.
   42    * <p>
   43    * This LogManager object:
   44    * <ul>
   45    * <li> Manages a hierarchical namespace of Logger objects.  All
   46    *      named Loggers are stored in this namespace.
   47    * <li> Manages a set of logging control properties.  These are
   48    *      simple key-value pairs that can be used by Handlers and
   49    *      other logging objects to configure themselves.
   50    * </ul>
   51    * <p>
   52    * The global LogManager object can be retrieved using LogManager.getLogManager().
   53    * The LogManager object is created during class initialization and
   54    * cannot subsequently be changed.
   55    * <p>
   56    * At startup the LogManager class is located using the
   57    * java.util.logging.manager system property.
   58    * <p>
   59    * By default, the LogManager reads its initial configuration from
   60    * a properties file "lib/logging.properties" in the JRE directory.
   61    * If you edit that property file you can change the default logging
   62    * configuration for all uses of that JRE.
   63    * <p>
   64    * In addition, the LogManager uses two optional system properties that
   65    * allow more control over reading the initial configuration:
   66    * <ul>
   67    * <li>"java.util.logging.config.class"
   68    * <li>"java.util.logging.config.file"
   69    * </ul>
   70    * These two properties may be set via the Preferences API, or as
   71    * command line property definitions to the "java" command, or as
   72    * system property definitions passed to JNI_CreateJavaVM.
   73    * <p>
   74    * If the "java.util.logging.config.class" property is set, then the
   75    * property value is treated as a class name.  The given class will be
   76    * loaded, an object will be instantiated, and that object's constructor
   77    * is responsible for reading in the initial configuration.  (That object
   78    * may use other system properties to control its configuration.)  The
   79    * alternate configuration class can use <tt>readConfiguration(InputStream)</tt>
   80    * to define properties in the LogManager.
   81    * <p>
   82    * If "java.util.logging.config.class" property is <b>not</b> set,
   83    * then the "java.util.logging.config.file" system property can be used
   84    * to specify a properties file (in java.util.Properties format). The
   85    * initial logging configuration will be read from this file.
   86    * <p>
   87    * If neither of these properties is defined then, as described
   88    * above, the LogManager will read its initial configuration from
   89    * a properties file "lib/logging.properties" in the JRE directory.
   90    * <p>
   91    * The properties for loggers and Handlers will have names starting
   92    * with the dot-separated name for the handler or logger.
   93    * <p>
   94    * The global logging properties may include:
   95    * <ul>
   96    * <li>A property "handlers".  This defines a whitespace or comma separated
   97    * list of class names for handler classes to load and register as
   98    * handlers on the root Logger (the Logger named "").  Each class
   99    * name must be for a Handler class which has a default constructor.
  100    * Note that these Handlers may be created lazily, when they are
  101    * first used.
  102    *
  103    * <li>A property "&lt;logger&gt;.handlers". This defines a whitespace or
  104    * comma separated list of class names for handlers classes to
  105    * load and register as handlers to the specified logger. Each class
  106    * name must be for a Handler class which has a default constructor.
  107    * Note that these Handlers may be created lazily, when they are
  108    * first used.
  109    *
  110    * <li>A property "&lt;logger&gt;.useParentHandlers". This defines a boolean
  111    * value. By default every logger calls its parent in addition to
  112    * handling the logging message itself, this often result in messages
  113    * being handled by the root logger as well. When setting this property
  114    * to false a Handler needs to be configured for this logger otherwise
  115    * no logging messages are delivered.
  116    *
  117    * <li>A property "config".  This property is intended to allow
  118    * arbitrary configuration code to be run.  The property defines a
  119    * whitespace or comma separated list of class names.  A new instance will be
  120    * created for each named class.  The default constructor of each class
  121    * may execute arbitrary code to update the logging configuration, such as
  122    * setting logger levels, adding handlers, adding filters, etc.
  123    * </ul>
  124    * <p>
  125    * Note that all classes loaded during LogManager configuration are
  126    * first searched on the system class path before any user class path.
  127    * That includes the LogManager class, any config classes, and any
  128    * handler classes.
  129    * <p>
  130    * Loggers are organized into a naming hierarchy based on their
  131    * dot separated names.  Thus "a.b.c" is a child of "a.b", but
  132    * "a.b1" and a.b2" are peers.
  133    * <p>
  134    * All properties whose names end with ".level" are assumed to define
  135    * log levels for Loggers.  Thus "foo.level" defines a log level for
  136    * the logger called "foo" and (recursively) for any of its children
  137    * in the naming hierarchy.  Log Levels are applied in the order they
  138    * are defined in the properties file.  Thus level settings for child
  139    * nodes in the tree should come after settings for their parents.
  140    * The property name ".level" can be used to set the level for the
  141    * root of the tree.
  142    * <p>
  143    * All methods on the LogManager object are multi-thread safe.
  144    *
  145    * @since 1.4
  146   */
  147   
  148   public class LogManager {
  149       // The global LogManager object
  150       private static LogManager manager;
  151   
  152       private final static Handler[] emptyHandlers = { };
  153       private Properties props = new Properties();
  154       private PropertyChangeSupport changes
  155                            = new PropertyChangeSupport(LogManager.class);
  156       private final static Level defaultLevel = Level.INFO;
  157   
  158       // Table of named Loggers that maps names to Loggers.
  159       private Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
  160       // Tree of named Loggers
  161       private LogNode root = new LogNode(null);
  162       private Logger rootLogger;
  163   
  164       // Have we done the primordial reading of the configuration file?
  165       // (Must be done after a suitable amount of java.lang.System
  166       // initialization has been done)
  167       private volatile boolean readPrimordialConfiguration;
  168       // Have we initialized global (root) handlers yet?
  169       // This gets set to false in readConfiguration
  170       private boolean initializedGlobalHandlers = true;
  171       // True if JVM death is imminent and the exit hook has been called.
  172       private boolean deathImminent;
  173   
  174       static {
  175           AccessController.doPrivileged(new PrivilegedAction<Object>() {
  176                   public Object run() {
  177                       String cname = null;
  178                       try {
  179                           cname = System.getProperty("java.util.logging.manager");
  180                           if (cname != null) {
  181                               try {
  182                                   Class clz = ClassLoader.getSystemClassLoader().loadClass(cname);
  183                                   manager = (LogManager) clz.newInstance();
  184                               } catch (ClassNotFoundException ex) {
  185                                   Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
  186                                   manager = (LogManager) clz.newInstance();
  187                               }
  188                           }
  189                       } catch (Exception ex) {
  190                           System.err.println("Could not load Logmanager \"" + cname + "\"");
  191                           ex.printStackTrace();
  192                       }
  193                       if (manager == null) {
  194                           manager = new LogManager();
  195                       }
  196   
  197                       // Create and retain Logger for the root of the namespace.
  198                       manager.rootLogger = manager.new RootLogger();
  199                       manager.addLogger(manager.rootLogger);
  200   
  201                       // Adding the global Logger. Doing so in the Logger.<clinit>
  202                       // would deadlock with the LogManager.<clinit>.
  203                       Logger.global.setLogManager(manager);
  204                       manager.addLogger(Logger.global);
  205   
  206                       // We don't call readConfiguration() here, as we may be running
  207                       // very early in the JVM startup sequence.  Instead readConfiguration
  208                       // will be called lazily in getLogManager().
  209                       return null;
  210                   }
  211               });
  212       }
  213   
  214   
  215       // This private class is used as a shutdown hook.
  216       // It does a "reset" to close all open handlers.
  217       private class Cleaner extends Thread {
  218   
  219           private Cleaner() {
  220               /* Set context class loader to null in order to avoid
  221                * keeping a strong reference to an application classloader.
  222                */
  223               this.setContextClassLoader(null);
  224           }
  225   
  226           public void run() {
  227               // This is to ensure the LogManager.<clinit> is completed
  228               // before synchronized block. Otherwise deadlocks are possible.
  229               LogManager mgr = manager;
  230   
  231               // If the global handlers haven't been initialized yet, we
  232               // don't want to initialize them just so we can close them!
  233               synchronized (LogManager.this) {
  234                   // Note that death is imminent.
  235                   deathImminent = true;
  236                   initializedGlobalHandlers = true;
  237               }
  238   
  239               // Do a reset to close all active handlers.
  240               reset();
  241           }
  242       }
  243   
  244   
  245       /**
  246        * Protected constructor.  This is protected so that container applications
  247        * (such as J2EE containers) can subclass the object.  It is non-public as
  248        * it is intended that there only be one LogManager object, whose value is
  249        * retrieved by calling Logmanager.getLogManager.
  250        */
  251       protected LogManager() {
  252           // Add a shutdown hook to close the global handlers.
  253           try {
  254               Runtime.getRuntime().addShutdownHook(new Cleaner());
  255           } catch (IllegalStateException e) {
  256               // If the VM is already shutting down,
  257               // We do not need to register shutdownHook.
  258           }
  259       }
  260   
  261       /**
  262        * Return the global LogManager object.
  263        */
  264       public static LogManager getLogManager() {
  265           if (manager != null) {
  266               manager.readPrimordialConfiguration();
  267           }
  268           return manager;
  269       }
  270   
  271       private void readPrimordialConfiguration() {
  272           if (!readPrimordialConfiguration) {
  273               synchronized (this) {
  274                   if (!readPrimordialConfiguration) {
  275                       // If System.in/out/err are null, it's a good
  276                       // indication that we're still in the
  277                       // bootstrapping phase
  278                       if (System.out == null) {
  279                           return;
  280                       }
  281                       readPrimordialConfiguration = true;
  282                       try {
  283                           AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
  284                                   public Object run() throws Exception {
  285                                       readConfiguration();
  286   
  287                                       // Platform loggers begin to delegate to java.util.logging.Logger
  288                                       sun.util.logging.PlatformLogger.redirectPlatformLoggers();
  289   
  290                                       return null;
  291                                   }
  292                               });
  293                       } catch (Exception ex) {
  294                           // System.err.println("Can't read logging configuration:");
  295                           // ex.printStackTrace();
  296                       }
  297                   }
  298               }
  299           }
  300       }
  301   
  302       /**
  303        * Adds an event listener to be invoked when the logging
  304        * properties are re-read. Adding multiple instances of
  305        * the same event Listener results in multiple entries
  306        * in the property event listener table.
  307        *
  308        * @param l  event listener
  309        * @exception  SecurityException  if a security manager exists and if
  310        *             the caller does not have LoggingPermission("control").
  311        * @exception NullPointerException if the PropertyChangeListener is null.
  312        */
  313       public void addPropertyChangeListener(PropertyChangeListener l) throws SecurityException {
  314           if (l == null) {
  315               throw new NullPointerException();
  316           }
  317           checkAccess();
  318           changes.addPropertyChangeListener(l);
  319       }
  320   
  321       /**
  322        * Removes an event listener for property change events.
  323        * If the same listener instance has been added to the listener table
  324        * through multiple invocations of <CODE>addPropertyChangeListener</CODE>,
  325        * then an equivalent number of
  326        * <CODE>removePropertyChangeListener</CODE> invocations are required to remove
  327        * all instances of that listener from the listener table.
  328        * <P>
  329        * Returns silently if the given listener is not found.
  330        *
  331        * @param l  event listener (can be null)
  332        * @exception  SecurityException  if a security manager exists and if
  333        *             the caller does not have LoggingPermission("control").
  334        */
  335       public void removePropertyChangeListener(PropertyChangeListener l) throws SecurityException {
  336           checkAccess();
  337           changes.removePropertyChangeListener(l);
  338       }
  339   
  340       // Package-level method.
  341       // Find or create a specified logger instance. If a logger has
  342       // already been created with the given name it is returned.
  343       // Otherwise a new logger instance is created and registered
  344       // in the LogManager global namespace.
  345   
  346       // This method will always return a non-null Logger object.
  347       // Synchronization is not required here. All synchronization for
  348       // adding a new Logger object is handled by addLogger().
  349       Logger demandLogger(String name) {
  350           Logger result = getLogger(name);
  351           if (result == null) {
  352               // only allocate the new logger once
  353               Logger newLogger = new Logger(name, null);
  354               do {
  355                   if (addLogger(newLogger)) {
  356                       // We successfully added the new Logger that we
  357                       // created above so return it without refetching.
  358                       return newLogger;
  359                   }
  360   
  361                   // We didn't add the new Logger that we created above
  362                   // because another thread added a Logger with the same
  363                   // name after our null check above and before our call
  364                   // to addLogger(). We have to refetch the Logger because
  365                   // addLogger() returns a boolean instead of the Logger
  366                   // reference itself. However, if the thread that created
  367                   // the other Logger is not holding a strong reference to
  368                   // the other Logger, then it is possible for the other
  369                   // Logger to be GC'ed after we saw it in addLogger() and
  370                   // before we can refetch it. If it has been GC'ed then
  371                   // we'll just loop around and try again.
  372                   result = getLogger(name);
  373               } while (result == null);
  374           }
  375           return result;
  376       }
  377   
  378       // If logger.getUseParentHandlers() returns 'true' and any of the logger's
  379       // parents have levels or handlers defined, make sure they are instantiated.
  380       private void processParentHandlers(Logger logger, String name) {
  381           int ix = 1;
  382           for (;;) {
  383               int ix2 = name.indexOf(".", ix);
  384               if (ix2 < 0) {
  385                   break;
  386               }
  387               String pname = name.substring(0,ix2);
  388   
  389               if (getProperty(pname+".level")    != null ||
  390                   getProperty(pname+".handlers") != null) {
  391                   // This pname has a level/handlers definition.
  392                   // Make sure it exists.
  393                   demandLogger(pname);
  394               }
  395               ix = ix2+1;
  396           }
  397       }
  398   
  399       // Add new per logger handlers.
  400       // We need to raise privilege here. All our decisions will
  401       // be made based on the logging configuration, which can
  402       // only be modified by trusted code.
  403       private void loadLoggerHandlers(final Logger logger, final String name,
  404                                       final String handlersPropertyName) {
  405           AccessController.doPrivileged(new PrivilegedAction<Object>() {
  406               public Object run() {
  407                   if (logger != rootLogger) {
  408                       boolean useParent = getBooleanProperty(name + ".useParentHandlers", true);
  409                       if (!useParent) {
  410                           logger.setUseParentHandlers(false);
  411                       }
  412                   }
  413   
  414                   String names[] = parseClassNames(handlersPropertyName);
  415                   for (int i = 0; i < names.length; i++) {
  416                       String word = names[i];
  417                       try {
  418                           Class   clz = ClassLoader.getSystemClassLoader().loadClass(word);
  419                           Handler hdl = (Handler) clz.newInstance();
  420                           try {
  421                               // Check if there is a property defining the
  422                               // this handler's level.
  423                               String levs = getProperty(word + ".level");
  424                               if (levs != null) {
  425                                   hdl.setLevel(Level.parse(levs));
  426                               }
  427                           } catch (Exception ex) {
  428                               System.err.println("Can't set level for " + word);
  429                               // Probably a bad level. Drop through.
  430                           }
  431                           // Add this Handler to the logger
  432                           logger.addHandler(hdl);
  433                       } catch (Exception ex) {
  434                           System.err.println("Can't load log handler \"" + word + "\"");
  435                           System.err.println("" + ex);
  436                           ex.printStackTrace();
  437                       }
  438                   }
  439                   return null;
  440               }});
  441       }
  442   
  443   
  444       // loggerRefQueue holds LoggerWeakRef objects for Logger objects
  445       // that have been GC'ed.
  446       private final ReferenceQueue<Logger> loggerRefQueue
  447           = new ReferenceQueue<>();
  448   
  449       // Package-level inner class.
  450       // Helper class for managing WeakReferences to Logger objects.
  451       //
  452       // LogManager.namedLoggers
  453       //     - has weak references to all named Loggers
  454       //     - namedLoggers keeps the LoggerWeakRef objects for the named
  455       //       Loggers around until we can deal with the book keeping for
  456       //       the named Logger that is being GC'ed.
  457       // LogManager.LogNode.loggerRef
  458       //     - has a weak reference to a named Logger
  459       //     - the LogNode will also keep the LoggerWeakRef objects for
  460       //       the named Loggers around; currently LogNodes never go away.
  461       // Logger.kids
  462       //     - has a weak reference to each direct child Logger; this
  463       //       includes anonymous and named Loggers
  464       //     - anonymous Loggers are always children of the rootLogger
  465       //       which is a strong reference; rootLogger.kids keeps the
  466       //       LoggerWeakRef objects for the anonymous Loggers around
  467       //       until we can deal with the book keeping.
  468       //
  469       final class LoggerWeakRef extends WeakReference<Logger> {
  470           private String                name;       // for namedLoggers cleanup
  471           private LogNode               node;       // for loggerRef cleanup
  472           private WeakReference<Logger> parentRef;  // for kids cleanup
  473   
  474           LoggerWeakRef(Logger logger) {
  475               super(logger, loggerRefQueue);
  476   
  477               name = logger.getName();  // save for namedLoggers cleanup
  478           }
  479   
  480           // dispose of this LoggerWeakRef object
  481           void dispose() {
  482               if (node != null) {
  483                   // if we have a LogNode, then we were a named Logger
  484                   // so clear namedLoggers weak ref to us
  485                   manager.namedLoggers.remove(name);
  486                   name = null;  // clear our ref to the Logger's name
  487   
  488                   node.loggerRef = null;  // clear LogNode's weak ref to us
  489                   node = null;            // clear our ref to LogNode
  490               }
  491   
  492               if (parentRef != null) {
  493                   // this LoggerWeakRef has or had a parent Logger
  494                   Logger parent = parentRef.get();
  495                   if (parent != null) {
  496                       // the parent Logger is still there so clear the
  497                       // parent Logger's weak ref to us
  498                       parent.removeChildLogger(this);
  499                   }
  500                   parentRef = null;  // clear our weak ref to the parent Logger
  501               }
  502           }
  503   
  504           // set the node field to the specified value
  505           void setNode(LogNode node) {
  506               this.node = node;
  507           }
  508   
  509           // set the parentRef field to the specified value
  510           void setParentRef(WeakReference<Logger> parentRef) {
  511               this.parentRef = parentRef;
  512           }
  513       }
  514   
  515       // Package-level method.
  516       // Drain some Logger objects that have been GC'ed.
  517       //
  518       // drainLoggerRefQueueBounded() is called by addLogger() below
  519       // and by Logger.getAnonymousLogger(String) so we'll drain up to
  520       // MAX_ITERATIONS GC'ed Loggers for every Logger we add.
  521       //
  522       // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives
  523       // us about a 50/50 mix in increased weak ref counts versus
  524       // decreased weak ref counts in the AnonLoggerWeakRefLeak test.
  525       // Here are stats for cleaning up sets of 400 anonymous Loggers:
  526       //   - test duration 1 minute
  527       //   - sample size of 125 sets of 400
  528       //   - average: 1.99 ms
  529       //   - minimum: 0.57 ms
  530       //   - maximum: 25.3 ms
  531       //
  532       // The same config gives us a better decreased weak ref count
  533       // than increased weak ref count in the LoggerWeakRefLeak test.
  534       // Here are stats for cleaning up sets of 400 named Loggers:
  535       //   - test duration 2 minutes
  536       //   - sample size of 506 sets of 400
  537       //   - average: 0.57 ms
  538       //   - minimum: 0.02 ms
  539       //   - maximum: 10.9 ms
  540       //
  541       private final static int MAX_ITERATIONS = 400;
  542       final synchronized void drainLoggerRefQueueBounded() {
  543           for (int i = 0; i < MAX_ITERATIONS; i++) {
  544               if (loggerRefQueue == null) {
  545                   // haven't finished loading LogManager yet
  546                   break;
  547               }
  548   
  549               LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll();
  550               if (ref == null) {
  551                   break;
  552               }
  553               // a Logger object has been GC'ed so clean it up
  554               ref.dispose();
  555           }
  556       }
  557   
  558       /**
  559        * Add a named logger.  This does nothing and returns false if a logger
  560        * with the same name is already registered.
  561        * <p>
  562        * The Logger factory methods call this method to register each
  563        * newly created Logger.
  564        * <p>
  565        * The application should retain its own reference to the Logger
  566        * object to avoid it being garbage collected.  The LogManager
  567        * may only retain a weak reference.
  568        *
  569        * @param   logger the new logger.
  570        * @return  true if the argument logger was registered successfully,
  571        *          false if a logger of that name already exists.
  572        * @exception NullPointerException if the logger name is null.
  573        */
  574       public synchronized boolean addLogger(Logger logger) {
  575           final String name = logger.getName();
  576           if (name == null) {
  577               throw new NullPointerException();
  578           }
  579   
  580           // cleanup some Loggers that have been GC'ed
  581           drainLoggerRefQueueBounded();
  582   
  583           LoggerWeakRef ref = namedLoggers.get(name);
  584           if (ref != null) {
  585               if (ref.get() == null) {
  586                   // It's possible that the Logger was GC'ed after the
  587                   // drainLoggerRefQueueBounded() call above so allow
  588                   // a new one to be registered.
  589                   namedLoggers.remove(name);
  590               } else {
  591                   // We already have a registered logger with the given name.
  592                   return false;
  593               }
  594           }
  595   
  596           // We're adding a new logger.
  597           // Note that we are creating a weak reference here.
  598           ref = new LoggerWeakRef(logger);
  599           namedLoggers.put(name, ref);
  600   
  601           // Apply any initial level defined for the new logger.
  602           Level level = getLevelProperty(name+".level", null);
  603           if (level != null) {
  604               doSetLevel(logger, level);
  605           }
  606   
  607           // Do we have a per logger handler too?
  608           // Note: this will add a 200ms penalty
  609           loadLoggerHandlers(logger, name, name+".handlers");
  610           processParentHandlers(logger, name);
  611   
  612           // Find the new node and its parent.
  613           LogNode node = findNode(name);
  614           node.loggerRef = ref;
  615           Logger parent = null;
  616           LogNode nodep = node.parent;
  617           while (nodep != null) {
  618               LoggerWeakRef nodeRef = nodep.loggerRef;
  619               if (nodeRef != null) {
  620                   parent = nodeRef.get();
  621                   if (parent != null) {
  622                       break;
  623                   }
  624               }
  625               nodep = nodep.parent;
  626           }
  627   
  628           if (parent != null) {
  629               doSetParent(logger, parent);
  630           }
  631           // Walk over the children and tell them we are their new parent.
  632           node.walkAndSetParent(logger);
  633   
  634           // new LogNode is ready so tell the LoggerWeakRef about it
  635           ref.setNode(node);
  636   
  637           return true;
  638       }
  639   
  640   
  641       // Private method to set a level on a logger.
  642       // If necessary, we raise privilege before doing the call.
  643       private static void doSetLevel(final Logger logger, final Level level) {
  644           SecurityManager sm = System.getSecurityManager();
  645           if (sm == null) {
  646               // There is no security manager, so things are easy.
  647               logger.setLevel(level);
  648               return;
  649           }
  650           // There is a security manager.  Raise privilege before
  651           // calling setLevel.
  652           AccessController.doPrivileged(new PrivilegedAction<Object>() {
  653               public Object run() {
  654                   logger.setLevel(level);
  655                   return null;
  656               }});
  657       }
  658   
  659   
  660   
  661       // Private method to set a parent on a logger.
  662       // If necessary, we raise privilege before doing the setParent call.
  663       private static void doSetParent(final Logger logger, final Logger parent) {
  664           SecurityManager sm = System.getSecurityManager();
  665           if (sm == null) {
  666               // There is no security manager, so things are easy.
  667               logger.setParent(parent);
  668               return;
  669           }
  670           // There is a security manager.  Raise privilege before
  671           // calling setParent.
  672           AccessController.doPrivileged(new PrivilegedAction<Object>() {
  673               public Object run() {
  674                   logger.setParent(parent);
  675                   return null;
  676               }});
  677       }
  678   
  679       // Find a node in our tree of logger nodes.
  680       // If necessary, create it.
  681       private LogNode findNode(String name) {
  682           if (name == null || name.equals("")) {
  683               return root;
  684           }
  685           LogNode node = root;
  686           while (name.length() > 0) {
  687               int ix = name.indexOf(".");
  688               String head;
  689               if (ix > 0) {
  690                   head = name.substring(0,ix);
  691                   name = name.substring(ix+1);
  692               } else {
  693                   head = name;
  694                   name = "";
  695               }
  696               if (node.children == null) {
  697                   node.children = new HashMap<>();
  698               }
  699               LogNode child = node.children.get(head);
  700               if (child == null) {
  701                   child = new LogNode(node);
  702                   node.children.put(head, child);
  703               }
  704               node = child;
  705           }
  706           return node;
  707       }
  708   
  709       /**
  710        * Method to find a named logger.
  711        * <p>
  712        * Note that since untrusted code may create loggers with
  713        * arbitrary names this method should not be relied on to
  714        * find Loggers for security sensitive logging.
  715        * It is also important to note that the Logger associated with the
  716        * String {@code name} may be garbage collected at any time if there
  717        * is no strong reference to the Logger. The caller of this method
  718        * must check the return value for null in order to properly handle
  719        * the case where the Logger has been garbage collected.
  720        * <p>
  721        * @param name name of the logger
  722        * @return  matching logger or null if none is found
  723        */
  724       public synchronized Logger getLogger(String name) {
  725           LoggerWeakRef ref = namedLoggers.get(name);
  726           if (ref == null) {
  727               return null;
  728           }
  729           Logger logger = ref.get();
  730           if (logger == null) {
  731               // Hashtable holds stale weak reference
  732               // to a logger which has been GC-ed.
  733               namedLoggers.remove(name);
  734           }
  735           return logger;
  736       }
  737   
  738       /**
  739        * Get an enumeration of known logger names.
  740        * <p>
  741        * Note:  Loggers may be added dynamically as new classes are loaded.
  742        * This method only reports on the loggers that are currently registered.
  743        * It is also important to note that this method only returns the name
  744        * of a Logger, not a strong reference to the Logger itself.
  745        * The returned String does nothing to prevent the Logger from being
  746        * garbage collected. In particular, if the returned name is passed
  747        * to {@code LogManager.getLogger()}, then the caller must check the
  748        * return value from {@code LogManager.getLogger()} for null to properly
  749        * handle the case where the Logger has been garbage collected in the
  750        * time since its name was returned by this method.
  751        * <p>
  752        * @return  enumeration of logger name strings
  753        */
  754       public synchronized Enumeration<String> getLoggerNames() {
  755           return namedLoggers.keys();
  756       }
  757   
  758       /**
  759        * Reinitialize the logging properties and reread the logging configuration.
  760        * <p>
  761        * The same rules are used for locating the configuration properties
  762        * as are used at startup.  So normally the logging properties will
  763        * be re-read from the same file that was used at startup.
  764        * <P>
  765        * Any log level definitions in the new configuration file will be
  766        * applied using Logger.setLevel(), if the target Logger exists.
  767        * <p>
  768        * A PropertyChangeEvent will be fired after the properties are read.
  769        *
  770        * @exception  SecurityException  if a security manager exists and if
  771        *             the caller does not have LoggingPermission("control").
  772        * @exception  IOException if there are IO problems reading the configuration.
  773        */
  774       public void readConfiguration() throws IOException, SecurityException {
  775           checkAccess();
  776   
  777           // if a configuration class is specified, load it and use it.
  778           String cname = System.getProperty("java.util.logging.config.class");
  779           if (cname != null) {
  780               try {
  781                   // Instantiate the named class.  It is its constructor's
  782                   // responsibility to initialize the logging configuration, by
  783                   // calling readConfiguration(InputStream) with a suitable stream.
  784                   try {
  785                       Class clz = ClassLoader.getSystemClassLoader().loadClass(cname);
  786                       clz.newInstance();
  787                       return;
  788                   } catch (ClassNotFoundException ex) {
  789                       Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
  790                       clz.newInstance();
  791                       return;
  792                   }
  793               } catch (Exception ex) {
  794                   System.err.println("Logging configuration class \"" + cname + "\" failed");
  795                   System.err.println("" + ex);
  796                   // keep going and useful config file.
  797               }
  798           }
  799   
  800           String fname = System.getProperty("java.util.logging.config.file");
  801           if (fname == null) {
  802               fname = System.getProperty("java.home");
  803               if (fname == null) {
  804                   throw new Error("Can't find java.home ??");
  805               }
  806               File f = new File(fname, "lib");
  807               f = new File(f, "logging.properties");
  808               fname = f.getCanonicalPath();
  809           }
  810           InputStream in = new FileInputStream(fname);
  811           BufferedInputStream bin = new BufferedInputStream(in);
  812           try {
  813               readConfiguration(bin);
  814           } finally {
  815               if (in != null) {
  816                   in.close();
  817               }
  818           }
  819       }
  820   
  821       /**
  822        * Reset the logging configuration.
  823        * <p>
  824        * For all named loggers, the reset operation removes and closes
  825        * all Handlers and (except for the root logger) sets the level
  826        * to null.  The root logger's level is set to Level.INFO.
  827        *
  828        * @exception  SecurityException  if a security manager exists and if
  829        *             the caller does not have LoggingPermission("control").
  830        */
  831   
  832       public void reset() throws SecurityException {
  833           checkAccess();
  834           synchronized (this) {
  835               props = new Properties();
  836               // Since we are doing a reset we no longer want to initialize
  837               // the global handlers, if they haven't been initialized yet.
  838               initializedGlobalHandlers = true;
  839           }
  840           Enumeration enum_ = getLoggerNames();
  841           while (enum_.hasMoreElements()) {
  842               String name = (String)enum_.nextElement();
  843               resetLogger(name);
  844           }
  845       }
  846   
  847   
  848       // Private method to reset an individual target logger.
  849       private void resetLogger(String name) {
  850           Logger logger = getLogger(name);
  851           if (logger == null) {
  852               return;
  853           }
  854           // Close all the Logger's handlers.
  855           Handler[] targets = logger.getHandlers();
  856           for (int i = 0; i < targets.length; i++) {
  857               Handler h = targets[i];
  858               logger.removeHandler(h);
  859               try {
  860                   h.close();
  861               } catch (Exception ex) {
  862                   // Problems closing a handler?  Keep going...
  863               }
  864           }
  865           if (name != null && name.equals("")) {
  866               // This is the root logger.
  867               logger.setLevel(defaultLevel);
  868           } else {
  869               logger.setLevel(null);
  870           }
  871       }
  872   
  873       // get a list of whitespace separated classnames from a property.
  874       private String[] parseClassNames(String propertyName) {
  875           String hands = getProperty(propertyName);
  876           if (hands == null) {
  877               return new String[0];
  878           }
  879           hands = hands.trim();
  880           int ix = 0;
  881           Vector<String> result = new Vector<>();
  882           while (ix < hands.length()) {
  883               int end = ix;
  884               while (end < hands.length()) {
  885                   if (Character.isWhitespace(hands.charAt(end))) {
  886                       break;
  887                   }
  888                   if (hands.charAt(end) == ',') {
  889                       break;
  890                   }
  891                   end++;
  892               }
  893               String word = hands.substring(ix, end);
  894               ix = end+1;
  895               word = word.trim();
  896               if (word.length() == 0) {
  897                   continue;
  898               }
  899               result.add(word);
  900           }
  901           return result.toArray(new String[result.size()]);
  902       }
  903   
  904       /**
  905        * Reinitialize the logging properties and reread the logging configuration
  906        * from the given stream, which should be in java.util.Properties format.
  907        * A PropertyChangeEvent will be fired after the properties are read.
  908        * <p>
  909        * Any log level definitions in the new configuration file will be
  910        * applied using Logger.setLevel(), if the target Logger exists.
  911        *
  912        * @param ins       stream to read properties from
  913        * @exception  SecurityException  if a security manager exists and if
  914        *             the caller does not have LoggingPermission("control").
  915        * @exception  IOException if there are problems reading from the stream.
  916        */
  917       public void readConfiguration(InputStream ins) throws IOException, SecurityException {
  918           checkAccess();
  919           reset();
  920   
  921           // Load the properties
  922           props.load(ins);
  923           // Instantiate new configuration objects.
  924           String names[] = parseClassNames("config");
  925   
  926           for (int i = 0; i < names.length; i++) {
  927               String word = names[i];
  928               try {
  929                   Class clz = ClassLoader.getSystemClassLoader().loadClass(word);
  930                   clz.newInstance();
  931               } catch (Exception ex) {
  932                   System.err.println("Can't load config class \"" + word + "\"");
  933                   System.err.println("" + ex);
  934                   // ex.printStackTrace();
  935               }
  936           }
  937   
  938           // Set levels on any pre-existing loggers, based on the new properties.
  939           setLevelsOnExistingLoggers();
  940   
  941           // Notify any interested parties that our properties have changed.
  942           changes.firePropertyChange(null, null, null);
  943   
  944           // Note that we need to reinitialize global handles when
  945           // they are first referenced.
  946           synchronized (this) {
  947               initializedGlobalHandlers = false;
  948           }
  949       }
  950   
  951       /**
  952        * Get the value of a logging property.
  953        * The method returns null if the property is not found.
  954        * @param name      property name
  955        * @return          property value
  956        */
  957       public String getProperty(String name) {
  958           return props.getProperty(name);
  959       }
  960   
  961       // Package private method to get a String property.
  962       // If the property is not defined we return the given
  963       // default value.
  964       String getStringProperty(String name, String defaultValue) {
  965           String val = getProperty(name);
  966           if (val == null) {
  967               return defaultValue;
  968           }
  969           return val.trim();
  970       }
  971   
  972       // Package private method to get an integer property.
  973       // If the property is not defined or cannot be parsed
  974       // we return the given default value.
  975       int getIntProperty(String name, int defaultValue) {
  976           String val = getProperty(name);
  977           if (val == null) {
  978               return defaultValue;
  979           }
  980           try {
  981               return Integer.parseInt(val.trim());
  982           } catch (Exception ex) {
  983               return defaultValue;
  984           }
  985       }
  986   
  987       // Package private method to get a boolean property.
  988       // If the property is not defined or cannot be parsed
  989       // we return the given default value.
  990       boolean getBooleanProperty(String name, boolean defaultValue) {
  991           String val = getProperty(name);
  992           if (val == null) {
  993               return defaultValue;
  994           }
  995           val = val.toLowerCase();
  996           if (val.equals("true") || val.equals("1")) {
  997               return true;
  998           } else if (val.equals("false") || val.equals("0")) {
  999               return false;
 1000           }
 1001           return defaultValue;
 1002       }
 1003   
 1004       // Package private method to get a Level property.
 1005       // If the property is not defined or cannot be parsed
 1006       // we return the given default value.
 1007       Level getLevelProperty(String name, Level defaultValue) {
 1008           String val = getProperty(name);
 1009           if (val == null) {
 1010               return defaultValue;
 1011           }
 1012           try {
 1013               return Level.parse(val.trim());
 1014           } catch (Exception ex) {
 1015               return defaultValue;
 1016           }
 1017       }
 1018   
 1019       // Package private method to get a filter property.
 1020       // We return an instance of the class named by the "name"
 1021       // property. If the property is not defined or has problems
 1022       // we return the defaultValue.
 1023       Filter getFilterProperty(String name, Filter defaultValue) {
 1024           String val = getProperty(name);
 1025           try {
 1026               if (val != null) {
 1027                   Class clz = ClassLoader.getSystemClassLoader().loadClass(val);
 1028                   return (Filter) clz.newInstance();
 1029               }
 1030           } catch (Exception ex) {
 1031               // We got one of a variety of exceptions in creating the
 1032               // class or creating an instance.
 1033               // Drop through.
 1034           }
 1035           // We got an exception.  Return the defaultValue.
 1036           return defaultValue;
 1037       }
 1038   
 1039   
 1040       // Package private method to get a formatter property.
 1041       // We return an instance of the class named by the "name"
 1042       // property. If the property is not defined or has problems
 1043       // we return the defaultValue.
 1044       Formatter getFormatterProperty(String name, Formatter defaultValue) {
 1045           String val = getProperty(name);
 1046           try {
 1047               if (val != null) {
 1048                   Class clz = ClassLoader.getSystemClassLoader().loadClass(val);
 1049                   return (Formatter) clz.newInstance();
 1050               }
 1051           } catch (Exception ex) {
 1052               // We got one of a variety of exceptions in creating the
 1053               // class or creating an instance.
 1054               // Drop through.
 1055           }
 1056           // We got an exception.  Return the defaultValue.
 1057           return defaultValue;
 1058       }
 1059   
 1060       // Private method to load the global handlers.
 1061       // We do the real work lazily, when the global handlers
 1062       // are first used.
 1063       private synchronized void initializeGlobalHandlers() {
 1064           if (initializedGlobalHandlers) {
 1065               return;
 1066           }
 1067   
 1068           initializedGlobalHandlers = true;
 1069   
 1070           if (deathImminent) {
 1071               // Aaargh...
 1072               // The VM is shutting down and our exit hook has been called.
 1073               // Avoid allocating global handlers.
 1074               return;
 1075           }
 1076           loadLoggerHandlers(rootLogger, null, "handlers");
 1077       }
 1078   
 1079   
 1080       private Permission ourPermission = new LoggingPermission("control", null);
 1081   
 1082       /**
 1083        * Check that the current context is trusted to modify the logging
 1084        * configuration.  This requires LoggingPermission("control").
 1085        * <p>
 1086        * If the check fails we throw a SecurityException, otherwise
 1087        * we return normally.
 1088        *
 1089        * @exception  SecurityException  if a security manager exists and if
 1090        *             the caller does not have LoggingPermission("control").
 1091        */
 1092       public void checkAccess() throws SecurityException {
 1093           SecurityManager sm = System.getSecurityManager();
 1094           if (sm == null) {
 1095               return;
 1096           }
 1097           sm.checkPermission(ourPermission);
 1098       }
 1099   
 1100       // Nested class to represent a node in our tree of named loggers.
 1101       private static class LogNode {
 1102           HashMap<String,LogNode> children;
 1103           LoggerWeakRef loggerRef;
 1104           LogNode parent;
 1105   
 1106           LogNode(LogNode parent) {
 1107               this.parent = parent;
 1108           }
 1109   
 1110           // Recursive method to walk the tree below a node and set
 1111           // a new parent logger.
 1112           void walkAndSetParent(Logger parent) {
 1113               if (children == null) {
 1114                   return;
 1115               }
 1116               Iterator<LogNode> values = children.values().iterator();
 1117               while (values.hasNext()) {
 1118                   LogNode node = values.next();
 1119                   LoggerWeakRef ref = node.loggerRef;
 1120                   Logger logger = (ref == null) ? null : ref.get();
 1121                   if (logger == null) {
 1122                       node.walkAndSetParent(parent);
 1123                   } else {
 1124                       doSetParent(logger, parent);
 1125                   }
 1126               }
 1127           }
 1128       }
 1129   
 1130       // We use a subclass of Logger for the root logger, so
 1131       // that we only instantiate the global handlers when they
 1132       // are first needed.
 1133       private class RootLogger extends Logger {
 1134   
 1135           private RootLogger() {
 1136               super("", null);
 1137               setLevel(defaultLevel);
 1138           }
 1139   
 1140           public void log(LogRecord record) {
 1141               // Make sure that the global handlers have been instantiated.
 1142               initializeGlobalHandlers();
 1143               super.log(record);
 1144           }
 1145   
 1146           public void addHandler(Handler h) {
 1147               initializeGlobalHandlers();
 1148               super.addHandler(h);
 1149           }
 1150   
 1151           public void removeHandler(Handler h) {
 1152               initializeGlobalHandlers();
 1153               super.removeHandler(h);
 1154           }
 1155   
 1156           public Handler[] getHandlers() {
 1157               initializeGlobalHandlers();
 1158               return super.getHandlers();
 1159           }
 1160       }
 1161   
 1162   
 1163       // Private method to be called when the configuration has
 1164       // changed to apply any level settings to any pre-existing loggers.
 1165       synchronized private void setLevelsOnExistingLoggers() {
 1166           Enumeration enum_ = props.propertyNames();
 1167           while (enum_.hasMoreElements()) {
 1168               String key = (String)enum_.nextElement();
 1169               if (!key.endsWith(".level")) {
 1170                   // Not a level definition.
 1171                   continue;
 1172               }
 1173               int ix = key.length() - 6;
 1174               String name = key.substring(0, ix);
 1175               Level level = getLevelProperty(key, null);
 1176               if (level == null) {
 1177                   System.err.println("Bad level value for property: " + key);
 1178                   continue;
 1179               }
 1180               Logger l = getLogger(name);
 1181               if (l == null) {
 1182                   continue;
 1183               }
 1184               l.setLevel(level);
 1185           }
 1186       }
 1187   
 1188       // Management Support
 1189       private static LoggingMXBean loggingMXBean = null;
 1190       /**
 1191        * String representation of the
 1192        * {@link javax.management.ObjectName} for the management interface
 1193        * for the logging facility.
 1194        *
 1195        * @see java.lang.management.PlatformLoggingMXBean
 1196        * @see java.util.logging.LoggingMXBean
 1197        *
 1198        * @since 1.5
 1199        */
 1200       public final static String LOGGING_MXBEAN_NAME
 1201           = "java.util.logging:type=Logging";
 1202   
 1203       /**
 1204        * Returns <tt>LoggingMXBean</tt> for managing loggers.
 1205        * An alternative way to manage loggers is through the
 1206        * {@link java.lang.management.PlatformLoggingMXBean} interface
 1207        * that can be obtained by calling:
 1208        * <pre>
 1209        *     PlatformLoggingMXBean logging = {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
 1210        *         ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class);
 1211        * </pre>
 1212        *
 1213        * @return a {@link LoggingMXBean} object.
 1214        *
 1215        * @see java.lang.management.PlatformLoggingMXBean
 1216        * @since 1.5
 1217        */
 1218       public static synchronized LoggingMXBean getLoggingMXBean() {
 1219           if (loggingMXBean == null) {
 1220               loggingMXBean =  new Logging();
 1221           }
 1222           return loggingMXBean;
 1223       }
 1224   
 1225   }

Home » openjdk-7 » java » util » logging » [javadoc | source]