127 * Loggers are organized into a naming hierarchy based on their
128 * dot separated names. Thus "a.b.c" is a child of "a.b", but
129 * "a.b1" and a.b2" are peers.
130 * <p>
131 * All properties whose names end with ".level" are assumed to define
132 * log levels for Loggers. Thus "foo.level" defines a log level for
133 * the logger called "foo" and (recursively) for any of its children
134 * in the naming hierarchy. Log Levels are applied in the order they
135 * are defined in the properties file. Thus level settings for child
136 * nodes in the tree should come after settings for their parents.
137 * The property name ".level" can be used to set the level for the
138 * root of the tree.
139 * <p>
140 * All methods on the LogManager object are multi-thread safe.
141 *
142 * @since 1.4
143 */
144
145 public class LogManager {
146 // The global LogManager object
147 private static LogManager manager;
148
149 private Properties props = new Properties();
150 private final static Level defaultLevel = Level.INFO;
151
152 // The map of the registered listeners. The map value is the registration
153 // count to allow for cases where the same listener is registered many times.
154 private final Map<Object,Integer> listenerMap = new HashMap<>();
155
156 // LoggerContext for system loggers and user loggers
157 private final LoggerContext systemContext = new SystemLoggerContext();
158 private final LoggerContext userContext = new LoggerContext();
159 private Logger rootLogger;
160
161 // Have we done the primordial reading of the configuration file?
162 // (Must be done after a suitable amount of java.lang.System
163 // initialization has been done)
164 private volatile boolean readPrimordialConfiguration;
165 // Have we initialized global (root) handlers yet?
166 // This gets set to false in readConfiguration
167 private boolean initializedGlobalHandlers = true;
168 // True if JVM death is imminent and the exit hook has been called.
169 private boolean deathImminent;
170
171 static {
172 AccessController.doPrivileged(new PrivilegedAction<Object>() {
173 public Object run() {
174 String cname = null;
175 try {
176 cname = System.getProperty("java.util.logging.manager");
177 if (cname != null) {
178 try {
179 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
180 manager = (LogManager) clz.newInstance();
181 } catch (ClassNotFoundException ex) {
182 Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
183 manager = (LogManager) clz.newInstance();
184 }
185 }
186 } catch (Exception ex) {
187 System.err.println("Could not load Logmanager \"" + cname + "\"");
188 ex.printStackTrace();
189 }
190 if (manager == null) {
191 manager = new LogManager();
192 }
193
194 // Create and retain Logger for the root of the namespace.
195 manager.rootLogger = manager.new RootLogger();
196 // since by design the global manager's userContext and
197 // systemContext don't have their requiresDefaultLoggers
198 // flag set - we make sure to add the root logger to
199 // the global manager's default contexts here.
200 manager.addLogger(manager.rootLogger);
201 manager.systemContext.addLocalLogger(manager.rootLogger, false);
202 manager.userContext.addLocalLogger(manager.rootLogger, false);
203
204 // Adding the global Logger. Doing so in the Logger.<clinit>
205 // would deadlock with the LogManager.<clinit>.
206 // Do not call Logger.getGlobal() here as this might trigger
207 // the deadlock too.
208 @SuppressWarnings("deprecation")
209 final Logger global = Logger.global;
210 global.setLogManager(manager);
211
212 // Make sure the global logger will be registered in the
213 // global manager's default contexts.
214 manager.addLogger(global);
215 manager.systemContext.addLocalLogger(global, false);
216 manager.userContext.addLocalLogger(global, false);
217
218 // We don't call readConfiguration() here, as we may be running
219 // very early in the JVM startup sequence. Instead readConfiguration
220 // will be called lazily in getLogManager().
221 return null;
222 }
223 });
224 }
225
226
227 // This private class is used as a shutdown hook.
228 // It does a "reset" to close all open handlers.
229 private class Cleaner extends Thread {
230
231 private Cleaner() {
232 /* Set context class loader to null in order to avoid
233 * keeping a strong reference to an application classloader.
234 */
235 this.setContextClassLoader(null);
236 }
237
238 public void run() {
239 // This is to ensure the LogManager.<clinit> is completed
240 // before synchronized block. Otherwise deadlocks are possible.
241 LogManager mgr = manager;
242
243 // If the global handlers haven't been initialized yet, we
244 // don't want to initialize them just so we can close them!
245 synchronized (LogManager.this) {
246 // Note that death is imminent.
247 deathImminent = true;
248 initializedGlobalHandlers = true;
249 }
250
251 // Do a reset to close all active handlers.
252 reset();
253 }
254 }
255
256
257 /**
258 * Protected constructor. This is protected so that container applications
259 * (such as J2EE containers) can subclass the object. It is non-public as
260 * it is intended that there only be one LogManager object, whose value is
261 * retrieved by calling LogManager.getLogManager.
262 */
263 protected LogManager() {
264 // Add a shutdown hook to close the global handlers.
265 try {
266 Runtime.getRuntime().addShutdownHook(new Cleaner());
267 } catch (IllegalStateException e) {
268 // If the VM is already shutting down,
269 // We do not need to register shutdownHook.
270 }
271 }
272
273 /**
274 * Returns the global LogManager object.
275 * @return the global LogManager object
276 */
277 public static LogManager getLogManager() {
278 if (manager != null) {
279 manager.readPrimordialConfiguration();
280 }
281 return manager;
282 }
283
284 private void readPrimordialConfiguration() {
285 if (!readPrimordialConfiguration) {
286 synchronized (this) {
287 if (!readPrimordialConfiguration) {
288 // If System.in/out/err are null, it's a good
289 // indication that we're still in the
290 // bootstrapping phase
291 if (System.out == null) {
292 return;
293 }
294 readPrimordialConfiguration = true;
295
296 try {
297 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
298 public Void run() throws Exception {
299 readConfiguration();
300
301 // Platform loggers begin to delegate to java.util.logging.Logger
302 sun.util.logging.PlatformLogger.redirectPlatformLoggers();
303 return null;
304 }
305 });
306 } catch (Exception ex) {
307 // System.err.println("Can't read logging configuration:");
308 // ex.printStackTrace();
309 }
310 }
311 }
312 }
313 }
314
315 /**
316 * Adds an event listener to be invoked when the logging
317 * properties are re-read. Adding multiple instances of
318 * the same event Listener results in multiple entries
319 * in the property event listener table.
320 *
321 * <p><b>WARNING:</b> This method is omitted from this class in all subset
322 * Profiles of Java SE that do not include the {@code java.beans} package.
323 * </p>
324 *
325 * @param l event listener
326 * @exception SecurityException if a security manager exists and if
327 * the caller does not have LoggingPermission("control").
328 * @exception NullPointerException if the PropertyChangeListener is null.
375 if (l != null) {
376 PropertyChangeListener listener = l;
377 synchronized (listenerMap) {
378 Integer value = listenerMap.get(listener);
379 if (value != null) {
380 // remove from map if registration count is 1, otherwise
381 // just decrement its count
382 int i = value.intValue();
383 if (i == 1) {
384 listenerMap.remove(listener);
385 } else {
386 assert i > 1;
387 listenerMap.put(listener, i - 1);
388 }
389 }
390 }
391 }
392 }
393
394 // LoggerContext maps from AppContext
395 private static WeakHashMap<Object, LoggerContext> contextsMap = null;
396
397 // Returns the LoggerContext for the user code (i.e. application or AppContext).
398 // Loggers are isolated from each AppContext.
399 private LoggerContext getUserContext() {
400 LoggerContext context = null;
401
402 SecurityManager sm = System.getSecurityManager();
403 JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess();
404 if (sm != null && javaAwtAccess != null) {
405 // for each applet, it has its own LoggerContext isolated from others
406 synchronized (javaAwtAccess) {
407 // find the AppContext of the applet code
408 // will be null if we are in the main app context.
409 final Object ecx = javaAwtAccess.getAppletContext();
410 if (ecx != null) {
411 if (contextsMap == null) {
412 contextsMap = new WeakHashMap<>();
413 }
414 context = contextsMap.get(ecx);
415 if (context == null) {
416 // Create a new LoggerContext for the applet.
417 // The new logger context has its requiresDefaultLoggers
418 // flag set to true - so that these loggers will be
419 // lazily added when the context is firt accessed.
420 context = new LoggerContext(true);
421 contextsMap.put(ecx, context);
422 }
423 }
424 }
425 }
426 // for standalone app, return userContext
427 return context != null ? context : userContext;
428 }
429
430 private List<LoggerContext> contexts() {
431 List<LoggerContext> cxs = new ArrayList<>();
432 cxs.add(systemContext);
433 cxs.add(getUserContext());
434 return cxs;
435 }
436
437 // Find or create a specified logger instance. If a logger has
438 // already been created with the given name it is returned.
439 // Otherwise a new logger instance is created and registered
440 // in the LogManager global namespace.
441 // This method will always return a non-null Logger object.
442 // Synchronization is not required here. All synchronization for
443 // adding a new Logger object is handled by addLogger().
444 //
445 // This method must delegate to the LogManager implementation to
446 // add a new Logger or return the one that has been added previously
447 // as a LogManager subclass may override the addLogger, getLogger,
448 // readConfiguration, and other methods.
449 Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
450 Logger result = getLogger(name);
451 if (result == null) {
452 // only allocate the new logger once
453 Logger newLogger = new Logger(name, resourceBundleName, caller);
454 do {
455 if (addLogger(newLogger)) {
456 // We successfully added the new Logger that we
457 // created above so return it without refetching.
458 return newLogger;
459 }
460
461 // We didn't add the new Logger that we created above
462 // because another thread added a Logger with the same
463 // name after our null check above and before our call
464 // to addLogger(). We have to refetch the Logger because
465 // addLogger() returns a boolean instead of the Logger
466 // reference itself. However, if the thread that created
467 // the other Logger is not holding a strong reference to
468 // the other Logger, then it is possible for the other
469 // Logger to be GC'ed after we saw it in addLogger() and
470 // before we can refetch it. If it has been GC'ed then
471 // we'll just loop around and try again.
472 result = getLogger(name);
473 } while (result == null);
474 }
475 return result;
476 }
477
478 Logger demandSystemLogger(String name, String resourceBundleName) {
479 // Add a system logger in the system context's namespace
480 final Logger sysLogger = systemContext.demandLogger(name, resourceBundleName);
481
482 // Add the system logger to the LogManager's namespace if not exist
483 // so that there is only one single logger of the given name.
484 // System loggers are visible to applications unless a logger of
485 // the same name has been added.
486 Logger logger;
487 do {
488 // First attempt to call addLogger instead of getLogger
489 // This would avoid potential bug in custom LogManager.getLogger
490 // implementation that adds a logger if does not exist
491 if (addLogger(sysLogger)) {
492 // successfully added the new system logger
493 logger = sysLogger;
494 } else {
495 logger = getLogger(name);
496 }
497 } while (logger == null);
498
499 // LogManager will set the sysLogger's handlers via LogManager.addLogger method.
500 if (logger != sysLogger && sysLogger.getHandlers().length == 0) {
501 // if logger already exists but handlers not set
502 final Logger l = logger;
503 AccessController.doPrivileged(new PrivilegedAction<Void>() {
504 public Void run() {
505 for (Handler hdl : l.getHandlers()) {
506 sysLogger.addHandler(hdl);
507 }
508 return null;
509 }
510 });
511 }
512 return sysLogger;
513 }
514
515 // LoggerContext maintains the logger namespace per context.
516 // The default LogManager implementation has one system context and user
517 // context. The system context is used to maintain the namespace for
518 // all system loggers and is queried by the system code. If a system logger
519 // doesn't exist in the user context, it'll also be added to the user context.
520 // The user context is queried by the user code and all other loggers are
521 // added in the user context.
522 static class LoggerContext {
523 // Table of named Loggers that maps names to Loggers.
524 private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
525 // Tree of named Loggers
526 private final LogNode root;
527 private final boolean requiresDefaultLoggers;
528 private LoggerContext() {
529 this(false);
530 }
531 private LoggerContext(boolean requiresDefaultLoggers) {
532 this.root = new LogNode(null, this);
533 this.requiresDefaultLoggers = requiresDefaultLoggers;
534 }
535
536 Logger demandLogger(String name, String resourceBundleName) {
537 // a LogManager subclass may have its own implementation to add and
538 // get a Logger. So delegate to the LogManager to do the work.
539 return manager.demandLogger(name, resourceBundleName, null);
540 }
541
542
543 // Due to subtle deadlock issues getUserContext() no longer
544 // calls addLocalLogger(rootLogger);
545 // Therefore - we need to add the default loggers later on.
546 // Checks that the context is properly initialized
547 // This is necessary before calling e.g. find(name)
548 // or getLoggerNames()
549 //
550 private void ensureInitialized() {
551 if (requiresDefaultLoggers) {
552 // Ensure that the root and global loggers are set.
553 ensureDefaultLogger(manager.rootLogger);
554 ensureDefaultLogger(Logger.global);
555 }
556 }
557
558
559 synchronized Logger findLogger(String name) {
560 // ensure that this context is properly initialized before
561 // looking for loggers.
562 ensureInitialized();
563 LoggerWeakRef ref = namedLoggers.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 removeLogger(name);
572 }
573 return logger;
574 }
575
576 // This method is called before adding a logger to the
577 // context.
578 // 'logger' is the context that will be added.
579 // This method will ensure that the defaults loggers are added
580 // before adding 'logger'.
581 //
582 private void ensureAllDefaultLoggers(Logger logger) {
583 if (requiresDefaultLoggers) {
584 final String name = logger.getName();
585 if (!name.isEmpty()) {
586 ensureDefaultLogger(manager.rootLogger);
587 }
588 if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
589 ensureDefaultLogger(Logger.global);
590 }
591 }
592 }
593
594 private void ensureDefaultLogger(Logger logger) {
595 // Used for lazy addition of root logger and global logger
596 // to a LoggerContext.
597
598 // This check is simple sanity: we do not want that this
599 // method be called for anything else than Logger.global
600 // or owner.rootLogger.
601 if (!requiresDefaultLoggers || logger == null
602 || logger != Logger.global && logger != manager.rootLogger) {
603
604 // the case where we have a non null logger which is neither
605 // Logger.global nor manager.rootLogger indicates a serious
606 // issue - as ensureDefaultLogger should never be called
607 // with any other loggers than one of these two (or null - if
608 // e.g manager.rootLogger is not yet initialized)...
609 assert logger == null;
610
611 return;
612 }
613
614 // Adds the logger if it's not already there.
615 if (!namedLoggers.containsKey(logger.getName())) {
616 // It is important to prevent addLocalLogger to
617 // call ensureAllDefaultLoggers when we're in the process
618 // off adding one of those default loggers - as this would
619 // immediately cause a stack overflow.
620 // Therefore we must pass addDefaultLoggersIfNeeded=false,
621 // even if requiresDefaultLoggers is true.
622 addLocalLogger(logger, false);
623 }
624 }
625
626 boolean addLocalLogger(Logger logger) {
627 // no need to add default loggers if it's not required
628 return addLocalLogger(logger, requiresDefaultLoggers);
629 }
630
631 // Add a logger to this context. This method will only set its level
632 // and process parent loggers. It doesn't set its handlers.
633 synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) {
634 // addDefaultLoggersIfNeeded serves to break recursion when adding
635 // default loggers. If we're adding one of the default loggers
636 // (we're being called from ensureDefaultLogger()) then
637 // addDefaultLoggersIfNeeded will be false: we don't want to
638 // call ensureAllDefaultLoggers again.
639 //
640 // Note: addDefaultLoggersIfNeeded can also be false when
641 // requiresDefaultLoggers is false - since calling
642 // ensureAllDefaultLoggers would have no effect in this case.
643 if (addDefaultLoggersIfNeeded) {
644 ensureAllDefaultLoggers(logger);
645 }
646
647 final String name = logger.getName();
648 if (name == null) {
649 throw new NullPointerException();
650 }
651 LoggerWeakRef ref = namedLoggers.get(name);
652 if (ref != null) {
653 if (ref.get() == null) {
654 // It's possible that the Logger was GC'ed after a
655 // drainLoggerRefQueueBounded() call above so allow
656 // a new one to be registered.
657 removeLogger(name);
658 } else {
659 // We already have a registered logger with the given name.
660 return false;
661 }
662 }
663
664 // We're adding a new logger.
665 // Note that we are creating a weak reference here.
666 ref = manager.new LoggerWeakRef(logger);
667 namedLoggers.put(name, ref);
668
669 // Apply any initial level defined for the new logger.
670 Level level = manager.getLevelProperty(name + ".level", null);
671 if (level != null) {
672 doSetLevel(logger, level);
673 }
674
675 // instantiation of the handler is done in the LogManager.addLogger
676 // implementation as a handler class may be only visible to LogManager
677 // subclass for the custom log manager case
678 processParentHandlers(logger, name);
679
680 // Find the new node and its parent.
681 LogNode node = getNode(name);
682 node.loggerRef = ref;
683 Logger parent = null;
684 LogNode nodep = node.parent;
685 while (nodep != null) {
686 LoggerWeakRef nodeRef = nodep.loggerRef;
687 if (nodeRef != null) {
688 parent = nodeRef.get();
689 if (parent != null) {
690 break;
702 ref.setNode(node);
703 return true;
704 }
705
706 // note: all calls to removeLogger are synchronized on LogManager's
707 // intrinsic lock
708 void removeLogger(String name) {
709 namedLoggers.remove(name);
710 }
711
712 synchronized Enumeration<String> getLoggerNames() {
713 // ensure that this context is properly initialized before
714 // returning logger names.
715 ensureInitialized();
716 return namedLoggers.keys();
717 }
718
719 // If logger.getUseParentHandlers() returns 'true' and any of the logger's
720 // parents have levels or handlers defined, make sure they are instantiated.
721 private void processParentHandlers(final Logger logger, final String name) {
722 AccessController.doPrivileged(new PrivilegedAction<Void>() {
723 public Void run() {
724 if (logger != manager.rootLogger) {
725 boolean useParent = manager.getBooleanProperty(name + ".useParentHandlers", true);
726 if (!useParent) {
727 logger.setUseParentHandlers(false);
728 }
729 }
730 return null;
731 }
732 });
733
734 int ix = 1;
735 for (;;) {
736 int ix2 = name.indexOf(".", ix);
737 if (ix2 < 0) {
738 break;
739 }
740 String pname = name.substring(0, ix2);
741 if (manager.getProperty(pname + ".level") != null ||
742 manager.getProperty(pname + ".handlers") != null) {
743 // This pname has a level/handlers definition.
744 // Make sure it exists.
745 demandLogger(pname, null);
746 }
747 ix = ix2+1;
748 }
749 }
750
751 // Gets a node in our tree of logger nodes.
752 // If necessary, create it.
753 LogNode getNode(String name) {
754 if (name == null || name.equals("")) {
755 return root;
756 }
757 LogNode node = root;
758 while (name.length() > 0) {
759 int ix = name.indexOf(".");
760 String head;
761 if (ix > 0) {
762 head = name.substring(0, ix);
763 name = name.substring(ix + 1);
764 } else {
765 head = name;
766 name = "";
767 }
768 if (node.children == null) {
769 node.children = new HashMap<>();
770 }
771 LogNode child = node.children.get(head);
772 if (child == null) {
773 child = new LogNode(node, this);
774 node.children.put(head, child);
775 }
776 node = child;
777 }
778 return node;
779 }
780 }
781
782 static class SystemLoggerContext extends LoggerContext {
783 // Add a system logger in the system context's namespace as well as
784 // in the LogManager's namespace if not exist so that there is only
785 // one single logger of the given name. System loggers are visible
786 // to applications unless a logger of the same name has been added.
787 Logger demandLogger(String name, String resourceBundleName) {
788 Logger result = findLogger(name);
789 if (result == null) {
790 // only allocate the new system logger once
791 Logger newLogger = new Logger(name, resourceBundleName);
792 do {
793 if (addLocalLogger(newLogger)) {
794 // We successfully added the new Logger that we
795 // created above so return it without refetching.
796 result = newLogger;
797 } else {
798 // We didn't add the new Logger that we created above
799 // because another thread added a Logger with the same
800 // name after our null check above and before our call
801 // to addLogger(). We have to refetch the Logger because
802 // addLogger() returns a boolean instead of the Logger
803 // reference itself. However, if the thread that created
804 // the other Logger is not holding a strong reference to
805 // the other Logger, then it is possible for the other
806 // Logger to be GC'ed after we saw it in addLogger() and
807 // before we can refetch it. If it has been GC'ed then
808 // we'll just loop around and try again.
809 result = findLogger(name);
810 }
811 } while (result == null);
812 }
813 return result;
814 }
815 }
816
817 // Add new per logger handlers.
818 // We need to raise privilege here. All our decisions will
819 // be made based on the logging configuration, which can
820 // only be modified by trusted code.
821 private void loadLoggerHandlers(final Logger logger, final String name,
822 final String handlersPropertyName)
823 {
824 AccessController.doPrivileged(new PrivilegedAction<Object>() {
825 public Object run() {
826 String names[] = parseClassNames(handlersPropertyName);
827 for (int i = 0; i < names.length; i++) {
828 String word = names[i];
829 try {
830 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
831 Handler hdl = (Handler) clz.newInstance();
832 // Check if there is a property defining the
833 // this handler's level.
834 String levs = getProperty(word + ".level");
835 if (levs != null) {
836 Level l = Level.findLevel(levs);
837 if (l != null) {
838 hdl.setLevel(l);
839 } else {
840 // Probably a bad level. Drop through.
841 System.err.println("Can't set level for " + word);
842 }
843 }
844 // Add this Handler to the logger
997 // Note: this will add a 200ms penalty
998 loadLoggerHandlers(logger, name, name + ".handlers");
999 return true;
1000 } else {
1001 return false;
1002 }
1003 }
1004
1005 // Private method to set a level on a logger.
1006 // If necessary, we raise privilege before doing the call.
1007 private static void doSetLevel(final Logger logger, final Level level) {
1008 SecurityManager sm = System.getSecurityManager();
1009 if (sm == null) {
1010 // There is no security manager, so things are easy.
1011 logger.setLevel(level);
1012 return;
1013 }
1014 // There is a security manager. Raise privilege before
1015 // calling setLevel.
1016 AccessController.doPrivileged(new PrivilegedAction<Object>() {
1017 public Object run() {
1018 logger.setLevel(level);
1019 return null;
1020 }});
1021 }
1022
1023 // Private method to set a parent on a logger.
1024 // If necessary, we raise privilege before doing the setParent call.
1025 private static void doSetParent(final Logger logger, final Logger parent) {
1026 SecurityManager sm = System.getSecurityManager();
1027 if (sm == null) {
1028 // There is no security manager, so things are easy.
1029 logger.setParent(parent);
1030 return;
1031 }
1032 // There is a security manager. Raise privilege before
1033 // calling setParent.
1034 AccessController.doPrivileged(new PrivilegedAction<Object>() {
1035 public Object run() {
1036 logger.setParent(parent);
1037 return null;
1038 }});
1039 }
1040
1041 /**
1042 * Method to find a named logger.
1043 * <p>
1044 * Note that since untrusted code may create loggers with
1045 * arbitrary names this method should not be relied on to
1046 * find Loggers for security sensitive logging.
1047 * It is also important to note that the Logger associated with the
1048 * String {@code name} may be garbage collected at any time if there
1049 * is no strong reference to the Logger. The caller of this method
1050 * must check the return value for null in order to properly handle
1051 * the case where the Logger has been garbage collected.
1052 * <p>
1053 * @param name name of the logger
1054 * @return matching logger or null if none is found
1112 clz.newInstance();
1113 return;
1114 }
1115 } catch (Exception ex) {
1116 System.err.println("Logging configuration class \"" + cname + "\" failed");
1117 System.err.println("" + ex);
1118 // keep going and useful config file.
1119 }
1120 }
1121
1122 String fname = System.getProperty("java.util.logging.config.file");
1123 if (fname == null) {
1124 fname = System.getProperty("java.home");
1125 if (fname == null) {
1126 throw new Error("Can't find java.home ??");
1127 }
1128 File f = new File(fname, "lib");
1129 f = new File(f, "logging.properties");
1130 fname = f.getCanonicalPath();
1131 }
1132 InputStream in = new FileInputStream(fname);
1133 BufferedInputStream bin = new BufferedInputStream(in);
1134 try {
1135 readConfiguration(bin);
1136 } finally {
1137 if (in != null) {
1138 in.close();
1139 }
1140 }
1141 }
1142
1143 /**
1144 * Reset the logging configuration.
1145 * <p>
1146 * For all named loggers, the reset operation removes and closes
1147 * all Handlers and (except for the root logger) sets the level
1148 * to null. The root logger's level is set to Level.INFO.
1149 *
1150 * @exception SecurityException if a security manager exists and if
1151 * the caller does not have LoggingPermission("control").
1152 */
1153
1154 public void reset() throws SecurityException {
1155 checkPermission();
1156 synchronized (this) {
1157 props = new Properties();
1158 // Since we are doing a reset we no longer want to initialize
1159 // the global handlers, if they haven't been initialized yet.
1160 initializedGlobalHandlers = true;
1161 }
1184 // Problems closing a handler? Keep going...
1185 }
1186 }
1187 String name = logger.getName();
1188 if (name != null && name.equals("")) {
1189 // This is the root logger.
1190 logger.setLevel(defaultLevel);
1191 } else {
1192 logger.setLevel(null);
1193 }
1194 }
1195
1196 // get a list of whitespace separated classnames from a property.
1197 private String[] parseClassNames(String propertyName) {
1198 String hands = getProperty(propertyName);
1199 if (hands == null) {
1200 return new String[0];
1201 }
1202 hands = hands.trim();
1203 int ix = 0;
1204 Vector<String> result = new Vector<>();
1205 while (ix < hands.length()) {
1206 int end = ix;
1207 while (end < hands.length()) {
1208 if (Character.isWhitespace(hands.charAt(end))) {
1209 break;
1210 }
1211 if (hands.charAt(end) == ',') {
1212 break;
1213 }
1214 end++;
1215 }
1216 String word = hands.substring(ix, end);
1217 ix = end+1;
1218 word = word.trim();
1219 if (word.length() == 0) {
1220 continue;
1221 }
1222 result.add(word);
1223 }
1224 return result.toArray(new String[result.size()]);
1454 if (children == null) {
1455 return;
1456 }
1457 Iterator<LogNode> values = children.values().iterator();
1458 while (values.hasNext()) {
1459 LogNode node = values.next();
1460 LoggerWeakRef ref = node.loggerRef;
1461 Logger logger = (ref == null) ? null : ref.get();
1462 if (logger == null) {
1463 node.walkAndSetParent(parent);
1464 } else {
1465 doSetParent(logger, parent);
1466 }
1467 }
1468 }
1469 }
1470
1471 // We use a subclass of Logger for the root logger, so
1472 // that we only instantiate the global handlers when they
1473 // are first needed.
1474 private class RootLogger extends Logger {
1475 private RootLogger() {
1476 super("", null);
1477 setLevel(defaultLevel);
1478 }
1479
1480 public void log(LogRecord record) {
1481 // Make sure that the global handlers have been instantiated.
1482 initializeGlobalHandlers();
1483 super.log(record);
1484 }
1485
1486 public void addHandler(Handler h) {
1487 initializeGlobalHandlers();
1488 super.addHandler(h);
1489 }
1490
1491 public void removeHandler(Handler h) {
1492 initializeGlobalHandlers();
1493 super.removeHandler(h);
1494 }
1495
1496 public Handler[] getHandlers() {
1497 initializeGlobalHandlers();
1498 return super.getHandlers();
1499 }
1500 }
1501
1502
1503 // Private method to be called when the configuration has
1504 // changed to apply any level settings to any pre-existing loggers.
1505 synchronized private void setLevelsOnExistingLoggers() {
1506 Enumeration<?> enum_ = props.propertyNames();
1507 while (enum_.hasMoreElements()) {
1508 String key = (String)enum_.nextElement();
1509 if (!key.endsWith(".level")) {
1510 // Not a level definition.
1511 continue;
1512 }
1513 int ix = key.length() - 6;
1514 String name = key.substring(0, ix);
1515 Level level = getLevelProperty(key, null);
|
127 * Loggers are organized into a naming hierarchy based on their
128 * dot separated names. Thus "a.b.c" is a child of "a.b", but
129 * "a.b1" and a.b2" are peers.
130 * <p>
131 * All properties whose names end with ".level" are assumed to define
132 * log levels for Loggers. Thus "foo.level" defines a log level for
133 * the logger called "foo" and (recursively) for any of its children
134 * in the naming hierarchy. Log Levels are applied in the order they
135 * are defined in the properties file. Thus level settings for child
136 * nodes in the tree should come after settings for their parents.
137 * The property name ".level" can be used to set the level for the
138 * root of the tree.
139 * <p>
140 * All methods on the LogManager object are multi-thread safe.
141 *
142 * @since 1.4
143 */
144
145 public class LogManager {
146 // The global LogManager object
147 private static final LogManager manager;
148
149 private Properties props = new Properties();
150 private final static Level defaultLevel = Level.INFO;
151
152 // The map of the registered listeners. The map value is the registration
153 // count to allow for cases where the same listener is registered many times.
154 private final Map<Object,Integer> listenerMap = new HashMap<>();
155
156 // LoggerContext for system loggers and user loggers
157 private final LoggerContext systemContext = new SystemLoggerContext();
158 private final LoggerContext userContext = new LoggerContext();
159 private Logger rootLogger;
160
161 // Have we done the primordial reading of the configuration file?
162 // (Must be done after a suitable amount of java.lang.System
163 // initialization has been done)
164 private volatile boolean readPrimordialConfiguration;
165 // Have we initialized global (root) handlers yet?
166 // This gets set to false in readConfiguration
167 private boolean initializedGlobalHandlers = true;
168 // True if JVM death is imminent and the exit hook has been called.
169 private boolean deathImminent;
170
171 static {
172 manager = AccessController.doPrivileged(new PrivilegedAction<LogManager>() {
173 @Override
174 public LogManager run() {
175 LogManager mgr = null;
176 String cname = null;
177 try {
178 cname = System.getProperty("java.util.logging.manager");
179 if (cname != null) {
180 try {
181 Class<?> clz = ClassLoader.getSystemClassLoader()
182 .loadClass(cname);
183 mgr = (LogManager) clz.newInstance();
184 } catch (ClassNotFoundException ex) {
185 Class<?> clz = Thread.currentThread()
186 .getContextClassLoader().loadClass(cname);
187 mgr = (LogManager) clz.newInstance();
188 }
189 }
190 } catch (Exception ex) {
191 System.err.println("Could not load Logmanager \"" + cname + "\"");
192 ex.printStackTrace();
193 }
194 if (mgr == null) {
195 mgr = new LogManager();
196 }
197 return mgr;
198
199 }
200 });
201 }
202
203
204 // This private class is used as a shutdown hook.
205 // It does a "reset" to close all open handlers.
206 private class Cleaner extends Thread {
207
208 private Cleaner() {
209 /* Set context class loader to null in order to avoid
210 * keeping a strong reference to an application classloader.
211 */
212 this.setContextClassLoader(null);
213 }
214
215 @Override
216 public void run() {
217 // This is to ensure the LogManager.<clinit> is completed
218 // before synchronized block. Otherwise deadlocks are possible.
219 LogManager mgr = manager;
220
221 // If the global handlers haven't been initialized yet, we
222 // don't want to initialize them just so we can close them!
223 synchronized (LogManager.this) {
224 // Note that death is imminent.
225 deathImminent = true;
226 initializedGlobalHandlers = true;
227 }
228
229 // Do a reset to close all active handlers.
230 reset();
231 }
232 }
233
234
235 /**
236 * Protected constructor. This is protected so that container applications
237 * (such as J2EE containers) can subclass the object. It is non-public as
238 * it is intended that there only be one LogManager object, whose value is
239 * retrieved by calling LogManager.getLogManager.
240 */
241 protected LogManager() {
242 // Add a shutdown hook to close the global handlers.
243 try {
244 Runtime.getRuntime().addShutdownHook(new Cleaner());
245 } catch (IllegalStateException e) {
246 // If the VM is already shutting down,
247 // We do not need to register shutdownHook.
248 }
249 }
250
251 /**
252 * Lazy initialization: if this instance of manager is the global
253 * manager then this method will read the initial configuration and
254 * add the root logger and global logger by calling addLogger().
255 *
256 * Note that it is subtly different from what we do in LoggerContext.
257 * In LoggerContext we're patching up the logger context tree in order to add
258 * the root and global logger *to the context tree*.
259 *
260 * For this to work, addLogger() must have already have been called
261 * once on the LogManager instance for the default logger being
262 * added.
263 *
264 * This is why ensureLogManagerInitialized() needs to be called before
265 * any logger is added to any logger context.
266 *
267 **/
268 private boolean initializedCalled=false;
269 final synchronized void ensureLogManagerInitialized() {
270 final LogManager owner = this;
271 if (initializedCalled || owner != manager) {
272 // we don't want to do this twice, and we don't want to do
273 // this on private manager instances.
274 return;
275 }
276 initializedCalled = true;
277 AccessController.doPrivileged(new PrivilegedAction<Object>() {
278 @Override
279 public Object run() {
280 if (rootLogger == null) {
281 // Read configuration. This was previously triggered
282 // by the new RootLogger() constructor - but no longer.
283 owner.readPrimordialConfiguration();
284
285 // Create and retain Logger for the root of the namespace.
286 owner.rootLogger = owner.new RootLogger();
287 owner.addLogger(owner.rootLogger);
288
289 // Adding the global Logger. Doing so in the Logger.<clinit>
290 // would deadlock with the LogManager.<clinit>.
291 // Do not call Logger.getGlobal() here as this might trigger
292 // the deadlock too.
293 @SuppressWarnings("deprecation")
294 final Logger global = Logger.global;
295
296 // Make sure the global logger will be registered in the
297 // global manager
298 owner.addLogger(global);
299 }
300 return null;
301 }
302 });
303 }
304
305 /**
306 * Returns the global LogManager object.
307 * @return the global LogManager object
308 */
309 public static LogManager getLogManager() {
310 if (manager != null) {
311 manager.ensureLogManagerInitialized();
312 }
313 return manager;
314 }
315
316 private void readPrimordialConfiguration() {
317 if (!readPrimordialConfiguration) {
318 synchronized (this) {
319 if (!readPrimordialConfiguration) {
320 // If System.in/out/err are null, it's a good
321 // indication that we're still in the
322 // bootstrapping phase
323 if (System.out == null) {
324 return;
325 }
326 readPrimordialConfiguration = true;
327
328 try {
329 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
330 @Override
331 public Void run() throws Exception {
332 readConfiguration();
333
334 // Platform loggers begin to delegate to java.util.logging.Logger
335 sun.util.logging.PlatformLogger.redirectPlatformLoggers();
336 return null;
337 }
338 });
339 } catch (Exception ex) {
340 // System.err.println("Can't read logging configuration:");
341 // ex.printStackTrace();
342 assert false : "Exception raised while reading logging configuration: " + ex;
343 }
344 }
345 }
346 }
347 }
348
349 /**
350 * Adds an event listener to be invoked when the logging
351 * properties are re-read. Adding multiple instances of
352 * the same event Listener results in multiple entries
353 * in the property event listener table.
354 *
355 * <p><b>WARNING:</b> This method is omitted from this class in all subset
356 * Profiles of Java SE that do not include the {@code java.beans} package.
357 * </p>
358 *
359 * @param l event listener
360 * @exception SecurityException if a security manager exists and if
361 * the caller does not have LoggingPermission("control").
362 * @exception NullPointerException if the PropertyChangeListener is null.
409 if (l != null) {
410 PropertyChangeListener listener = l;
411 synchronized (listenerMap) {
412 Integer value = listenerMap.get(listener);
413 if (value != null) {
414 // remove from map if registration count is 1, otherwise
415 // just decrement its count
416 int i = value.intValue();
417 if (i == 1) {
418 listenerMap.remove(listener);
419 } else {
420 assert i > 1;
421 listenerMap.put(listener, i - 1);
422 }
423 }
424 }
425 }
426 }
427
428 // LoggerContext maps from AppContext
429 private WeakHashMap<Object, LoggerContext> contextsMap = null;
430
431 // Returns the LoggerContext for the user code (i.e. application or AppContext).
432 // Loggers are isolated from each AppContext.
433 private LoggerContext getUserContext() {
434 LoggerContext context = null;
435
436 SecurityManager sm = System.getSecurityManager();
437 JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess();
438 if (sm != null && javaAwtAccess != null) {
439 // for each applet, it has its own LoggerContext isolated from others
440 synchronized (javaAwtAccess) {
441 // find the AppContext of the applet code
442 // will be null if we are in the main app context.
443 final Object ecx = javaAwtAccess.getAppletContext();
444 if (ecx != null) {
445 if (contextsMap == null) {
446 contextsMap = new WeakHashMap<>();
447 }
448 context = contextsMap.get(ecx);
449 if (context == null) {
450 // Create a new LoggerContext for the applet.
451 context = new LoggerContext();
452 contextsMap.put(ecx, context);
453 }
454 }
455 }
456 }
457 // for standalone app, return userContext
458 return context != null ? context : userContext;
459 }
460
461 // The system context.
462 final LoggerContext getSystemContext() {
463 return systemContext;
464 }
465
466 private List<LoggerContext> contexts() {
467 List<LoggerContext> cxs = new ArrayList<>();
468 cxs.add(getSystemContext());
469 cxs.add(getUserContext());
470 return cxs;
471 }
472
473 // Find or create a specified logger instance. If a logger has
474 // already been created with the given name it is returned.
475 // Otherwise a new logger instance is created and registered
476 // in the LogManager global namespace.
477 // This method will always return a non-null Logger object.
478 // Synchronization is not required here. All synchronization for
479 // adding a new Logger object is handled by addLogger().
480 //
481 // This method must delegate to the LogManager implementation to
482 // add a new Logger or return the one that has been added previously
483 // as a LogManager subclass may override the addLogger, getLogger,
484 // readConfiguration, and other methods.
485 Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
486 Logger result = getLogger(name);
487 if (result == null) {
488 // only allocate the new logger once
489 Logger newLogger = new Logger(name, resourceBundleName, caller, this);
490 do {
491 if (addLogger(newLogger)) {
492 // We successfully added the new Logger that we
493 // created above so return it without refetching.
494 return newLogger;
495 }
496
497 // We didn't add the new Logger that we created above
498 // because another thread added a Logger with the same
499 // name after our null check above and before our call
500 // to addLogger(). We have to refetch the Logger because
501 // addLogger() returns a boolean instead of the Logger
502 // reference itself. However, if the thread that created
503 // the other Logger is not holding a strong reference to
504 // the other Logger, then it is possible for the other
505 // Logger to be GC'ed after we saw it in addLogger() and
506 // before we can refetch it. If it has been GC'ed then
507 // we'll just loop around and try again.
508 result = getLogger(name);
509 } while (result == null);
510 }
511 return result;
512 }
513
514 Logger demandSystemLogger(String name, String resourceBundleName) {
515 // Add a system logger in the system context's namespace
516 final Logger sysLogger = getSystemContext().demandLogger(name, resourceBundleName);
517
518 // Add the system logger to the LogManager's namespace if not exist
519 // so that there is only one single logger of the given name.
520 // System loggers are visible to applications unless a logger of
521 // the same name has been added.
522 Logger logger;
523 do {
524 // First attempt to call addLogger instead of getLogger
525 // This would avoid potential bug in custom LogManager.getLogger
526 // implementation that adds a logger if does not exist
527 if (addLogger(sysLogger)) {
528 // successfully added the new system logger
529 logger = sysLogger;
530 } else {
531 logger = getLogger(name);
532 }
533 } while (logger == null);
534
535 // LogManager will set the sysLogger's handlers via LogManager.addLogger method.
536 if (logger != sysLogger && sysLogger.getHandlers().length == 0) {
537 // if logger already exists but handlers not set
538 final Logger l = logger;
539 AccessController.doPrivileged(new PrivilegedAction<Void>() {
540 @Override
541 public Void run() {
542 for (Handler hdl : l.getHandlers()) {
543 sysLogger.addHandler(hdl);
544 }
545 return null;
546 }
547 });
548 }
549 return sysLogger;
550 }
551
552 // LoggerContext maintains the logger namespace per context.
553 // The default LogManager implementation has one system context and user
554 // context. The system context is used to maintain the namespace for
555 // all system loggers and is queried by the system code. If a system logger
556 // doesn't exist in the user context, it'll also be added to the user context.
557 // The user context is queried by the user code and all other loggers are
558 // added in the user context.
559 class LoggerContext {
560 // Table of named Loggers that maps names to Loggers.
561 private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
562 // Tree of named Loggers
563 private final LogNode root;
564 private LoggerContext() {
565 this.root = new LogNode(null, this);
566 }
567
568
569 // Tells whether default loggers are required in this context.
570 // If true, the default loggers will be lazily added.
571 final boolean requiresDefaultLoggers() {
572 final boolean requiresDefaultLoggers = (getOwner() == manager);
573 if (requiresDefaultLoggers) {
574 getOwner().ensureLogManagerInitialized();
575 }
576 return requiresDefaultLoggers;
577 }
578
579 // This context's LogManager.
580 final LogManager getOwner() {
581 return LogManager.this;
582 }
583
584 // This context owner's root logger, which if not null, and if
585 // the context requires default loggers, will be added to the context
586 // logger's tree.
587 final Logger getRootLogger() {
588 return getOwner().rootLogger;
589 }
590
591 // The global logger, which if not null, and if
592 // the context requires default loggers, will be added to the context
593 // logger's tree.
594 final Logger getGlobalLogger() {
595 @SuppressWarnings("deprecated") // avoids initialization cycles.
596 final Logger global = Logger.global;
597 return global;
598 }
599
600 Logger demandLogger(String name, String resourceBundleName) {
601 // a LogManager subclass may have its own implementation to add and
602 // get a Logger. So delegate to the LogManager to do the work.
603 final LogManager owner = getOwner();
604 return owner.demandLogger(name, resourceBundleName, null);
605 }
606
607
608 // Due to subtle deadlock issues getUserContext() no longer
609 // calls addLocalLogger(rootLogger);
610 // Therefore - we need to add the default loggers later on.
611 // Checks that the context is properly initialized
612 // This is necessary before calling e.g. find(name)
613 // or getLoggerNames()
614 //
615 private void ensureInitialized() {
616 if (requiresDefaultLoggers()) {
617 // Ensure that the root and global loggers are set.
618 ensureDefaultLogger(getRootLogger());
619 ensureDefaultLogger(getGlobalLogger());
620 }
621 }
622
623
624 synchronized Logger findLogger(String name) {
625 // ensure that this context is properly initialized before
626 // looking for loggers.
627 ensureInitialized();
628 LoggerWeakRef ref = namedLoggers.get(name);
629 if (ref == null) {
630 return null;
631 }
632 Logger logger = ref.get();
633 if (logger == null) {
634 // Hashtable holds stale weak reference
635 // to a logger which has been GC-ed.
636 removeLogger(name);
637 }
638 return logger;
639 }
640
641 // This method is called before adding a logger to the
642 // context.
643 // 'logger' is the context that will be added.
644 // This method will ensure that the defaults loggers are added
645 // before adding 'logger'.
646 //
647 private void ensureAllDefaultLoggers(Logger logger) {
648 if (requiresDefaultLoggers()) {
649 final String name = logger.getName();
650 if (!name.isEmpty()) {
651 ensureDefaultLogger(getRootLogger());
652 if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
653 ensureDefaultLogger(getGlobalLogger());
654 }
655 }
656 }
657 }
658
659 private void ensureDefaultLogger(Logger logger) {
660 // Used for lazy addition of root logger and global logger
661 // to a LoggerContext.
662
663 // This check is simple sanity: we do not want that this
664 // method be called for anything else than Logger.global
665 // or owner.rootLogger.
666 if (!requiresDefaultLoggers() || logger == null
667 || logger != Logger.global && logger != LogManager.this.rootLogger) {
668
669 // the case where we have a non null logger which is neither
670 // Logger.global nor manager.rootLogger indicates a serious
671 // issue - as ensureDefaultLogger should never be called
672 // with any other loggers than one of these two (or null - if
673 // e.g manager.rootLogger is not yet initialized)...
674 assert logger == null;
675
676 return;
677 }
678
679 // Adds the logger if it's not already there.
680 if (!namedLoggers.containsKey(logger.getName())) {
681 // It is important to prevent addLocalLogger to
682 // call ensureAllDefaultLoggers when we're in the process
683 // off adding one of those default loggers - as this would
684 // immediately cause a stack overflow.
685 // Therefore we must pass addDefaultLoggersIfNeeded=false,
686 // even if requiresDefaultLoggers is true.
687 addLocalLogger(logger, false);
688 }
689 }
690
691 boolean addLocalLogger(Logger logger) {
692 // no need to add default loggers if it's not required
693 return addLocalLogger(logger, requiresDefaultLoggers());
694 }
695
696 // Add a logger to this context. This method will only set its level
697 // and process parent loggers. It doesn't set its handlers.
698 synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) {
699 // addDefaultLoggersIfNeeded serves to break recursion when adding
700 // default loggers. If we're adding one of the default loggers
701 // (we're being called from ensureDefaultLogger()) then
702 // addDefaultLoggersIfNeeded will be false: we don't want to
703 // call ensureAllDefaultLoggers again.
704 //
705 // Note: addDefaultLoggersIfNeeded can also be false when
706 // requiresDefaultLoggers is false - since calling
707 // ensureAllDefaultLoggers would have no effect in this case.
708 if (addDefaultLoggersIfNeeded) {
709 ensureAllDefaultLoggers(logger);
710 }
711
712 final String name = logger.getName();
713 if (name == null) {
714 throw new NullPointerException();
715 }
716 LoggerWeakRef ref = namedLoggers.get(name);
717 if (ref != null) {
718 if (ref.get() == null) {
719 // It's possible that the Logger was GC'ed after a
720 // drainLoggerRefQueueBounded() call above so allow
721 // a new one to be registered.
722 removeLogger(name);
723 } else {
724 // We already have a registered logger with the given name.
725 return false;
726 }
727 }
728
729 // We're adding a new logger.
730 // Note that we are creating a weak reference here.
731 final LogManager owner = getOwner();
732 logger.setLogManager(owner);
733 ref = owner.new LoggerWeakRef(logger);
734 namedLoggers.put(name, ref);
735
736 // Apply any initial level defined for the new logger.
737 Level level = owner.getLevelProperty(name + ".level", null);
738 if (level != null) {
739 doSetLevel(logger, level);
740 }
741
742 // instantiation of the handler is done in the LogManager.addLogger
743 // implementation as a handler class may be only visible to LogManager
744 // subclass for the custom log manager case
745 processParentHandlers(logger, name);
746
747 // Find the new node and its parent.
748 LogNode node = getNode(name);
749 node.loggerRef = ref;
750 Logger parent = null;
751 LogNode nodep = node.parent;
752 while (nodep != null) {
753 LoggerWeakRef nodeRef = nodep.loggerRef;
754 if (nodeRef != null) {
755 parent = nodeRef.get();
756 if (parent != null) {
757 break;
769 ref.setNode(node);
770 return true;
771 }
772
773 // note: all calls to removeLogger are synchronized on LogManager's
774 // intrinsic lock
775 void removeLogger(String name) {
776 namedLoggers.remove(name);
777 }
778
779 synchronized Enumeration<String> getLoggerNames() {
780 // ensure that this context is properly initialized before
781 // returning logger names.
782 ensureInitialized();
783 return namedLoggers.keys();
784 }
785
786 // If logger.getUseParentHandlers() returns 'true' and any of the logger's
787 // parents have levels or handlers defined, make sure they are instantiated.
788 private void processParentHandlers(final Logger logger, final String name) {
789 final LogManager owner = getOwner();
790 AccessController.doPrivileged(new PrivilegedAction<Void>() {
791 @Override
792 public Void run() {
793 if (logger != owner.rootLogger) {
794 boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
795 if (!useParent) {
796 logger.setUseParentHandlers(false);
797 }
798 }
799 return null;
800 }
801 });
802
803 int ix = 1;
804 for (;;) {
805 int ix2 = name.indexOf(".", ix);
806 if (ix2 < 0) {
807 break;
808 }
809 String pname = name.substring(0, ix2);
810 if (owner.getProperty(pname + ".level") != null ||
811 owner.getProperty(pname + ".handlers") != null) {
812 // This pname has a level/handlers definition.
813 // Make sure it exists.
814 demandLogger(pname, null);
815 }
816 ix = ix2+1;
817 }
818 }
819
820 // Gets a node in our tree of logger nodes.
821 // If necessary, create it.
822 LogNode getNode(String name) {
823 if (name == null || name.equals("")) {
824 return root;
825 }
826 LogNode node = root;
827 while (name.length() > 0) {
828 int ix = name.indexOf(".");
829 String head;
830 if (ix > 0) {
831 head = name.substring(0, ix);
832 name = name.substring(ix + 1);
833 } else {
834 head = name;
835 name = "";
836 }
837 if (node.children == null) {
838 node.children = new HashMap<>();
839 }
840 LogNode child = node.children.get(head);
841 if (child == null) {
842 child = new LogNode(node, this);
843 node.children.put(head, child);
844 }
845 node = child;
846 }
847 return node;
848 }
849 }
850
851 final class SystemLoggerContext extends LoggerContext {
852 // Add a system logger in the system context's namespace as well as
853 // in the LogManager's namespace if not exist so that there is only
854 // one single logger of the given name. System loggers are visible
855 // to applications unless a logger of the same name has been added.
856 @Override
857 Logger demandLogger(String name, String resourceBundleName) {
858 Logger result = findLogger(name);
859 if (result == null) {
860 // only allocate the new system logger once
861 Logger newLogger = new Logger(name, resourceBundleName, null, getOwner());
862 do {
863 if (addLocalLogger(newLogger)) {
864 // We successfully added the new Logger that we
865 // created above so return it without refetching.
866 result = newLogger;
867 } else {
868 // We didn't add the new Logger that we created above
869 // because another thread added a Logger with the same
870 // name after our null check above and before our call
871 // to addLogger(). We have to refetch the Logger because
872 // addLogger() returns a boolean instead of the Logger
873 // reference itself. However, if the thread that created
874 // the other Logger is not holding a strong reference to
875 // the other Logger, then it is possible for the other
876 // Logger to be GC'ed after we saw it in addLogger() and
877 // before we can refetch it. If it has been GC'ed then
878 // we'll just loop around and try again.
879 result = findLogger(name);
880 }
881 } while (result == null);
882 }
883 return result;
884 }
885 }
886
887 // Add new per logger handlers.
888 // We need to raise privilege here. All our decisions will
889 // be made based on the logging configuration, which can
890 // only be modified by trusted code.
891 private void loadLoggerHandlers(final Logger logger, final String name,
892 final String handlersPropertyName)
893 {
894 AccessController.doPrivileged(new PrivilegedAction<Object>() {
895 @Override
896 public Object run() {
897 String names[] = parseClassNames(handlersPropertyName);
898 for (int i = 0; i < names.length; i++) {
899 String word = names[i];
900 try {
901 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
902 Handler hdl = (Handler) clz.newInstance();
903 // Check if there is a property defining the
904 // this handler's level.
905 String levs = getProperty(word + ".level");
906 if (levs != null) {
907 Level l = Level.findLevel(levs);
908 if (l != null) {
909 hdl.setLevel(l);
910 } else {
911 // Probably a bad level. Drop through.
912 System.err.println("Can't set level for " + word);
913 }
914 }
915 // Add this Handler to the logger
1068 // Note: this will add a 200ms penalty
1069 loadLoggerHandlers(logger, name, name + ".handlers");
1070 return true;
1071 } else {
1072 return false;
1073 }
1074 }
1075
1076 // Private method to set a level on a logger.
1077 // If necessary, we raise privilege before doing the call.
1078 private static void doSetLevel(final Logger logger, final Level level) {
1079 SecurityManager sm = System.getSecurityManager();
1080 if (sm == null) {
1081 // There is no security manager, so things are easy.
1082 logger.setLevel(level);
1083 return;
1084 }
1085 // There is a security manager. Raise privilege before
1086 // calling setLevel.
1087 AccessController.doPrivileged(new PrivilegedAction<Object>() {
1088 @Override
1089 public Object run() {
1090 logger.setLevel(level);
1091 return null;
1092 }});
1093 }
1094
1095 // Private method to set a parent on a logger.
1096 // If necessary, we raise privilege before doing the setParent call.
1097 private static void doSetParent(final Logger logger, final Logger parent) {
1098 SecurityManager sm = System.getSecurityManager();
1099 if (sm == null) {
1100 // There is no security manager, so things are easy.
1101 logger.setParent(parent);
1102 return;
1103 }
1104 // There is a security manager. Raise privilege before
1105 // calling setParent.
1106 AccessController.doPrivileged(new PrivilegedAction<Object>() {
1107 @Override
1108 public Object run() {
1109 logger.setParent(parent);
1110 return null;
1111 }});
1112 }
1113
1114 /**
1115 * Method to find a named logger.
1116 * <p>
1117 * Note that since untrusted code may create loggers with
1118 * arbitrary names this method should not be relied on to
1119 * find Loggers for security sensitive logging.
1120 * It is also important to note that the Logger associated with the
1121 * String {@code name} may be garbage collected at any time if there
1122 * is no strong reference to the Logger. The caller of this method
1123 * must check the return value for null in order to properly handle
1124 * the case where the Logger has been garbage collected.
1125 * <p>
1126 * @param name name of the logger
1127 * @return matching logger or null if none is found
1185 clz.newInstance();
1186 return;
1187 }
1188 } catch (Exception ex) {
1189 System.err.println("Logging configuration class \"" + cname + "\" failed");
1190 System.err.println("" + ex);
1191 // keep going and useful config file.
1192 }
1193 }
1194
1195 String fname = System.getProperty("java.util.logging.config.file");
1196 if (fname == null) {
1197 fname = System.getProperty("java.home");
1198 if (fname == null) {
1199 throw new Error("Can't find java.home ??");
1200 }
1201 File f = new File(fname, "lib");
1202 f = new File(f, "logging.properties");
1203 fname = f.getCanonicalPath();
1204 }
1205 final InputStream in = new FileInputStream(fname);
1206 final BufferedInputStream bin = new BufferedInputStream(in);
1207 try {
1208 readConfiguration(bin);
1209 } finally {
1210 in.close();
1211 }
1212 }
1213
1214 /**
1215 * Reset the logging configuration.
1216 * <p>
1217 * For all named loggers, the reset operation removes and closes
1218 * all Handlers and (except for the root logger) sets the level
1219 * to null. The root logger's level is set to Level.INFO.
1220 *
1221 * @exception SecurityException if a security manager exists and if
1222 * the caller does not have LoggingPermission("control").
1223 */
1224
1225 public void reset() throws SecurityException {
1226 checkPermission();
1227 synchronized (this) {
1228 props = new Properties();
1229 // Since we are doing a reset we no longer want to initialize
1230 // the global handlers, if they haven't been initialized yet.
1231 initializedGlobalHandlers = true;
1232 }
1255 // Problems closing a handler? Keep going...
1256 }
1257 }
1258 String name = logger.getName();
1259 if (name != null && name.equals("")) {
1260 // This is the root logger.
1261 logger.setLevel(defaultLevel);
1262 } else {
1263 logger.setLevel(null);
1264 }
1265 }
1266
1267 // get a list of whitespace separated classnames from a property.
1268 private String[] parseClassNames(String propertyName) {
1269 String hands = getProperty(propertyName);
1270 if (hands == null) {
1271 return new String[0];
1272 }
1273 hands = hands.trim();
1274 int ix = 0;
1275 final List<String> result = new ArrayList<>();
1276 while (ix < hands.length()) {
1277 int end = ix;
1278 while (end < hands.length()) {
1279 if (Character.isWhitespace(hands.charAt(end))) {
1280 break;
1281 }
1282 if (hands.charAt(end) == ',') {
1283 break;
1284 }
1285 end++;
1286 }
1287 String word = hands.substring(ix, end);
1288 ix = end+1;
1289 word = word.trim();
1290 if (word.length() == 0) {
1291 continue;
1292 }
1293 result.add(word);
1294 }
1295 return result.toArray(new String[result.size()]);
1525 if (children == null) {
1526 return;
1527 }
1528 Iterator<LogNode> values = children.values().iterator();
1529 while (values.hasNext()) {
1530 LogNode node = values.next();
1531 LoggerWeakRef ref = node.loggerRef;
1532 Logger logger = (ref == null) ? null : ref.get();
1533 if (logger == null) {
1534 node.walkAndSetParent(parent);
1535 } else {
1536 doSetParent(logger, parent);
1537 }
1538 }
1539 }
1540 }
1541
1542 // We use a subclass of Logger for the root logger, so
1543 // that we only instantiate the global handlers when they
1544 // are first needed.
1545 private final class RootLogger extends Logger {
1546 private RootLogger() {
1547 // We do not the protected Logger two args constructor here,
1548 // to avoid calling LOgManager.getLogManager() from within the
1549 // RootLogger constructor.
1550 super("", null, null, LogManager.this);
1551 setLevel(defaultLevel);
1552 }
1553
1554 @Override
1555 public void log(LogRecord record) {
1556 // Make sure that the global handlers have been instantiated.
1557 initializeGlobalHandlers();
1558 super.log(record);
1559 }
1560
1561 @Override
1562 public void addHandler(Handler h) {
1563 initializeGlobalHandlers();
1564 super.addHandler(h);
1565 }
1566
1567 @Override
1568 public void removeHandler(Handler h) {
1569 initializeGlobalHandlers();
1570 super.removeHandler(h);
1571 }
1572
1573 @Override
1574 public Handler[] getHandlers() {
1575 initializeGlobalHandlers();
1576 return super.getHandlers();
1577 }
1578 }
1579
1580
1581 // Private method to be called when the configuration has
1582 // changed to apply any level settings to any pre-existing loggers.
1583 synchronized private void setLevelsOnExistingLoggers() {
1584 Enumeration<?> enum_ = props.propertyNames();
1585 while (enum_.hasMoreElements()) {
1586 String key = (String)enum_.nextElement();
1587 if (!key.endsWith(".level")) {
1588 // Not a level definition.
1589 continue;
1590 }
1591 int ix = key.length() - 6;
1592 String name = key.substring(0, ix);
1593 Level level = getLevelProperty(key, null);
|