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