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.
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.
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
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
1451 globalHandlersState = STATE_READING_CONFIG;
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.BiPredicate;
40 import java.util.function.Function;
41 import java.util.function.Predicate;
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.
778 return false;
779 }
780 }
781
782 // We're adding a new logger.
783 // Note that we are creating a weak reference here.
784 final LogManager owner = getOwner();
785 logger.setLogManager(owner);
786 ref = owner.new LoggerWeakRef(logger);
787
788 // Apply any initial level defined for the new logger, unless
789 // the logger's level is already initialized
790 Level level = owner.getLevelProperty(name + ".level", null);
791 if (level != null && !logger.isLevelInitialized()) {
792 doSetLevel(logger, level);
793 }
794
795 // instantiation of the handler is done in the LogManager.addLogger
796 // implementation as a handler class may be only visible to LogManager
797 // subclass for the custom log manager case
798 processParentHandlers(logger, name, VisitedLoggers.of(null));
799
800 // Find the new node and its parent.
801 LogNode node = getNode(name);
802 node.loggerRef = ref;
803 Logger parent = null;
804 LogNode nodep = node.parent;
805 while (nodep != null) {
806 LoggerWeakRef nodeRef = nodep.loggerRef;
807 if (nodeRef != null) {
808 parent = nodeRef.get();
809 if (parent != null) {
810 break;
811 }
812 }
813 nodep = nodep.parent;
814 }
815
816 if (parent != null) {
817 doSetParent(logger, parent);
818 }
826 // soon as it is published in namedLoggers (findLogger takes
827 // benefit of the ConcurrentHashMap implementation of namedLoggers
828 // to avoid synchronizing on retrieval when that is possible).
829 namedLoggers.put(name, ref);
830 return true;
831 }
832
833 void removeLoggerRef(String name, LoggerWeakRef ref) {
834 namedLoggers.remove(name, ref);
835 }
836
837 synchronized Enumeration<String> getLoggerNames() {
838 // ensure that this context is properly initialized before
839 // returning logger names.
840 ensureInitialized();
841 return Collections.enumeration(namedLoggers.keySet());
842 }
843
844 // If logger.getUseParentHandlers() returns 'true' and any of the logger's
845 // parents have levels or handlers defined, make sure they are instantiated.
846 private void processParentHandlers(final Logger logger, final String name,
847 BiPredicate<Logger,String> visited) {
848 final LogManager owner = getOwner();
849 AccessController.doPrivileged(new PrivilegedAction<Void>() {
850 @Override
851 public Void run() {
852 if (logger != owner.rootLogger) {
853 boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
854 if (!useParent) {
855 logger.setUseParentHandlers(false);
856 }
857 }
858 return null;
859 }
860 });
861
862 int ix = 1;
863 for (;;) {
864 int ix2 = name.indexOf('.', ix);
865 if (ix2 < 0) {
866 break;
867 }
868 String pname = name.substring(0, ix2);
869 if (owner.getProperty(pname + ".level") != null ||
870 owner.getProperty(pname + ".handlers") != null) {
871 // This pname has a level/handlers definition.
872 // Make sure it exists.
873 if (visited.test(demandLogger(pname, null, null), pname)) {
874 break;
875 }
876 }
877 ix = ix2+1;
878 }
879 }
880
881 // Gets a node in our tree of logger nodes.
882 // If necessary, create it.
883 LogNode getNode(String name) {
884 if (name == null || name.equals("")) {
885 return root;
886 }
887 LogNode node = root;
888 while (name.length() > 0) {
889 int ix = name.indexOf('.');
890 String head;
891 if (ix > 0) {
892 head = name.substring(0, ix);
893 name = name.substring(ix + 1);
894 } else {
895 head = name;
935 // the other Logger is not holding a strong reference to
936 // the other Logger, then it is possible for the other
937 // Logger to be GC'ed after we saw it in addLogger() and
938 // before we can refetch it. If it has been GC'ed then
939 // we'll just loop around and try again.
940 result = findLogger(name);
941 }
942 } while (result == null);
943 }
944 return result;
945 }
946 }
947
948 // Add new per logger handlers.
949 // We need to raise privilege here. All our decisions will
950 // be made based on the logging configuration, which can
951 // only be modified by trusted code.
952 private void loadLoggerHandlers(final Logger logger, final String name,
953 final String handlersPropertyName)
954 {
955 AccessController.doPrivileged(new PrivilegedAction<Void>() {
956 @Override
957 public Void run() {
958 setLoggerHandlers(logger, name, handlersPropertyName,
959 createLoggerHandlers(name, handlersPropertyName));
960 return null;
961 }
962 });
963 }
964
965 private void setLoggerHandlers(final Logger logger, final String name,
966 final String handlersPropertyName,
967 List<Handler> handlers)
968 {
969 final boolean ensureCloseOnReset = ! handlers.isEmpty()
970 && getBooleanProperty(handlersPropertyName + ".ensureCloseOnReset",true);
971 int count = 0;
972 for (Handler hdl : handlers) {
973 logger.addHandler(hdl);
974 if (++count == 1 && ensureCloseOnReset) {
975 // add this logger to the closeOnResetLoggers list.
976 closeOnResetLoggers.addIfAbsent(CloseOnReset.create(logger));
977 }
978 }
979 }
980
981 private List<Handler> createLoggerHandlers(final String name, final String handlersPropertyName)
982 {
983 String names[] = parseClassNames(handlersPropertyName);
984 List<Handler> handlers = new ArrayList<>(names.length);
985 for (String type : names) {
986 try {
987 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(type);
988 Handler hdl = (Handler) clz.newInstance();
989 // Check if there is a property defining the
990 // this handler's level.
991 String levs = getProperty(type + ".level");
992 if (levs != null) {
993 Level l = Level.findLevel(levs);
994 if (l != null) {
995 hdl.setLevel(l);
996 } else {
997 // Probably a bad level. Drop through.
998 System.err.println("Can't set level for " + type);
999 }
1000 }
1001 // Add this Handler to the logger
1002 handlers.add(hdl);
1003 } catch (Exception ex) {
1004 System.err.println("Can't load log handler \"" + type + "\"");
1005 System.err.println("" + ex);
1006 ex.printStackTrace();
1007 }
1008 }
1009
1010 return handlers;
1011 }
1012
1013
1014 // loggerRefQueue holds LoggerWeakRef objects for Logger objects
1015 // that have been GC'ed.
1016 private final ReferenceQueue<Logger> loggerRefQueue
1017 = new ReferenceQueue<>();
1018
1019 // Package-level inner class.
1020 // Helper class for managing WeakReferences to Logger objects.
1021 //
1022 // LogManager.namedLoggers
1023 // - has weak references to all named Loggers
1024 // - namedLoggers keeps the LoggerWeakRef objects for the named
1025 // Loggers around until we can deal with the book keeping for
1026 // the named Logger that is being GC'ed.
1027 // LogManager.LogNode.loggerRef
1028 // - has a weak reference to a named Logger
1029 // - the LogNode will also keep the LoggerWeakRef objects for
1030 // the named Loggers around; currently LogNodes never go away.
1263 *
1264 * @return enumeration of logger name strings
1265 */
1266 public Enumeration<String> getLoggerNames() {
1267 return getUserContext().getLoggerNames();
1268 }
1269
1270 /**
1271 * Reinitialize the logging properties and reread the logging configuration.
1272 * <p>
1273 * The same rules are used for locating the configuration properties
1274 * as are used at startup. So normally the logging properties will
1275 * be re-read from the same file that was used at startup.
1276 * <P>
1277 * Any log level definitions in the new configuration file will be
1278 * applied using Logger.setLevel(), if the target Logger exists.
1279 * <p>
1280 * Any {@linkplain #addConfigurationListener registered configuration
1281 * listener} will be invoked after the properties are read.
1282 *
1283 * @apiNote {@code readConfiguration} is principally useful for subclasses
1284 * of LogManager which may override this method to plug in their
1285 * own primordial custom configuration.
1286 * <p>
1287 * Calling this method directly from the application code after the
1288 * LogManager has been initialized is discouraged.
1289 *
1290 * Applications that wish to update the LogManager
1291 * configuration after the LogManager has been initialized should
1292 * call {@link #updateConfiguration(java.util.function.Function)}
1293 * instead.
1294 *
1295 * @exception SecurityException if a security manager exists and if
1296 * the caller does not have LoggingPermission("control").
1297 * @exception IOException if there are IO problems reading the configuration.
1298 */
1299 public void readConfiguration() throws IOException, SecurityException {
1300 checkPermission();
1301
1302 // if a configuration class is specified, load it and use it.
1303 String cname = System.getProperty("java.util.logging.config.class");
1304 if (cname != null) {
1305 try {
1306 // Instantiate the named class. It is its constructor's
1307 // responsibility to initialize the logging configuration, by
1308 // calling readConfiguration(InputStream) with a suitable stream.
1309 try {
1310 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
1311 clz.newInstance();
1312 return;
1313 } catch (ClassNotFoundException ex) {
1314 Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
1315 clz.newInstance();
1316 return;
1317 }
1318 } catch (Exception ex) {
1319 System.err.println("Logging configuration class \"" + cname + "\" failed");
1320 System.err.println("" + ex);
1321 // keep going and useful config file.
1322 }
1323 }
1324
1325 String fname = getConfigurationFileName();
1326 try (final InputStream in = new FileInputStream(fname)) {
1327 final BufferedInputStream bin = new BufferedInputStream(in);
1328 readConfiguration(bin);
1329 }
1330 }
1331
1332 String getConfigurationFileName() throws IOException {
1333 String fname = System.getProperty("java.util.logging.config.file");
1334 if (fname == null) {
1335 fname = System.getProperty("java.home");
1336 if (fname == null) {
1337 throw new Error("Can't find java.home ??");
1338 }
1339 fname = Paths.get(fname, "conf", "logging.properties")
1340 .toAbsolutePath().normalize().toString();
1341 }
1342 return fname;
1343 }
1344
1345 /**
1346 * Reset the logging configuration.
1347 * <p>
1348 * For all named loggers, the reset operation removes and closes
1349 * all Handlers and (except for the root logger) sets the level
1350 * to null. The root logger's level is set to Level.INFO.
1351 *
1352 * @exception SecurityException if a security manager exists and if
1353 * the caller does not have LoggingPermission("control").
1354 */
1355
1356 public void reset() throws SecurityException {
1357 checkPermission();
1358
1359 List<CloseOnReset> persistent;
1360
1361 // We don't want reset() and readConfiguration()
1362 // to run in parallel
1454 String word = hands.substring(ix, end);
1455 ix = end+1;
1456 word = word.trim();
1457 if (word.length() == 0) {
1458 continue;
1459 }
1460 result.add(word);
1461 }
1462 return result.toArray(new String[result.size()]);
1463 }
1464
1465 /**
1466 * Reinitialize the logging properties and reread the logging configuration
1467 * from the given stream, which should be in java.util.Properties format.
1468 * Any {@linkplain #addConfigurationListener registered configuration
1469 * listener} will be invoked after the properties are read.
1470 * <p>
1471 * Any log level definitions in the new configuration file will be
1472 * applied using Logger.setLevel(), if the target Logger exists.
1473 *
1474 * @apiNote {@code readConfiguration} is principally useful for subclasses
1475 * of LogManager which may override this method to plug in their
1476 * own primordial custom configuration.
1477 * <p>
1478 * Calling this method directly from the application code after the
1479 * LogManager has been initialized is discouraged.
1480 * Applications that wish to update the LogManager configuration after
1481 * the LogManager has been initialized should call
1482 * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}
1483 * instead.
1484 *
1485 * @param ins stream to read properties from
1486 * @exception SecurityException if a security manager exists and if
1487 * the caller does not have LoggingPermission("control").
1488 * @exception IOException if there are problems reading from the stream.
1489 */
1490 public void readConfiguration(InputStream ins) throws IOException, SecurityException {
1491 checkPermission();
1492
1493 // We don't want reset() and readConfiguration() to run
1494 // in parallel.
1495 configurationLock.lock();
1496 try {
1497 if (globalHandlersState == STATE_SHUTDOWN) {
1498 // already in terminal state: don't even bother
1499 // to read the configuration
1500 return;
1501 }
1502
1503 // change state to STATE_READING_CONFIG to signal reset() to not change it
1504 globalHandlersState = STATE_READING_CONFIG;
1542 // Note that we need to reinitialize global handles when
1543 // they are first referenced.
1544 globalHandlersState = STATE_UNINITIALIZED;
1545 } catch (Throwable t) {
1546 // If there were any trouble, then set state to STATE_INITIALIZED
1547 // so that no global handlers reinitialization is performed on not fully
1548 // initialized configuration.
1549 globalHandlersState = STATE_INITIALIZED;
1550 // re-throw
1551 throw t;
1552 }
1553 } finally {
1554 configurationLock.unlock();
1555 }
1556
1557 // should be called out of lock to avoid dead-lock situations
1558 // when user code is involved
1559 invokeConfigurationListeners();
1560 }
1561
1562 // This enum enumerate the configuration properties that will be
1563 // re-established on existing loggers when the configuration is updated
1564 // with LogManager.updateConfiguration().
1565 //
1566 // Note that this works properly only for the global LogManager - as
1567 // Handler and its subclasses get their configuration from
1568 // LogManager.getLogManager().
1569 //
1570 static enum ConfigurationProperties implements Predicate<String> {
1571 LEVEL(".level"), HANDLERS(".handlers"), USEPARENT(".useParentHandlers");
1572 final String suffix;
1573 final int length;
1574 private ConfigurationProperties(String suffix) {
1575 this.suffix = Objects.requireNonNull(suffix);
1576 length = suffix.length();
1577 }
1578 @Override
1579 public boolean test(String key) {
1580 if (this == HANDLERS && suffix.substring(1).equals(key)) return true;
1581 if (this == HANDLERS && suffix.equals(key)) return false;
1582 return key.endsWith(suffix);
1583 }
1584 String key(String loggerName) {
1585 if (this == HANDLERS && (loggerName == null || loggerName.isEmpty())) {
1586 return suffix.substring(1);
1587 }
1588 return loggerName + suffix;
1589 }
1590 String loggerName(String key) {
1591 assert key.equals(suffix.substring(1)) && this == HANDLERS || key.endsWith(suffix);
1592 if (this == HANDLERS && suffix.substring(1).equals(key)) return "";
1593 return key.substring(0, key.length() - length);
1594 }
1595
1596 /**
1597 * If the property is one that should be reestablished by
1598 * updateConfiguration, returns the name of the logger for which the
1599 * property is configured. Otherwise, returns null.
1600 * @param property a property key in 'props'
1601 * @return the name of the logger on which the property is to be set,
1602 * if the property is one that we should reestablish.
1603 * {@code null} otherwise.
1604 */
1605 static String getLoggerName(String property) {
1606 for (ConfigurationProperties p : ConfigurationProperties.ALL) {
1607 if (p.test(property)) {
1608 return p.loggerName(property);
1609 }
1610 }
1611 return null; // Not a property that should be reestablished.
1612 }
1613
1614 /**
1615 * If the property is one that should be reestablished by
1616 * updateConfiguration, returns the corresponding
1617 * ConfigurationProperties object. Otherwise, returns null.
1618 * @param property a property key in 'props'
1619 * @return the corresponding ConfigurationProperties object,
1620 * if the property is one that we should reestablish.
1621 * {@code null} otherwise.
1622 */
1623 static ConfigurationProperties of(String property) {
1624 for (ConfigurationProperties p : ConfigurationProperties.ALL) {
1625 if (p.test(property)) {
1626 return p;
1627 }
1628 }
1629 return null; // Not a property that should be reestablished.
1630 }
1631
1632 /**
1633 * Returns true if the given property is one that should be reestablished.
1634 * Used to filter property name streams.
1635 * @param property a property key from the configuration.
1636 * @return true if this property is of interest for updateConfiguration.
1637 */
1638 static boolean isOf(String property) {
1639 return of(property) != null;
1640 }
1641
1642 /**
1643 * Returns true if the new property value is different from the old.
1644 * @param k a property key in the configuration
1645 * @param previous the old configuration
1646 * @param next the new configuration
1647 * @return true if the property is changing value between the two
1648 * configurations.
1649 */
1650 static boolean isChanging(String k, Properties previous, Properties next) {
1651 final String p = trim(previous.getProperty(k, null));
1652 final String n = trim(next.getProperty(k, null));
1653 return ! Objects.equals(p,n);
1654 }
1655
1656 /**
1657 * Applies the remapping function for the given key to the next
1658 * configuration.
1659 * If the remapping function is null then this method does nothing.
1660 * Otherwise, it calls the remapping function to compute the value
1661 * that should be associated with {@code key} in the resulting
1662 * configuration, and applies it to {@code next}.
1663 * If the remapping function returns {@code null} the key is removed
1664 * from {@code next}.
1665 *
1666 * @param k a property key in the configuration
1667 * @param previous the old configuration
1668 * @param next the new configuration (modified by this function)
1669 * @param remappingFunction the remapping function.
1670 */
1671 static void merge(String k, Properties previous, Properties next,
1672 BiFunction<String, String, String> remappingFunction) {
1673 if (remappingFunction != null) {
1674 String p = trim(previous.getProperty(k, null));
1675 String n = trim(next.getProperty(k, null));
1676 String mapped = trim(remappingFunction.apply(p,n));
1677 if (!Objects.equals(n, mapped)) {
1678 if (mapped == null) {
1679 next.remove(k);
1680 } else {
1681 next.setProperty(k, mapped);
1682 }
1683 }
1684 }
1685 }
1686
1687 private static final EnumSet<ConfigurationProperties> ALL =
1688 EnumSet.allOf(ConfigurationProperties.class);
1689 }
1690
1691 // trim the value if not null.
1692 private static String trim(String value) {
1693 return value == null ? null : value.trim();
1694 }
1695
1696 /**
1697 * An object that keep track of loggers we have already visited.
1698 * Used when updating configuration, to avoid processing the same logger
1699 * twice.
1700 */
1701 static final class VisitedLoggers implements BiPredicate<Logger, String> {
1702 final Map<Logger,String> visited;
1703 private VisitedLoggers(Map<Logger,String> visited) {
1704 this.visited = visited;
1705 }
1706 @Override
1707 public boolean test(Logger logger, String name) {
1708 return visited != null && visited.put(logger, name) != null;
1709 }
1710 public void clear() {
1711 if (visited != null) visited.clear();
1712 }
1713
1714 public static VisitedLoggers of(Map<Logger, String> visited) {
1715 assert visited == null || visited instanceof IdentityHashMap;
1716 return visited == null ? NEVER : new VisitedLoggers(visited);
1717 }
1718
1719 // An object that considers that no logger has ever been visited.
1720 // This is used when processParentHandlers is called from
1721 // LoggerContext.addLocalLogger
1722 static final VisitedLoggers NEVER = new VisitedLoggers(null);
1723 }
1724
1725
1726 /**
1727 * Type of the modification for a given property. One of ADDED, CHANGED,
1728 * or REMOVED.
1729 */
1730 static enum ModType {
1731 ADDED, // property had no value in the old conf, but has one in the new.
1732 CHANGED, // property has a different value in the old conf and the new conf.
1733 REMOVED; // property has no value in the new conf, but had one in the old.
1734 static ModType of(String previous, String next) {
1735 if (previous == null && next != null) {
1736 return ADDED;
1737 }
1738 if (next == null && previous != null) {
1739 return REMOVED;
1740 }
1741 if (!Objects.equals(trim(previous), trim(next))) {
1742 return CHANGED;
1743 }
1744 return null;
1745 }
1746 }
1747
1748 /**
1749 * Updates an existing configuration.
1750 * <p>
1751 * @implSpec
1752 * This is equivalent to calling:
1753 * <pre>
1754 * try (final InputStream in = new FileInputStream(<logging.properties>)) {
1755 * final BufferedInputStream bin = new BufferedInputStream(in);
1756 * updateConfiguration(bin, remapper);
1757 * }
1758 * </pre>
1759 * where {@code <logging.properties>} is the logging configuration file path.
1760 *
1761 * @param remapper Used to control how the old configuration and
1762 * candidate configuration will be mapped into the new configuration.
1763 * See {@link
1764 * #updateConfiguration(java.io.InputStream, java.util.function.Function)}
1765 * for more details.
1766 *
1767 * @exception SecurityException if a security manager exists and if
1768 * the caller does not have LoggingPermission("control"), or
1769 * does not have the permissions required to set up the
1770 * configuration (e.g. open file specified for FileHandlers
1771 * etc...)
1772 *
1773 * @exception IOException if there are problems reading from the
1774 * logging configuration file.
1775 */
1776 public void updateConfiguration(Function<String, BiFunction<String,String,String>> remapper)
1777 throws IOException {
1778 checkPermission();
1779 String fname = getConfigurationFileName();
1780 try (final InputStream in = new FileInputStream(fname)) {
1781 final BufferedInputStream bin = new BufferedInputStream(in);
1782 updateConfiguration(bin, remapper);
1783 }
1784 }
1785
1786 /**
1787 * Update the logging configuration. The given {@code remapper} function
1788 * is invoked for each configuration key in order to produce a resulting
1789 * logging configuration to to be applied. The result of applying that new
1790 * configuration is detailed in the table below.
1791 * The registered {@linkplain #addConfigurationListener configuration
1792 * listener} will be invoked after the configuration is successfully updated.
1793 * <br>
1794 * <table>
1795 * <caption>Updating configuration properties</caption>
1796 * <tr>
1797 * <th>Property</th>
1798 * <th>Resulting Behavior</th>
1799 * </tr>
1800 * <tr>
1801 * <td valign="top">{@code <logger>.level}</td>
1802 * <td>
1803 * <ul>
1804 * <li>If the new configuration defines a level for a logger and
1805 * if the new level is different than the level specified in the
1806 * the old configuration, or not specified in
1807 * the old configuration, then the level for that logger will be
1808 * updated, and the change propagated to the existing logger children,
1809 * if necessary.
1810 * </li>
1811 * <li>If the old configuration defined a level for a logger, and the new
1812 * configuration doesn't, then the logger level remains unchanged.
1813 * To completely replace a configuration - the caller should therefore
1814 * call {@link #reset() reset} to empty the current configuration, before
1815 * calling {@code updateConfiguration}.
1816 * </li>
1817 * </ul>
1818 * </td>
1819 * <tr>
1820 * <td valign="top">{@code <logger>.useParentHandlers}</td>
1821 * <td>
1822 * <ul>
1823 * <li>If either the new or the old value for the useParentHandlers property
1824 * is not null, then if the logger exists or if children for
1825 * that logger exist, that logger will be updated to the new value.
1826 * The value of the useParentHandlers property is the value specified
1827 * in the configuration; if not specified, the default is true.
1828 * </li>
1829 * </ul>
1830 * </td>
1831 * </tr>
1832 * <tr>
1833 * <td valign="top">{@code <logger>.handlers}</td>
1834 * <td>
1835 * <ul>
1836 * <li>If the new configuration defines a list of handlers for a logger,
1837 * and if the new list is different than the list specified in the old
1838 * configuration for that logger (that could be empty), if the logger
1839 * exists or whose children exist, the handlers associated with that logger
1840 * are closed and removed and the new handlers will be created per the new
1841 * configuration and added to that logger.
1842 * </li>
1843 * <li>If the old configuration defined some handlers for a logger, and
1844 * the new configuration doesn't, if that logger exists,
1845 * its handlers will be removed and closed.
1846 * </li>
1847 * <li>Changing the list of handlers on an existing logger will cause all
1848 * its previous handlers to be removed and closed, regardless of whether
1849 * they had been created from the configuration or programmatically.
1850 * The old handlers will be replaced by new handlers, if any.
1851 * </li>
1852 * </ul>
1853 * </td>
1854 * </tr>
1855 * <tr>
1856 * <td valign="top">{@code <handler-name>.*}</td>
1857 * <td>
1858 * <ul>
1859 * <li>Properties configured/changed on handler classes will only affect
1860 * newly created handlers. If a node is configured with the same list
1861 * of handlers in the old and the new configuration, then these handlers
1862 * will remain unchanged.
1863 * </li>
1864 * </ul>
1865 * </td>
1866 * </tr>
1867 * </table>
1868 * <p>
1869 * To completely reestablish a configuration, an application can first call
1870 * {@link #reset() reset} to fully remove the old configuration, followed by
1871 * {@code updateConfiguration} to establish the new configuration.
1872 *
1873 * @param ins stream to read properties from
1874 * @param remapper a functional interface that takes a configuration
1875 * key and returns a remapping function for values associated
1876 * with that key. The remapping function takes the old value
1877 * and the candidate new value as parameters and produces the
1878 * actual value to be applied. If the {@code remapper} is null,
1879 * or if the remapping function it returns is null, then the
1880 * candidate value will be the new value.
1881 * A {@code null} value passed as parameter to the remapping
1882 * function indicates that no value was present in the
1883 * corresponding configuration.
1884 * <p>
1885 * Examples of {@code remapper} are:
1886 * <ul><li>{@code (k) -> ((o, n) -> n)}: always take the new value.
1887 * equivalent to passing null for {@code remapper}</li>
1888 * <li>{@code (k) -> ((o, n) -> n == null ? o : n)}: keep the
1889 * old value if the candidate configuration has no value
1890 * for that key, otherwise take the new value.
1891 * This is equivalent to adding the new
1892 * configuration to the old, letting new values override
1893 * old values, as what would be obtained with
1894 * {@code result.putAll(old); result.putAll(candidate)}.
1895 * </li>
1896 * <li>{@code (k) -> ((o, n) -> o == null ? n : o)}: only
1897 * take the new value if the old configuration didn't
1898 * have a value for that key.
1899 * This is equivalent to appending the new
1900 * configuration to the old, without letting new values
1901 * override old values, as what would be obtained with
1902 * {@code result.putAll(candidate); result.putAll(old)}.
1903 * </li>
1904 * <li>
1905 * <pre>{@code (k) -> k.endsWith(".handlers")}
1906 * {@code ? ((o, n) -> (o == null ? n : o))}
1907 * {@code : ((o, n) -> n)}</pre> do not let the new configuration
1908 * override existing per logger handlers. Only root handlers
1909 * can be overridden.</li>
1910 * </ul>
1911 * @throws SecurityException if a security manager exists and if
1912 * the caller does not have LoggingPermission("control"), or
1913 * does not have the permissions required to set up the
1914 * configuration (e.g. open files specified for FileHandlers)
1915 *
1916 * @exception IOException if there are problems reading from the
1917 * logging configuration file.
1918 */
1919 public void updateConfiguration(InputStream ins,
1920 Function<String, BiFunction<String,String,String>> remapper)
1921 throws IOException {
1922 checkPermission();
1923 ensureLogManagerInitialized();
1924 drainLoggerRefQueueBounded();
1925
1926 final Properties previous;
1927 final Set<String> updatePropertyNames;
1928 Properties next = new Properties();
1929 next.load(ins);
1930 List<LoggerContext> cxs = Collections.emptyList();
1931 final VisitedLoggers visited = VisitedLoggers.of(new IdentityHashMap<>());
1932
1933 if (globalHandlersState == STATE_SHUTDOWN) return;
1934
1935 // exclusive lock: readConfiguration/reset/updateConfiguration can't
1936 // run concurrently.
1937 // configurationLock.writeLock().lock();
1938 configurationLock.lock();
1939 try {
1940 if (globalHandlersState == STATE_SHUTDOWN) return;
1941 previous = props;
1942
1943 // Builds a TreeSet of all (old and new) property names.
1944 updatePropertyNames =
1945 Stream.concat(previous.stringPropertyNames().stream(),
1946 next.stringPropertyNames().stream())
1947 .collect(Collectors.toCollection(TreeSet::new));
1948
1949 if (remapper != null) {
1950 // remapper will potentially modify the content of
1951 // 'next', so we need to call it before affecting props=next.
1952 // give a chance to the remapper to control all
1953 // properties - not just those we will reset.
1954 updatePropertyNames.stream()
1955 .forEachOrdered(k -> ConfigurationProperties
1956 .merge(k, previous, next, remapper.apply(k)));
1957 }
1958
1959 props = next;
1960
1961 // allKeys will contain all keys:
1962 // - which correspond to a configuration property we are interested in
1963 // (first filter)
1964 // - whose value is changing (new, removed, different) in the configuration
1965 // (second filter)
1966 final Stream<String> allKeys = updatePropertyNames.stream()
1967 .filter(ConfigurationProperties::isOf)
1968 .filter(k -> ConfigurationProperties.isChanging(k, previous, next));
1969
1970 // Group configuration properties by logger name
1971 // We use a TreeMap so that parent loggers will be visited before
1972 // child loggers.
1973 final Map<String, TreeSet<String>> loggerConfigs =
1974 allKeys.collect(
1975 Collectors.groupingBy(
1976 ConfigurationProperties::getLoggerName,
1977 TreeMap::new,
1978 Collectors.toCollection(TreeSet::new)));
1979
1980 if (!loggerConfigs.isEmpty()) {
1981 cxs = contexts();
1982 }
1983 final List<Logger> tmp = cxs.isEmpty()
1984 ? Collections.emptyList() : new ArrayList<>(cxs.size());
1985 for (Map.Entry<String, TreeSet<String>> e : loggerConfigs.entrySet()) {
1986 // This can be a logger name, or something else...
1987 // The only thing we know is that we found a property
1988 // we are interested in.
1989 // For instance, if we found x.y.z.level, then x.y.z could be
1990 // a logger, but it could also be a handler class...
1991 // Anyway...
1992 final String name = e.getKey();
1993 final Set<String> properties = e.getValue();
1994 tmp.clear();
1995 for (LoggerContext cx : cxs) {
1996 Logger l = cx.findLogger(name);
1997 if (l == null) continue;
1998 if (!visited.test(l, name)) {
1999 tmp.add(l);
2000 }
2001 }
2002 if (tmp.isEmpty()) continue;
2003 for (String pk : properties) {
2004 ConfigurationProperties cp = ConfigurationProperties.of(pk);
2005 String p = previous.getProperty(pk, null);
2006 String n = next.getProperty(pk, null);
2007
2008 // Determines the type of modification.
2009 ModType mod = ModType.of(p, n);
2010
2011 // mod == null means that the two values are equals, there
2012 // is nothing to do. Usually, this should not happen as such
2013 // properties should have been filtered above.
2014 // It could happen however if the properties had
2015 // trailing/leading whitespaces.
2016 if (mod == null) continue;
2017
2018 switch (cp) {
2019 case LEVEL:
2020 if (mod == ModType.REMOVED) continue;
2021 Level level = Level.findLevel(trim(n));
2022 if (level == null) {
2023 if (name.isEmpty()) {
2024 rootLogger.setLevel(level);
2025 }
2026 for (Logger l : tmp) {
2027 if (!name.isEmpty() || l != rootLogger) {
2028 l.setLevel(level);
2029 }
2030 }
2031 }
2032 break;
2033 case USEPARENT:
2034 if (!name.isEmpty()) {
2035 boolean useParent = getBooleanProperty(pk, true);
2036 if (n != null || p != null) {
2037 // reset the flag only if the previous value
2038 // or the new value are not null.
2039 for (Logger l : tmp) {
2040 l.setUseParentHandlers(useParent);
2041 }
2042 }
2043 }
2044 break;
2045 case HANDLERS:
2046 List<Handler> hdls = null;
2047 if (name.isEmpty()) {
2048 // special handling for the root logger.
2049 globalHandlersState = STATE_READING_CONFIG;
2050 try {
2051 closeHandlers(rootLogger);
2052 globalHandlersState = STATE_UNINITIALIZED;
2053 } catch (Throwable t) {
2054 globalHandlersState = STATE_INITIALIZED;
2055 throw t;
2056 }
2057 }
2058 for (Logger l : tmp) {
2059 if (l == rootLogger) continue;
2060 closeHandlers(l);
2061 if (mod == ModType.REMOVED) {
2062 closeOnResetLoggers.removeIf(c -> c.logger == l);
2063 continue;
2064 }
2065 if (hdls == null) {
2066 hdls = name.isEmpty()
2067 ? Arrays.asList(rootLogger.getHandlers())
2068 : createLoggerHandlers(name, pk);
2069 }
2070 setLoggerHandlers(l, name, pk, hdls);
2071 }
2072 break;
2073 default: break;
2074 }
2075 }
2076 }
2077 } finally {
2078 configurationLock.unlock();
2079 visited.clear();
2080 }
2081
2082 // Now ensure that if an existing logger has acquired a new parent
2083 // in the configuration, this new parent will be created - if needed,
2084 // and added to the context of the existing child.
2085 //
2086 drainLoggerRefQueueBounded();
2087 for (LoggerContext cx : cxs) {
2088 for (Enumeration<String> names = cx.getLoggerNames() ; names.hasMoreElements();) {
2089 String name = names.nextElement();
2090 if (name.isEmpty()) continue; // don't need to process parents on root.
2091 Logger l = cx.findLogger(name);
2092 if (l != null && !visited.test(l, name)) {
2093 // should pass visited here to cut the processing when
2094 // reaching a logger already visited.
2095 cx.processParentHandlers(l, name, visited);
2096 }
2097 }
2098 }
2099
2100 // We changed the configuration: invoke configuration listeners
2101 invokeConfigurationListeners();
2102 }
2103
2104 /**
2105 * Get the value of a logging property.
2106 * The method returns null if the property is not found.
2107 * @param name property name
2108 * @return property value
2109 */
2110 public String getProperty(String name) {
2111 return props.getProperty(name);
2112 }
2113
2114 // Package private method to get a String property.
2115 // If the property is not defined we return the given
2116 // default value.
2117 String getStringProperty(String name, String defaultValue) {
2118 String val = getProperty(name);
2119 if (val == null) {
2120 return defaultValue;
2121 }
2122 return val.trim();
2123 }
|