1 /* 2 * Copyright (c) 2000, 2016, 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 // add new Level 394 Level levelObject = new Level(name, x); 395 // There's no need to use a reachability fence here because 396 // KnownLevel keeps a strong reference on the level when 397 // level.getClass() == Level.class. 398 return KnownLevel.findByValue(x, KnownLevel::mirrored).get(); 399 } 400 } catch (NumberFormatException ex) { 401 // Not an integer. 402 // Drop through. 403 } 404 405 level = KnownLevel.findByLocalizedLevelName(name, 406 KnownLevel::mirrored); 407 if (level.isPresent()) { 408 return level.get(); 409 } 410 411 return null; 412 } 413 414 /** 415 * Returns a string representation of this Level. 416 * 417 * @return the non-localized name of the Level, for example "INFO". 418 */ 419 @Override 420 public final String toString() { 421 return name; 422 } 423 424 /** 425 * Get the integer value for this level. This integer value 426 * can be used for efficient ordering comparisons between 427 * Level objects. 428 * @return the integer value for this level. 429 */ 430 public final int intValue() { 431 return value; 432 } 433 434 private static final long serialVersionUID = -8176160795706313070L; 435 436 // Serialization magic to prevent "doppelgangers". 437 // This is a performance optimization. 438 private Object readResolve() { 439 Optional<Level> level = KnownLevel.matches(this); 440 if (level.isPresent()) { 441 return level.get(); 442 } 443 // Woops. Whoever sent us this object knows 444 // about a new log level. Add it to our list. 445 return new Level(this.name, this.value, this.resourceBundleName); 446 } 447 448 /** 449 * Parse a level name string into a Level. 450 * <p> 451 * The argument string may consist of either a level name 452 * or an integer value. 453 * <p> 454 * For example: 455 * <ul> 456 * <li> "SEVERE" 457 * <li> "1000" 458 * </ul> 459 * 460 * @param name string to be parsed 461 * @throws NullPointerException if the name is null 462 * @throws IllegalArgumentException if the value is not valid. 463 * Valid values are integers between <CODE>Integer.MIN_VALUE</CODE> 464 * and <CODE>Integer.MAX_VALUE</CODE>, and all known level names. 465 * Known names are the levels defined by this class (e.g., <CODE>FINE</CODE>, 466 * <CODE>FINER</CODE>, <CODE>FINEST</CODE>), or created by this class with 467 * appropriate package access, or new levels defined or created 468 * by subclasses. 469 * 470 * @return The parsed value. Passing an integer that corresponds to a known name 471 * (e.g., 700) will return the associated name (e.g., <CODE>CONFIG</CODE>). 472 * Passing an integer that does not (e.g., 1) will return a new level name 473 * initialized to that value. 474 */ 475 public static synchronized Level parse(String name) throws IllegalArgumentException { 476 // Check that name is not null. 477 name.length(); 478 479 Optional<Level> level; 480 481 // Look for a known Level with the given non-localized name. 482 level = KnownLevel.findByName(name, KnownLevel::referent); 483 if (level.isPresent()) { 484 return level.get(); 485 } 486 487 // Now, check if the given name is an integer. If so, 488 // first look for a Level with the given value and then 489 // if necessary create one. 490 try { 491 int x = Integer.parseInt(name); 492 level = KnownLevel.findByValue(x, KnownLevel::referent); 493 if (level.isPresent()) { 494 return level.get(); 495 } 496 // add new Level. 497 Level levelObject = new Level(name, x); 498 // There's no need to use a reachability fence here because 499 // KnownLevel keeps a strong reference on the level when 500 // level.getClass() == Level.class. 501 return KnownLevel.findByValue(x, KnownLevel::referent).get(); 502 } catch (NumberFormatException ex) { 503 // Not an integer. 504 // Drop through. 505 } 506 507 // Finally, look for a known level with the given localized name, 508 // in the current default locale. 509 // This is relatively expensive, but not excessively so. 510 level = KnownLevel.findByLocalizedLevelName(name, KnownLevel::referent); 511 if (level .isPresent()) { 512 return level.get(); 513 } 514 515 // OK, we've tried everything and failed 516 throw new IllegalArgumentException("Bad level \"" + name + "\""); 517 } 518 519 /** 520 * Compare two objects for value equality. 521 * @return true if and only if the two objects have the same level value. 522 */ 523 @Override 524 public boolean equals(Object ox) { 525 try { 526 Level lx = (Level)ox; 527 return (lx.value == this.value); 528 } catch (Exception ex) { 529 return false; 530 } 531 } 532 533 /** 534 * Generate a hashcode. 535 * @return a hashcode based on the level value 536 */ 537 @Override 538 public int hashCode() { 539 return this.value; 540 } 541 542 // KnownLevel class maintains the global list of all known levels. 543 // The API allows multiple custom Level instances of the same name/value 544 // be created. This class provides convenient methods to find a level 545 // by a given name, by a given value, or by a given localized name. 546 // 547 // KnownLevel wraps the following Level objects: 548 // 1. levelObject: standard Level object or custom Level object 549 // 2. mirroredLevel: Level object representing the level specified in the 550 // logging configuration. 551 // 552 // Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods 553 // are non-final but the name and resource bundle name are parameters to 554 // the Level constructor. Use the mirroredLevel object instead of the 555 // levelObject to prevent the logging framework to execute foreign code 556 // implemented by untrusted Level subclass. 557 // 558 // Implementation Notes: 559 // If Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods 560 // were final, the following KnownLevel implementation can be removed. 561 // Future API change should take this into consideration. 562 static final class KnownLevel extends WeakReference<Level> { 563 private static Map<String, List<KnownLevel>> nameToLevels = new HashMap<>(); 564 private static Map<Integer, List<KnownLevel>> intToLevels = new HashMap<>(); 565 private static final ReferenceQueue<Level> QUEUE = new ReferenceQueue<>(); 566 567 // CUSTOM_LEVEL_CLV is used to register custom level instances with 568 // their defining class loader, so that they are garbage collected 569 // if and only if their class loader is no longer strongly 570 // referenced. 571 private static final ClassLoaderValue<List<Level>> CUSTOM_LEVEL_CLV = 572 new ClassLoaderValue<>(); 573 574 final Level mirroredLevel; // mirror of the custom Level 575 KnownLevel(Level l) { 576 super(l, QUEUE); 577 if (l.getClass() == Level.class) { 578 this.mirroredLevel = l; 579 } else { 580 // this mirrored level object is hidden 581 this.mirroredLevel = new Level(l.name, l.value, 582 l.resourceBundleName, false); 583 } 584 } 585 586 Optional<Level> mirrored() { 587 return Optional.of(mirroredLevel); 588 } 589 590 Optional<Level> referent() { 591 return Optional.ofNullable(get()); 592 } 593 594 private void remove() { 595 Optional.ofNullable(nameToLevels.get(mirroredLevel.name)) 596 .ifPresent((x) -> x.remove(this)); 597 Optional.ofNullable(intToLevels.get(mirroredLevel.value)) 598 .ifPresent((x) -> x.remove(this)); 599 } 600 601 // Remove all stale KnownLevel instances 602 static synchronized void purge() { 603 Reference<? extends Level> ref; 604 while ((ref = QUEUE.poll()) != null) { 605 if (ref instanceof KnownLevel) { 606 ((KnownLevel)ref).remove(); 607 } 608 } 609 } 610 611 private static void registerWithClassLoader(Level customLevel) { 612 PrivilegedAction<ClassLoader> pa = 613 () -> customLevel.getClass().getClassLoader(); 614 PrivilegedAction<String> pn = customLevel.getClass()::getName; 615 final String name = AccessController.doPrivileged(pn); 616 final ClassLoader cl = AccessController.doPrivileged(pa); 617 CUSTOM_LEVEL_CLV.computeIfAbsent(cl, (c, v) -> new ArrayList<>()) 618 .add(customLevel); 619 } 620 621 static synchronized void add(Level l) { 622 purge(); 623 // the mirroredLevel object is always added to the list 624 // before the custom Level instance 625 KnownLevel o = new KnownLevel(l); 626 List<KnownLevel> list = nameToLevels.get(l.name); 627 if (list == null) { 628 list = new ArrayList<>(); 629 nameToLevels.put(l.name, list); 630 } 631 list.add(o); 632 633 list = intToLevels.get(l.value); 634 if (list == null) { 635 list = new ArrayList<>(); 636 intToLevels.put(l.value, list); 637 } 638 list.add(o); 639 640 // keep the custom level reachable from its class loader 641 // This will ensure that custom level values are not GC'ed 642 // until there class loader is GC'ed. 643 if (o.mirroredLevel != l) { 644 registerWithClassLoader(l); 645 } 646 647 } 648 649 // Returns a KnownLevel with the given non-localized name. 650 static synchronized Optional<Level> findByName(String name, 651 Function<KnownLevel, Optional<Level>> selector) { 652 purge(); 653 return nameToLevels.getOrDefault(name, Collections.emptyList()) 654 .stream() 655 .map(selector) 656 .flatMap(Optional::stream) 657 .findFirst(); 658 } 659 660 // Returns a KnownLevel with the given value. 661 static synchronized Optional<Level> findByValue(int value, 662 Function<KnownLevel, Optional<Level>> selector) { 663 purge(); 664 return intToLevels.getOrDefault(value, Collections.emptyList()) 665 .stream() 666 .map(selector) 667 .flatMap(Optional::stream) 668 .findFirst(); 669 } 670 671 // Returns a KnownLevel with the given localized name matching 672 // by calling the Level.getLocalizedLevelName() method (i.e. found 673 // from the resourceBundle associated with the Level object). 674 // This method does not call Level.getLocalizedName() that may 675 // be overridden in a subclass implementation 676 static synchronized Optional<Level> findByLocalizedLevelName(String name, 677 Function<KnownLevel, Optional<Level>> selector) { 678 purge(); 679 return nameToLevels.values().stream() 680 .flatMap(List::stream) 681 .map(selector) 682 .flatMap(Optional::stream) 683 .filter(l -> name.equals(l.getLocalizedLevelName())) 684 .findFirst(); 685 } 686 687 static synchronized Optional<Level> matches(Level l) { 688 purge(); 689 List<KnownLevel> list = nameToLevels.get(l.name); 690 if (list != null) { 691 for (KnownLevel ref : list) { 692 Level levelObject = ref.get(); 693 if (levelObject == null) continue; 694 Level other = ref.mirroredLevel; 695 Class<? extends Level> type = levelObject.getClass(); 696 if (l.value == other.value && 697 (l.resourceBundleName == other.resourceBundleName || 698 (l.resourceBundleName != null && 699 l.resourceBundleName.equals(other.resourceBundleName)))) { 700 if (type == l.getClass()) { 701 return Optional.of(levelObject); 702 } 703 } 704 } 705 } 706 return Optional.empty(); 707 } 708 } 709 710 }