15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26
27 package java.util.logging;
28
29 import java.io.*;
30 import java.util.*;
31 import java.security.*;
32 import java.lang.ref.ReferenceQueue;
33 import java.lang.ref.WeakReference;
34 import java.util.concurrent.ConcurrentHashMap;
35 import java.util.concurrent.CopyOnWriteArrayList;
36 import java.util.concurrent.locks.ReentrantLock;
37 import sun.misc.JavaAWTAccess;
38 import sun.misc.ManagedLocalsThread;
39 import sun.misc.SharedSecrets;
40
41 /**
42 * There is a single global LogManager object that is used to
43 * maintain a set of shared state about Loggers and log services.
44 * <p>
45 * This LogManager object:
46 * <ul>
47 * <li> Manages a hierarchical namespace of Logger objects. All
48 * named Loggers are stored in this namespace.
49 * <li> Manages a set of logging control properties. These are
50 * simple key-value pairs that can be used by Handlers and
51 * other logging objects to configure themselves.
52 * </ul>
53 * <p>
54 * The global LogManager object can be retrieved using LogManager.getLogManager().
55 * The LogManager object is created during class initialization and
56 * cannot subsequently be changed.
57 * <p>
58 * At startup the LogManager class is located using the
59 * java.util.logging.manager system property.
60 * <p>
61 * The LogManager defines two optional system properties that allow control over
62 * the initial configuration:
63 * <ul>
64 * <li>"java.util.logging.config.class"
65 * <li>"java.util.logging.config.file"
66 * </ul>
67 * These two properties may be specified on the command line to the "java"
68 * command, or as system property definitions passed to JNI_CreateJavaVM.
69 * <p>
70 * If the "java.util.logging.config.class" property is set, then the
71 * property value is treated as a class name. The given class will be
72 * loaded, an object will be instantiated, and that object's constructor
73 * is responsible for reading in the initial configuration. (That object
74 * may use other system properties to control its configuration.) The
75 * alternate configuration class can use {@code readConfiguration(InputStream)}
76 * to define properties in the LogManager.
77 * <p>
78 * If "java.util.logging.config.class" property is <b>not</b> set,
79 * then the "java.util.logging.config.file" system property can be used
80 * to specify a properties file (in java.util.Properties format). The
81 * initial logging configuration will be read from this file.
82 * <p>
83 * If neither of these properties is defined then the LogManager uses its
84 * default configuration. The default configuration is typically loaded from the
85 * properties file "{@code conf/logging.properties}" in the Java installation
86 * directory.
87 * <p>
88 * The properties for loggers and Handlers will have names starting
89 * with the dot-separated name for the handler or logger.
90 * <p>
91 * The global logging properties may include:
92 * <ul>
93 * <li>A property "handlers". This defines a whitespace or comma separated
94 * list of class names for handler classes to load and register as
95 * handlers on the root Logger (the Logger named ""). Each class
96 * name must be for a Handler class which has a default constructor.
97 * Note that these Handlers may be created lazily, when they are
98 * first used.
99 *
100 * <li>A property "<logger>.handlers". This defines a whitespace or
101 * comma separated list of class names for handlers classes to
102 * load and register as handlers to the specified logger. Each class
103 * name must be for a Handler class which has a default constructor.
104 * Note that these Handlers may be created lazily, when they are
105 * first used.
106 *
107 * <li>A property "<logger>.handlers.ensureCloseOnReset". This defines a
108 * a boolean value. If "<logger>.handlers" is not defined or is empty,
109 * this property is ignored. Otherwise it defaults to {@code true}. When the
110 * value is {@code true}, the handlers associated with the logger are guaranteed
771 return false;
772 }
773 }
774
775 // We're adding a new logger.
776 // Note that we are creating a weak reference here.
777 final LogManager owner = getOwner();
778 logger.setLogManager(owner);
779 ref = owner.new LoggerWeakRef(logger);
780
781 // Apply any initial level defined for the new logger, unless
782 // the logger's level is already initialized
783 Level level = owner.getLevelProperty(name + ".level", null);
784 if (level != null && !logger.isLevelInitialized()) {
785 doSetLevel(logger, level);
786 }
787
788 // instantiation of the handler is done in the LogManager.addLogger
789 // implementation as a handler class may be only visible to LogManager
790 // subclass for the custom log manager case
791 processParentHandlers(logger, name);
792
793 // Find the new node and its parent.
794 LogNode node = getNode(name);
795 node.loggerRef = ref;
796 Logger parent = null;
797 LogNode nodep = node.parent;
798 while (nodep != null) {
799 LoggerWeakRef nodeRef = nodep.loggerRef;
800 if (nodeRef != null) {
801 parent = nodeRef.get();
802 if (parent != null) {
803 break;
804 }
805 }
806 nodep = nodep.parent;
807 }
808
809 if (parent != null) {
810 doSetParent(logger, parent);
811 }
819 // soon as it is published in namedLoggers (findLogger takes
820 // benefit of the ConcurrentHashMap implementation of namedLoggers
821 // to avoid synchronizing on retrieval when that is possible).
822 namedLoggers.put(name, ref);
823 return true;
824 }
825
826 void removeLoggerRef(String name, LoggerWeakRef ref) {
827 namedLoggers.remove(name, ref);
828 }
829
830 synchronized Enumeration<String> getLoggerNames() {
831 // ensure that this context is properly initialized before
832 // returning logger names.
833 ensureInitialized();
834 return Collections.enumeration(namedLoggers.keySet());
835 }
836
837 // If logger.getUseParentHandlers() returns 'true' and any of the logger's
838 // parents have levels or handlers defined, make sure they are instantiated.
839 private void processParentHandlers(final Logger logger, final String name) {
840 final LogManager owner = getOwner();
841 AccessController.doPrivileged(new PrivilegedAction<Void>() {
842 @Override
843 public Void run() {
844 if (logger != owner.rootLogger) {
845 boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
846 if (!useParent) {
847 logger.setUseParentHandlers(false);
848 }
849 }
850 return null;
851 }
852 });
853
854 int ix = 1;
855 for (;;) {
856 int ix2 = name.indexOf('.', ix);
857 if (ix2 < 0) {
858 break;
859 }
860 String pname = name.substring(0, ix2);
861 if (owner.getProperty(pname + ".level") != null ||
862 owner.getProperty(pname + ".handlers") != null) {
863 // This pname has a level/handlers definition.
864 // Make sure it exists.
865 demandLogger(pname, null, null);
866 }
867 ix = ix2+1;
868 }
869 }
870
871 // Gets a node in our tree of logger nodes.
872 // If necessary, create it.
873 LogNode getNode(String name) {
874 if (name == null || name.equals("")) {
875 return root;
876 }
877 LogNode node = root;
878 while (name.length() > 0) {
879 int ix = name.indexOf('.');
880 String head;
881 if (ix > 0) {
882 head = name.substring(0, ix);
883 name = name.substring(ix + 1);
884 } else {
885 head = name;
925 // the other Logger is not holding a strong reference to
926 // the other Logger, then it is possible for the other
927 // Logger to be GC'ed after we saw it in addLogger() and
928 // before we can refetch it. If it has been GC'ed then
929 // we'll just loop around and try again.
930 result = findLogger(name);
931 }
932 } while (result == null);
933 }
934 return result;
935 }
936 }
937
938 // Add new per logger handlers.
939 // We need to raise privilege here. All our decisions will
940 // be made based on the logging configuration, which can
941 // only be modified by trusted code.
942 private void loadLoggerHandlers(final Logger logger, final String name,
943 final String handlersPropertyName)
944 {
945 AccessController.doPrivileged(new PrivilegedAction<Object>() {
946 @Override
947 public Object run() {
948 String names[] = parseClassNames(handlersPropertyName);
949 final boolean ensureCloseOnReset = names.length > 0
950 && getBooleanProperty(handlersPropertyName + ".ensureCloseOnReset",true);
951
952 int count = 0;
953 for (String type : names) {
954 try {
955 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(type);
956 Handler hdl = (Handler) clz.newInstance();
957 // Check if there is a property defining the
958 // this handler's level.
959 String levs = getProperty(type + ".level");
960 if (levs != null) {
961 Level l = Level.findLevel(levs);
962 if (l != null) {
963 hdl.setLevel(l);
964 } else {
965 // Probably a bad level. Drop through.
966 System.err.println("Can't set level for " + type);
967 }
968 }
969 // Add this Handler to the logger
970 logger.addHandler(hdl);
971 if (++count == 1 && ensureCloseOnReset) {
972 // add this logger to the closeOnResetLoggers list.
973 closeOnResetLoggers.addIfAbsent(CloseOnReset.create(logger));
974 }
975 } catch (Exception ex) {
976 System.err.println("Can't load log handler \"" + type + "\"");
977 System.err.println("" + ex);
978 ex.printStackTrace();
979 }
980 }
981
982 return null;
983 }
984 });
985 }
986
987
988 // loggerRefQueue holds LoggerWeakRef objects for Logger objects
989 // that have been GC'ed.
990 private final ReferenceQueue<Logger> loggerRefQueue
991 = new ReferenceQueue<>();
992
993 // Package-level inner class.
994 // Helper class for managing WeakReferences to Logger objects.
995 //
996 // LogManager.namedLoggers
997 // - has weak references to all named Loggers
998 // - namedLoggers keeps the LoggerWeakRef objects for the named
999 // Loggers around until we can deal with the book keeping for
1000 // the named Logger that is being GC'ed.
1001 // LogManager.LogNode.loggerRef
1002 // - has a weak reference to a named Logger
1003 // - the LogNode will also keep the LoggerWeakRef objects for
1004 // the named Loggers around; currently LogNodes never go away.
1225 * Get an enumeration of known logger names.
1226 * <p>
1227 * Note: Loggers may be added dynamically as new classes are loaded.
1228 * This method only reports on the loggers that are currently registered.
1229 * It is also important to note that this method only returns the name
1230 * of a Logger, not a strong reference to the Logger itself.
1231 * The returned String does nothing to prevent the Logger from being
1232 * garbage collected. In particular, if the returned name is passed
1233 * to {@code LogManager.getLogger()}, then the caller must check the
1234 * return value from {@code LogManager.getLogger()} for null to properly
1235 * handle the case where the Logger has been garbage collected in the
1236 * time since its name was returned by this method.
1237 *
1238 * @return enumeration of logger name strings
1239 */
1240 public Enumeration<String> getLoggerNames() {
1241 return getUserContext().getLoggerNames();
1242 }
1243
1244 /**
1245 * Reinitialize the logging properties and reread the logging configuration.
1246 * <p>
1247 * The same rules are used for locating the configuration properties
1248 * as are used at startup. So normally the logging properties will
1249 * be re-read from the same file that was used at startup.
1250 * <P>
1251 * Any log level definitions in the new configuration file will be
1252 * applied using Logger.setLevel(), if the target Logger exists.
1253 * <p>
1254 * Any {@linkplain #addConfigurationListener registered configuration
1255 * listener} will be invoked after the properties are read.
1256 *
1257 * @exception SecurityException if a security manager exists and if
1258 * the caller does not have LoggingPermission("control").
1259 * @exception IOException if there are IO problems reading the configuration.
1260 */
1261 public void readConfiguration() throws IOException, SecurityException {
1262 checkPermission();
1263
1264 // if a configuration class is specified, load it and use it.
1265 String cname = System.getProperty("java.util.logging.config.class");
1266 if (cname != null) {
1267 try {
1268 // Instantiate the named class. It is its constructor's
1269 // responsibility to initialize the logging configuration, by
1270 // calling readConfiguration(InputStream) with a suitable stream.
1271 try {
1272 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
1273 clz.newInstance();
1274 return;
1275 } catch (ClassNotFoundException ex) {
1276 Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
1277 clz.newInstance();
1278 return;
1279 }
1280 } catch (Exception ex) {
1281 System.err.println("Logging configuration class \"" + cname + "\" failed");
1282 System.err.println("" + ex);
1283 // keep going and useful config file.
1284 }
1285 }
1286
1287 String fname = System.getProperty("java.util.logging.config.file");
1288 if (fname == null) {
1289 fname = System.getProperty("java.home");
1290 if (fname == null) {
1291 throw new Error("Can't find java.home ??");
1292 }
1293 File f = new File(fname, "conf");
1294 f = new File(f, "logging.properties");
1295 fname = f.getCanonicalPath();
1296 }
1297 try (final InputStream in = new FileInputStream(fname)) {
1298 final BufferedInputStream bin = new BufferedInputStream(in);
1299 readConfiguration(bin);
1300 }
1301 }
1302
1303 /**
1304 * Reset the logging configuration.
1305 * <p>
1306 * For all named loggers, the reset operation removes and closes
1307 * all Handlers and (except for the root logger) sets the level
1308 * to null. The root logger's level is set to Level.INFO.
1309 *
1310 * @exception SecurityException if a security manager exists and if
1311 * the caller does not have LoggingPermission("control").
1312 */
1313
1314 public void reset() throws SecurityException {
1315 checkPermission();
1316
1317 List<CloseOnReset> persistent;
1318
1319 // We don't want reset() and readConfiguration()
1320 // to run in parallel
1321 configurationLock.lock();
1322 try {
1323 // install new empty properties
1324 props = new Properties();
1325 // make sure we keep the loggers persistent until reset is done.
1326 // Those are the loggers for which we previously created a
1327 // handler from the configuration, and we need to prevent them
1328 // from being gc'ed until those handlers are closed.
1404 if (Character.isWhitespace(hands.charAt(end))) {
1405 break;
1406 }
1407 if (hands.charAt(end) == ',') {
1408 break;
1409 }
1410 end++;
1411 }
1412 String word = hands.substring(ix, end);
1413 ix = end+1;
1414 word = word.trim();
1415 if (word.length() == 0) {
1416 continue;
1417 }
1418 result.add(word);
1419 }
1420 return result.toArray(new String[result.size()]);
1421 }
1422
1423 /**
1424 * Reinitialize the logging properties and reread the logging configuration
1425 * from the given stream, which should be in java.util.Properties format.
1426 * Any {@linkplain #addConfigurationListener registered configuration
1427 * listener} will be invoked after the properties are read.
1428 * <p>
1429 * Any log level definitions in the new configuration file will be
1430 * applied using Logger.setLevel(), if the target Logger exists.
1431 *
1432 * @param ins stream to read properties from
1433 * @exception SecurityException if a security manager exists and if
1434 * the caller does not have LoggingPermission("control").
1435 * @exception IOException if there are problems reading from the stream.
1436 */
1437 public void readConfiguration(InputStream ins) throws IOException, SecurityException {
1438 checkPermission();
1439
1440 // We don't want reset() and readConfiguration() to run
1441 // in parallel.
1442 configurationLock.lock();
1443 try {
1444 if (globalHandlersState == STATE_SHUTDOWN) {
1445 // already in terminal state: don't even bother
1446 // to read the configuration
1447 return;
1448 }
1449
1450 // change state to STATE_READING_CONFIG to signal reset() to not change it
1489 // Note that we need to reinitialize global handles when
1490 // they are first referenced.
1491 globalHandlersState = STATE_UNINITIALIZED;
1492 } catch (Throwable t) {
1493 // If there were any trouble, then set state to STATE_INITIALIZED
1494 // so that no global handlers reinitialization is performed on not fully
1495 // initialized configuration.
1496 globalHandlersState = STATE_INITIALIZED;
1497 // re-throw
1498 throw t;
1499 }
1500 } finally {
1501 configurationLock.unlock();
1502 }
1503
1504 // should be called out of lock to avoid dead-lock situations
1505 // when user code is involved
1506 invokeConfigurationListeners();
1507 }
1508
1509 /**
1510 * Get the value of a logging property.
1511 * The method returns null if the property is not found.
1512 * @param name property name
1513 * @return property value
1514 */
1515 public String getProperty(String name) {
1516 return props.getProperty(name);
1517 }
1518
1519 // Package private method to get a String property.
1520 // If the property is not defined we return the given
1521 // default value.
1522 String getStringProperty(String name, String defaultValue) {
1523 String val = getProperty(name);
1524 if (val == null) {
1525 return defaultValue;
1526 }
1527 return val.trim();
1528 }
|
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26
27 package java.util.logging;
28
29 import java.io.*;
30 import java.util.*;
31 import java.security.*;
32 import java.lang.ref.ReferenceQueue;
33 import java.lang.ref.WeakReference;
34 import java.util.concurrent.ConcurrentHashMap;
35 import java.nio.file.Paths;
36 import java.util.concurrent.CopyOnWriteArrayList;
37 import java.util.concurrent.locks.ReentrantLock;
38 import java.util.function.BiFunction;
39 import java.util.function.Function;
40 import java.util.function.Predicate;
41 import java.util.function.Supplier;
42 import java.util.stream.Collectors;
43 import java.util.stream.Stream;
44 import sun.misc.JavaAWTAccess;
45 import sun.misc.ManagedLocalsThread;
46 import sun.misc.SharedSecrets;
47
48 /**
49 * There is a single global LogManager object that is used to
50 * maintain a set of shared state about Loggers and log services.
51 * <p>
52 * This LogManager object:
53 * <ul>
54 * <li> Manages a hierarchical namespace of Logger objects. All
55 * named Loggers are stored in this namespace.
56 * <li> Manages a set of logging control properties. These are
57 * simple key-value pairs that can be used by Handlers and
58 * other logging objects to configure themselves.
59 * </ul>
60 * <p>
61 * The global LogManager object can be retrieved using LogManager.getLogManager().
62 * The LogManager object is created during class initialization and
63 * cannot subsequently be changed.
64 * <p>
65 * At startup the LogManager class is located using the
66 * java.util.logging.manager system property.
67 *
68 * <h3>LogManager Configuration</h3>
69 *
70 * A LogManager initializes the logging configuration via
71 * the {@link #readConfiguration()} method during LogManager initialization.
72 * By default, LogManager default configuration is used.
73 * The logging configuration read by LogManager must be in the
74 * {@linkplain Properties properties file} format.
75 * <p>
76 * The LogManager defines two optional system properties that allow control over
77 * the initial configuration, as specified in the {@link #readConfiguration()}
78 * method:
79 * <ul>
80 * <li>"java.util.logging.config.class"
81 * <li>"java.util.logging.config.file"
82 * </ul>
83 * <p>
84 * These two system properties may be specified on the command line to the "java"
85 * command, or as system property definitions passed to JNI_CreateJavaVM.
86 * <p>
87 * The {@linkplain Properties properties} for loggers and Handlers will have
88 * names starting with the dot-separated name for the handler or logger.<br>
89 * The global logging properties may include:
90 * <ul>
91 * <li>A property "handlers". This defines a whitespace or comma separated
92 * list of class names for handler classes to load and register as
93 * handlers on the root Logger (the Logger named ""). Each class
94 * name must be for a Handler class which has a default constructor.
95 * Note that these Handlers may be created lazily, when they are
96 * first used.
97 *
98 * <li>A property "<logger>.handlers". This defines a whitespace or
99 * comma separated list of class names for handlers classes to
100 * load and register as handlers to the specified logger. Each class
101 * name must be for a Handler class which has a default constructor.
102 * Note that these Handlers may be created lazily, when they are
103 * first used.
104 *
105 * <li>A property "<logger>.handlers.ensureCloseOnReset". This defines a
106 * a boolean value. If "<logger>.handlers" is not defined or is empty,
107 * this property is ignored. Otherwise it defaults to {@code true}. When the
108 * value is {@code true}, the handlers associated with the logger are guaranteed
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
779 // Apply any initial level defined for the new logger, unless
780 // the logger's level is already initialized
781 Level level = owner.getLevelProperty(name + ".level", null);
782 if (level != null && !logger.isLevelInitialized()) {
783 doSetLevel(logger, level);
784 }
785
786 // instantiation of the handler is done in the LogManager.addLogger
787 // implementation as a handler class may be only visible to LogManager
788 // subclass for the custom log manager case
789 processParentHandlers(logger, name, VisitedLoggers.NEVER);
790
791 // Find the new node and its parent.
792 LogNode node = getNode(name);
793 node.loggerRef = ref;
794 Logger parent = null;
795 LogNode nodep = node.parent;
796 while (nodep != null) {
797 LoggerWeakRef nodeRef = nodep.loggerRef;
798 if (nodeRef != null) {
799 parent = nodeRef.get();
800 if (parent != null) {
801 break;
802 }
803 }
804 nodep = nodep.parent;
805 }
806
807 if (parent != null) {
808 doSetParent(logger, parent);
809 }
817 // soon as it is published in namedLoggers (findLogger takes
818 // benefit of the ConcurrentHashMap implementation of namedLoggers
819 // to avoid synchronizing on retrieval when that is possible).
820 namedLoggers.put(name, ref);
821 return true;
822 }
823
824 void removeLoggerRef(String name, LoggerWeakRef ref) {
825 namedLoggers.remove(name, ref);
826 }
827
828 synchronized Enumeration<String> getLoggerNames() {
829 // ensure that this context is properly initialized before
830 // returning logger names.
831 ensureInitialized();
832 return Collections.enumeration(namedLoggers.keySet());
833 }
834
835 // If logger.getUseParentHandlers() returns 'true' and any of the logger's
836 // parents have levels or handlers defined, make sure they are instantiated.
837 private void processParentHandlers(final Logger logger, final String name,
838 Predicate<Logger> visited) {
839 final LogManager owner = getOwner();
840 AccessController.doPrivileged(new PrivilegedAction<Void>() {
841 @Override
842 public Void run() {
843 if (logger != owner.rootLogger) {
844 boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
845 if (!useParent) {
846 logger.setUseParentHandlers(false);
847 }
848 }
849 return null;
850 }
851 });
852
853 int ix = 1;
854 for (;;) {
855 int ix2 = name.indexOf('.', ix);
856 if (ix2 < 0) {
857 break;
858 }
859 String pname = name.substring(0, ix2);
860 if (owner.getProperty(pname + ".level") != null ||
861 owner.getProperty(pname + ".handlers") != null) {
862 // This pname has a level/handlers definition.
863 // Make sure it exists.
864 if (visited.test(demandLogger(pname, null, null))) {
865 break;
866 }
867 }
868 ix = ix2+1;
869 }
870 }
871
872 // Gets a node in our tree of logger nodes.
873 // If necessary, create it.
874 LogNode getNode(String name) {
875 if (name == null || name.equals("")) {
876 return root;
877 }
878 LogNode node = root;
879 while (name.length() > 0) {
880 int ix = name.indexOf('.');
881 String head;
882 if (ix > 0) {
883 head = name.substring(0, ix);
884 name = name.substring(ix + 1);
885 } else {
886 head = name;
926 // the other Logger is not holding a strong reference to
927 // the other Logger, then it is possible for the other
928 // Logger to be GC'ed after we saw it in addLogger() and
929 // before we can refetch it. If it has been GC'ed then
930 // we'll just loop around and try again.
931 result = findLogger(name);
932 }
933 } while (result == null);
934 }
935 return result;
936 }
937 }
938
939 // Add new per logger handlers.
940 // We need to raise privilege here. All our decisions will
941 // be made based on the logging configuration, which can
942 // only be modified by trusted code.
943 private void loadLoggerHandlers(final Logger logger, final String name,
944 final String handlersPropertyName)
945 {
946 AccessController.doPrivileged(new PrivilegedAction<Void>() {
947 @Override
948 public Void run() {
949 setLoggerHandlers(logger, name, handlersPropertyName,
950 createLoggerHandlers(name, handlersPropertyName));
951 return null;
952 }
953 });
954 }
955
956 private void setLoggerHandlers(final Logger logger, final String name,
957 final String handlersPropertyName,
958 List<Handler> handlers)
959 {
960 final boolean ensureCloseOnReset = ! handlers.isEmpty()
961 && getBooleanProperty(handlersPropertyName + ".ensureCloseOnReset",true);
962 int count = 0;
963 for (Handler hdl : handlers) {
964 logger.addHandler(hdl);
965 if (++count == 1 && ensureCloseOnReset) {
966 // add this logger to the closeOnResetLoggers list.
967 closeOnResetLoggers.addIfAbsent(CloseOnReset.create(logger));
968 }
969 }
970 }
971
972 private List<Handler> createLoggerHandlers(final String name, final String handlersPropertyName)
973 {
974 String names[] = parseClassNames(handlersPropertyName);
975 List<Handler> handlers = new ArrayList<>(names.length);
976 for (String type : names) {
977 try {
978 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(type);
979 Handler hdl = (Handler) clz.newInstance();
980 // Check if there is a property defining the
981 // this handler's level.
982 String levs = getProperty(type + ".level");
983 if (levs != null) {
984 Level l = Level.findLevel(levs);
985 if (l != null) {
986 hdl.setLevel(l);
987 } else {
988 // Probably a bad level. Drop through.
989 System.err.println("Can't set level for " + type);
990 }
991 }
992 // Add this Handler to the logger
993 handlers.add(hdl);
994 } catch (Exception ex) {
995 System.err.println("Can't load log handler \"" + type + "\"");
996 System.err.println("" + ex);
997 ex.printStackTrace();
998 }
999 }
1000
1001 return handlers;
1002 }
1003
1004
1005 // loggerRefQueue holds LoggerWeakRef objects for Logger objects
1006 // that have been GC'ed.
1007 private final ReferenceQueue<Logger> loggerRefQueue
1008 = new ReferenceQueue<>();
1009
1010 // Package-level inner class.
1011 // Helper class for managing WeakReferences to Logger objects.
1012 //
1013 // LogManager.namedLoggers
1014 // - has weak references to all named Loggers
1015 // - namedLoggers keeps the LoggerWeakRef objects for the named
1016 // Loggers around until we can deal with the book keeping for
1017 // the named Logger that is being GC'ed.
1018 // LogManager.LogNode.loggerRef
1019 // - has a weak reference to a named Logger
1020 // - the LogNode will also keep the LoggerWeakRef objects for
1021 // the named Loggers around; currently LogNodes never go away.
1242 * Get an enumeration of known logger names.
1243 * <p>
1244 * Note: Loggers may be added dynamically as new classes are loaded.
1245 * This method only reports on the loggers that are currently registered.
1246 * It is also important to note that this method only returns the name
1247 * of a Logger, not a strong reference to the Logger itself.
1248 * The returned String does nothing to prevent the Logger from being
1249 * garbage collected. In particular, if the returned name is passed
1250 * to {@code LogManager.getLogger()}, then the caller must check the
1251 * return value from {@code LogManager.getLogger()} for null to properly
1252 * handle the case where the Logger has been garbage collected in the
1253 * time since its name was returned by this method.
1254 *
1255 * @return enumeration of logger name strings
1256 */
1257 public Enumeration<String> getLoggerNames() {
1258 return getUserContext().getLoggerNames();
1259 }
1260
1261 /**
1262 * Reads and initializes the logging configuration.
1263 * <p>
1264 * If the "java.util.logging.config.class" system property is set, then the
1265 * property value is treated as a class name. The given class will be
1266 * loaded, an object will be instantiated, and that object's constructor
1267 * is responsible for reading in the initial configuration. (That object
1268 * may use other system properties to control its configuration.) The
1269 * alternate configuration class can use {@code readConfiguration(InputStream)}
1270 * to define properties in the LogManager.
1271 * <p>
1272 * If "java.util.logging.config.class" system property is <b>not</b> set,
1273 * then this method will read the initial configuration from a properties
1274 * file and calls the {@link #readConfiguration(InputStream)} method to initialize
1275 * the configuration. The "java.util.logging.config.file" system property can be used
1276 * to specify the properties file that will be read as the initial configuration;
1277 * if not set, then the LogManager default configuration is used.
1278 * The default configuration is typically loaded from the
1279 * properties file "{@code conf/logging.properties}" in the Java installation
1280 * directory.
1281 *
1282 * <p>
1283 * Any {@linkplain #addConfigurationListener registered configuration
1284 * listener} will be invoked after the properties are read.
1285 *
1286 * @apiNote This {@code readConfiguration} method should only be used for
1287 * initializing the configuration during LogManager initialization or
1288 * used with the "java.util.logging.config.class" property.
1289 * When this method is called after loggers have been created, and
1290 * the "java.util.logging.config.class" system property is not set, all
1291 * existing loggers will be {@linkplain #reset() reset}. Then any
1292 * existing loggers that have a level property specified in the new
1293 * configuration stream will be {@linkplain
1294 * Logger#setLevel(java.util.logging.Level) set} to the specified log level.
1295 * <p>
1296 * To properly update the logging configuration, use the
1297 * {@link #updateConfiguration(java.util.function.Function)} or
1298 * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}
1299 * methods instead.
1300 *
1301 * @exception SecurityException if a security manager exists and if
1302 * the caller does not have LoggingPermission("control").
1303 * @exception IOException if there are IO problems reading the configuration.
1304 */
1305 public void readConfiguration() throws IOException, SecurityException {
1306 checkPermission();
1307
1308 // if a configuration class is specified, load it and use it.
1309 String cname = System.getProperty("java.util.logging.config.class");
1310 if (cname != null) {
1311 try {
1312 // Instantiate the named class. It is its constructor's
1313 // responsibility to initialize the logging configuration, by
1314 // calling readConfiguration(InputStream) with a suitable stream.
1315 try {
1316 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
1317 clz.newInstance();
1318 return;
1319 } catch (ClassNotFoundException ex) {
1320 Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
1321 clz.newInstance();
1322 return;
1323 }
1324 } catch (Exception ex) {
1325 System.err.println("Logging configuration class \"" + cname + "\" failed");
1326 System.err.println("" + ex);
1327 // keep going and useful config file.
1328 }
1329 }
1330
1331 String fname = getConfigurationFileName();
1332 try (final InputStream in = new FileInputStream(fname)) {
1333 final BufferedInputStream bin = new BufferedInputStream(in);
1334 readConfiguration(bin);
1335 }
1336 }
1337
1338 String getConfigurationFileName() throws IOException {
1339 String fname = System.getProperty("java.util.logging.config.file");
1340 if (fname == null) {
1341 fname = System.getProperty("java.home");
1342 if (fname == null) {
1343 throw new Error("Can't find java.home ??");
1344 }
1345 fname = Paths.get(fname, "conf", "logging.properties")
1346 .toAbsolutePath().normalize().toString();
1347 }
1348 return fname;
1349 }
1350
1351 /**
1352 * Reset the logging configuration.
1353 * <p>
1354 * For all named loggers, the reset operation removes and closes
1355 * all Handlers and (except for the root logger) sets the level
1356 * to null. The root logger's level is set to Level.INFO, and
1357 * its handlers will be recreated lazily when they are first used.
1358 *
1359 * @exception SecurityException if a security manager exists and if
1360 * the caller does not have LoggingPermission("control").
1361 */
1362
1363 public void reset() throws SecurityException {
1364 checkPermission();
1365
1366 List<CloseOnReset> persistent;
1367
1368 // We don't want reset() and readConfiguration()
1369 // to run in parallel
1370 configurationLock.lock();
1371 try {
1372 // install new empty properties
1373 props = new Properties();
1374 // make sure we keep the loggers persistent until reset is done.
1375 // Those are the loggers for which we previously created a
1376 // handler from the configuration, and we need to prevent them
1377 // from being gc'ed until those handlers are closed.
1453 if (Character.isWhitespace(hands.charAt(end))) {
1454 break;
1455 }
1456 if (hands.charAt(end) == ',') {
1457 break;
1458 }
1459 end++;
1460 }
1461 String word = hands.substring(ix, end);
1462 ix = end+1;
1463 word = word.trim();
1464 if (word.length() == 0) {
1465 continue;
1466 }
1467 result.add(word);
1468 }
1469 return result.toArray(new String[result.size()]);
1470 }
1471
1472 /**
1473 * Reads and initializes the logging configuration from the given input stream,
1474 * which should be in {@linkplain java.util.Properties properties file} format.
1475 * <p>
1476 * Any {@linkplain #addConfigurationListener registered configuration
1477 * listener} will be invoked after the properties are read.
1478 * <p>
1479 * @apiNote This {@code readConfiguration} method should only be used for
1480 * initializing the configuration during LogManager initialization or
1481 * used with the "java.util.logging.config.class" property.
1482 * When this method is called after loggers have been created, all
1483 * existing loggers will be {@linkplain #reset() reset}. Then any
1484 * existing loggers that have a level property specified in the new
1485 * configuration stream will be {@linkplain
1486 * Logger#setLevel(java.util.logging.Level) set} to the specified log level.
1487 * <p>
1488 * To properly update the logging configuration, use the
1489 * {@link #updateConfiguration(java.util.function.Function)} or
1490 * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}
1491 * methods instead.
1492 *
1493 * @param ins stream to read properties from
1494 * @exception SecurityException if a security manager exists and if
1495 * the caller does not have LoggingPermission("control").
1496 * @exception IOException if there are problems reading from the stream.
1497 */
1498 public void readConfiguration(InputStream ins) throws IOException, SecurityException {
1499 checkPermission();
1500
1501 // We don't want reset() and readConfiguration() to run
1502 // in parallel.
1503 configurationLock.lock();
1504 try {
1505 if (globalHandlersState == STATE_SHUTDOWN) {
1506 // already in terminal state: don't even bother
1507 // to read the configuration
1508 return;
1509 }
1510
1511 // change state to STATE_READING_CONFIG to signal reset() to not change it
1550 // Note that we need to reinitialize global handles when
1551 // they are first referenced.
1552 globalHandlersState = STATE_UNINITIALIZED;
1553 } catch (Throwable t) {
1554 // If there were any trouble, then set state to STATE_INITIALIZED
1555 // so that no global handlers reinitialization is performed on not fully
1556 // initialized configuration.
1557 globalHandlersState = STATE_INITIALIZED;
1558 // re-throw
1559 throw t;
1560 }
1561 } finally {
1562 configurationLock.unlock();
1563 }
1564
1565 // should be called out of lock to avoid dead-lock situations
1566 // when user code is involved
1567 invokeConfigurationListeners();
1568 }
1569
1570 // This enum enumerate the configuration properties that will be
1571 // updated on existing loggers when the configuration is updated
1572 // with LogManager.updateConfiguration().
1573 //
1574 // Note that this works properly only for the global LogManager - as
1575 // Handler and its subclasses get their configuration from
1576 // LogManager.getLogManager().
1577 //
1578 static enum ConfigProperty {
1579 LEVEL(".level"), HANDLERS(".handlers"), USEPARENT(".useParentHandlers");
1580 final String suffix;
1581 final int length;
1582 private ConfigProperty(String suffix) {
1583 this.suffix = Objects.requireNonNull(suffix);
1584 length = suffix.length();
1585 }
1586
1587 public boolean handleKey(String key) {
1588 if (this == HANDLERS && suffix.substring(1).equals(key)) return true;
1589 if (this == HANDLERS && suffix.equals(key)) return false;
1590 return key.endsWith(suffix);
1591 }
1592 String key(String loggerName) {
1593 if (this == HANDLERS && (loggerName == null || loggerName.isEmpty())) {
1594 return suffix.substring(1);
1595 }
1596 return loggerName + suffix;
1597 }
1598 String loggerName(String key) {
1599 assert key.equals(suffix.substring(1)) && this == HANDLERS || key.endsWith(suffix);
1600 if (this == HANDLERS && suffix.substring(1).equals(key)) return "";
1601 return key.substring(0, key.length() - length);
1602 }
1603
1604 /**
1605 * If the property is one that should be updated on existing loggers by
1606 * updateConfiguration, returns the name of the logger for which the
1607 * property is configured. Otherwise, returns null.
1608 * @param property a property key in 'props'
1609 * @return the name of the logger on which the property is to be set,
1610 * if the property is one that should be updated on existing
1611 * loggers, {@code null} otherwise.
1612 */
1613 static String getLoggerName(String property) {
1614 for (ConfigProperty p : ConfigProperty.ALL) {
1615 if (p.handleKey(property)) {
1616 return p.loggerName(property);
1617 }
1618 }
1619 return null; // Not a property that should be updated.
1620 }
1621
1622 /**
1623 * Find the ConfigProperty corresponding to the given
1624 * property key (may find none).
1625 * @param property a property key in 'props'
1626 * @return An optional containing a ConfigProperty object,
1627 * if the property is one that should be updated on existing
1628 * loggers, empty otherwise.
1629 */
1630 static Optional<ConfigProperty> find(String property) {
1631 return ConfigProperty.ALL.stream()
1632 .filter(p -> p.handleKey(property))
1633 .findFirst();
1634 }
1635
1636 /**
1637 * Returns true if the given property is one that should be updated
1638 * on existing loggers.
1639 * Used to filter property name streams.
1640 * @param property a property key from the configuration.
1641 * @return true if this property is of interest for updateConfiguration.
1642 */
1643 static boolean matches(String property) {
1644 return find(property).isPresent();
1645 }
1646
1647 /**
1648 * Returns true if the new property value is different from the old,
1649 * and therefore needs to be updated on existing loggers.
1650 * @param k a property key in the configuration
1651 * @param previous the old configuration
1652 * @param next the new configuration
1653 * @return true if the property is changing value between the two
1654 * configurations.
1655 */
1656 static boolean needsUpdating(String k, Properties previous, Properties next) {
1657 final String p = trim(previous.getProperty(k, null));
1658 final String n = trim(next.getProperty(k, null));
1659 return ! Objects.equals(p,n);
1660 }
1661
1662 /**
1663 * Applies the mapping function for the given key to the next
1664 * configuration.
1665 * If the mapping function is null then this method does nothing.
1666 * Otherwise, it calls the mapping function to compute the value
1667 * that should be associated with {@code key} in the resulting
1668 * configuration, and applies it to {@code next}.
1669 * If the mapping function returns {@code null} the key is removed
1670 * from {@code next}.
1671 *
1672 * @param k a property key in the configuration
1673 * @param previous the old configuration
1674 * @param next the new configuration (modified by this function)
1675 * @param remappingFunction the mapping function.
1676 */
1677 static void merge(String k, Properties previous, Properties next,
1678 BiFunction<String, String, String> mappingFunction) {
1679 String p = trim(previous.getProperty(k, null));
1680 String n = trim(next.getProperty(k, null));
1681 String mapped = trim(mappingFunction.apply(p,n));
1682 if (!Objects.equals(n, mapped)) {
1683 if (mapped == null) {
1684 next.remove(k);
1685 } else {
1686 next.setProperty(k, mapped);
1687 }
1688 }
1689 }
1690
1691 private static final EnumSet<ConfigProperty> ALL =
1692 EnumSet.allOf(ConfigProperty.class);
1693 }
1694
1695 // trim the value if not null.
1696 private static String trim(String value) {
1697 return value == null ? null : value.trim();
1698 }
1699
1700 /**
1701 * An object that keep track of loggers we have already visited.
1702 * Used when updating configuration, to avoid processing the same logger
1703 * twice.
1704 */
1705 static final class VisitedLoggers implements Predicate<Logger> {
1706 final IdentityHashMap<Logger,Boolean> visited;
1707 private VisitedLoggers(IdentityHashMap<Logger,Boolean> visited) {
1708 this.visited = visited;
1709 }
1710 VisitedLoggers() {
1711 this(new IdentityHashMap<>());
1712 }
1713 @Override
1714 public boolean test(Logger logger) {
1715 return visited != null && visited.put(logger, Boolean.TRUE) != null;
1716 }
1717 public void clear() {
1718 if (visited != null) visited.clear();
1719 }
1720
1721 // An object that considers that no logger has ever been visited.
1722 // This is used when processParentHandlers is called from
1723 // LoggerContext.addLocalLogger
1724 static final VisitedLoggers NEVER = new VisitedLoggers(null);
1725 }
1726
1727
1728 /**
1729 * Type of the modification for a given property. One of SAME, ADDED, CHANGED,
1730 * or REMOVED.
1731 */
1732 static enum ModType {
1733 SAME, // property had no value in the old and new conf, or had the
1734 // same value in both.
1735 ADDED, // property had no value in the old conf, but has one in the new.
1736 CHANGED, // property has a different value in the old conf and the new conf.
1737 REMOVED; // property has no value in the new conf, but had one in the old.
1738 static ModType of(String previous, String next) {
1739 if (previous == null && next != null) {
1740 return ADDED;
1741 }
1742 if (next == null && previous != null) {
1743 return REMOVED;
1744 }
1745 if (!Objects.equals(trim(previous), trim(next))) {
1746 return CHANGED;
1747 }
1748 return SAME;
1749 }
1750 }
1751
1752 /**
1753 * Updates the logging configuration.
1754 * <p>
1755 * This method reads the new configuration from a
1756 * properties file and calls the {@link
1757 * #updateConfiguration(java.io.InputStream, java.util.function.Function)
1758 * updateConfiguration(ins, mapper)} method to
1759 * update the configuration.
1760 * <p>
1761 * If the "java.util.logging.config.class" system property is set, this
1762 * method simply ignores it.
1763 * <br>
1764 * The "java.util.logging.config.file" system property can be used
1765 * to specify the properties file that will be read as the new configuration;
1766 * if not set, then the LogManager default configuration is used.
1767 * The default configuration is typically loaded from the
1768 * properties file "{@code conf/logging.properties}" in the Java installation
1769 * directory.
1770 *
1771 * @param mapper a functional interface that takes a configuration
1772 * key <i>k</i> and returns a function <i>f(o,n)</i> whose returned
1773 * value <i>v</i> will be applied to the resulting configuration.
1774 * For each configuration key <i>k</i>, the mapped function <i>f</i> will
1775 * be invoked with the value associated with <i>k</i> in the old
1776 * configuration (i.e <i>o</i>) and the value associated with
1777 * <i>k</i> in the new configuration (i.e. <i>n</i>).
1778 * <br> A {@code null} value for <i>o</i> or <i>n</i> indicates that no
1779 * value was present for <i>k</i> in the corresponding configuration.
1780 * A {@code null} value for <i>v</i> indicates that there should be no
1781 * value associated with <i>k</i> in the resulting configuration.
1782 *
1783 * @throws SecurityException if a security manager exists and if
1784 * the caller does not have LoggingPermission("control"), or
1785 * does not have the permissions required to set up the
1786 * configuration (e.g. open file specified for FileHandlers
1787 * etc...)
1788 *
1789 * @throws NullPointerException if {@code mapper} returns a {@code null}
1790 * function when invoked.
1791 *
1792 * @throws IOException if there are problems reading from the
1793 * logging configuration file.
1794 *
1795 * @see #updateConfiguration(java.io.InputStream, java.util.function.Function)
1796 */
1797 public void updateConfiguration(Function<String, BiFunction<String,String,String>> mapper)
1798 throws IOException {
1799 checkPermission();
1800 ensureLogManagerInitialized();
1801 drainLoggerRefQueueBounded();
1802
1803 String fname = getConfigurationFileName();
1804 try (final InputStream in = new FileInputStream(fname)) {
1805 final BufferedInputStream bin = new BufferedInputStream(in);
1806 updateConfiguration(bin, mapper);
1807 }
1808 }
1809
1810 /**
1811 * Updates the logging configuration.
1812 * <p>
1813 * For each configuration key in the {@linkplain
1814 * #getProperty(java.lang.String) existing configuration} and
1815 * the given input stream configuration, the given {@code mapper} function
1816 * is invoked to map from the configuration key to a function,
1817 * <i>f(o,n)</i>, that takes the old value and new value and returns
1818 * the resulting value to be applied in the resulting configuration,
1819 * as specified in the table below.
1820 * <p>Let <i>k</i> be a configuration key in the old or new configuration,
1821 * <i>o</i> be the old value (i.e. the value associated
1822 * with <i>k</i> in the old configuration), <i>n</i> be the
1823 * new value (i.e. the value associated with <i>k</i> in the new
1824 * configuration), and <i>f</i> be the function returned
1825 * by {@code mapper.apply(}<i>k</i>{@code )}: then <i>v = f(o,n)</i> is the
1826 * resulting value (i.e. the value that will be associated with
1827 * <i>k</i> in the resulting configuration).
1828 * <br>
1829 * LogManager properties are updated with the resulting value in the
1830 * resulting configuration.
1831 * <br>A {@code null} value may be passed to function
1832 * <i>f</i> to indicate that the corresponding configuration has no
1833 * configuration key <i>k</i>.
1834 * A {@code null} value may be returned by <i>f</i> to indicate that
1835 * there should be no value associated with <i>k</i> in the resulting
1836 * configuration.
1837 * <p>
1838 * If {@code mapper} is {@code null}, then <i>v</i> will be set to
1839 * <i>n</i>.
1840 * <p>
1841 * The registered {@linkplain #addConfigurationListener configuration
1842 * listener} will be invoked after the configuration is successfully updated.
1843 * <br><br>
1844 * <table summary="Updating configuration properties">
1845 * <tr>
1846 * <th>Property</th>
1847 * <th>Resulting Behavior</th>
1848 * </tr>
1849 * <tr>
1850 * <td valign="top">{@code <logger>.level}</td>
1851 * <td>
1852 * <ul>
1853 * <li>If the resulting configuration defines a level for a logger and
1854 * if the resulting level is different than the level specified in the
1855 * the old configuration, or not specified in
1856 * the old configuration, then if the logger exists or if children for
1857 * that logger exist, the level for that logger will be updated,
1858 * and the change propagated to any existing logger children.
1859 * This may cause the logger to be created, if necessary.
1860 * </li>
1861 * <li>If the old configuration defined a level for a logger, and the
1862 * resulting configuration doesn't, then this change will not be
1863 * propagated to existing loggers, if any.
1864 * To completely replace a configuration - the caller should therefore
1865 * call {@link #reset() reset} to empty the current configuration,
1866 * before calling {@code updateConfiguration}.
1867 * </li>
1868 * </ul>
1869 * </td>
1870 * <tr>
1871 * <td valign="top">{@code <logger>.useParentHandlers}</td>
1872 * <td>
1873 * <ul>
1874 * <li>If either the resulting or the old value for the useParentHandlers
1875 * property is not null, then if the logger exists or if children for
1876 * that logger exist, that logger will be updated to the resulting
1877 * value.
1878 * The value of the useParentHandlers property is the value specified
1879 * in the configuration; if not specified, the default is true.
1880 * </li>
1881 * </ul>
1882 * </td>
1883 * </tr>
1884 * <tr>
1885 * <td valign="top">{@code <logger>.handlers}</td>
1886 * <td>
1887 * <ul>
1888 * <li>If the resulting configuration defines a list of handlers for a
1889 * logger, and if the resulting list is different than the list
1890 * specified in the old configuration for that logger (that could be
1891 * empty), then if the logger exists or its children exist, the
1892 * handlers associated with that logger are closed and removed and
1893 * the new handlers will be created per the resulting configuration
1894 * and added to that logger, creating that logger if necessary.
1895 * </li>
1896 * <li>If the old configuration defined some handlers for a logger, and
1897 * the resulting configuration doesn't, if that logger exists,
1898 * its handlers will be removed and closed.
1899 * </li>
1900 * <li>Changing the list of handlers on an existing logger will cause all
1901 * its previous handlers to be removed and closed, regardless of whether
1902 * they had been created from the configuration or programmatically.
1903 * The old handlers will be replaced by new handlers, if any.
1904 * </li>
1905 * </ul>
1906 * </td>
1907 * </tr>
1908 * <tr>
1909 * <td valign="top">{@code <handler-name>.*}</td>
1910 * <td>
1911 * <ul>
1912 * <li>Properties configured/changed on handler classes will only affect
1913 * newly created handlers. If a node is configured with the same list
1914 * of handlers in the old and the resulting configuration, then these
1915 * handlers will remain unchanged.
1916 * </li>
1917 * </ul>
1918 * </td>
1919 * </tr>
1920 * <tr>
1921 * <td valign="top">{@code config} and any other property</td>
1922 * <td>
1923 * <ul>
1924 * <li>The resulting value for these property will be stored in the
1925 * LogManager properties, but {@code updateConfiguration} will not parse
1926 * or process their values.
1927 * </li>
1928 * </ul>
1929 * </td>
1930 * </tr>
1931 * </table>
1932 * <p>
1933 * <em>Example mapper functions:</em>
1934 * <br><br>
1935 * <ul>
1936 * <li>Replace all logging properties with the new configuration:
1937 * <br>
1938 * {@code (k) -> ((o, n) -> n)}
1939 * <p>
1940 * This is equivalent to passing a null {@code mapper} parameter</li>
1941 * <li>Merge the new configuration and old configuration and use the
1942 * new value if <i>k</i> exists in the new configuration.
1943 * <br>
1944 * {@code (k) -> ((o, n) -> n == null ? o : n)}:
1945 * <p>
1946 * as if merging two collections as follows:
1947 * <br>
1948 * {@code result.putAll(oldc); result.putAll(newc)}.</li>
1949 * <li>Merge the new configuration and old configuration and use the old
1950 * value if <i>k</i> exists in the old configuration:
1951 * <br>
1952 * {@code (k) -> ((o, n) -> o == null ? n : o)}
1953 * <p>
1954 * as if merging two collections as follows:
1955 * <br>
1956 * {@code result.putAll(newc); result.putAll(oldc)}.</li>
1957 * <li>Replace all properties with the new configuration except the handler
1958 * property to configure Logger's handler that is not root logger:
1959 * <br>
1960 * <pre>{@code (k) -> k.endsWith(".handlers")}
1961 * {@code ? ((o, n) -> (o == null ? n : o))}
1962 * {@code : ((o, n) -> n)}</pre>
1963 * </li>
1964 * </ul>
1965 * <p>
1966 * To completely reinitialize a configuration, an application can first call
1967 * {@link #reset() reset} to fully remove the old configuration, followed by
1968 * {@code updateConfiguration} to initialize the new configuration.
1969 *
1970 * @param ins a stream to read properties from
1971 * @param mapper a functional interface that takes a configuration
1972 * key <i>k</i> and returns a function <i>f(o,n)</i> whose returned
1973 * value <i>v</i> will be applied to the resulting configuration.
1974 * For each configuration key <i>k</i>, the mapped function <i>f</i> will
1975 * be invoked with the value associated with <i>k</i> in the old
1976 * configuration (i.e <i>o</i>) and the value associated with
1977 * <i>k</i> in the new configuration (i.e. <i>n</i>).
1978 * <br> A {@code null} value for <i>o</i> or <i>n</i> indicates that no
1979 * value was present for <i>k</i> in the corresponding configuration.
1980 * A {@code null} value for <i>v</i> indicates that there should be no
1981 * value associated with <i>k</i> in the resulting configuration.
1982 *
1983 * @throws SecurityException if a security manager exists and if
1984 * the caller does not have LoggingPermission("control"), or
1985 * does not have the permissions required to set up the
1986 * configuration (e.g. open files specified for FileHandlers)
1987 *
1988 * @throws NullPointerException if {@code ins} is null or if
1989 * {@code mapper} returns a null function when invoked.
1990 *
1991 * @throws IOException if there are problems reading from the
1992 * logging configuration file.
1993 */
1994 public void updateConfiguration(InputStream ins,
1995 Function<String, BiFunction<String,String,String>> mapper)
1996 throws IOException {
1997 checkPermission();
1998 ensureLogManagerInitialized();
1999 drainLoggerRefQueueBounded();
2000
2001 final Properties previous;
2002 final Set<String> updatePropertyNames;
2003 List<LoggerContext> cxs = Collections.emptyList();
2004 final VisitedLoggers visited = new VisitedLoggers();
2005 final Properties next = new Properties();
2006 next.load(ins);
2007
2008 if (globalHandlersState == STATE_SHUTDOWN) return;
2009
2010 // exclusive lock: readConfiguration/reset/updateConfiguration can't
2011 // run concurrently.
2012 // configurationLock.writeLock().lock();
2013 configurationLock.lock();
2014 try {
2015 if (globalHandlersState == STATE_SHUTDOWN) return;
2016 previous = props;
2017
2018 // Builds a TreeSet of all (old and new) property names.
2019 updatePropertyNames =
2020 Stream.concat(previous.stringPropertyNames().stream(),
2021 next.stringPropertyNames().stream())
2022 .collect(Collectors.toCollection(TreeSet::new));
2023
2024 if (mapper != null) {
2025 // mapper will potentially modify the content of
2026 // 'next', so we need to call it before affecting props=next.
2027 // give a chance to the mapper to control all
2028 // properties - not just those we will reset.
2029 updatePropertyNames.stream()
2030 .forEachOrdered(k -> ConfigProperty
2031 .merge(k, previous, next,
2032 Objects.requireNonNull(mapper.apply(k))));
2033 }
2034
2035 props = next;
2036
2037 // allKeys will contain all keys:
2038 // - which correspond to a configuration property we are interested in
2039 // (first filter)
2040 // - whose value needs to be updated (because it's new, removed, or
2041 // different) in the resulting configuration (second filter)
2042 final Stream<String> allKeys = updatePropertyNames.stream()
2043 .filter(ConfigProperty::matches)
2044 .filter(k -> ConfigProperty.needsUpdating(k, previous, next));
2045
2046 // Group configuration properties by logger name
2047 // We use a TreeMap so that parent loggers will be visited before
2048 // child loggers.
2049 final Map<String, TreeSet<String>> loggerConfigs =
2050 allKeys.collect(Collectors.groupingBy(ConfigProperty::getLoggerName,
2051 TreeMap::new,
2052 Collectors.toCollection(TreeSet::new)));
2053
2054 if (!loggerConfigs.isEmpty()) {
2055 cxs = contexts();
2056 }
2057 final List<Logger> loggers = cxs.isEmpty()
2058 ? Collections.emptyList() : new ArrayList<>(cxs.size());
2059 for (Map.Entry<String, TreeSet<String>> e : loggerConfigs.entrySet()) {
2060 // This can be a logger name, or something else...
2061 // The only thing we know is that we found a property
2062 // we are interested in.
2063 // For instance, if we found x.y.z.level, then x.y.z could be
2064 // a logger, but it could also be a handler class...
2065 // Anyway...
2066 final String name = e.getKey();
2067 final Set<String> properties = e.getValue();
2068 loggers.clear();
2069 for (LoggerContext cx : cxs) {
2070 Logger l = cx.findLogger(name);
2071 if (l == null) continue;
2072 if (!visited.test(l)) {
2073 loggers.add(l);
2074 }
2075 }
2076 if (loggers.isEmpty()) continue;
2077 for (String pk : properties) {
2078 ConfigProperty cp = ConfigProperty.find(pk).get();
2079 String p = previous.getProperty(pk, null);
2080 String n = next.getProperty(pk, null);
2081
2082 // Determines the type of modification.
2083 ModType mod = ModType.of(p, n);
2084
2085 // mod == SAME means that the two values are equals, there
2086 // is nothing to do. Usually, this should not happen as such
2087 // properties should have been filtered above.
2088 // It could happen however if the properties had
2089 // trailing/leading whitespaces.
2090 if (mod == ModType.SAME) continue;
2091
2092 switch (cp) {
2093 case LEVEL:
2094 if (mod == ModType.REMOVED) continue;
2095 Level level = Level.findLevel(trim(n));
2096 if (level == null) {
2097 if (name.isEmpty()) {
2098 rootLogger.setLevel(level);
2099 }
2100 for (Logger l : loggers) {
2101 if (!name.isEmpty() || l != rootLogger) {
2102 l.setLevel(level);
2103 }
2104 }
2105 }
2106 break;
2107 case USEPARENT:
2108 if (!name.isEmpty()) {
2109 boolean useParent = getBooleanProperty(pk, true);
2110 if (n != null || p != null) {
2111 // reset the flag only if the previous value
2112 // or the new value are not null.
2113 for (Logger l : loggers) {
2114 l.setUseParentHandlers(useParent);
2115 }
2116 }
2117 }
2118 break;
2119 case HANDLERS:
2120 List<Handler> hdls = null;
2121 if (name.isEmpty()) {
2122 // special handling for the root logger.
2123 globalHandlersState = STATE_READING_CONFIG;
2124 try {
2125 closeHandlers(rootLogger);
2126 globalHandlersState = STATE_UNINITIALIZED;
2127 } catch (Throwable t) {
2128 globalHandlersState = STATE_INITIALIZED;
2129 throw t;
2130 }
2131 }
2132 for (Logger l : loggers) {
2133 if (l == rootLogger) continue;
2134 closeHandlers(l);
2135 if (mod == ModType.REMOVED) {
2136 closeOnResetLoggers.removeIf(c -> c.logger == l);
2137 continue;
2138 }
2139 if (hdls == null) {
2140 hdls = name.isEmpty()
2141 ? Arrays.asList(rootLogger.getHandlers())
2142 : createLoggerHandlers(name, pk);
2143 }
2144 setLoggerHandlers(l, name, pk, hdls);
2145 }
2146 break;
2147 default: break;
2148 }
2149 }
2150 }
2151 } finally {
2152 configurationLock.unlock();
2153 visited.clear();
2154 }
2155
2156 // Now ensure that if an existing logger has acquired a new parent
2157 // in the configuration, this new parent will be created - if needed,
2158 // and added to the context of the existing child.
2159 //
2160 drainLoggerRefQueueBounded();
2161 for (LoggerContext cx : cxs) {
2162 for (Enumeration<String> names = cx.getLoggerNames() ; names.hasMoreElements();) {
2163 String name = names.nextElement();
2164 if (name.isEmpty()) continue; // don't need to process parents on root.
2165 Logger l = cx.findLogger(name);
2166 if (l != null && !visited.test(l)) {
2167 // should pass visited here to cut the processing when
2168 // reaching a logger already visited.
2169 cx.processParentHandlers(l, name, visited);
2170 }
2171 }
2172 }
2173
2174 // We changed the configuration: invoke configuration listeners
2175 invokeConfigurationListeners();
2176 }
2177
2178 /**
2179 * Get the value of a logging property.
2180 * The method returns null if the property is not found.
2181 * @param name property name
2182 * @return property value
2183 */
2184 public String getProperty(String name) {
2185 return props.getProperty(name);
2186 }
2187
2188 // Package private method to get a String property.
2189 // If the property is not defined we return the given
2190 // default value.
2191 String getStringProperty(String name, String defaultValue) {
2192 String val = getProperty(name);
2193 if (val == null) {
2194 return defaultValue;
2195 }
2196 return val.trim();
2197 }
|