Path: blob/master/src/java.logging/share/classes/java/util/logging/Logger.java
41159 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.util.logging;2627import java.lang.ref.WeakReference;28import java.security.AccessController;29import java.security.PrivilegedAction;30import java.util.ArrayList;31import java.util.Iterator;32import java.util.Locale;33import java.util.MissingResourceException;34import java.util.Objects;35import java.util.ResourceBundle;36import java.util.concurrent.CopyOnWriteArrayList;37import java.util.function.Supplier;3839import jdk.internal.access.JavaUtilResourceBundleAccess;40import jdk.internal.access.SharedSecrets;41import jdk.internal.reflect.CallerSensitive;42import jdk.internal.reflect.Reflection;43import static jdk.internal.logger.DefaultLoggerFinder.isSystem;4445/**46* A Logger object is used to log messages for a specific47* system or application component. Loggers are normally named,48* using a hierarchical dot-separated namespace. Logger names49* can be arbitrary strings, but they should normally be based on50* the package name or class name of the logged component, such51* as java.net or javax.swing. In addition it is possible to create52* "anonymous" Loggers that are not stored in the Logger namespace.53* <p>54* Logger objects may be obtained by calls on one of the getLogger55* factory methods. These will either create a new Logger or56* return a suitable existing Logger. It is important to note that57* the Logger returned by one of the {@code getLogger} factory methods58* may be garbage collected at any time if a strong reference to the59* Logger is not kept.60* <p>61* Logging messages will be forwarded to registered Handler62* objects, which can forward the messages to a variety of63* destinations, including consoles, files, OS logs, etc.64* <p>65* Each Logger keeps track of a "parent" Logger, which is its66* nearest existing ancestor in the Logger namespace.67* <p>68* Each Logger has a "Level" associated with it. This reflects69* a minimum Level that this logger cares about. If a Logger's70* level is set to {@code null}, then its effective level is inherited71* from its parent, which may in turn obtain it recursively from its72* parent, and so on up the tree.73* <p>74* The log level can be configured based on the properties from the75* logging configuration file, as described in the description76* of the LogManager class. However it may also be dynamically changed77* by calls on the Logger.setLevel method. If a logger's level is78* changed the change may also affect child loggers, since any child79* logger that has {@code null} as its level will inherit its80* effective level from its parent.81* <p>82* On each logging call the Logger initially performs a cheap83* check of the request level (e.g., SEVERE or FINE) against the84* effective log level of the logger. If the request level is85* lower than the log level, the logging call returns immediately.86* <p>87* After passing this initial (cheap) test, the Logger will allocate88* a LogRecord to describe the logging message. It will then call a89* Filter (if present) to do a more detailed check on whether the90* record should be published. If that passes it will then publish91* the LogRecord to its output Handlers. By default, loggers also92* publish to their parent's Handlers, recursively up the tree.93* <p>94* Each Logger may have a {@code ResourceBundle} associated with it.95* The {@code ResourceBundle} may be specified by name, using the96* {@link #getLogger(java.lang.String, java.lang.String)} factory97* method, or by value - using the {@link98* #setResourceBundle(java.util.ResourceBundle) setResourceBundle} method.99* This bundle will be used for localizing logging messages.100* If a Logger does not have its own {@code ResourceBundle} or resource bundle101* name, then it will inherit the {@code ResourceBundle} or resource bundle name102* from its parent, recursively up the tree.103* <p>104* Most of the logger output methods take a "msg" argument. This105* msg argument may be either a raw value or a localization key.106* During formatting, if the logger has (or inherits) a localization107* {@code ResourceBundle} and if the {@code ResourceBundle} has a mapping for108* the msg string, then the msg string is replaced by the localized value.109* Otherwise the original msg string is used. Typically, formatters use110* java.text.MessageFormat style formatting to format parameters, so111* for example a format string "{0} {1}" would format two parameters112* as strings.113* <p>114* A set of methods alternatively take a "msgSupplier" instead of a "msg"115* argument. These methods take a {@link Supplier}{@code <String>} function116* which is invoked to construct the desired log message only when the message117* actually is to be logged based on the effective log level thus eliminating118* unnecessary message construction. For example, if the developer wants to119* log system health status for diagnosis, with the String-accepting version,120* the code would look like:121* <pre>{@code122*123* class DiagnosisMessages {124* static String systemHealthStatus() {125* // collect system health information126* ...127* }128* }129* ...130* logger.log(Level.FINER, DiagnosisMessages.systemHealthStatus());131* }</pre>132* With the above code, the health status is collected unnecessarily even when133* the log level FINER is disabled. With the Supplier-accepting version as134* below, the status will only be collected when the log level FINER is135* enabled.136* <pre>{@code137*138* logger.log(Level.FINER, DiagnosisMessages::systemHealthStatus);139* }</pre>140* <p>141* When looking for a {@code ResourceBundle}, the logger will first look at142* whether a bundle was specified using {@link143* #setResourceBundle(java.util.ResourceBundle) setResourceBundle}, and then144* only whether a resource bundle name was specified through the {@link145* #getLogger(java.lang.String, java.lang.String) getLogger} factory method.146* If no {@code ResourceBundle} or no resource bundle name is found,147* then it will use the nearest {@code ResourceBundle} or resource bundle148* name inherited from its parent tree.<br>149* When a {@code ResourceBundle} was inherited or specified through the150* {@link151* #setResourceBundle(java.util.ResourceBundle) setResourceBundle} method, then152* that {@code ResourceBundle} will be used. Otherwise if the logger only153* has or inherited a resource bundle name, then that resource bundle name154* will be mapped to a {@code ResourceBundle} object, using the default Locale155* at the time of logging.156* <br id="ResourceBundleMapping">When mapping resource bundle names to157* {@code ResourceBundle} objects, the logger will first try to use the158* Thread's {@linkplain java.lang.Thread#getContextClassLoader() context class159* loader} to map the given resource bundle name to a {@code ResourceBundle}.160* If the thread context class loader is {@code null}, it will try the161* {@linkplain java.lang.ClassLoader#getSystemClassLoader() system class loader}162* instead. If the {@code ResourceBundle} is still not found, it will use the163* class loader of the first caller of the {@link164* #getLogger(java.lang.String, java.lang.String) getLogger} factory method.165* <p>166* Formatting (including localization) is the responsibility of167* the output Handler, which will typically call a Formatter.168* <p>169* Note that formatting need not occur synchronously. It may be delayed170* until a LogRecord is actually written to an external sink.171* <p>172* The logging methods are grouped in five main categories:173* <ul>174* <li><p>175* There are a set of "log" methods that take a log level, a message176* string, and optionally some parameters to the message string.177* <li><p>178* There are a set of "logp" methods (for "log precise") that are179* like the "log" methods, but also take an explicit source class name180* and method name.181* <li><p>182* There are a set of "logrb" method (for "log with resource bundle")183* that are like the "logp" method, but also take an explicit resource184* bundle object for use in localizing the log message.185* <li><p>186* There are convenience methods for tracing method entries (the187* "entering" methods), method returns (the "exiting" methods) and188* throwing exceptions (the "throwing" methods).189* <li><p>190* Finally, there are a set of convenience methods for use in the191* very simplest cases, when a developer simply wants to log a192* simple string at a given log level. These methods are named193* after the standard Level names ("severe", "warning", "info", etc.)194* and take a single argument, a message string.195* </ul>196* <p>197* For the methods that do not take an explicit source name and198* method name, the Logging framework will make a "best effort"199* to determine which class and method called into the logging method.200* However, it is important to realize that this automatically inferred201* information may only be approximate (or may even be quite wrong!).202* Virtual machines are allowed to do extensive optimizations when203* JITing and may entirely remove stack frames, making it impossible204* to reliably locate the calling class and method.205* <P>206* All methods on Logger are multi-thread safe.207* <p>208* <b>Subclassing Information:</b> Note that a LogManager class may209* provide its own implementation of named Loggers for any point in210* the namespace. Therefore, any subclasses of Logger (unless they211* are implemented in conjunction with a new LogManager class) should212* take care to obtain a Logger instance from the LogManager class and213* should delegate operations such as "isLoggable" and "log(LogRecord)"214* to that instance. Note that in order to intercept all logging215* output, subclasses need only override the log(LogRecord) method.216* All the other logging methods are implemented as calls on this217* log(LogRecord) method.218*219* @since 1.4220*/221public class Logger {222private static final Handler emptyHandlers[] = new Handler[0];223private static final int offValue = Level.OFF.intValue();224225static final String SYSTEM_LOGGER_RB_NAME = "sun.util.logging.resources.logging";226227// This class is immutable and it is important that it remains so.228private static final class LoggerBundle {229final String resourceBundleName; // Base name of the bundle.230final ResourceBundle userBundle; // Bundle set through setResourceBundle.231private LoggerBundle(String resourceBundleName, ResourceBundle bundle) {232this.resourceBundleName = resourceBundleName;233this.userBundle = bundle;234}235boolean isSystemBundle() {236return SYSTEM_LOGGER_RB_NAME.equals(resourceBundleName);237}238static LoggerBundle get(String name, ResourceBundle bundle) {239if (name == null && bundle == null) {240return NO_RESOURCE_BUNDLE;241} else if (SYSTEM_LOGGER_RB_NAME.equals(name) && bundle == null) {242return SYSTEM_BUNDLE;243} else {244return new LoggerBundle(name, bundle);245}246}247}248249// This instance will be shared by all loggers created by the system250// code251private static final LoggerBundle SYSTEM_BUNDLE =252new LoggerBundle(SYSTEM_LOGGER_RB_NAME, null);253254// This instance indicates that no resource bundle has been specified yet,255// and it will be shared by all loggers which have no resource bundle.256private static final LoggerBundle NO_RESOURCE_BUNDLE =257new LoggerBundle(null, null);258259// Calling SharedSecrets.getJavaUtilResourceBundleAccess()260// forces the initialization of ResourceBundle.class, which261// can be too early if the VM has not finished booting yet.262private static final class RbAccess {263static final JavaUtilResourceBundleAccess RB_ACCESS =264SharedSecrets.getJavaUtilResourceBundleAccess();265}266267// A value class that holds the logger configuration data.268// This configuration can be shared between an application logger269// and a system logger of the same name.270private static final class ConfigurationData {271272// The delegate field is used to avoid races while273// merging configuration. This will ensure that any pending274// configuration action on an application logger will either275// be finished before the merge happens, or will be forwarded276// to the system logger configuration after the merge is completed.277// By default delegate=this.278private volatile ConfigurationData delegate;279280volatile boolean useParentHandlers;281volatile Filter filter;282volatile Level levelObject;283volatile int levelValue; // current effective level value284final CopyOnWriteArrayList<Handler> handlers =285new CopyOnWriteArrayList<>();286287ConfigurationData() {288delegate = this;289useParentHandlers = true;290levelValue = Level.INFO.intValue();291}292293void setUseParentHandlers(boolean flag) {294useParentHandlers = flag;295if (delegate != this) {296// merge in progress - propagate value to system peer.297final ConfigurationData system = delegate;298synchronized (system) {299system.useParentHandlers = useParentHandlers;300}301}302}303304void setFilter(Filter f) {305filter = f;306if (delegate != this) {307// merge in progress - propagate value to system peer.308final ConfigurationData system = delegate;309synchronized (system) {310system.filter = filter;311}312}313}314315void setLevelObject(Level l) {316levelObject = l;317if (delegate != this) {318// merge in progress - propagate value to system peer.319final ConfigurationData system = delegate;320synchronized (system) {321system.levelObject = levelObject;322}323}324}325326void setLevelValue(int v) {327levelValue = v;328if (delegate != this) {329// merge in progress - propagate value to system peer.330final ConfigurationData system = delegate;331synchronized (system) {332system.levelValue = levelValue;333}334}335}336337void addHandler(Handler h) {338if (handlers.add(h)) {339if (delegate != this) {340// merge in progress - propagate value to system peer.341final ConfigurationData system = delegate;342synchronized (system) {343system.handlers.addIfAbsent(h);344}345}346}347}348349void removeHandler(Handler h) {350if (handlers.remove(h)) {351if (delegate != this) {352// merge in progress - propagate value to system peer.353final ConfigurationData system = delegate;354synchronized (system) {355system.handlers.remove(h);356}357}358}359}360361ConfigurationData merge(Logger systemPeer) {362if (!systemPeer.isSystemLogger) {363// should never come here364throw new InternalError("not a system logger");365}366367ConfigurationData system = systemPeer.config;368369if (system == this) {370// nothing to do371return system;372}373374synchronized (system) {375// synchronize before checking on delegate to counter376// race conditions where two threads might attempt to377// merge concurrently378if (delegate == system) {379// merge already performed;380return system;381}382383// publish system as the temporary delegate configuration.384// This should take care of potential race conditions where385// an other thread might attempt to call e.g. setlevel on386// the application logger while merge is in progress.387// (see implementation of ConfigurationData::setLevel)388delegate = system;389390// merge this config object data into the system config391system.useParentHandlers = useParentHandlers;392system.filter = filter;393system.levelObject = levelObject;394system.levelValue = levelValue;395396// Prevent race condition in case two threads attempt to merge397// configuration and add handlers at the same time. We don't want398// to add the same handlers twice.399//400// Handlers are created and loaded by LogManager.addLogger. If we401// reach here, then it means that the application logger has402// been created first and added with LogManager.addLogger, and the403// system logger was created after - and no handler has been added404// to it by LogManager.addLogger. Therefore, system.handlers405// should be empty.406//407// A non empty cfg.handlers list indicates a race condition408// where two threads might attempt to merge the configuration409// or add handlers concurrently. Though of no consequence for410// the other data (level etc...) this would be an issue if we411// added the same handlers twice.412//413for (Handler h : handlers) {414if (!system.handlers.contains(h)) {415systemPeer.addHandler(h);416}417}418system.handlers.retainAll(handlers);419system.handlers.addAllAbsent(handlers);420}421422// sanity: update effective level after merging423synchronized(treeLock) {424systemPeer.updateEffectiveLevel();425}426427return system;428}429430}431432// The logger configuration data. Ideally, this should be final433// for system loggers, and replace-once for application loggers.434// When an application requests a logger by name, we do not know a-priori435// whether that corresponds to a system logger name or not.436// So if no system logger by that name already exists, we simply return an437// application logger.438// If a system class later requests a system logger of the same name, then439// the application logger and system logger configurations will be merged440// in a single instance of ConfigurationData that both loggers will share.441private volatile ConfigurationData config;442443private volatile LogManager manager;444private String name;445private volatile LoggerBundle loggerBundle = NO_RESOURCE_BUNDLE;446private boolean anonymous;447448// Cache to speed up behavior of findResourceBundle:449private WeakReference<ResourceBundle> catalogRef; // Cached resource bundle450private String catalogName; // name associated with catalog451private Locale catalogLocale; // locale associated with catalog452453// The fields relating to parent-child relationships and levels454// are managed under a separate lock, the treeLock.455private static final Object treeLock = new Object();456// We keep weak references from parents to children, but strong457// references from children to parents.458private volatile Logger parent; // our nearest parent.459private ArrayList<LogManager.LoggerWeakRef> kids; // WeakReferences to loggers that have us as parent460private WeakReference<Module> callerModuleRef;461private final boolean isSystemLogger;462463/**464* GLOBAL_LOGGER_NAME is a name for the global logger.465*466* @since 1.6467*/468public static final String GLOBAL_LOGGER_NAME = "global";469470/**471* Return global logger object with the name Logger.GLOBAL_LOGGER_NAME.472*473* @return global logger object474* @since 1.7475*/476public static final Logger getGlobal() {477// In order to break a cyclic dependence between the LogManager478// and Logger static initializers causing deadlocks, the global479// logger is created with a special constructor that does not480// initialize its log manager.481//482// If an application calls Logger.getGlobal() before any logger483// has been initialized, it is therefore possible that the484// LogManager class has not been initialized yet, and therefore485// Logger.global.manager will be null.486//487// In order to finish the initialization of the global logger, we488// will therefore call LogManager.getLogManager() here.489//490// To prevent race conditions we also need to call491// LogManager.getLogManager() unconditionally here.492// Indeed we cannot rely on the observed value of global.manager,493// because global.manager will become not null somewhere during494// the initialization of LogManager.495// If two threads are calling getGlobal() concurrently, one thread496// will see global.manager null and call LogManager.getLogManager(),497// but the other thread could come in at a time when global.manager498// is already set although ensureLogManagerInitialized is not finished499// yet...500// Calling LogManager.getLogManager() unconditionally will fix that.501502LogManager.getLogManager();503504// Now the global LogManager should be initialized,505// and the global logger should have been added to506// it, unless we were called within the constructor of a LogManager507// subclass installed as LogManager, in which case global.manager508// would still be null, and global will be lazily initialized later on.509510return global;511}512513/**514* The "global" Logger object is provided as a convenience to developers515* who are making casual use of the Logging package. Developers516* who are making serious use of the logging package (for example517* in products) should create and use their own Logger objects,518* with appropriate names, so that logging can be controlled on a519* suitable per-Logger granularity. Developers also need to keep a520* strong reference to their Logger objects to prevent them from521* being garbage collected.522*523* @deprecated Initialization of this field is prone to deadlocks.524* The field must be initialized by the Logger class initialization525* which may cause deadlocks with the LogManager class initialization.526* In such cases two class initialization wait for each other to complete.527* The preferred way to get the global logger object is via the call528* {@code Logger.getGlobal()}.529* For compatibility with old JDK versions where the530* {@code Logger.getGlobal()} is not available use the call531* {@code Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)}532* or {@code Logger.getLogger("global")}.533*/534@Deprecated535public static final Logger global = new Logger(GLOBAL_LOGGER_NAME);536537/**538* Protected method to construct a logger for a named subsystem.539* <p>540* The logger will be initially configured with a null Level541* and with useParentHandlers set to true.542*543* @param name A name for the logger. This should544* be a dot-separated name and should normally545* be based on the package name or class name546* of the subsystem, such as java.net547* or javax.swing. It may be null for anonymous Loggers.548* @param resourceBundleName name of ResourceBundle to be used for localizing549* messages for this logger. May be null if none550* of the messages require localization.551* @throws MissingResourceException if the resourceBundleName is non-null and552* no corresponding resource can be found.553*/554protected Logger(String name, String resourceBundleName) {555this(name, resourceBundleName, null, LogManager.getLogManager(), false);556}557558Logger(String name, String resourceBundleName, Module caller,559LogManager manager, boolean isSystemLogger) {560this.manager = manager;561this.isSystemLogger = isSystemLogger;562this.config = new ConfigurationData();563this.name = name;564setupResourceInfo(resourceBundleName, caller);565}566567// Called by LogManager when a system logger is created568// after a user logger of the same name.569// Ensure that both loggers will share the same570// configuration.571final void mergeWithSystemLogger(Logger system) {572// sanity checks573if (!system.isSystemLogger574|| anonymous575|| name == null576|| !name.equals(system.name)) {577// should never come here578throw new InternalError("invalid logger merge");579}580checkPermission();581final ConfigurationData cfg = config;582if (cfg != system.config) {583config = cfg.merge(system);584}585}586587private void setCallerModuleRef(Module callerModule) {588if (callerModule != null) {589this.callerModuleRef = new WeakReference<>(callerModule);590}591}592593private Module getCallerModule() {594return (callerModuleRef != null)595? callerModuleRef.get()596: null;597}598599// This constructor is used only to create the global Logger.600// It is needed to break a cyclic dependence between the LogManager601// and Logger static initializers causing deadlocks.602private Logger(String name) {603// The manager field is not initialized here.604this.name = name;605this.isSystemLogger = true;606config = new ConfigurationData();607}608609// It is called from LoggerContext.addLocalLogger() when the logger610// is actually added to a LogManager.611void setLogManager(LogManager manager) {612this.manager = manager;613}614615private void checkPermission() throws SecurityException {616if (!anonymous) {617if (manager == null) {618// Complete initialization of the global Logger.619manager = LogManager.getLogManager();620}621manager.checkPermission();622}623}624625// Until all JDK code converted to call sun.util.logging.PlatformLogger626// (see 7054233), we need to determine if Logger.getLogger is to add627// a system logger or user logger.628//629// As an interim solution, if the immediate caller whose caller loader is630// null, we assume it's a system logger and add it to the system context.631// These system loggers only set the resource bundle to the given632// resource bundle name (rather than the default system resource bundle).633private static class SystemLoggerHelper {634static boolean disableCallerCheck = getBooleanProperty("sun.util.logging.disableCallerCheck");635private static boolean getBooleanProperty(final String key) {636@SuppressWarnings("removal")637String s = AccessController.doPrivileged(new PrivilegedAction<String>() {638@Override639public String run() {640return System.getProperty(key);641}642});643return Boolean.parseBoolean(s);644}645}646647private static Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {648LogManager manager = LogManager.getLogManager();649if (!SystemLoggerHelper.disableCallerCheck) {650if (isSystem(caller.getModule())) {651return manager.demandSystemLogger(name, resourceBundleName, caller);652}653}654return manager.demandLogger(name, resourceBundleName, caller);655// ends up calling new Logger(name, resourceBundleName, caller)656// iff the logger doesn't exist already657}658659/**660* Find or create a logger for a named subsystem. If a logger has661* already been created with the given name it is returned. Otherwise662* a new logger is created.663* <p>664* If a new logger is created its log level will be configured665* based on the LogManager configuration and it will be configured666* to also send logging output to its parent's Handlers. It will667* be registered in the LogManager global namespace.668* <p>669* Note: The LogManager may only retain a weak reference to the newly670* created Logger. It is important to understand that a previously671* created Logger with the given name may be garbage collected at any672* time if there is no strong reference to the Logger. In particular,673* this means that two back-to-back calls like674* {@code getLogger("MyLogger").log(...)} may use different Logger675* objects named "MyLogger" if there is no strong reference to the676* Logger named "MyLogger" elsewhere in the program.677*678* @param name A name for the logger. This should679* be a dot-separated name and should normally680* be based on the package name or class name681* of the subsystem, such as java.net682* or javax.swing683* @return a suitable Logger684* @throws NullPointerException if the name is null.685*/686687// Synchronization is not required here. All synchronization for688// adding a new Logger object is handled by LogManager.addLogger().689@CallerSensitive690public static Logger getLogger(String name) {691// This method is intentionally not a wrapper around a call692// to getLogger(name, resourceBundleName). If it were then693// this sequence:694//695// getLogger("Foo", "resourceBundleForFoo");696// getLogger("Foo");697//698// would throw an IllegalArgumentException in the second call699// because the wrapper would result in an attempt to replace700// the existing "resourceBundleForFoo" with null.701return Logger.getLogger(name, Reflection.getCallerClass());702}703704/**705* Find or create a logger for a named subsystem on behalf706* of the given caller.707*708* This method is called by {@link #getLogger(java.lang.String)} after709* it has obtained a reference to its caller's class.710*711* @param name A name for the logger.712* @param callerClass The class that called {@link713* #getLogger(java.lang.String)}.714* @return a suitable Logger for {@code callerClass}.715*/716private static Logger getLogger(String name, Class<?> callerClass) {717return demandLogger(name, null, callerClass);718}719720/**721* Find or create a logger for a named subsystem. If a logger has722* already been created with the given name it is returned. Otherwise723* a new logger is created.724*725* <p>726* If a new logger is created its log level will be configured727* based on the LogManager and it will be configured to also send logging728* output to its parent's Handlers. It will be registered in729* the LogManager global namespace.730* <p>731* Note: The LogManager may only retain a weak reference to the newly732* created Logger. It is important to understand that a previously733* created Logger with the given name may be garbage collected at any734* time if there is no strong reference to the Logger. In particular,735* this means that two back-to-back calls like736* {@code getLogger("MyLogger", ...).log(...)} may use different Logger737* objects named "MyLogger" if there is no strong reference to the738* Logger named "MyLogger" elsewhere in the program.739* <p>740* If the named Logger already exists and does not yet have a741* localization resource bundle then the given resource bundle742* name is used. If the named Logger already exists and has743* a different resource bundle name then an IllegalArgumentException744* is thrown.745*746* @param name A name for the logger. This should747* be a dot-separated name and should normally748* be based on the package name or class name749* of the subsystem, such as java.net750* or javax.swing751* @param resourceBundleName name of ResourceBundle to be used for localizing752* messages for this logger. May be {@code null}753* if none of the messages require localization.754* @return a suitable Logger755* @throws MissingResourceException if the resourceBundleName is non-null and756* no corresponding resource can be found.757* @throws IllegalArgumentException if the Logger already exists and uses758* a different resource bundle name; or if759* {@code resourceBundleName} is {@code null} but the named760* logger has a resource bundle set.761* @throws NullPointerException if the name is null.762*/763764// Synchronization is not required here. All synchronization for765// adding a new Logger object is handled by LogManager.addLogger().766@CallerSensitive767public static Logger getLogger(String name, String resourceBundleName) {768return Logger.getLogger(name, resourceBundleName, Reflection.getCallerClass());769}770771/**772* Find or create a logger for a named subsystem on behalf773* of the given caller.774*775* This method is called by {@link776* #getLogger(java.lang.String, java.lang.String)} after777* it has obtained a reference to its caller's class.778*779* @param name A name for the logger.780* @param resourceBundleName name of ResourceBundle to be used for localizing781* messages for this logger. May be {@code null}782* if none of the messages require localization.783* @param callerClass The class that called {@link784* #getLogger(java.lang.String, java.lang.String)}.785* This class will also be used for locating the786* resource bundle if {@code resourceBundleName} is787* not {@code null}.788* @return a suitable Logger for {@code callerClass}.789*/790private static Logger getLogger(String name, String resourceBundleName,791Class<?> callerClass) {792Logger result = demandLogger(name, resourceBundleName, callerClass);793794// MissingResourceException or IllegalArgumentException can be795// thrown by setupResourceInfo().796// We have to set the callers ClassLoader here in case demandLogger797// above found a previously created Logger. This can happen, for798// example, if Logger.getLogger(name) is called and subsequently799// Logger.getLogger(name, resourceBundleName) is called. In this case800// we won't necessarily have the correct classloader saved away, so801// we need to set it here, too.802803result.setupResourceInfo(resourceBundleName, callerClass);804return result;805}806807// package-private808// Add a platform logger to the system context.809// i.e. caller of sun.util.logging.PlatformLogger.getLogger810static Logger getPlatformLogger(String name) {811LogManager manager = LogManager.getLogManager();812813// all loggers in the system context will default to814// the system logger's resource bundle - therefore the caller won't815// be needed and can be null.816Logger result = manager.demandSystemLogger(name, SYSTEM_LOGGER_RB_NAME, (Module)null);817return result;818}819820/**821* Create an anonymous Logger. The newly created Logger is not822* registered in the LogManager namespace. There will be no823* access checks on updates to the logger.824* <p>825* This factory method is primarily intended for use from applets.826* Because the resulting Logger is anonymous it can be kept private827* by the creating class. This removes the need for normal security828* checks, which in turn allows untrusted applet code to update829* the control state of the Logger. For example an applet can do830* a setLevel or an addHandler on an anonymous Logger.831* <p>832* Even although the new logger is anonymous, it is configured833* to have the root logger ("") as its parent. This means that834* by default it inherits its effective level and handlers835* from the root logger. Changing its parent via the836* {@link #setParent(java.util.logging.Logger) setParent} method837* will still require the security permission specified by that method.838*839* @return a newly created private Logger840*/841public static Logger getAnonymousLogger() {842return getAnonymousLogger(null);843}844845/**846* Create an anonymous Logger. The newly created Logger is not847* registered in the LogManager namespace. There will be no848* access checks on updates to the logger.849* <p>850* This factory method is primarily intended for use from applets.851* Because the resulting Logger is anonymous it can be kept private852* by the creating class. This removes the need for normal security853* checks, which in turn allows untrusted applet code to update854* the control state of the Logger. For example an applet can do855* a setLevel or an addHandler on an anonymous Logger.856* <p>857* Even although the new logger is anonymous, it is configured858* to have the root logger ("") as its parent. This means that859* by default it inherits its effective level and handlers860* from the root logger. Changing its parent via the861* {@link #setParent(java.util.logging.Logger) setParent} method862* will still require the security permission specified by that method.863*864* @param resourceBundleName name of ResourceBundle to be used for localizing865* messages for this logger.866* May be null if none of the messages require localization.867* @return a newly created private Logger868* @throws MissingResourceException if the resourceBundleName is non-null and869* no corresponding resource can be found.870*/871872// Synchronization is not required here. All synchronization for873// adding a new anonymous Logger object is handled by doSetParent().874@CallerSensitive875public static Logger getAnonymousLogger(String resourceBundleName) {876LogManager manager = LogManager.getLogManager();877// cleanup some Loggers that have been GC'ed878manager.drainLoggerRefQueueBounded();879final Class<?> callerClass = Reflection.getCallerClass();880final Module module = callerClass.getModule();881Logger result = new Logger(null, resourceBundleName,882module, manager, false);883result.anonymous = true;884Logger root = manager.getLogger("");885result.doSetParent(root);886return result;887}888889/**890* Retrieve the localization resource bundle for this891* logger.892* This method will return a {@code ResourceBundle} that was either893* set by the {@link894* #setResourceBundle(java.util.ResourceBundle) setResourceBundle} method or895* <a href="#ResourceBundleMapping">mapped from the896* the resource bundle name</a> set via the {@link897* Logger#getLogger(java.lang.String, java.lang.String) getLogger} factory898* method for the current default locale.899* <br>Note that if the result is {@code null}, then the Logger will use a resource900* bundle or resource bundle name inherited from its parent.901*902* @return localization bundle (may be {@code null})903*/904public ResourceBundle getResourceBundle() {905return findResourceBundle(getResourceBundleName(), true);906}907908/**909* Retrieve the localization resource bundle name for this910* logger.911* This is either the name specified through the {@link912* #getLogger(java.lang.String, java.lang.String) getLogger} factory method,913* or the {@linkplain ResourceBundle#getBaseBundleName() base name} of the914* ResourceBundle set through {@link915* #setResourceBundle(java.util.ResourceBundle) setResourceBundle} method.916* <br>Note that if the result is {@code null}, then the Logger will use a resource917* bundle or resource bundle name inherited from its parent.918*919* @return localization bundle name (may be {@code null})920*/921public String getResourceBundleName() {922return loggerBundle.resourceBundleName;923}924925/**926* Set a filter to control output on this Logger.927* <P>928* After passing the initial "level" check, the Logger will929* call this Filter to check if a log record should really930* be published.931*932* @param newFilter a filter object (may be null)933* @throws SecurityException if a security manager exists,934* this logger is not anonymous, and the caller935* does not have LoggingPermission("control").936*/937public void setFilter(Filter newFilter) throws SecurityException {938checkPermission();939config.setFilter(newFilter);940}941942/**943* Get the current filter for this Logger.944*945* @return a filter object (may be null)946*/947public Filter getFilter() {948return config.filter;949}950951/**952* Log a LogRecord.953* <p>954* All the other logging methods in this class call through955* this method to actually perform any logging. Subclasses can956* override this single method to capture all log activity.957*958* @param record the LogRecord to be published959*/960public void log(LogRecord record) {961if (!isLoggable(record.getLevel())) {962return;963}964Filter theFilter = config.filter;965if (theFilter != null && !theFilter.isLoggable(record)) {966return;967}968969// Post the LogRecord to all our Handlers, and then to970// our parents' handlers, all the way up the tree.971972Logger logger = this;973while (logger != null) {974final Handler[] loggerHandlers = isSystemLogger975? logger.accessCheckedHandlers()976: logger.getHandlers();977978for (Handler handler : loggerHandlers) {979handler.publish(record);980}981982final boolean useParentHdls = isSystemLogger983? logger.config.useParentHandlers984: logger.getUseParentHandlers();985986if (!useParentHdls) {987break;988}989990logger = isSystemLogger ? logger.parent : logger.getParent();991}992}993994// private support method for logging.995// We fill in the logger name, resource bundle name, and996// resource bundle and then call "void log(LogRecord)".997private void doLog(LogRecord lr) {998lr.setLoggerName(name);999final LoggerBundle lb = getEffectiveLoggerBundle();1000final ResourceBundle bundle = lb.userBundle;1001final String ebname = lb.resourceBundleName;1002if (ebname != null && bundle != null) {1003lr.setResourceBundleName(ebname);1004lr.setResourceBundle(bundle);1005}1006log(lr);1007}100810091010//================================================================1011// Start of convenience methods WITHOUT className and methodName1012//================================================================10131014/**1015* Log a message, with no arguments.1016* <p>1017* If the logger is currently enabled for the given message1018* level then the given message is forwarded to all the1019* registered output Handler objects.1020*1021* @param level One of the message level identifiers, e.g., SEVERE1022* @param msg The string message (or a key in the message catalog)1023*/1024public void log(Level level, String msg) {1025if (!isLoggable(level)) {1026return;1027}1028LogRecord lr = new LogRecord(level, msg);1029doLog(lr);1030}10311032/**1033* Log a message, which is only to be constructed if the logging level1034* is such that the message will actually be logged.1035* <p>1036* If the logger is currently enabled for the given message1037* level then the message is constructed by invoking the provided1038* supplier function and forwarded to all the registered output1039* Handler objects.1040*1041* @param level One of the message level identifiers, e.g., SEVERE1042* @param msgSupplier A function, which when called, produces the1043* desired log message1044* @since 1.81045*/1046public void log(Level level, Supplier<String> msgSupplier) {1047if (!isLoggable(level)) {1048return;1049}1050LogRecord lr = new LogRecord(level, msgSupplier.get());1051doLog(lr);1052}10531054/**1055* Log a message, with one object parameter.1056* <p>1057* If the logger is currently enabled for the given message1058* level then a corresponding LogRecord is created and forwarded1059* to all the registered output Handler objects.1060*1061* @param level One of the message level identifiers, e.g., SEVERE1062* @param msg The string message (or a key in the message catalog)1063* @param param1 parameter to the message1064*/1065public void log(Level level, String msg, Object param1) {1066if (!isLoggable(level)) {1067return;1068}1069LogRecord lr = new LogRecord(level, msg);1070Object params[] = { param1 };1071lr.setParameters(params);1072doLog(lr);1073}10741075/**1076* Log a message, with an array of object arguments.1077* <p>1078* If the logger is currently enabled for the given message1079* level then a corresponding LogRecord is created and forwarded1080* to all the registered output Handler objects.1081*1082* @param level One of the message level identifiers, e.g., SEVERE1083* @param msg The string message (or a key in the message catalog)1084* @param params array of parameters to the message1085*/1086public void log(Level level, String msg, Object params[]) {1087if (!isLoggable(level)) {1088return;1089}1090LogRecord lr = new LogRecord(level, msg);1091lr.setParameters(params);1092doLog(lr);1093}10941095/**1096* Log a message, with associated Throwable information.1097* <p>1098* If the logger is currently enabled for the given message1099* level then the given arguments are stored in a LogRecord1100* which is forwarded to all registered output handlers.1101* <p>1102* Note that the thrown argument is stored in the LogRecord thrown1103* property, rather than the LogRecord parameters property. Thus it is1104* processed specially by output Formatters and is not treated1105* as a formatting parameter to the LogRecord message property.1106*1107* @param level One of the message level identifiers, e.g., SEVERE1108* @param msg The string message (or a key in the message catalog)1109* @param thrown Throwable associated with log message.1110*/1111public void log(Level level, String msg, Throwable thrown) {1112if (!isLoggable(level)) {1113return;1114}1115LogRecord lr = new LogRecord(level, msg);1116lr.setThrown(thrown);1117doLog(lr);1118}11191120/**1121* Log a lazily constructed message, with associated Throwable information.1122* <p>1123* If the logger is currently enabled for the given message level then the1124* message is constructed by invoking the provided supplier function. The1125* message and the given {@link Throwable} are then stored in a {@link1126* LogRecord} which is forwarded to all registered output handlers.1127* <p>1128* Note that the thrown argument is stored in the LogRecord thrown1129* property, rather than the LogRecord parameters property. Thus it is1130* processed specially by output Formatters and is not treated1131* as a formatting parameter to the LogRecord message property.1132*1133* @param level One of the message level identifiers, e.g., SEVERE1134* @param thrown Throwable associated with log message.1135* @param msgSupplier A function, which when called, produces the1136* desired log message1137* @since 1.81138*/1139public void log(Level level, Throwable thrown, Supplier<String> msgSupplier) {1140if (!isLoggable(level)) {1141return;1142}1143LogRecord lr = new LogRecord(level, msgSupplier.get());1144lr.setThrown(thrown);1145doLog(lr);1146}11471148//================================================================1149// Start of convenience methods WITH className and methodName1150//================================================================11511152/**1153* Log a message, specifying source class and method,1154* with no arguments.1155* <p>1156* If the logger is currently enabled for the given message1157* level then the given message is forwarded to all the1158* registered output Handler objects.1159*1160* @param level One of the message level identifiers, e.g., SEVERE1161* @param sourceClass name of class that issued the logging request1162* @param sourceMethod name of method that issued the logging request1163* @param msg The string message (or a key in the message catalog)1164*/1165public void logp(Level level, String sourceClass, String sourceMethod, String msg) {1166if (!isLoggable(level)) {1167return;1168}1169LogRecord lr = new LogRecord(level, msg);1170lr.setSourceClassName(sourceClass);1171lr.setSourceMethodName(sourceMethod);1172doLog(lr);1173}11741175/**1176* Log a lazily constructed message, specifying source class and method,1177* with no arguments.1178* <p>1179* If the logger is currently enabled for the given message1180* level then the message is constructed by invoking the provided1181* supplier function and forwarded to all the registered output1182* Handler objects.1183*1184* @param level One of the message level identifiers, e.g., SEVERE1185* @param sourceClass name of class that issued the logging request1186* @param sourceMethod name of method that issued the logging request1187* @param msgSupplier A function, which when called, produces the1188* desired log message1189* @since 1.81190*/1191public void logp(Level level, String sourceClass, String sourceMethod,1192Supplier<String> msgSupplier) {1193if (!isLoggable(level)) {1194return;1195}1196LogRecord lr = new LogRecord(level, msgSupplier.get());1197lr.setSourceClassName(sourceClass);1198lr.setSourceMethodName(sourceMethod);1199doLog(lr);1200}12011202/**1203* Log a message, specifying source class and method,1204* with a single object parameter to the log message.1205* <p>1206* If the logger is currently enabled for the given message1207* level then a corresponding LogRecord is created and forwarded1208* to all the registered output Handler objects.1209*1210* @param level One of the message level identifiers, e.g., SEVERE1211* @param sourceClass name of class that issued the logging request1212* @param sourceMethod name of method that issued the logging request1213* @param msg The string message (or a key in the message catalog)1214* @param param1 Parameter to the log message.1215*/1216public void logp(Level level, String sourceClass, String sourceMethod,1217String msg, Object param1) {1218if (!isLoggable(level)) {1219return;1220}1221LogRecord lr = new LogRecord(level, msg);1222lr.setSourceClassName(sourceClass);1223lr.setSourceMethodName(sourceMethod);1224Object params[] = { param1 };1225lr.setParameters(params);1226doLog(lr);1227}12281229/**1230* Log a message, specifying source class and method,1231* with an array of object arguments.1232* <p>1233* If the logger is currently enabled for the given message1234* level then a corresponding LogRecord is created and forwarded1235* to all the registered output Handler objects.1236*1237* @param level One of the message level identifiers, e.g., SEVERE1238* @param sourceClass name of class that issued the logging request1239* @param sourceMethod name of method that issued the logging request1240* @param msg The string message (or a key in the message catalog)1241* @param params Array of parameters to the message1242*/1243public void logp(Level level, String sourceClass, String sourceMethod,1244String msg, Object params[]) {1245if (!isLoggable(level)) {1246return;1247}1248LogRecord lr = new LogRecord(level, msg);1249lr.setSourceClassName(sourceClass);1250lr.setSourceMethodName(sourceMethod);1251lr.setParameters(params);1252doLog(lr);1253}12541255/**1256* Log a message, specifying source class and method,1257* with associated Throwable information.1258* <p>1259* If the logger is currently enabled for the given message1260* level then the given arguments are stored in a LogRecord1261* which is forwarded to all registered output handlers.1262* <p>1263* Note that the thrown argument is stored in the LogRecord thrown1264* property, rather than the LogRecord parameters property. Thus it is1265* processed specially by output Formatters and is not treated1266* as a formatting parameter to the LogRecord message property.1267*1268* @param level One of the message level identifiers, e.g., SEVERE1269* @param sourceClass name of class that issued the logging request1270* @param sourceMethod name of method that issued the logging request1271* @param msg The string message (or a key in the message catalog)1272* @param thrown Throwable associated with log message.1273*/1274public void logp(Level level, String sourceClass, String sourceMethod,1275String msg, Throwable thrown) {1276if (!isLoggable(level)) {1277return;1278}1279LogRecord lr = new LogRecord(level, msg);1280lr.setSourceClassName(sourceClass);1281lr.setSourceMethodName(sourceMethod);1282lr.setThrown(thrown);1283doLog(lr);1284}12851286/**1287* Log a lazily constructed message, specifying source class and method,1288* with associated Throwable information.1289* <p>1290* If the logger is currently enabled for the given message level then the1291* message is constructed by invoking the provided supplier function. The1292* message and the given {@link Throwable} are then stored in a {@link1293* LogRecord} which is forwarded to all registered output handlers.1294* <p>1295* Note that the thrown argument is stored in the LogRecord thrown1296* property, rather than the LogRecord parameters property. Thus it is1297* processed specially by output Formatters and is not treated1298* as a formatting parameter to the LogRecord message property.1299*1300* @param level One of the message level identifiers, e.g., SEVERE1301* @param sourceClass name of class that issued the logging request1302* @param sourceMethod name of method that issued the logging request1303* @param thrown Throwable associated with log message.1304* @param msgSupplier A function, which when called, produces the1305* desired log message1306* @since 1.81307*/1308public void logp(Level level, String sourceClass, String sourceMethod,1309Throwable thrown, Supplier<String> msgSupplier) {1310if (!isLoggable(level)) {1311return;1312}1313LogRecord lr = new LogRecord(level, msgSupplier.get());1314lr.setSourceClassName(sourceClass);1315lr.setSourceMethodName(sourceMethod);1316lr.setThrown(thrown);1317doLog(lr);1318}131913201321//=========================================================================1322// Start of convenience methods WITH className, methodName and bundle name.1323//=========================================================================13241325// Private support method for logging for "logrb" methods.1326// We fill in the logger name, resource bundle name, and1327// resource bundle and then call "void log(LogRecord)".1328private void doLog(LogRecord lr, String rbname) {1329lr.setLoggerName(name);1330if (rbname != null) {1331lr.setResourceBundleName(rbname);1332lr.setResourceBundle(findResourceBundle(rbname, false));1333}1334log(lr);1335}13361337// Private support method for logging for "logrb" methods.1338private void doLog(LogRecord lr, ResourceBundle rb) {1339lr.setLoggerName(name);1340if (rb != null) {1341lr.setResourceBundleName(rb.getBaseBundleName());1342lr.setResourceBundle(rb);1343}1344log(lr);1345}13461347/**1348* Log a message, specifying source class, method, and resource bundle name1349* with no arguments.1350* <p>1351* If the logger is currently enabled for the given message1352* level then the given message is forwarded to all the1353* registered output Handler objects.1354* <p>1355* The msg string is localized using the named resource bundle. If the1356* resource bundle name is null, or an empty String or invalid1357* then the msg string is not localized.1358*1359* @param level One of the message level identifiers, e.g., SEVERE1360* @param sourceClass name of class that issued the logging request1361* @param sourceMethod name of method that issued the logging request1362* @param bundleName name of resource bundle to localize msg,1363* can be null1364* @param msg The string message (or a key in the message catalog)1365* @deprecated Use {@link #logrb(java.util.logging.Level, java.lang.String,1366* java.lang.String, java.util.ResourceBundle, java.lang.String,1367* java.lang.Object...)} instead.1368*/1369@Deprecated1370public void logrb(Level level, String sourceClass, String sourceMethod,1371String bundleName, String msg) {1372if (!isLoggable(level)) {1373return;1374}1375LogRecord lr = new LogRecord(level, msg);1376lr.setSourceClassName(sourceClass);1377lr.setSourceMethodName(sourceMethod);1378doLog(lr, bundleName);1379}13801381/**1382* Log a message, specifying source class, method, and resource bundle name,1383* with a single object parameter to the log message.1384* <p>1385* If the logger is currently enabled for the given message1386* level then a corresponding LogRecord is created and forwarded1387* to all the registered output Handler objects.1388* <p>1389* The msg string is localized using the named resource bundle. If the1390* resource bundle name is null, or an empty String or invalid1391* then the msg string is not localized.1392*1393* @param level One of the message level identifiers, e.g., SEVERE1394* @param sourceClass name of class that issued the logging request1395* @param sourceMethod name of method that issued the logging request1396* @param bundleName name of resource bundle to localize msg,1397* can be null1398* @param msg The string message (or a key in the message catalog)1399* @param param1 Parameter to the log message.1400* @deprecated Use {@link #logrb(java.util.logging.Level, java.lang.String,1401* java.lang.String, java.util.ResourceBundle, java.lang.String,1402* java.lang.Object...)} instead1403*/1404@Deprecated1405public void logrb(Level level, String sourceClass, String sourceMethod,1406String bundleName, String msg, Object param1) {1407if (!isLoggable(level)) {1408return;1409}1410LogRecord lr = new LogRecord(level, msg);1411lr.setSourceClassName(sourceClass);1412lr.setSourceMethodName(sourceMethod);1413Object params[] = { param1 };1414lr.setParameters(params);1415doLog(lr, bundleName);1416}14171418/**1419* Log a message, specifying source class, method, and resource bundle name,1420* with an array of object arguments.1421* <p>1422* If the logger is currently enabled for the given message1423* level then a corresponding LogRecord is created and forwarded1424* to all the registered output Handler objects.1425* <p>1426* The msg string is localized using the named resource bundle. If the1427* resource bundle name is null, or an empty String or invalid1428* then the msg string is not localized.1429*1430* @param level One of the message level identifiers, e.g., SEVERE1431* @param sourceClass name of class that issued the logging request1432* @param sourceMethod name of method that issued the logging request1433* @param bundleName name of resource bundle to localize msg,1434* can be null.1435* @param msg The string message (or a key in the message catalog)1436* @param params Array of parameters to the message1437* @deprecated Use {@link #logrb(java.util.logging.Level, java.lang.String,1438* java.lang.String, java.util.ResourceBundle, java.lang.String,1439* java.lang.Object...)} instead.1440*/1441@Deprecated1442public void logrb(Level level, String sourceClass, String sourceMethod,1443String bundleName, String msg, Object params[]) {1444if (!isLoggable(level)) {1445return;1446}1447LogRecord lr = new LogRecord(level, msg);1448lr.setSourceClassName(sourceClass);1449lr.setSourceMethodName(sourceMethod);1450lr.setParameters(params);1451doLog(lr, bundleName);1452}14531454/**1455* Log a message, specifying source class, method, and resource bundle,1456* with an optional list of message parameters.1457* <p>1458* If the logger is currently enabled for the given message1459* {@code level} then a corresponding {@code LogRecord} is created and1460* forwarded to all the registered output {@code Handler} objects.1461* <p>1462* The {@code msg} string is localized using the given resource bundle.1463* If the resource bundle is {@code null}, then the {@code msg} string is not1464* localized.1465*1466* @param level One of the message level identifiers, e.g., {@code SEVERE}1467* @param sourceClass Name of the class that issued the logging request1468* @param sourceMethod Name of the method that issued the logging request1469* @param bundle Resource bundle to localize {@code msg},1470* can be {@code null}.1471* @param msg The string message (or a key in the message catalog)1472* @param params Parameters to the message (optional, may be none).1473* @since 1.81474*/1475public void logrb(Level level, String sourceClass, String sourceMethod,1476ResourceBundle bundle, String msg, Object... params) {1477if (!isLoggable(level)) {1478return;1479}1480LogRecord lr = new LogRecord(level, msg);1481lr.setSourceClassName(sourceClass);1482lr.setSourceMethodName(sourceMethod);1483if (params != null && params.length != 0) {1484lr.setParameters(params);1485}1486doLog(lr, bundle);1487}14881489/**1490* Log a message, specifying source class, method, and resource bundle,1491* with an optional list of message parameters.1492* <p>1493* If the logger is currently enabled for the given message1494* {@code level} then a corresponding {@code LogRecord} is created1495* and forwarded to all the registered output {@code Handler} objects.1496* <p>1497* The {@code msg} string is localized using the given resource bundle.1498* If the resource bundle is {@code null}, then the {@code msg} string is not1499* localized.1500*1501* @param level One of the message level identifiers, e.g., {@code SEVERE}1502* @param bundle Resource bundle to localize {@code msg};1503* can be {@code null}.1504* @param msg The string message (or a key in the message catalog)1505* @param params Parameters to the message (optional, may be none).1506* @since 91507*/1508public void logrb(Level level, ResourceBundle bundle, String msg, Object... params) {1509if (!isLoggable(level)) {1510return;1511}1512LogRecord lr = new LogRecord(level, msg);1513if (params != null && params.length != 0) {1514lr.setParameters(params);1515}1516doLog(lr, bundle);1517}15181519/**1520* Log a message, specifying source class, method, and resource bundle name,1521* with associated Throwable information.1522* <p>1523* If the logger is currently enabled for the given message1524* level then the given arguments are stored in a LogRecord1525* which is forwarded to all registered output handlers.1526* <p>1527* The msg string is localized using the named resource bundle. If the1528* resource bundle name is null, or an empty String or invalid1529* then the msg string is not localized.1530* <p>1531* Note that the thrown argument is stored in the LogRecord thrown1532* property, rather than the LogRecord parameters property. Thus it is1533* processed specially by output Formatters and is not treated1534* as a formatting parameter to the LogRecord message property.1535*1536* @param level One of the message level identifiers, e.g., SEVERE1537* @param sourceClass name of class that issued the logging request1538* @param sourceMethod name of method that issued the logging request1539* @param bundleName name of resource bundle to localize msg,1540* can be null1541* @param msg The string message (or a key in the message catalog)1542* @param thrown Throwable associated with log message.1543* @deprecated Use {@link #logrb(java.util.logging.Level, java.lang.String,1544* java.lang.String, java.util.ResourceBundle, java.lang.String,1545* java.lang.Throwable)} instead.1546*/1547@Deprecated1548public void logrb(Level level, String sourceClass, String sourceMethod,1549String bundleName, String msg, Throwable thrown) {1550if (!isLoggable(level)) {1551return;1552}1553LogRecord lr = new LogRecord(level, msg);1554lr.setSourceClassName(sourceClass);1555lr.setSourceMethodName(sourceMethod);1556lr.setThrown(thrown);1557doLog(lr, bundleName);1558}15591560/**1561* Log a message, specifying source class, method, and resource bundle,1562* with associated Throwable information.1563* <p>1564* If the logger is currently enabled for the given message1565* {@code level} then the given arguments are stored in a {@code LogRecord}1566* which is forwarded to all registered output handlers.1567* <p>1568* The {@code msg} string is localized using the given resource bundle.1569* If the resource bundle is {@code null}, then the {@code msg} string is not1570* localized.1571* <p>1572* Note that the {@code thrown} argument is stored in the {@code LogRecord}1573* {@code thrown} property, rather than the {@code LogRecord}1574* {@code parameters} property. Thus it is1575* processed specially by output {@code Formatter} objects and is not treated1576* as a formatting parameter to the {@code LogRecord} {@code message} property.1577*1578* @param level One of the message level identifiers, e.g., {@code SEVERE}1579* @param sourceClass Name of the class that issued the logging request1580* @param sourceMethod Name of the method that issued the logging request1581* @param bundle Resource bundle to localize {@code msg},1582* can be {@code null}1583* @param msg The string message (or a key in the message catalog)1584* @param thrown Throwable associated with the log message.1585* @since 1.81586*/1587public void logrb(Level level, String sourceClass, String sourceMethod,1588ResourceBundle bundle, String msg, Throwable thrown) {1589if (!isLoggable(level)) {1590return;1591}1592LogRecord lr = new LogRecord(level, msg);1593lr.setSourceClassName(sourceClass);1594lr.setSourceMethodName(sourceMethod);1595lr.setThrown(thrown);1596doLog(lr, bundle);1597}15981599/**1600* Log a message, specifying source class, method, and resource bundle,1601* with associated Throwable information.1602* <p>1603* If the logger is currently enabled for the given message1604* {@code level} then the given arguments are stored in a {@code LogRecord}1605* which is forwarded to all registered output handlers.1606* <p>1607* The {@code msg} string is localized using the given resource bundle.1608* If the resource bundle is {@code null}, then the {@code msg} string is not1609* localized.1610* <p>1611* Note that the {@code thrown} argument is stored in the {@code LogRecord}1612* {@code thrown} property, rather than the {@code LogRecord}1613* {@code parameters} property. Thus it is1614* processed specially by output {@code Formatter} objects and is not treated1615* as a formatting parameter to the {@code LogRecord} {@code message}1616* property.1617*1618* @param level One of the message level identifiers, e.g., {@code SEVERE}1619* @param bundle Resource bundle to localize {@code msg};1620* can be {@code null}.1621* @param msg The string message (or a key in the message catalog)1622* @param thrown Throwable associated with the log message.1623* @since 91624*/1625public void logrb(Level level, ResourceBundle bundle, String msg,1626Throwable thrown) {1627if (!isLoggable(level)) {1628return;1629}1630LogRecord lr = new LogRecord(level, msg);1631lr.setThrown(thrown);1632doLog(lr, bundle);1633}16341635//======================================================================1636// Start of convenience methods for logging method entries and returns.1637//======================================================================16381639/**1640* Log a method entry.1641* <p>1642* This is a convenience method that can be used to log entry1643* to a method. A LogRecord with message "ENTRY", log level1644* FINER, and the given sourceMethod and sourceClass is logged.1645*1646* @param sourceClass name of class that issued the logging request1647* @param sourceMethod name of method that is being entered1648*/1649public void entering(String sourceClass, String sourceMethod) {1650logp(Level.FINER, sourceClass, sourceMethod, "ENTRY");1651}16521653/**1654* Log a method entry, with one parameter.1655* <p>1656* This is a convenience method that can be used to log entry1657* to a method. A LogRecord with message "ENTRY {0}", log level1658* FINER, and the given sourceMethod, sourceClass, and parameter1659* is logged.1660*1661* @param sourceClass name of class that issued the logging request1662* @param sourceMethod name of method that is being entered1663* @param param1 parameter to the method being entered1664*/1665public void entering(String sourceClass, String sourceMethod, Object param1) {1666logp(Level.FINER, sourceClass, sourceMethod, "ENTRY {0}", param1);1667}16681669/**1670* Log a method entry, with an array of parameters.1671* <p>1672* This is a convenience method that can be used to log entry1673* to a method. A LogRecord with message "ENTRY" (followed by a1674* format {N} indicator for each entry in the parameter array),1675* log level FINER, and the given sourceMethod, sourceClass, and1676* parameters is logged.1677*1678* @param sourceClass name of class that issued the logging request1679* @param sourceMethod name of method that is being entered1680* @param params array of parameters to the method being entered1681*/1682public void entering(String sourceClass, String sourceMethod, Object params[]) {1683String msg = "ENTRY";1684if (params == null ) {1685logp(Level.FINER, sourceClass, sourceMethod, msg);1686return;1687}1688if (!isLoggable(Level.FINER)) return;1689if (params.length > 0) {1690final StringBuilder b = new StringBuilder(msg);1691for (int i = 0; i < params.length; i++) {1692b.append(' ').append('{').append(i).append('}');1693}1694msg = b.toString();1695}1696logp(Level.FINER, sourceClass, sourceMethod, msg, params);1697}16981699/**1700* Log a method return.1701* <p>1702* This is a convenience method that can be used to log returning1703* from a method. A LogRecord with message "RETURN", log level1704* FINER, and the given sourceMethod and sourceClass is logged.1705*1706* @param sourceClass name of class that issued the logging request1707* @param sourceMethod name of the method1708*/1709public void exiting(String sourceClass, String sourceMethod) {1710logp(Level.FINER, sourceClass, sourceMethod, "RETURN");1711}171217131714/**1715* Log a method return, with result object.1716* <p>1717* This is a convenience method that can be used to log returning1718* from a method. A LogRecord with message "RETURN {0}", log level1719* FINER, and the gives sourceMethod, sourceClass, and result1720* object is logged.1721*1722* @param sourceClass name of class that issued the logging request1723* @param sourceMethod name of the method1724* @param result Object that is being returned1725*/1726public void exiting(String sourceClass, String sourceMethod, Object result) {1727logp(Level.FINER, sourceClass, sourceMethod, "RETURN {0}", result);1728}17291730/**1731* Log throwing an exception.1732* <p>1733* This is a convenience method to log that a method is1734* terminating by throwing an exception. The logging is done1735* using the FINER level.1736* <p>1737* If the logger is currently enabled for the given message1738* level then the given arguments are stored in a LogRecord1739* which is forwarded to all registered output handlers. The1740* LogRecord's message is set to "THROW".1741* <p>1742* Note that the thrown argument is stored in the LogRecord thrown1743* property, rather than the LogRecord parameters property. Thus it is1744* processed specially by output Formatters and is not treated1745* as a formatting parameter to the LogRecord message property.1746*1747* @param sourceClass name of class that issued the logging request1748* @param sourceMethod name of the method.1749* @param thrown The Throwable that is being thrown.1750*/1751public void throwing(String sourceClass, String sourceMethod, Throwable thrown) {1752if (!isLoggable(Level.FINER)) {1753return;1754}1755LogRecord lr = new LogRecord(Level.FINER, "THROW");1756lr.setSourceClassName(sourceClass);1757lr.setSourceMethodName(sourceMethod);1758lr.setThrown(thrown);1759doLog(lr);1760}17611762//=======================================================================1763// Start of simple convenience methods using level names as method names1764//=======================================================================17651766/**1767* Log a SEVERE message.1768* <p>1769* If the logger is currently enabled for the SEVERE message1770* level then the given message is forwarded to all the1771* registered output Handler objects.1772*1773* @param msg The string message (or a key in the message catalog)1774*/1775public void severe(String msg) {1776log(Level.SEVERE, msg);1777}17781779/**1780* Log a WARNING message.1781* <p>1782* If the logger is currently enabled for the WARNING message1783* level then the given message is forwarded to all the1784* registered output Handler objects.1785*1786* @param msg The string message (or a key in the message catalog)1787*/1788public void warning(String msg) {1789log(Level.WARNING, msg);1790}17911792/**1793* Log an INFO message.1794* <p>1795* If the logger is currently enabled for the INFO message1796* level then the given message is forwarded to all the1797* registered output Handler objects.1798*1799* @param msg The string message (or a key in the message catalog)1800*/1801public void info(String msg) {1802log(Level.INFO, msg);1803}18041805/**1806* Log a CONFIG message.1807* <p>1808* If the logger is currently enabled for the CONFIG message1809* level then the given message is forwarded to all the1810* registered output Handler objects.1811*1812* @param msg The string message (or a key in the message catalog)1813*/1814public void config(String msg) {1815log(Level.CONFIG, msg);1816}18171818/**1819* Log a FINE message.1820* <p>1821* If the logger is currently enabled for the FINE message1822* level then the given message is forwarded to all the1823* registered output Handler objects.1824*1825* @param msg The string message (or a key in the message catalog)1826*/1827public void fine(String msg) {1828log(Level.FINE, msg);1829}18301831/**1832* Log a FINER message.1833* <p>1834* If the logger is currently enabled for the FINER message1835* level then the given message is forwarded to all the1836* registered output Handler objects.1837*1838* @param msg The string message (or a key in the message catalog)1839*/1840public void finer(String msg) {1841log(Level.FINER, msg);1842}18431844/**1845* Log a FINEST message.1846* <p>1847* If the logger is currently enabled for the FINEST message1848* level then the given message is forwarded to all the1849* registered output Handler objects.1850*1851* @param msg The string message (or a key in the message catalog)1852*/1853public void finest(String msg) {1854log(Level.FINEST, msg);1855}18561857//=======================================================================1858// Start of simple convenience methods using level names as method names1859// and use Supplier<String>1860//=======================================================================18611862/**1863* Log a SEVERE message, which is only to be constructed if the logging1864* level is such that the message will actually be logged.1865* <p>1866* If the logger is currently enabled for the SEVERE message1867* level then the message is constructed by invoking the provided1868* supplier function and forwarded to all the registered output1869* Handler objects.1870*1871* @param msgSupplier A function, which when called, produces the1872* desired log message1873* @since 1.81874*/1875public void severe(Supplier<String> msgSupplier) {1876log(Level.SEVERE, msgSupplier);1877}18781879/**1880* Log a WARNING message, which is only to be constructed if the logging1881* level is such that the message will actually be logged.1882* <p>1883* If the logger is currently enabled for the WARNING message1884* level then the message is constructed by invoking the provided1885* supplier function and forwarded to all the registered output1886* Handler objects.1887*1888* @param msgSupplier A function, which when called, produces the1889* desired log message1890* @since 1.81891*/1892public void warning(Supplier<String> msgSupplier) {1893log(Level.WARNING, msgSupplier);1894}18951896/**1897* Log a INFO message, which is only to be constructed if the logging1898* level is such that the message will actually be logged.1899* <p>1900* If the logger is currently enabled for the INFO message1901* level then the message is constructed by invoking the provided1902* supplier function and forwarded to all the registered output1903* Handler objects.1904*1905* @param msgSupplier A function, which when called, produces the1906* desired log message1907* @since 1.81908*/1909public void info(Supplier<String> msgSupplier) {1910log(Level.INFO, msgSupplier);1911}19121913/**1914* Log a CONFIG message, which is only to be constructed if the logging1915* level is such that the message will actually be logged.1916* <p>1917* If the logger is currently enabled for the CONFIG message1918* level then the message is constructed by invoking the provided1919* supplier function and forwarded to all the registered output1920* Handler objects.1921*1922* @param msgSupplier A function, which when called, produces the1923* desired log message1924* @since 1.81925*/1926public void config(Supplier<String> msgSupplier) {1927log(Level.CONFIG, msgSupplier);1928}19291930/**1931* Log a FINE message, which is only to be constructed if the logging1932* level is such that the message will actually be logged.1933* <p>1934* If the logger is currently enabled for the FINE message1935* level then the message is constructed by invoking the provided1936* supplier function and forwarded to all the registered output1937* Handler objects.1938*1939* @param msgSupplier A function, which when called, produces the1940* desired log message1941* @since 1.81942*/1943public void fine(Supplier<String> msgSupplier) {1944log(Level.FINE, msgSupplier);1945}19461947/**1948* Log a FINER message, which is only to be constructed if the logging1949* level is such that the message will actually be logged.1950* <p>1951* If the logger is currently enabled for the FINER message1952* level then the message is constructed by invoking the provided1953* supplier function and forwarded to all the registered output1954* Handler objects.1955*1956* @param msgSupplier A function, which when called, produces the1957* desired log message1958* @since 1.81959*/1960public void finer(Supplier<String> msgSupplier) {1961log(Level.FINER, msgSupplier);1962}19631964/**1965* Log a FINEST message, which is only to be constructed if the logging1966* level is such that the message will actually be logged.1967* <p>1968* If the logger is currently enabled for the FINEST message1969* level then the message is constructed by invoking the provided1970* supplier function and forwarded to all the registered output1971* Handler objects.1972*1973* @param msgSupplier A function, which when called, produces the1974* desired log message1975* @since 1.81976*/1977public void finest(Supplier<String> msgSupplier) {1978log(Level.FINEST, msgSupplier);1979}19801981//================================================================1982// End of convenience methods1983//================================================================19841985/**1986* Set the log level specifying which message levels will be1987* logged by this logger. Message levels lower than this1988* value will be discarded. The level value Level.OFF1989* can be used to turn off logging.1990* <p>1991* If the new level is null, it means that this node should1992* inherit its level from its nearest ancestor with a specific1993* (non-null) level value.1994*1995* @param newLevel the new value for the log level (may be null)1996* @throws SecurityException if a security manager exists,1997* this logger is not anonymous, and the caller1998* does not have LoggingPermission("control").1999*/2000public void setLevel(Level newLevel) throws SecurityException {2001checkPermission();2002synchronized (treeLock) {2003config.setLevelObject(newLevel);2004updateEffectiveLevel();2005}2006}20072008final boolean isLevelInitialized() {2009return config.levelObject != null;2010}20112012/**2013* Get the log Level that has been specified for this Logger.2014* The result may be null, which means that this logger's2015* effective level will be inherited from its parent.2016*2017* @return this Logger's level2018*/2019public Level getLevel() {2020return config.levelObject;2021}20222023/**2024* Check if a message of the given level would actually be logged2025* by this logger. This check is based on the Loggers effective level,2026* which may be inherited from its parent.2027*2028* @param level a message logging level2029* @return true if the given message level is currently being logged.2030*/2031public boolean isLoggable(Level level) {2032int levelValue = config.levelValue;2033if (level.intValue() < levelValue || levelValue == offValue) {2034return false;2035}2036return true;2037}20382039/**2040* Get the name for this logger.2041* @return logger name. Will be null for anonymous Loggers.2042*/2043public String getName() {2044return name;2045}20462047/**2048* Add a log Handler to receive logging messages.2049* <p>2050* By default, Loggers also send their output to their parent logger.2051* Typically the root Logger is configured with a set of Handlers2052* that essentially act as default handlers for all loggers.2053*2054* @param handler a logging Handler2055* @throws SecurityException if a security manager exists,2056* this logger is not anonymous, and the caller2057* does not have LoggingPermission("control").2058*/2059public void addHandler(Handler handler) throws SecurityException {2060Objects.requireNonNull(handler);2061checkPermission();2062config.addHandler(handler);2063}20642065/**2066* Remove a log Handler.2067* <P>2068* Returns silently if the given Handler is not found or is null2069*2070* @param handler a logging Handler2071* @throws SecurityException if a security manager exists,2072* this logger is not anonymous, and the caller2073* does not have LoggingPermission("control").2074*/2075public void removeHandler(Handler handler) throws SecurityException {2076checkPermission();2077if (handler == null) {2078return;2079}2080config.removeHandler(handler);2081}20822083/**2084* Get the Handlers associated with this logger.2085*2086* @return an array of all registered Handlers2087*/2088public Handler[] getHandlers() {2089return accessCheckedHandlers();2090}20912092// This method should ideally be marked final - but unfortunately2093// it needs to be overridden by LogManager.RootLogger2094Handler[] accessCheckedHandlers() {2095return config.handlers.toArray(emptyHandlers);2096}20972098/**2099* Specify whether or not this logger should send its output2100* to its parent Logger. This means that any LogRecords will2101* also be written to the parent's Handlers, and potentially2102* to its parent, recursively up the namespace.2103*2104* @param useParentHandlers true if output is to be sent to the2105* logger's parent.2106* @throws SecurityException if a security manager exists,2107* this logger is not anonymous, and the caller2108* does not have LoggingPermission("control").2109*/2110public void setUseParentHandlers(boolean useParentHandlers) {2111checkPermission();2112config.setUseParentHandlers(useParentHandlers);2113}21142115/**2116* Discover whether or not this logger is sending its output2117* to its parent logger.2118*2119* @return true if output is to be sent to the logger's parent2120*/2121public boolean getUseParentHandlers() {2122return config.useParentHandlers;2123}21242125private ResourceBundle catalog() {2126WeakReference<ResourceBundle> ref = catalogRef;2127return ref == null ? null : ref.get();2128}21292130/**2131* Private utility method to map a resource bundle name to an2132* actual resource bundle, using a simple one-entry cache.2133* Returns null for a null name.2134* May also return null if we can't find the resource bundle and2135* there is no suitable previous cached value.2136*2137* @param name the ResourceBundle to locate2138* @param useCallersModule if true search using the caller's module.2139* @return ResourceBundle specified by name or null if not found2140*/2141private synchronized ResourceBundle findResourceBundle(String name,2142boolean useCallersModule) {2143// When this method is called from logrb, useCallersModule==false, and2144// the resource bundle 'name' is the argument provided to logrb.2145// It may, or may not be, equal to lb.resourceBundleName.2146// Otherwise, useCallersModule==true, and name is the resource bundle2147// name that is set (or will be set) in this logger.2148//2149// When useCallersModule is false, or when the caller's module is2150// null, or when the caller's module is an unnamed module, we look2151// first in the TCCL (or the System ClassLoader if the TCCL is null)2152// to locate the resource bundle.2153//2154// Otherwise, if useCallersModule is true, and the caller's module is not2155// null, and the caller's module is named, we look in the caller's module2156// to locate the resource bundle.2157//2158// Finally, if the caller's module is not null and is unnamed, and2159// useCallersModule is true, we look in the caller's module class loader2160// (unless we already looked there in step 1).21612162// Return a null bundle for a null name.2163if (name == null) {2164return null;2165}21662167Locale currentLocale = Locale.getDefault();2168final LoggerBundle lb = loggerBundle;2169ResourceBundle catalog = catalog();21702171// Normally we should hit on our simple one entry cache.2172if (lb.userBundle != null &&2173name.equals(lb.resourceBundleName)) {2174return lb.userBundle;2175} else if (catalog != null && currentLocale.equals(catalogLocale)2176&& name.equals(catalogName)) {2177return catalog;2178}21792180// Use the thread's context ClassLoader. If there isn't one, use the2181// {@linkplain java.lang.ClassLoader#getSystemClassLoader() system ClassLoader}.2182ClassLoader cl = Thread.currentThread().getContextClassLoader();2183if (cl == null) {2184cl = ClassLoader.getSystemClassLoader();2185}21862187final Module callerModule = getCallerModule();21882189// If useCallersModule is false, we are called by logrb, with a name2190// that is provided by the user. In that case we will look in the TCCL.2191// We also look in the TCCL if callerModule is null or unnamed.2192if (!useCallersModule || callerModule == null || !callerModule.isNamed()) {2193try {2194Module mod = cl.getUnnamedModule();2195catalog = RbAccess.RB_ACCESS.getBundle(name, currentLocale, mod);2196catalogRef = new WeakReference<>(catalog);2197catalogName = name;2198catalogLocale = currentLocale;2199return catalog;2200} catch (MissingResourceException ex) {2201// We can't find the ResourceBundle in the default2202// ClassLoader. Drop through.2203if (useCallersModule && callerModule != null) {2204try {2205// We are called by an unnamed module: try with the2206// unnamed module class loader:2207PrivilegedAction<ClassLoader> getModuleClassLoader =2208() -> callerModule.getClassLoader();2209@SuppressWarnings("removal")2210ClassLoader moduleCL =2211AccessController.doPrivileged(getModuleClassLoader);2212// moduleCL can be null if the logger is created by a class2213// appended to the bootclasspath.2214// If moduleCL is null we would use cl, but we already tried2215// that above (we first looked in the TCCL for unnamed2216// caller modules) - so there no point in trying again: we2217// won't find anything more this second time.2218// In this case just return null.2219if (moduleCL == cl || moduleCL == null) return null;22202221// we already tried the TCCL and found nothing - so try2222// with the module's loader this time.2223catalog = ResourceBundle.getBundle(name, currentLocale,2224moduleCL);2225catalogRef = new WeakReference<>(catalog);2226catalogName = name;2227catalogLocale = currentLocale;2228return catalog;2229} catch (MissingResourceException x) {2230return null; // no luck2231}2232} else {2233return null;2234}2235}2236} else {2237// we should have:2238// useCallersModule && callerModule != null && callerModule.isNamed();2239// Try with the caller's module2240try {2241// Use the caller's module2242catalog = RbAccess.RB_ACCESS.getBundle(name, currentLocale, callerModule);2243catalogRef = new WeakReference<>(catalog);2244catalogName = name;2245catalogLocale = currentLocale;2246return catalog;2247} catch (MissingResourceException ex) {2248return null; // no luck2249}2250}2251}22522253private void setupResourceInfo(String name, Class<?> caller) {2254final Module module = caller == null ? null : caller.getModule();2255setupResourceInfo(name, module);2256}22572258// Private utility method to initialize our one entry2259// resource bundle name cache and the callers Module2260// Note: for consistency reasons, we are careful to check2261// that a suitable ResourceBundle exists before setting the2262// resourceBundleName field.2263// Synchronized to prevent races in setting the fields.2264private synchronized void setupResourceInfo(String name,2265Module callerModule) {2266final LoggerBundle lb = loggerBundle;2267if (lb.resourceBundleName != null) {2268// this Logger already has a ResourceBundle22692270if (lb.resourceBundleName.equals(name)) {2271// the names match so there is nothing more to do2272return;2273}22742275// cannot change ResourceBundles once they are set2276throw new IllegalArgumentException(2277lb.resourceBundleName + " != " + name);2278}22792280if (name == null) {2281return;2282}22832284setCallerModuleRef(callerModule);22852286if (isSystemLogger && (callerModule != null && !isSystem(callerModule))) {2287checkPermission();2288}22892290if (name.equals(SYSTEM_LOGGER_RB_NAME)) {2291loggerBundle = SYSTEM_BUNDLE;2292} else {2293ResourceBundle bundle = findResourceBundle(name, true);2294if (bundle == null) {2295// We've failed to find an expected ResourceBundle.2296// unset the caller's module since we were unable to find the2297// the bundle using it2298this.callerModuleRef = null;2299throw new MissingResourceException("Can't find " + name + " bundle from ",2300name, "");2301}23022303loggerBundle = LoggerBundle.get(name, null);2304}2305}23062307/**2308* Sets a resource bundle on this logger.2309* All messages will be logged using the given resource bundle for its2310* specific {@linkplain ResourceBundle#getLocale locale}.2311* @param bundle The resource bundle that this logger shall use.2312* @throws NullPointerException if the given bundle is {@code null}.2313* @throws IllegalArgumentException if the given bundle doesn't have a2314* {@linkplain ResourceBundle#getBaseBundleName base name},2315* or if this logger already has a resource bundle set but2316* the given bundle has a different base name.2317* @throws SecurityException if a security manager exists,2318* this logger is not anonymous, and the caller2319* does not have LoggingPermission("control").2320* @since 1.82321*/2322public void setResourceBundle(ResourceBundle bundle) {2323checkPermission();23242325// Will throw NPE if bundle is null.2326final String baseName = bundle.getBaseBundleName();23272328// bundle must have a name2329if (baseName == null || baseName.isEmpty()) {2330throw new IllegalArgumentException("resource bundle must have a name");2331}23322333synchronized (this) {2334LoggerBundle lb = loggerBundle;2335final boolean canReplaceResourceBundle = lb.resourceBundleName == null2336|| lb.resourceBundleName.equals(baseName);23372338if (!canReplaceResourceBundle) {2339throw new IllegalArgumentException("can't replace resource bundle");2340}234123422343loggerBundle = LoggerBundle.get(baseName, bundle);2344}2345}23462347/**2348* Return the parent for this Logger.2349* <p>2350* This method returns the nearest extant parent in the namespace.2351* Thus if a Logger is called "a.b.c.d", and a Logger called "a.b"2352* has been created but no logger "a.b.c" exists, then a call of2353* getParent on the Logger "a.b.c.d" will return the Logger "a.b".2354* <p>2355* The result will be null if it is called on the root Logger2356* in the namespace.2357*2358* @return nearest existing parent Logger2359*/2360public Logger getParent() {2361// Note: this used to be synchronized on treeLock. However, this only2362// provided memory semantics, as there was no guarantee that the caller2363// would synchronize on treeLock (in fact, there is no way for external2364// callers to so synchronize). Therefore, we have made parent volatile2365// instead.2366return parent;2367}23682369/**2370* Set the parent for this Logger. This method is used by2371* the LogManager to update a Logger when the namespace changes.2372* <p>2373* It should not be called from application code.2374*2375* @param parent the new parent logger2376* @throws SecurityException if a security manager exists and if2377* the caller does not have LoggingPermission("control").2378*/2379public void setParent(Logger parent) {2380if (parent == null) {2381throw new NullPointerException();2382}23832384// check permission for all loggers, including anonymous loggers2385if (manager == null) {2386manager = LogManager.getLogManager();2387}2388manager.checkPermission();23892390doSetParent(parent);2391}23922393// Private method to do the work for parenting a child2394// Logger onto a parent logger.2395private void doSetParent(Logger newParent) {23962397// System.err.println("doSetParent \"" + getName() + "\" \""2398// + newParent.getName() + "\"");23992400synchronized (treeLock) {24012402// Remove ourself from any previous parent.2403LogManager.LoggerWeakRef ref = null;2404if (parent != null) {2405// assert parent.kids != null;2406for (Iterator<LogManager.LoggerWeakRef> iter = parent.kids.iterator(); iter.hasNext(); ) {2407ref = iter.next();2408if (ref.refersTo(this)) {2409// ref is used down below to complete the reparenting2410iter.remove();2411break;2412} else {2413ref = null;2414}2415}2416// We have now removed ourself from our parents' kids.2417}24182419// Set our new parent.2420parent = newParent;2421if (parent.kids == null) {2422parent.kids = new ArrayList<>(2);2423}2424if (ref == null) {2425// we didn't have a previous parent2426ref = manager.new LoggerWeakRef(this);2427}2428ref.setParentRef(new WeakReference<>(parent));2429parent.kids.add(ref);24302431// As a result of the reparenting, the effective level2432// may have changed for us and our children.2433updateEffectiveLevel();24342435}2436}24372438// Package-level method.2439// Remove the weak reference for the specified child Logger from the2440// kid list. We should only be called from LoggerWeakRef.dispose().2441final void removeChildLogger(LogManager.LoggerWeakRef child) {2442synchronized (treeLock) {2443for (Iterator<LogManager.LoggerWeakRef> iter = kids.iterator(); iter.hasNext(); ) {2444LogManager.LoggerWeakRef ref = iter.next();2445if (ref == child) {2446iter.remove();2447return;2448}2449}2450}2451}24522453// Recalculate the effective level for this node and2454// recursively for our children.24552456private void updateEffectiveLevel() {2457// assert Thread.holdsLock(treeLock);24582459// Figure out our current effective level.2460int newLevelValue;2461final ConfigurationData cfg = config;2462final Level levelObject = cfg.levelObject;2463if (levelObject != null) {2464newLevelValue = levelObject.intValue();2465} else {2466if (parent != null) {2467newLevelValue = parent.config.levelValue;2468} else {2469// This may happen during initialization.2470newLevelValue = Level.INFO.intValue();2471}2472}24732474// If our effective value hasn't changed, we're done.2475if (cfg.levelValue == newLevelValue) {2476return;2477}24782479cfg.setLevelValue(newLevelValue);24802481// System.err.println("effective level: \"" + getName() + "\" := " + level);24822483// Recursively update the level on each of our kids.2484if (kids != null) {2485for (LogManager.LoggerWeakRef ref : kids) {2486Logger kid = ref.get();2487if (kid != null) {2488kid.updateEffectiveLevel();2489}2490}2491}2492}249324942495// Private method to get the potentially inherited2496// resource bundle and resource bundle name for this Logger.2497// This method never returns null.2498private LoggerBundle getEffectiveLoggerBundle() {2499final LoggerBundle lb = loggerBundle;2500if (lb.isSystemBundle()) {2501return SYSTEM_BUNDLE;2502}25032504// first take care of this logger2505final ResourceBundle b = getResourceBundle();2506if (b != null && b == lb.userBundle) {2507return lb;2508} else if (b != null) {2509// either lb.userBundle is null or getResourceBundle() is2510// overriden2511final String rbName = getResourceBundleName();2512return LoggerBundle.get(rbName, b);2513}25142515// no resource bundle was specified on this logger, look up the2516// parent stack.2517Logger target = this.parent;2518while (target != null) {2519final LoggerBundle trb = target.loggerBundle;2520if (trb.isSystemBundle()) {2521return SYSTEM_BUNDLE;2522}2523if (trb.userBundle != null) {2524return trb;2525}2526final String rbName = isSystemLogger2527// ancestor of a system logger is expected to be a system logger.2528// ignore resource bundle name if it's not.2529? (target.isSystemLogger ? trb.resourceBundleName : null)2530: target.getResourceBundleName();2531if (rbName != null) {2532return LoggerBundle.get(rbName,2533findResourceBundle(rbName, true));2534}2535target = isSystemLogger ? target.parent : target.getParent();2536}2537return NO_RESOURCE_BUNDLE;2538}25392540}254125422543