1 /* 2 * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.util.logging; 27 28 import java.lang.ref.Reference; 29 import java.lang.ref.ReferenceQueue; 30 import java.lang.ref.WeakReference; 31 import java.security.AccessController; 32 import java.security.PrivilegedAction; 33 import java.util.ArrayList; 34 import java.util.Collections; 35 import java.util.HashMap; 36 import java.util.List; 37 import java.util.Locale; 38 import java.util.Map; 39 import java.util.Optional; 40 import java.util.ResourceBundle; 41 import java.util.function.Function; 42 import jdk.internal.loader.ClassLoaderValue; 43 import jdk.internal.misc.JavaUtilResourceBundleAccess; 44 import jdk.internal.misc.SharedSecrets; 45 46 /** 47 * The Level class defines a set of standard logging levels that 48 * can be used to control logging output. The logging Level objects 49 * are ordered and are specified by ordered integers. Enabling logging 50 * at a given level also enables logging at all higher levels. 51 * <p> 52 * Clients should normally use the predefined Level constants such 53 * as Level.SEVERE. 54 * <p> 55 * The levels in descending order are: 56 * <ul> 57 * <li>SEVERE (highest value) 58 * <li>WARNING 59 * <li>INFO 60 * <li>CONFIG 61 * <li>FINE 62 * <li>FINER 63 * <li>FINEST (lowest value) 64 * </ul> 65 * In addition there is a level OFF that can be used to turn 66 * off logging, and a level ALL that can be used to enable 67 * logging of all messages. 68 * <p> 69 * It is possible for third parties to define additional logging 70 * levels by subclassing Level. In such cases subclasses should 71 * take care to chose unique integer level values and to ensure that 72 * they maintain the Object uniqueness property across serialization 73 * by defining a suitable readResolve method. 74 * 75 * @since 1.4 76 */ 77 78 public class Level implements java.io.Serializable { 79 private static final String defaultBundle = 80 "sun.util.logging.resources.logging"; 81 82 // Calling SharedSecrets.getJavaUtilResourceBundleAccess() 83 // forces the initialization of ResourceBundle.class, which 84 // can be too early if the VM has not finished booting yet. 85 private static final class RbAccess { 86 static final JavaUtilResourceBundleAccess RB_ACCESS = 87 SharedSecrets.getJavaUtilResourceBundleAccess(); 88 } 89 90 /** 91 * @serial The non-localized name of the level. 92 */ 93 private final String name; 94 95 /** 96 * @serial The integer value of the level. 97 */ 98 private final int value; 99 100 /** 101 * @serial The resource bundle name to be used in localizing the level name. 102 */ 103 private final String resourceBundleName; 104 105 // localized level name 106 private transient String localizedLevelName; 107 private transient Locale cachedLocale; 108 109 /** 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 */ 113 public static final Level OFF = new Level("OFF",Integer.MAX_VALUE, defaultBundle); 114 115 /** 116 * SEVERE is a message level indicating a serious failure. 117 * <p> 118 * In general SEVERE messages should describe events that are 119 * of considerable importance and which will prevent normal 120 * program execution. They should be reasonably intelligible 121 * to end users and to system administrators. 122 * This level is initialized to <CODE>1000</CODE>. 123 */ 124 public static final Level SEVERE = new Level("SEVERE",1000, defaultBundle); 125 126 /** 127 * WARNING is a message level indicating a potential problem. 128 * <p> 129 * In general WARNING messages should describe events that will 130 * be of interest to end users or system managers, or which 131 * indicate potential problems. 132 * This level is initialized to <CODE>900</CODE>. 133 */ 134 public static final Level WARNING = new Level("WARNING", 900, defaultBundle); 135 136 /** 137 * INFO is a message level for informational messages. 138 * <p> 139 * Typically INFO messages will be written to the console 140 * or its equivalent. So the INFO level should only be 141 * used for reasonably significant messages that will 142 * make sense to end users and system administrators. 143 * This level is initialized to <CODE>800</CODE>. 144 */ 145 public static final Level INFO = new Level("INFO", 800, defaultBundle); 146 147 /** 148 * CONFIG is a message level for static configuration messages. 149 * <p> 150 * CONFIG messages are intended to provide a variety of static 151 * configuration information, to assist in debugging problems 152 * 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 */ 157 public static final Level CONFIG = new Level("CONFIG", 700, defaultBundle); 158 159 /** 160 * FINE is a message level providing tracing information. 161 * <p> 162 * All of FINE, FINER, and FINEST are intended for relatively 163 * detailed tracing. The exact meaning of the three levels will 164 * vary between subsystems, but in general, FINEST should be used 165 * for the most voluminous detailed output, FINER for somewhat 166 * less detailed output, and FINE for the lowest volume (and 167 * most important) messages. 168 * <p> 169 * In general the FINE level should be used for information 170 * that will be broadly interesting to developers who do not have 171 * a specialized interest in the specific subsystem. 172 * <p> 173 * FINE messages might include things like minor (recoverable) 174 * failures. Issues indicating potential performance problems 175 * are also worth logging as FINE. 176 * This level is initialized to <CODE>500</CODE>. 177 */ 178 public static final Level FINE = new Level("FINE", 500, defaultBundle); 179 180 /** 181 * FINER indicates a fairly detailed tracing message. 182 * By default logging calls for entering, returning, or throwing 183 * an exception are traced at this level. 184 * This level is initialized to <CODE>400</CODE>. 185 */ 186 public static final Level FINER = new Level("FINER", 400, defaultBundle); 187 188 /** 189 * FINEST indicates a highly detailed tracing message. 190 * This level is initialized to <CODE>300</CODE>. 191 */ 192 public static final Level FINEST = new Level("FINEST", 300, defaultBundle); 193 194 /** 195 * ALL indicates that all messages should be logged. 196 * This level is initialized to <CODE>Integer.MIN_VALUE</CODE>. 197 */ 198 public static final Level ALL = new Level("ALL", Integer.MIN_VALUE, defaultBundle); 199 200 private static final Level[] standardLevels = { 201 OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL 202 }; 203 204 /** 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 Level 209 * objects such as SEVERE or FINEST. However, if clients need to 210 * add new logging levels, they may subclass Level and define new 211 * 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 null 215 */ 216 protected Level(String name, int value) { 217 this(name, value, null); 218 } 219 220 /** 221 * Create a named Level with a given integer value and a 222 * 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 in 227 * localizing the given name. If the resourceBundleName is null 228 * or an empty string, it is ignored. 229 * @throws NullPointerException if the name is null 230 */ 231 protected Level(String name, int value, String resourceBundleName) { 232 this(name, value, resourceBundleName, true); 233 } 234 235 // private constructor to specify whether this instance should be added 236 // to the KnownLevel list from which Level.parse method does its look up 237 private Level(String name, int value, String resourceBundleName, boolean visible) { 238 if (name == null) { 239 throw new NullPointerException(); 240 } 241 this.name = name; 242 this.value = value; 243 this.resourceBundleName = resourceBundleName; 244 this.localizedLevelName = resourceBundleName == null ? name : null; 245 this.cachedLocale = null; 246 if (visible) { 247 KnownLevel.add(this); 248 } 249 } 250 251 /** 252 * Return the level's localization resource bundle name, or 253 * null if no localization bundle is defined. 254 * 255 * @return localization resource bundle name 256 */ 257 public String getResourceBundleName() { 258 return resourceBundleName; 259 } 260 261 /** 262 * Return the non-localized string name of the Level. 263 * 264 * @return non-localized name 265 */ 266 public String getName() { 267 return name; 268 } 269 270 /** 271 * Return the localized string name of the Level, for 272 * the current default locale. 273 * <p> 274 * If no localization information is available, the 275 * non-localized name is returned. 276 * 277 * @return localized name 278 */ 279 public String getLocalizedName() { 280 return getLocalizedLevelName(); 281 } 282 283 // package-private getLevelName() is used by the implementation 284 // instead of getName() to avoid calling the subclass's version 285 final String getLevelName() { 286 return this.name; 287 } 288 289 private String computeLocalizedLevelName(Locale newLocale) { 290 // Resource bundle should be loaded from the defining module 291 // or its defining class loader, if it's unnamed module, 292 // of this Level instance that can be a custom Level subclass; 293 Module module = this.getClass().getModule(); 294 ResourceBundle rb = RbAccess.RB_ACCESS.getBundle(resourceBundleName, 295 newLocale, module); 296 297 final String localizedName = rb.getString(name); 298 final boolean isDefaultBundle = defaultBundle.equals(resourceBundleName); 299 if (!isDefaultBundle) return localizedName; 300 301 // This is a trick to determine whether the name has been translated 302 // or not. If it has not been translated, we need to use Locale.ROOT 303 // when calling toUpperCase(). 304 final Locale rbLocale = rb.getLocale(); 305 final Locale locale = 306 Locale.ROOT.equals(rbLocale) 307 || name.equals(localizedName.toUpperCase(Locale.ROOT)) 308 ? Locale.ROOT : rbLocale; 309 310 // ALL CAPS in a resource bundle's message indicates no translation 311 // needed per Oracle translation guideline. To workaround this 312 // in Oracle JDK implementation, convert the localized level name 313 // to uppercase for compatibility reason. 314 return Locale.ROOT.equals(locale) ? name : localizedName.toUpperCase(locale); 315 } 316 317 // Avoid looking up the localizedLevelName twice if we already 318 // have it. 319 final String getCachedLocalizedLevelName() { 320 321 if (localizedLevelName != null) { 322 if (cachedLocale != null) { 323 if (cachedLocale.equals(Locale.getDefault())) { 324 // OK: our cached value was looked up with the same 325 // locale. We can use it. 326 return localizedLevelName; 327 } 328 } 329 } 330 331 if (resourceBundleName == null) { 332 // No resource bundle: just use the name. 333 return name; 334 } 335 336 // We need to compute the localized name. 337 // Either because it's the first time, or because our cached 338 // value is for a different locale. Just return null. 339 return null; 340 } 341 342 final synchronized String getLocalizedLevelName() { 343 344 // See if we have a cached localized name 345 final String cachedLocalizedName = getCachedLocalizedLevelName(); 346 if (cachedLocalizedName != null) { 347 return cachedLocalizedName; 348 } 349 350 // No cached localized name or cache invalid. 351 // Need to compute the localized name. 352 final Locale newLocale = Locale.getDefault(); 353 try { 354 localizedLevelName = computeLocalizedLevelName(newLocale); 355 } catch (Exception ex) { 356 localizedLevelName = name; 357 } 358 cachedLocale = newLocale; 359 return localizedLevelName; 360 } 361 362 // Returns a mirrored Level object that matches the given name as 363 // 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.parse 366 // 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 may 369 // return a different level value if there is a custom Level subclass 370 // that overrides Level.getLocalizedName() to return a different string 371 // than what's returned by the default implementation. 372 // 373 static Level findLevel(String name) { 374 if (name == null) { 375 throw new NullPointerException(); 376 } 377 378 Optional<Level> level; 379 380 // Look for a known Level with the given non-localized name. 381 level = KnownLevel.findByName(name, KnownLevel::mirrored); 382 if (level.isPresent()) { 383 return level.get(); 384 } 385 386 // Now, check if the given name is an integer. If so, 387 // first look for a Level with the given value and then 388 // if necessary create one. 389 try { 390 int x = Integer.parseInt(name); 391 level = KnownLevel.findByValue(x, KnownLevel::mirrored); 392 if (level.isPresent()) { 393 return level.get(); 394 } 395 // add new Level 396 Level levelObject = new Level(name, x); 397 // There's no need to use a reachability fence here because 398 // KnownLevel keeps a strong reference on the level when 399 // level.getClass() == Level.class. 400 return KnownLevel.findByValue(x, KnownLevel::mirrored).get(); 401 } catch (NumberFormatException ex) { 402 // Not an integer. 403 // Drop through. 404 } 405 406 level = KnownLevel.findByLocalizedLevelName(name, 407 KnownLevel::mirrored); 408 if (level.isPresent()) { 409 return level.get(); 410 } 411 412 return null; 413 } 414 415 /** 416 * Returns a string representation of this Level. 417 * 418 * @return the non-localized name of the Level, for example "INFO". 419 */ 420 @Override 421 public final String toString() { 422 return name; 423 } 424 425 /** 426 * Get the integer value for this level. This integer value 427 * can be used for efficient ordering comparisons between 428 * Level objects. 429 * @return the integer value for this level. 430 */ 431 public final int intValue() { 432 return value; 433 } 434 435 private static final long serialVersionUID = -8176160795706313070L; 436 437 // Serialization magic to prevent "doppelgangers". 438 // This is a performance optimization. 439 private Object readResolve() { 440 Optional<Level> level = KnownLevel.matches(this); 441 if (level.isPresent()) { 442 return level.get(); 443 } 444 // Woops. Whoever sent us this object knows 445 // about a new log level. Add it to our list. 446 return new Level(this.name, this.value, this.resourceBundleName); 447 } 448 449 /** 450 * Parse a level name string into a Level. 451 * <p> 452 * The argument string may consist of either a level name 453 * or an integer value. 454 * <p> 455 * For example: 456 * <ul> 457 * <li> "SEVERE" 458 * <li> "1000" 459 * </ul> 460 * 461 * @param name string to be parsed 462 * @throws NullPointerException if the name is null 463 * @throws IllegalArgumentException if the value is not valid. 464 * Valid values are integers between <CODE>Integer.MIN_VALUE</CODE> 465 * and <CODE>Integer.MAX_VALUE</CODE>, and all known level names. 466 * Known names are the levels defined by this class (e.g., <CODE>FINE</CODE>, 467 * <CODE>FINER</CODE>, <CODE>FINEST</CODE>), or created by this class with 468 * appropriate package access, or new levels defined or created 469 * by subclasses. 470 * 471 * @return The parsed value. Passing an integer that corresponds to a known name 472 * (e.g., 700) will return the associated name (e.g., <CODE>CONFIG</CODE>). 473 * Passing an integer that does not (e.g., 1) will return a new level name 474 * initialized to that value. 475 */ 476 public static synchronized Level parse(String name) throws IllegalArgumentException { 477 // Check that name is not null. 478 name.length(); 479 480 Optional<Level> level; 481 482 // Look for a known Level with the given non-localized name. 483 level = KnownLevel.findByName(name, KnownLevel::referent); 484 if (level.isPresent()) { 485 return level.get(); 486 } 487 488 // Now, check if the given name is an integer. If so, 489 // first look for a Level with the given value and then 490 // if necessary create one. 491 try { 492 int x = Integer.parseInt(name); 493 level = KnownLevel.findByValue(x, KnownLevel::referent); 494 if (level.isPresent()) { 495 return level.get(); 496 } 497 // add new Level. 498 Level levelObject = new Level(name, x); 499 // There's no need to use a reachability fence here because 500 // KnownLevel keeps a strong reference on the level when 501 // level.getClass() == Level.class. 502 return KnownLevel.findByValue(x, KnownLevel::referent).get(); 503 } catch (NumberFormatException ex) { 504 // Not an integer. 505 // Drop through. 506 } 507 508 // Finally, look for a known level with the given localized name, 509 // in the current default locale. 510 // This is relatively expensive, but not excessively so. 511 level = KnownLevel.findByLocalizedLevelName(name, KnownLevel::referent); 512 if (level .isPresent()) { 513 return level.get(); 514 } 515 516 // OK, we've tried everything and failed 517 throw new IllegalArgumentException("Bad level \"" + name + "\""); 518 } 519 520 /** 521 * Compare two objects for value equality. 522 * @return true if and only if the two objects have the same level value. 523 */ 524 @Override 525 public boolean equals(Object ox) { 526 try { 527 Level lx = (Level)ox; 528 return (lx.value == this.value); 529 } catch (Exception ex) { 530 return false; 531 } 532 } 533 534 /** 535 * Generate a hashcode. 536 * @return a hashcode based on the level value 537 */ 538 @Override 539 public int hashCode() { 540 return this.value; 541 } 542 543 // KnownLevel class maintains the global list of all known levels. 544 // The API allows multiple custom Level instances of the same name/value 545 // be created. This class provides convenient methods to find a level 546 // by a given name, by a given value, or by a given localized name. 547 // 548 // KnownLevel wraps the following Level objects: 549 // 1. levelObject: standard Level object or custom Level object 550 // 2. mirroredLevel: Level object representing the level specified in the 551 // logging configuration. 552 // 553 // Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods 554 // are non-final but the name and resource bundle name are parameters to 555 // the Level constructor. Use the mirroredLevel object instead of the 556 // levelObject to prevent the logging framework to execute foreign code 557 // implemented by untrusted Level subclass. 558 // 559 // Implementation Notes: 560 // If Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods 561 // were final, the following KnownLevel implementation can be removed. 562 // Future API change should take this into consideration. 563 static final class KnownLevel extends WeakReference<Level> { 564 private static Map<String, List<KnownLevel>> nameToLevels = new HashMap<>(); 565 private static Map<Integer, List<KnownLevel>> intToLevels = new HashMap<>(); 566 private static final ReferenceQueue<Level> QUEUE = new ReferenceQueue<>(); 567 568 // CUSTOM_LEVEL_CLV is used to register custom level instances with 569 // their defining class loader, so that they are garbage collected 570 // if and only if their class loader is no longer strongly 571 // referenced. 572 private static final ClassLoaderValue<List<Level>> CUSTOM_LEVEL_CLV = 573 new ClassLoaderValue<>(); 574 575 final Level mirroredLevel; // mirror of the custom Level 576 KnownLevel(Level l) { 577 super(l, QUEUE); 578 if (l.getClass() == Level.class) { 579 this.mirroredLevel = l; 580 } else { 581 // this mirrored level object is hidden 582 this.mirroredLevel = new Level(l.name, l.value, 583 l.resourceBundleName, false); 584 } 585 } 586 587 Optional<Level> mirrored() { 588 return Optional.of(mirroredLevel); 589 } 590 591 Optional<Level> referent() { 592 return Optional.ofNullable(get()); 593 } 594 595 private void remove() { 596 Optional.ofNullable(nameToLevels.get(mirroredLevel.name)) 597 .ifPresent((x) -> x.remove(this)); 598 Optional.ofNullable(intToLevels.get(mirroredLevel.value)) 599 .ifPresent((x) -> x.remove(this)); 600 } 601 602 // Remove all stale KnownLevel instances 603 static synchronized void purge() { 604 Reference<? extends Level> ref; 605 while ((ref = QUEUE.poll()) != null) { 606 if (ref instanceof KnownLevel) { 607 ((KnownLevel)ref).remove(); 608 } 609 } 610 } 611 612 private static void registerWithClassLoader(Level customLevel) { 613 PrivilegedAction<ClassLoader> pa = customLevel.getClass()::getClassLoader; 614 final ClassLoader cl = AccessController.doPrivileged(pa); 615 CUSTOM_LEVEL_CLV.computeIfAbsent(cl, (c, v) -> new ArrayList<>()) 616 .add(customLevel); 617 } 618 619 static synchronized void add(Level l) { 620 purge(); 621 // the mirroredLevel object is always added to the list 622 // before the custom Level instance 623 KnownLevel o = new KnownLevel(l); 624 nameToLevels.computeIfAbsent(l.name, (k) -> new ArrayList<>()) 625 .add(o); 626 intToLevels.computeIfAbsent(l.value, (k) -> new ArrayList<>()) 627 .add(o); 628 629 // keep the custom level reachable from its class loader 630 // This will ensure that custom level values are not GC'ed 631 // until there class loader is GC'ed. 632 if (o.mirroredLevel != l) { 633 registerWithClassLoader(l); 634 } 635 636 } 637 638 // Returns a KnownLevel with the given non-localized name. 639 static synchronized Optional<Level> findByName(String name, 640 Function<KnownLevel, Optional<Level>> selector) { 641 purge(); 642 return nameToLevels.getOrDefault(name, Collections.emptyList()) 643 .stream() 644 .map(selector) 645 .flatMap(Optional::stream) 646 .findFirst(); 647 } 648 649 // Returns a KnownLevel with the given value. 650 static synchronized Optional<Level> findByValue(int value, 651 Function<KnownLevel, Optional<Level>> selector) { 652 purge(); 653 return intToLevels.getOrDefault(value, Collections.emptyList()) 654 .stream() 655 .map(selector) 656 .flatMap(Optional::stream) 657 .findFirst(); 658 } 659 660 // Returns a KnownLevel with the given localized name matching 661 // by calling the Level.getLocalizedLevelName() method (i.e. found 662 // from the resourceBundle associated with the Level object). 663 // This method does not call Level.getLocalizedName() that may 664 // be overridden in a subclass implementation 665 static synchronized Optional<Level> findByLocalizedLevelName(String name, 666 Function<KnownLevel, Optional<Level>> selector) { 667 purge(); 668 return nameToLevels.values().stream() 669 .flatMap(List::stream) 670 .map(selector) 671 .flatMap(Optional::stream) 672 .filter(l -> name.equals(l.getLocalizedLevelName())) 673 .findFirst(); 674 } 675 676 static synchronized Optional<Level> matches(Level l) { 677 purge(); 678 List<KnownLevel> list = nameToLevels.get(l.name); 679 if (list != null) { 680 for (KnownLevel ref : list) { 681 Level levelObject = ref.get(); 682 if (levelObject == null) continue; 683 Level other = ref.mirroredLevel; 684 Class<? extends Level> type = levelObject.getClass(); 685 if (l.value == other.value && 686 (l.resourceBundleName == other.resourceBundleName || 687 (l.resourceBundleName != null && 688 l.resourceBundleName.equals(other.resourceBundleName)))) { 689 if (type == l.getClass()) { 690 return Optional.of(levelObject); 691 } 692 } 693 } 694 } 695 return Optional.empty(); 696 } 697 } 698 699 }