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