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 // non final field - make it volatile to make sure that other threads
160 // will see the new value once ensureLogManagerInitialized() has finished
161 // executing.
162 private volatile Logger rootLogger;
163 // Have we done the primordial reading of the configuration file?
164 // (Must be done after a suitable amount of java.lang.System
165 // initialization has been done)
166 private volatile boolean readPrimordialConfiguration;
167 // Have we initialized global (root) handlers yet?
168 // This gets set to false in readConfiguration
169 private boolean initializedGlobalHandlers = true;
653 if (requiresDefaultLoggers()) {
654 // Ensure that the root and global loggers are set.
655 ensureDefaultLogger(getRootLogger());
656 ensureDefaultLogger(getGlobalLogger());
657 }
658 }
659
660
661 synchronized Logger findLogger(String name) {
662 // ensure that this context is properly initialized before
663 // looking for loggers.
664 ensureInitialized();
665 LoggerWeakRef ref = namedLoggers.get(name);
666 if (ref == null) {
667 return null;
668 }
669 Logger logger = ref.get();
670 if (logger == null) {
671 // Hashtable holds stale weak reference
672 // to a logger which has been GC-ed.
673 removeLogger(name);
674 }
675 return logger;
676 }
677
678 // This method is called before adding a logger to the
679 // context.
680 // 'logger' is the context that will be added.
681 // This method will ensure that the defaults loggers are added
682 // before adding 'logger'.
683 //
684 private void ensureAllDefaultLoggers(Logger logger) {
685 if (requiresDefaultLoggers()) {
686 final String name = logger.getName();
687 if (!name.isEmpty()) {
688 ensureDefaultLogger(getRootLogger());
689 if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
690 ensureDefaultLogger(getGlobalLogger());
691 }
692 }
693 }
739 // addDefaultLoggersIfNeeded will be false: we don't want to
740 // call ensureAllDefaultLoggers again.
741 //
742 // Note: addDefaultLoggersIfNeeded can also be false when
743 // requiresDefaultLoggers is false - since calling
744 // ensureAllDefaultLoggers would have no effect in this case.
745 if (addDefaultLoggersIfNeeded) {
746 ensureAllDefaultLoggers(logger);
747 }
748
749 final String name = logger.getName();
750 if (name == null) {
751 throw new NullPointerException();
752 }
753 LoggerWeakRef ref = namedLoggers.get(name);
754 if (ref != null) {
755 if (ref.get() == null) {
756 // It's possible that the Logger was GC'ed after a
757 // drainLoggerRefQueueBounded() call above so allow
758 // a new one to be registered.
759 removeLogger(name);
760 } else {
761 // We already have a registered logger with the given name.
762 return false;
763 }
764 }
765
766 // We're adding a new logger.
767 // Note that we are creating a weak reference here.
768 final LogManager owner = getOwner();
769 logger.setLogManager(owner);
770 ref = owner.new LoggerWeakRef(logger);
771 namedLoggers.put(name, ref);
772
773 // Apply any initial level defined for the new logger, unless
774 // the logger's level is already initialized
775 Level level = owner.getLevelProperty(name + ".level", null);
776 if (level != null && !logger.isLevelInitialized()) {
777 doSetLevel(logger, level);
778 }
779
791 LoggerWeakRef nodeRef = nodep.loggerRef;
792 if (nodeRef != null) {
793 parent = nodeRef.get();
794 if (parent != null) {
795 break;
796 }
797 }
798 nodep = nodep.parent;
799 }
800
801 if (parent != null) {
802 doSetParent(logger, parent);
803 }
804 // Walk over the children and tell them we are their new parent.
805 node.walkAndSetParent(logger);
806 // new LogNode is ready so tell the LoggerWeakRef about it
807 ref.setNode(node);
808 return true;
809 }
810
811 // note: all calls to removeLogger are synchronized on LogManager's
812 // intrinsic lock
813 void removeLogger(String name) {
814 namedLoggers.remove(name);
815 }
816
817 synchronized Enumeration<String> getLoggerNames() {
818 // ensure that this context is properly initialized before
819 // returning logger names.
820 ensureInitialized();
821 return namedLoggers.keys();
822 }
823
824 // If logger.getUseParentHandlers() returns 'true' and any of the logger's
825 // parents have levels or handlers defined, make sure they are instantiated.
826 private void processParentHandlers(final Logger logger, final String name) {
827 final LogManager owner = getOwner();
828 AccessController.doPrivileged(new PrivilegedAction<Void>() {
829 @Override
830 public Void run() {
831 if (logger != owner.rootLogger) {
832 boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
833 if (!useParent) {
834 logger.setUseParentHandlers(false);
976 // - has weak references to all named Loggers
977 // - namedLoggers keeps the LoggerWeakRef objects for the named
978 // Loggers around until we can deal with the book keeping for
979 // the named Logger that is being GC'ed.
980 // LogManager.LogNode.loggerRef
981 // - has a weak reference to a named Logger
982 // - the LogNode will also keep the LoggerWeakRef objects for
983 // the named Loggers around; currently LogNodes never go away.
984 // Logger.kids
985 // - has a weak reference to each direct child Logger; this
986 // includes anonymous and named Loggers
987 // - anonymous Loggers are always children of the rootLogger
988 // which is a strong reference; rootLogger.kids keeps the
989 // LoggerWeakRef objects for the anonymous Loggers around
990 // until we can deal with the book keeping.
991 //
992 final class LoggerWeakRef extends WeakReference<Logger> {
993 private String name; // for namedLoggers cleanup
994 private LogNode node; // for loggerRef cleanup
995 private WeakReference<Logger> parentRef; // for kids cleanup
996
997 LoggerWeakRef(Logger logger) {
998 super(logger, loggerRefQueue);
999
1000 name = logger.getName(); // save for namedLoggers cleanup
1001 }
1002
1003 // dispose of this LoggerWeakRef object
1004 void dispose() {
1005 if (node != null) {
1006 // if we have a LogNode, then we were a named Logger
1007 // so clear namedLoggers weak ref to us
1008 node.context.removeLogger(name);
1009 name = null; // clear our ref to the Logger's name
1010
1011 node.loggerRef = null; // clear LogNode's weak ref to us
1012 node = null; // clear our ref to LogNode
1013 }
1014
1015 if (parentRef != null) {
1016 // this LoggerWeakRef has or had a parent Logger
1017 Logger parent = parentRef.get();
1018 if (parent != null) {
1019 // the parent Logger is still there so clear the
1020 // parent Logger's weak ref to us
1021 parent.removeChildLogger(this);
1022 }
1023 parentRef = null; // clear our weak ref to the parent Logger
1024 }
1025 }
1026
1027 // set the node field to the specified value
1028 void setNode(LogNode node) {
1045 // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives
1046 // us about a 50/50 mix in increased weak ref counts versus
1047 // decreased weak ref counts in the AnonLoggerWeakRefLeak test.
1048 // Here are stats for cleaning up sets of 400 anonymous Loggers:
1049 // - test duration 1 minute
1050 // - sample size of 125 sets of 400
1051 // - average: 1.99 ms
1052 // - minimum: 0.57 ms
1053 // - maximum: 25.3 ms
1054 //
1055 // The same config gives us a better decreased weak ref count
1056 // than increased weak ref count in the LoggerWeakRefLeak test.
1057 // Here are stats for cleaning up sets of 400 named Loggers:
1058 // - test duration 2 minutes
1059 // - sample size of 506 sets of 400
1060 // - average: 0.57 ms
1061 // - minimum: 0.02 ms
1062 // - maximum: 10.9 ms
1063 //
1064 private final static int MAX_ITERATIONS = 400;
1065 final synchronized void drainLoggerRefQueueBounded() {
1066 for (int i = 0; i < MAX_ITERATIONS; i++) {
1067 if (loggerRefQueue == null) {
1068 // haven't finished loading LogManager yet
1069 break;
1070 }
1071
1072 LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll();
1073 if (ref == null) {
1074 break;
1075 }
1076 // a Logger object has been GC'ed so clean it up
1077 ref.dispose();
1078 }
1079 }
1080
1081 /**
1082 * Add a named logger. This does nothing and returns false if a logger
1083 * with the same name is already registered.
1084 * <p>
1085 * The Logger factory methods call this method to register each
|
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 // 'props' is assigned within a lock but accessed without it.
150 // Declaring it volatile makes sure that another thread will not
151 // be able to see a partially constructed 'props' object.
152 // (seeing a partially constructed 'props' object can result in
153 // NPE being thrown in Hashtable.get(), because it leaves the door
154 // open for props.getProperties() to be called before the construcor
155 // of Hashtable is actually completed).
156 private volatile Properties props = new Properties();
157 private final static Level defaultLevel = Level.INFO;
158
159 // The map of the registered listeners. The map value is the registration
160 // count to allow for cases where the same listener is registered many times.
161 private final Map<Object,Integer> listenerMap = new HashMap<>();
162
163 // LoggerContext for system loggers and user loggers
164 private final LoggerContext systemContext = new SystemLoggerContext();
165 private final LoggerContext userContext = new LoggerContext();
166 // non final field - make it volatile to make sure that other threads
167 // will see the new value once ensureLogManagerInitialized() has finished
168 // executing.
169 private volatile Logger rootLogger;
170 // Have we done the primordial reading of the configuration file?
171 // (Must be done after a suitable amount of java.lang.System
172 // initialization has been done)
173 private volatile boolean readPrimordialConfiguration;
174 // Have we initialized global (root) handlers yet?
175 // This gets set to false in readConfiguration
176 private boolean initializedGlobalHandlers = true;
660 if (requiresDefaultLoggers()) {
661 // Ensure that the root and global loggers are set.
662 ensureDefaultLogger(getRootLogger());
663 ensureDefaultLogger(getGlobalLogger());
664 }
665 }
666
667
668 synchronized Logger findLogger(String name) {
669 // ensure that this context is properly initialized before
670 // looking for loggers.
671 ensureInitialized();
672 LoggerWeakRef ref = namedLoggers.get(name);
673 if (ref == null) {
674 return null;
675 }
676 Logger logger = ref.get();
677 if (logger == null) {
678 // Hashtable holds stale weak reference
679 // to a logger which has been GC-ed.
680 ref.dispose();
681 }
682 return logger;
683 }
684
685 // This method is called before adding a logger to the
686 // context.
687 // 'logger' is the context that will be added.
688 // This method will ensure that the defaults loggers are added
689 // before adding 'logger'.
690 //
691 private void ensureAllDefaultLoggers(Logger logger) {
692 if (requiresDefaultLoggers()) {
693 final String name = logger.getName();
694 if (!name.isEmpty()) {
695 ensureDefaultLogger(getRootLogger());
696 if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
697 ensureDefaultLogger(getGlobalLogger());
698 }
699 }
700 }
746 // addDefaultLoggersIfNeeded will be false: we don't want to
747 // call ensureAllDefaultLoggers again.
748 //
749 // Note: addDefaultLoggersIfNeeded can also be false when
750 // requiresDefaultLoggers is false - since calling
751 // ensureAllDefaultLoggers would have no effect in this case.
752 if (addDefaultLoggersIfNeeded) {
753 ensureAllDefaultLoggers(logger);
754 }
755
756 final String name = logger.getName();
757 if (name == null) {
758 throw new NullPointerException();
759 }
760 LoggerWeakRef ref = namedLoggers.get(name);
761 if (ref != null) {
762 if (ref.get() == null) {
763 // It's possible that the Logger was GC'ed after a
764 // drainLoggerRefQueueBounded() call above so allow
765 // a new one to be registered.
766 ref.dispose();
767 } else {
768 // We already have a registered logger with the given name.
769 return false;
770 }
771 }
772
773 // We're adding a new logger.
774 // Note that we are creating a weak reference here.
775 final LogManager owner = getOwner();
776 logger.setLogManager(owner);
777 ref = owner.new LoggerWeakRef(logger);
778 namedLoggers.put(name, ref);
779
780 // Apply any initial level defined for the new logger, unless
781 // the logger's level is already initialized
782 Level level = owner.getLevelProperty(name + ".level", null);
783 if (level != null && !logger.isLevelInitialized()) {
784 doSetLevel(logger, level);
785 }
786
798 LoggerWeakRef nodeRef = nodep.loggerRef;
799 if (nodeRef != null) {
800 parent = nodeRef.get();
801 if (parent != null) {
802 break;
803 }
804 }
805 nodep = nodep.parent;
806 }
807
808 if (parent != null) {
809 doSetParent(logger, parent);
810 }
811 // Walk over the children and tell them we are their new parent.
812 node.walkAndSetParent(logger);
813 // new LogNode is ready so tell the LoggerWeakRef about it
814 ref.setNode(node);
815 return true;
816 }
817
818 synchronized void removeLoggerRef(String name, LoggerWeakRef ref) {
819 namedLoggers.remove(name, ref);
820 }
821
822 synchronized Enumeration<String> getLoggerNames() {
823 // ensure that this context is properly initialized before
824 // returning logger names.
825 ensureInitialized();
826 return namedLoggers.keys();
827 }
828
829 // If logger.getUseParentHandlers() returns 'true' and any of the logger's
830 // parents have levels or handlers defined, make sure they are instantiated.
831 private void processParentHandlers(final Logger logger, final String name) {
832 final LogManager owner = getOwner();
833 AccessController.doPrivileged(new PrivilegedAction<Void>() {
834 @Override
835 public Void run() {
836 if (logger != owner.rootLogger) {
837 boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
838 if (!useParent) {
839 logger.setUseParentHandlers(false);
981 // - has weak references to all named Loggers
982 // - namedLoggers keeps the LoggerWeakRef objects for the named
983 // Loggers around until we can deal with the book keeping for
984 // the named Logger that is being GC'ed.
985 // LogManager.LogNode.loggerRef
986 // - has a weak reference to a named Logger
987 // - the LogNode will also keep the LoggerWeakRef objects for
988 // the named Loggers around; currently LogNodes never go away.
989 // Logger.kids
990 // - has a weak reference to each direct child Logger; this
991 // includes anonymous and named Loggers
992 // - anonymous Loggers are always children of the rootLogger
993 // which is a strong reference; rootLogger.kids keeps the
994 // LoggerWeakRef objects for the anonymous Loggers around
995 // until we can deal with the book keeping.
996 //
997 final class LoggerWeakRef extends WeakReference<Logger> {
998 private String name; // for namedLoggers cleanup
999 private LogNode node; // for loggerRef cleanup
1000 private WeakReference<Logger> parentRef; // for kids cleanup
1001 private boolean disposed = false; // avoid calling dispose twice
1002
1003 LoggerWeakRef(Logger logger) {
1004 super(logger, loggerRefQueue);
1005
1006 name = logger.getName(); // save for namedLoggers cleanup
1007 }
1008
1009 // dispose of this LoggerWeakRef object
1010 void dispose() {
1011 // Avoid calling dispose twice. When a Logger is gc'ed, its
1012 // LoggerWeakRef will be enqueued.
1013 // However, a new logger of the same name may be added (or looked
1014 // up) before the queue is drained. When that happens, dispose()
1015 // will be called by addLocalLogger() or findLogger().
1016 // Later when the queue is drained, dispose() will be called again
1017 // for the same LoggerWeakRef. Marking LoggerWeakRef as disposed
1018 // avoids processing the data twice (even though the code should
1019 // now be reentrant.
1020 synchronized(this) {
1021 // Note to maintainers:
1022 // Be careful not to call any method that tries to acquire
1023 // another lock from within this block - as this would surely
1024 // lead to deadlocks, given that dispose() can be called by
1025 // multiple threads, and from within different synchronized
1026 // methods/blocks.
1027 if (disposed) return;
1028 disposed = true;
1029 }
1030
1031 if (node != null) {
1032 // if we have a LogNode, then we were a named Logger
1033 // so clear namedLoggers weak ref to us
1034 node.context.removeLoggerRef(name, this);
1035 name = null; // clear our ref to the Logger's name
1036
1037 node.loggerRef = null; // clear LogNode's weak ref to us
1038 node = null; // clear our ref to LogNode
1039 }
1040
1041 if (parentRef != null) {
1042 // this LoggerWeakRef has or had a parent Logger
1043 Logger parent = parentRef.get();
1044 if (parent != null) {
1045 // the parent Logger is still there so clear the
1046 // parent Logger's weak ref to us
1047 parent.removeChildLogger(this);
1048 }
1049 parentRef = null; // clear our weak ref to the parent Logger
1050 }
1051 }
1052
1053 // set the node field to the specified value
1054 void setNode(LogNode node) {
1071 // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives
1072 // us about a 50/50 mix in increased weak ref counts versus
1073 // decreased weak ref counts in the AnonLoggerWeakRefLeak test.
1074 // Here are stats for cleaning up sets of 400 anonymous Loggers:
1075 // - test duration 1 minute
1076 // - sample size of 125 sets of 400
1077 // - average: 1.99 ms
1078 // - minimum: 0.57 ms
1079 // - maximum: 25.3 ms
1080 //
1081 // The same config gives us a better decreased weak ref count
1082 // than increased weak ref count in the LoggerWeakRefLeak test.
1083 // Here are stats for cleaning up sets of 400 named Loggers:
1084 // - test duration 2 minutes
1085 // - sample size of 506 sets of 400
1086 // - average: 0.57 ms
1087 // - minimum: 0.02 ms
1088 // - maximum: 10.9 ms
1089 //
1090 private final static int MAX_ITERATIONS = 400;
1091 final void drainLoggerRefQueueBounded() {
1092 for (int i = 0; i < MAX_ITERATIONS; i++) {
1093 if (loggerRefQueue == null) {
1094 // haven't finished loading LogManager yet
1095 break;
1096 }
1097
1098 LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll();
1099 if (ref == null) {
1100 break;
1101 }
1102 // a Logger object has been GC'ed so clean it up
1103 ref.dispose();
1104 }
1105 }
1106
1107 /**
1108 * Add a named logger. This does nothing and returns false if a logger
1109 * with the same name is already registered.
1110 * <p>
1111 * The Logger factory methods call this method to register each
|