History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/util/logging/LogManager.java (1 of 18352 files). Show all

Unified · Split

java/pkg/java/util/logging/LogManager.java+1225−0
@@ -0,0 +1,1225 @@
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
27package java.util.logging;
28
29import java.io.*;
30import java.util.*;
31import java.security.*;
32import java.lang.ref.ReferenceQueue;
33import java.lang.ref.WeakReference;
34import java.beans.PropertyChangeListener;
35import java.beans.PropertyChangeSupport;
36import java.net.URL;
37import 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
148public 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}