Path: blob/master/src/java.logging/share/classes/java/util/logging/Level.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.io.Serial;28import java.lang.ref.Reference;29import java.lang.ref.ReferenceQueue;30import java.lang.ref.WeakReference;31import java.security.AccessController;32import java.security.PrivilegedAction;33import java.util.ArrayList;34import java.util.Collections;35import java.util.HashMap;36import java.util.List;37import java.util.Locale;38import java.util.Map;39import java.util.Optional;40import java.util.ResourceBundle;41import java.util.function.Function;42import jdk.internal.loader.ClassLoaderValue;43import jdk.internal.access.JavaUtilResourceBundleAccess;44import jdk.internal.access.SharedSecrets;4546/**47* The Level class defines a set of standard logging levels that48* can be used to control logging output. The logging Level objects49* are ordered and are specified by ordered integers. Enabling logging50* at a given level also enables logging at all higher levels.51* <p>52* Clients should normally use the predefined Level constants such53* as Level.SEVERE.54* <p>55* The levels in descending order are:56* <ul>57* <li>SEVERE (highest value)58* <li>WARNING59* <li>INFO60* <li>CONFIG61* <li>FINE62* <li>FINER63* <li>FINEST (lowest value)64* </ul>65* In addition there is a level OFF that can be used to turn66* off logging, and a level ALL that can be used to enable67* logging of all messages.68* <p>69* It is possible for third parties to define additional logging70* levels by subclassing Level. In such cases subclasses should71* take care to chose unique integer level values and to ensure that72* they maintain the Object uniqueness property across serialization73* by defining a suitable readResolve method.74*75* @since 1.476*/7778public class Level implements java.io.Serializable {79private static final String defaultBundle =80"sun.util.logging.resources.logging";8182// Calling SharedSecrets.getJavaUtilResourceBundleAccess()83// forces the initialization of ResourceBundle.class, which84// can be too early if the VM has not finished booting yet.85private static final class RbAccess {86static final JavaUtilResourceBundleAccess RB_ACCESS =87SharedSecrets.getJavaUtilResourceBundleAccess();88}8990/**91* @serial The non-localized name of the level.92*/93private final String name;9495/**96* @serial The integer value of the level.97*/98private final int value;99100/**101* @serial The resource bundle name to be used in localizing the level name.102*/103private final String resourceBundleName;104105// localized level name106private transient String localizedLevelName;107private transient Locale cachedLocale;108109/**110* OFF is a special level that can be used to turn off logging.111* This level is initialized to <CODE>Integer.MAX_VALUE</CODE>.112*/113public static final Level OFF = new Level("OFF",Integer.MAX_VALUE, defaultBundle);114115/**116* SEVERE is a message level indicating a serious failure.117* <p>118* In general SEVERE messages should describe events that are119* of considerable importance and which will prevent normal120* program execution. They should be reasonably intelligible121* to end users and to system administrators.122* This level is initialized to <CODE>1000</CODE>.123*/124public static final Level SEVERE = new Level("SEVERE",1000, defaultBundle);125126/**127* WARNING is a message level indicating a potential problem.128* <p>129* In general WARNING messages should describe events that will130* be of interest to end users or system managers, or which131* indicate potential problems.132* This level is initialized to <CODE>900</CODE>.133*/134public static final Level WARNING = new Level("WARNING", 900, defaultBundle);135136/**137* INFO is a message level for informational messages.138* <p>139* Typically INFO messages will be written to the console140* or its equivalent. So the INFO level should only be141* used for reasonably significant messages that will142* make sense to end users and system administrators.143* This level is initialized to <CODE>800</CODE>.144*/145public static final Level INFO = new Level("INFO", 800, defaultBundle);146147/**148* CONFIG is a message level for static configuration messages.149* <p>150* CONFIG messages are intended to provide a variety of static151* configuration information, to assist in debugging problems152* that may be associated with particular configurations.153* For example, CONFIG message might include the CPU type,154* the graphics depth, the GUI look-and-feel, etc.155* This level is initialized to <CODE>700</CODE>.156*/157public static final Level CONFIG = new Level("CONFIG", 700, defaultBundle);158159/**160* FINE is a message level providing tracing information.161* <p>162* All of FINE, FINER, and FINEST are intended for relatively163* detailed tracing. The exact meaning of the three levels will164* vary between subsystems, but in general, FINEST should be used165* for the most voluminous detailed output, FINER for somewhat166* less detailed output, and FINE for the lowest volume (and167* most important) messages.168* <p>169* In general the FINE level should be used for information170* that will be broadly interesting to developers who do not have171* a specialized interest in the specific subsystem.172* <p>173* FINE messages might include things like minor (recoverable)174* failures. Issues indicating potential performance problems175* are also worth logging as FINE.176* This level is initialized to <CODE>500</CODE>.177*/178public static final Level FINE = new Level("FINE", 500, defaultBundle);179180/**181* FINER indicates a fairly detailed tracing message.182* By default logging calls for entering, returning, or throwing183* an exception are traced at this level.184* This level is initialized to <CODE>400</CODE>.185*/186public static final Level FINER = new Level("FINER", 400, defaultBundle);187188/**189* FINEST indicates a highly detailed tracing message.190* This level is initialized to <CODE>300</CODE>.191*/192public static final Level FINEST = new Level("FINEST", 300, defaultBundle);193194/**195* ALL indicates that all messages should be logged.196* This level is initialized to <CODE>Integer.MIN_VALUE</CODE>.197*/198public static final Level ALL = new Level("ALL", Integer.MIN_VALUE, defaultBundle);199200private static final Level[] standardLevels = {201OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL202};203204/**205* Create a named Level with a given integer value.206* <p>207* Note that this constructor is "protected" to allow subclassing.208* In general clients of logging should use one of the constant Level209* objects such as SEVERE or FINEST. However, if clients need to210* add new logging levels, they may subclass Level and define new211* constants.212* @param name the name of the Level, for example "SEVERE".213* @param value an integer value for the level.214* @throws NullPointerException if the name is null215*/216protected Level(String name, int value) {217this(name, value, null);218}219220/**221* Create a named Level with a given integer value and a222* given localization resource name.223*224* @param name the name of the Level, for example "SEVERE".225* @param value an integer value for the level.226* @param resourceBundleName name of a resource bundle to use in227* localizing the given name. If the resourceBundleName is null228* or an empty string, it is ignored.229* @throws NullPointerException if the name is null230*/231protected Level(String name, int value, String resourceBundleName) {232this(name, value, resourceBundleName, true);233}234235// private constructor to specify whether this instance should be added236// to the KnownLevel list from which Level.parse method does its look up237private Level(String name, int value, String resourceBundleName, boolean visible) {238if (name == null) {239throw new NullPointerException();240}241this.name = name;242this.value = value;243this.resourceBundleName = resourceBundleName;244this.localizedLevelName = resourceBundleName == null ? name : null;245this.cachedLocale = null;246if (visible) {247KnownLevel.add(this);248}249}250251/**252* Return the level's localization resource bundle name, or253* null if no localization bundle is defined.254*255* @return localization resource bundle name256*/257public String getResourceBundleName() {258return resourceBundleName;259}260261/**262* Return the non-localized string name of the Level.263*264* @return non-localized name265*/266public String getName() {267return name;268}269270/**271* Return the localized string name of the Level, for272* the current default locale.273* <p>274* If no localization information is available, the275* non-localized name is returned.276*277* @return localized name278*/279public String getLocalizedName() {280return getLocalizedLevelName();281}282283// package-private getLevelName() is used by the implementation284// instead of getName() to avoid calling the subclass's version285final String getLevelName() {286return this.name;287}288289private String computeLocalizedLevelName(Locale newLocale) {290// Resource bundle should be loaded from the defining module291// or its defining class loader, if it's unnamed module,292// of this Level instance that can be a custom Level subclass;293Module module = this.getClass().getModule();294ResourceBundle rb = RbAccess.RB_ACCESS.getBundle(resourceBundleName,295newLocale, module);296297final String localizedName = rb.getString(name);298final boolean isDefaultBundle = defaultBundle.equals(resourceBundleName);299if (!isDefaultBundle) return localizedName;300301// This is a trick to determine whether the name has been translated302// or not. If it has not been translated, we need to use Locale.ROOT303// when calling toUpperCase().304final Locale rbLocale = rb.getLocale();305final Locale locale =306Locale.ROOT.equals(rbLocale)307|| name.equals(localizedName.toUpperCase(Locale.ROOT))308? Locale.ROOT : rbLocale;309310// ALL CAPS in a resource bundle's message indicates no translation311// needed per Oracle translation guideline. To workaround this312// in Oracle JDK implementation, convert the localized level name313// to uppercase for compatibility reason.314return Locale.ROOT.equals(locale) ? name : localizedName.toUpperCase(locale);315}316317// Avoid looking up the localizedLevelName twice if we already318// have it.319final String getCachedLocalizedLevelName() {320321if (localizedLevelName != null) {322if (cachedLocale != null) {323if (cachedLocale.equals(Locale.getDefault())) {324// OK: our cached value was looked up with the same325// locale. We can use it.326return localizedLevelName;327}328}329}330331if (resourceBundleName == null) {332// No resource bundle: just use the name.333return name;334}335336// We need to compute the localized name.337// Either because it's the first time, or because our cached338// value is for a different locale. Just return null.339return null;340}341342final synchronized String getLocalizedLevelName() {343344// See if we have a cached localized name345final String cachedLocalizedName = getCachedLocalizedLevelName();346if (cachedLocalizedName != null) {347return cachedLocalizedName;348}349350// No cached localized name or cache invalid.351// Need to compute the localized name.352final Locale newLocale = Locale.getDefault();353try {354localizedLevelName = computeLocalizedLevelName(newLocale);355} catch (Exception ex) {356localizedLevelName = name;357}358cachedLocale = newLocale;359return localizedLevelName;360}361362// Returns a mirrored Level object that matches the given name as363// specified in the Level.parse method. Returns null if not found.364//365// It returns the same Level object as the one returned by Level.parse366// method if the given name is a non-localized name or integer.367//368// If the name is a localized name, findLevel and parse method may369// return a different level value if there is a custom Level subclass370// that overrides Level.getLocalizedName() to return a different string371// than what's returned by the default implementation.372//373static Level findLevel(String name) {374if (name == null) {375throw new NullPointerException();376}377378Optional<Level> level;379380// Look for a known Level with the given non-localized name.381level = KnownLevel.findByName(name, KnownLevel::mirrored);382if (level.isPresent()) {383return level.get();384}385386// Now, check if the given name is an integer. If so,387// first look for a Level with the given value and then388// if necessary create one.389try {390int x = Integer.parseInt(name);391level = KnownLevel.findByValue(x, KnownLevel::mirrored);392if (level.isPresent()) {393return level.get();394}395// add new Level396Level levelObject = new Level(name, x);397// There's no need to use a reachability fence here because398// KnownLevel keeps a strong reference on the level when399// level.getClass() == Level.class.400return KnownLevel.findByValue(x, KnownLevel::mirrored).get();401} catch (NumberFormatException ex) {402// Not an integer.403// Drop through.404}405406level = KnownLevel.findByLocalizedLevelName(name,407KnownLevel::mirrored);408if (level.isPresent()) {409return level.get();410}411412return null;413}414415/**416* Returns a string representation of this Level.417*418* @return the non-localized name of the Level, for example "INFO".419*/420@Override421public final String toString() {422return name;423}424425/**426* Get the integer value for this level. This integer value427* can be used for efficient ordering comparisons between428* Level objects.429* @return the integer value for this level.430*/431public final int intValue() {432return value;433}434435@Serial436private static final long serialVersionUID = -8176160795706313070L;437438/**439* Returns a {@code Level} instance with the same {@code name},440* {@code value}, and {@code resourceBundleName} as the deserialized441* object.442* @return a {@code Level} instance corresponding to the deserialized443* object.444*/445@Serial446private Object readResolve() {447// Serialization magic to prevent "doppelgangers".448// This is a performance optimization.449Optional<Level> level = KnownLevel.matches(this);450if (level.isPresent()) {451return level.get();452}453// Woops. Whoever sent us this object knows454// about a new log level. Add it to our list.455return new Level(this.name, this.value, this.resourceBundleName);456}457458/**459* Parse a level name string into a Level.460* <p>461* The argument string may consist of either a level name462* or an integer value.463* <p>464* For example:465* <ul>466* <li> "SEVERE"467* <li> "1000"468* </ul>469*470* @param name string to be parsed471* @throws NullPointerException if the name is null472* @throws IllegalArgumentException if the value is not valid.473* Valid values are integers between <CODE>Integer.MIN_VALUE</CODE>474* and <CODE>Integer.MAX_VALUE</CODE>, and all known level names.475* Known names are the levels defined by this class (e.g., <CODE>FINE</CODE>,476* <CODE>FINER</CODE>, <CODE>FINEST</CODE>), or created by this class with477* appropriate package access, or new levels defined or created478* by subclasses.479*480* @return The parsed value. Passing an integer that corresponds to a known name481* (e.g., 700) will return the associated name (e.g., <CODE>CONFIG</CODE>).482* Passing an integer that does not (e.g., 1) will return a new level name483* initialized to that value.484*/485public static synchronized Level parse(String name) throws IllegalArgumentException {486// Check that name is not null.487name.length();488489Optional<Level> level;490491// Look for a known Level with the given non-localized name.492level = KnownLevel.findByName(name, KnownLevel::referent);493if (level.isPresent()) {494return level.get();495}496497// Now, check if the given name is an integer. If so,498// first look for a Level with the given value and then499// if necessary create one.500try {501int x = Integer.parseInt(name);502level = KnownLevel.findByValue(x, KnownLevel::referent);503if (level.isPresent()) {504return level.get();505}506// add new Level.507Level levelObject = new Level(name, x);508// There's no need to use a reachability fence here because509// KnownLevel keeps a strong reference on the level when510// level.getClass() == Level.class.511return KnownLevel.findByValue(x, KnownLevel::referent).get();512} catch (NumberFormatException ex) {513// Not an integer.514// Drop through.515}516517// Finally, look for a known level with the given localized name,518// in the current default locale.519// This is relatively expensive, but not excessively so.520level = KnownLevel.findByLocalizedLevelName(name, KnownLevel::referent);521if (level .isPresent()) {522return level.get();523}524525// OK, we've tried everything and failed526throw new IllegalArgumentException("Bad level \"" + name + "\"");527}528529/**530* Compare two objects for value equality.531* @return true if and only if the two objects have the same level value.532*/533@Override534public boolean equals(Object ox) {535try {536Level lx = (Level)ox;537return (lx.value == this.value);538} catch (Exception ex) {539return false;540}541}542543/**544* Generate a hashcode.545* @return a hashcode based on the level value546*/547@Override548public int hashCode() {549return this.value;550}551552// KnownLevel class maintains the global list of all known levels.553// The API allows multiple custom Level instances of the same name/value554// be created. This class provides convenient methods to find a level555// by a given name, by a given value, or by a given localized name.556//557// KnownLevel wraps the following Level objects:558// 1. levelObject: standard Level object or custom Level object559// 2. mirroredLevel: Level object representing the level specified in the560// logging configuration.561//562// Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods563// are non-final but the name and resource bundle name are parameters to564// the Level constructor. Use the mirroredLevel object instead of the565// levelObject to prevent the logging framework to execute foreign code566// implemented by untrusted Level subclass.567//568// Implementation Notes:569// If Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods570// were final, the following KnownLevel implementation can be removed.571// Future API change should take this into consideration.572static final class KnownLevel extends WeakReference<Level> {573private static Map<String, List<KnownLevel>> nameToLevels = new HashMap<>();574private static Map<Integer, List<KnownLevel>> intToLevels = new HashMap<>();575private static final ReferenceQueue<Level> QUEUE = new ReferenceQueue<>();576577// CUSTOM_LEVEL_CLV is used to register custom level instances with578// their defining class loader, so that they are garbage collected579// if and only if their class loader is no longer strongly580// referenced.581private static final ClassLoaderValue<List<Level>> CUSTOM_LEVEL_CLV =582new ClassLoaderValue<>();583584final Level mirroredLevel; // mirror of the custom Level585KnownLevel(Level l) {586super(l, QUEUE);587if (l.getClass() == Level.class) {588this.mirroredLevel = l;589} else {590// this mirrored level object is hidden591this.mirroredLevel = new Level(l.name, l.value,592l.resourceBundleName, false);593}594}595596Optional<Level> mirrored() {597return Optional.of(mirroredLevel);598}599600Optional<Level> referent() {601return Optional.ofNullable(get());602}603604private void remove() {605Optional.ofNullable(nameToLevels.get(mirroredLevel.name))606.ifPresent((x) -> x.remove(this));607Optional.ofNullable(intToLevels.get(mirroredLevel.value))608.ifPresent((x) -> x.remove(this));609}610611// Remove all stale KnownLevel instances612static synchronized void purge() {613Reference<? extends Level> ref;614while ((ref = QUEUE.poll()) != null) {615if (ref instanceof KnownLevel) {616((KnownLevel)ref).remove();617}618}619}620621private static void registerWithClassLoader(Level customLevel) {622PrivilegedAction<ClassLoader> pa = customLevel.getClass()::getClassLoader;623@SuppressWarnings("removal")624final ClassLoader cl = AccessController.doPrivileged(pa);625CUSTOM_LEVEL_CLV.computeIfAbsent(cl, (c, v) -> new ArrayList<>())626.add(customLevel);627}628629static synchronized void add(Level l) {630purge();631// the mirroredLevel object is always added to the list632// before the custom Level instance633KnownLevel o = new KnownLevel(l);634nameToLevels.computeIfAbsent(l.name, (k) -> new ArrayList<>())635.add(o);636intToLevels.computeIfAbsent(l.value, (k) -> new ArrayList<>())637.add(o);638639// keep the custom level reachable from its class loader640// This will ensure that custom level values are not GC'ed641// until there class loader is GC'ed.642if (o.mirroredLevel != l) {643registerWithClassLoader(l);644}645646}647648// Returns a KnownLevel with the given non-localized name.649static synchronized Optional<Level> findByName(String name,650Function<KnownLevel, Optional<Level>> selector) {651purge();652return nameToLevels.getOrDefault(name, Collections.emptyList())653.stream()654.map(selector)655.flatMap(Optional::stream)656.findFirst();657}658659// Returns a KnownLevel with the given value.660static synchronized Optional<Level> findByValue(int value,661Function<KnownLevel, Optional<Level>> selector) {662purge();663return intToLevels.getOrDefault(value, Collections.emptyList())664.stream()665.map(selector)666.flatMap(Optional::stream)667.findFirst();668}669670// Returns a KnownLevel with the given localized name matching671// by calling the Level.getLocalizedLevelName() method (i.e. found672// from the resourceBundle associated with the Level object).673// This method does not call Level.getLocalizedName() that may674// be overridden in a subclass implementation675static synchronized Optional<Level> findByLocalizedLevelName(String name,676Function<KnownLevel, Optional<Level>> selector) {677purge();678return nameToLevels.values().stream()679.flatMap(List::stream)680.map(selector)681.flatMap(Optional::stream)682.filter(l -> name.equals(l.getLocalizedLevelName()))683.findFirst();684}685686static synchronized Optional<Level> matches(Level l) {687purge();688List<KnownLevel> list = nameToLevels.get(l.name);689if (list != null) {690for (KnownLevel ref : list) {691Level levelObject = ref.get();692if (levelObject == null) continue;693Level other = ref.mirroredLevel;694Class<? extends Level> type = levelObject.getClass();695if (l.value == other.value &&696(l.resourceBundleName == other.resourceBundleName ||697(l.resourceBundleName != null &&698l.resourceBundleName.equals(other.resourceBundleName)))) {699if (type == l.getClass()) {700return Optional.of(levelObject);701}702}703}704}705return Optional.empty();706}707}708709}710711712