1/* 2 * Copyright (c) 2000, 2010, 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 26package java.util.logging; 27import java.util.ArrayList; 28import java.util.HashMap; 29import java.util.List; 30import java.util.Locale; 31import java.util.Map; 32import java.util.MissingResourceException; 33import java.util.ResourceBundle; 34 35import dalvik.system.VMStack; 36 37/** 38 * The Level class defines a set of standard logging levels that 39 * can be used to control logging output. The logging Level objects 40 * are ordered and are specified by ordered integers. Enabling logging 41 * at a given level also enables logging at all higher levels. 42 * <p> 43 * Clients should normally use the predefined Level constants such 44 * as Level.SEVERE. 45 * <p> 46 * The levels in descending order are: 47 * <ul> 48 * <li>SEVERE (highest value) 49 * <li>WARNING 50 * <li>INFO 51 * <li>CONFIG 52 * <li>FINE 53 * <li>FINER 54 * <li>FINEST (lowest value) 55 * </ul> 56 * In addition there is a level OFF that can be used to turn 57 * off logging, and a level ALL that can be used to enable 58 * logging of all messages. 59 * <p> 60 * It is possible for third parties to define additional logging 61 * levels by subclassing Level. In such cases subclasses should 62 * take care to chose unique integer level values and to ensure that 63 * they maintain the Object uniqueness property across serialization 64 * by defining a suitable readResolve method. 65 * 66 * @since 1.4 67 */ 68 69public class Level implements java.io.Serializable { 70 private static String defaultBundle = "sun.util.logging.resources.logging"; 71 72 /** 73 * @serial The non-localized name of the level. 74 */ 75 private final String name; 76 77 /** 78 * @serial The integer value of the level. 79 */ 80 private final int value; 81 82 /** 83 * @serial The resource bundle name to be used in localizing the level name. 84 */ 85 private final String resourceBundleName; 86 87 // localized level name 88 private String localizedLevelName; 89 90 private transient ResourceBundle rb; 91 92 /** 93 * OFF is a special level that can be used to turn off logging. 94 * This level is initialized to <CODE>Integer.MAX_VALUE</CODE>. 95 */ 96 public static final Level OFF = new Level("OFF",Integer.MAX_VALUE, defaultBundle); 97 98 /** 99 * SEVERE is a message level indicating a serious failure. 100 * <p> 101 * In general SEVERE messages should describe events that are 102 * of considerable importance and which will prevent normal 103 * program execution. They should be reasonably intelligible 104 * to end users and to system administrators. 105 * This level is initialized to <CODE>1000</CODE>. 106 */ 107 public static final Level SEVERE = new Level("SEVERE",1000, defaultBundle); 108 109 /** 110 * WARNING is a message level indicating a potential problem. 111 * <p> 112 * In general WARNING messages should describe events that will 113 * be of interest to end users or system managers, or which 114 * indicate potential problems. 115 * This level is initialized to <CODE>900</CODE>. 116 */ 117 public static final Level WARNING = new Level("WARNING", 900, defaultBundle); 118 119 /** 120 * INFO is a message level for informational messages. 121 * <p> 122 * Typically INFO messages will be written to the console 123 * or its equivalent. So the INFO level should only be 124 * used for reasonably significant messages that will 125 * make sense to end users and system administrators. 126 * This level is initialized to <CODE>800</CODE>. 127 */ 128 public static final Level INFO = new Level("INFO", 800, defaultBundle); 129 130 /** 131 * CONFIG is a message level for static configuration messages. 132 * <p> 133 * CONFIG messages are intended to provide a variety of static 134 * configuration information, to assist in debugging problems 135 * that may be associated with particular configurations. 136 * For example, CONFIG message might include the CPU type, 137 * the graphics depth, the GUI look-and-feel, etc. 138 * This level is initialized to <CODE>700</CODE>. 139 */ 140 public static final Level CONFIG = new Level("CONFIG", 700, defaultBundle); 141 142 /** 143 * FINE is a message level providing tracing information. 144 * <p> 145 * All of FINE, FINER, and FINEST are intended for relatively 146 * detailed tracing. The exact meaning of the three levels will 147 * vary between subsystems, but in general, FINEST should be used 148 * for the most voluminous detailed output, FINER for somewhat 149 * less detailed output, and FINE for the lowest volume (and 150 * most important) messages. 151 * <p> 152 * In general the FINE level should be used for information 153 * that will be broadly interesting to developers who do not have 154 * a specialized interest in the specific subsystem. 155 * <p> 156 * FINE messages might include things like minor (recoverable) 157 * failures. Issues indicating potential performance problems 158 * are also worth logging as FINE. 159 * This level is initialized to <CODE>500</CODE>. 160 */ 161 public static final Level FINE = new Level("FINE", 500, defaultBundle); 162 163 /** 164 * FINER indicates a fairly detailed tracing message. 165 * By default logging calls for entering, returning, or throwing 166 * an exception are traced at this level. 167 * This level is initialized to <CODE>400</CODE>. 168 */ 169 public static final Level FINER = new Level("FINER", 400, defaultBundle); 170 171 /** 172 * FINEST indicates a highly detailed tracing message. 173 * This level is initialized to <CODE>300</CODE>. 174 */ 175 public static final Level FINEST = new Level("FINEST", 300, defaultBundle); 176 177 /** 178 * ALL indicates that all messages should be logged. 179 * This level is initialized to <CODE>Integer.MIN_VALUE</CODE>. 180 */ 181 public static final Level ALL = new Level("ALL", Integer.MIN_VALUE, defaultBundle); 182 183 /** 184 * Create a named Level with a given integer value. 185 * <p> 186 * Note that this constructor is "protected" to allow subclassing. 187 * In general clients of logging should use one of the constant Level 188 * objects such as SEVERE or FINEST. However, if clients need to 189 * add new logging levels, they may subclass Level and define new 190 * constants. 191 * @param name the name of the Level, for example "SEVERE". 192 * @param value an integer value for the level. 193 * @throws NullPointerException if the name is null 194 */ 195 protected Level(String name, int value) { 196 this(name, value, null); 197 } 198 199 /** 200 * Create a named Level with a given integer value and a 201 * given localization resource name. 202 * <p> 203 * @param name the name of the Level, for example "SEVERE". 204 * @param value an integer value for the level. 205 * @param resourceBundleName name of a resource bundle to use in 206 * localizing the given name. If the resourceBundleName is null 207 * or an empty string, it is ignored. 208 * @throws NullPointerException if the name is null 209 */ 210 protected Level(String name, int value, String resourceBundleName) { 211 if (name == null) { 212 throw new NullPointerException(); 213 } 214 this.name = name; 215 this.value = value; 216 this.resourceBundleName = resourceBundleName; 217 if (resourceBundleName != null) { 218 try { 219 ClassLoader cl = VMStack.getCallingClassLoader(); 220 if (cl != null) { 221 rb = ResourceBundle.getBundle(resourceBundleName, Locale.getDefault(), cl); 222 } else { 223 rb = ResourceBundle.getBundle(resourceBundleName); 224 } 225 } catch (MissingResourceException ex) { 226 rb = null; 227 } 228 } 229 this.localizedLevelName = resourceBundleName == null ? name : null; 230 KnownLevel.add(this); 231 } 232 233 /** 234 * Return the level's localization resource bundle name, or 235 * null if no localization bundle is defined. 236 * 237 * @return localization resource bundle name 238 */ 239 public String getResourceBundleName() { 240 return resourceBundleName; 241 } 242 243 /** 244 * Return the non-localized string name of the Level. 245 * 246 * @return non-localized name 247 */ 248 public String getName() { 249 return name; 250 } 251 252 /** 253 * Return the localized string name of the Level, for 254 * the current default locale. 255 * <p> 256 * If no localization information is available, the 257 * non-localized name is returned. 258 * 259 * @return localized name 260 */ 261 public String getLocalizedName() { 262 return getLocalizedLevelName(); 263 } 264 265 // package-private getLevelName() is used by the implementation 266 // instead of getName() to avoid calling the subclass's version 267 final String getLevelName() { 268 return this.name; 269 } 270 271 final synchronized String getLocalizedLevelName() { 272 if (localizedLevelName != null) { 273 return localizedLevelName; 274 } 275 276 try { 277 localizedLevelName = rb.getString(name); 278 } catch (Exception ex) { 279 localizedLevelName = name; 280 } 281 return localizedLevelName; 282 } 283 284 // Returns a mirrored Level object that matches the given name as 285 // specified in the Level.parse method. Returns null if not found. 286 // 287 // It returns the same Level object as the one returned by Level.parse 288 // method if the given name is a non-localized name or integer. 289 // 290 // If the name is a localized name, findLevel and parse method may 291 // return a different level value if there is a custom Level subclass 292 // that overrides Level.getLocalizedName() to return a different string 293 // than what's returned by the default implementation. 294 // 295 static Level findLevel(String name) { 296 if (name == null) { 297 throw new NullPointerException(); 298 } 299 300 KnownLevel level; 301 302 // Look for a known Level with the given non-localized name. 303 level = KnownLevel.findByName(name); 304 if (level != null) { 305 return level.mirroredLevel; 306 } 307 308 // Now, check if the given name is an integer. If so, 309 // first look for a Level with the given value and then 310 // if necessary create one. 311 try { 312 int x = Integer.parseInt(name); 313 level = KnownLevel.findByValue(x); 314 if (level == null) { 315 // add new Level 316 Level levelObject = new Level(name, x); 317 level = KnownLevel.findByValue(x); 318 } 319 return level.mirroredLevel; 320 } catch (NumberFormatException ex) { 321 // Not an integer. 322 // Drop through. 323 } 324 325 level = KnownLevel.findByLocalizedLevelName(name); 326 if (level != null) { 327 return level.mirroredLevel; 328 } 329 330 return null; 331 } 332 333 /** 334 * Returns a string representation of this Level. 335 * 336 * @return the non-localized name of the Level, for example "INFO". 337 */ 338 public final String toString() { 339 return name; 340 } 341 342 /** 343 * Get the integer value for this level. This integer value 344 * can be used for efficient ordering comparisons between 345 * Level objects. 346 * @return the integer value for this level. 347 */ 348 public final int intValue() { 349 return value; 350 } 351 352 private static final long serialVersionUID = -8176160795706313070L; 353 354 // Serialization magic to prevent "doppelgangers". 355 // This is a performance optimization. 356 private Object readResolve() { 357 KnownLevel o = KnownLevel.matches(this); 358 if (o != null) { 359 return o.levelObject; 360 } 361 362 // Woops. Whoever sent us this object knows 363 // about a new log level. Add it to our list. 364 Level level = new Level(this.name, this.value, this.resourceBundleName); 365 return level; 366 } 367 368 /** 369 * Parse a level name string into a Level. 370 * <p> 371 * The argument string may consist of either a level name 372 * or an integer value. 373 * <p> 374 * For example: 375 * <ul> 376 * <li> "SEVERE" 377 * <li> "1000" 378 * </ul> 379 * 380 * @param name string to be parsed 381 * @throws NullPointerException if the name is null 382 * @throws IllegalArgumentException if the value is not valid. 383 * Valid values are integers between <CODE>Integer.MIN_VALUE</CODE> 384 * and <CODE>Integer.MAX_VALUE</CODE>, and all known level names. 385 * Known names are the levels defined by this class (e.g., <CODE>FINE</CODE>, 386 * <CODE>FINER</CODE>, <CODE>FINEST</CODE>), or created by this class with 387 * appropriate package access, or new levels defined or created 388 * by subclasses. 389 * 390 * @return The parsed value. Passing an integer that corresponds to a known name 391 * (e.g., 700) will return the associated name (e.g., <CODE>CONFIG</CODE>). 392 * Passing an integer that does not (e.g., 1) will return a new level name 393 * initialized to that value. 394 */ 395 public static synchronized Level parse(String name) throws IllegalArgumentException { 396 // Check that name is not null. 397 name.length(); 398 399 KnownLevel level; 400 401 // Look for a known Level with the given non-localized name. 402 level = KnownLevel.findByName(name); 403 if (level != null) { 404 return level.levelObject; 405 } 406 407 // Now, check if the given name is an integer. If so, 408 // first look for a Level with the given value and then 409 // if necessary create one. 410 try { 411 int x = Integer.parseInt(name); 412 level = KnownLevel.findByValue(x); 413 if (level == null) { 414 // add new Level 415 Level levelObject = new Level(name, x); 416 level = KnownLevel.findByValue(x); 417 } 418 return level.levelObject; 419 } catch (NumberFormatException ex) { 420 // Not an integer. 421 // Drop through. 422 } 423 424 // Finally, look for a known level with the given localized name, 425 // in the current default locale. 426 // This is relatively expensive, but not excessively so. 427 level = KnownLevel.findByLocalizedName(name); 428 if (level != null) { 429 return level.levelObject; 430 } 431 432 // OK, we've tried everything and failed 433 throw new IllegalArgumentException("Bad level \"" + name + "\""); 434 } 435 436 /** 437 * Compare two objects for value equality. 438 * @return true if and only if the two objects have the same level value. 439 */ 440 public boolean equals(Object ox) { 441 try { 442 Level lx = (Level)ox; 443 return (lx.value == this.value); 444 } catch (Exception ex) { 445 return false; 446 } 447 } 448 449 /** 450 * Generate a hashcode. 451 * @return a hashcode based on the level value 452 */ 453 public int hashCode() { 454 return this.value; 455 } 456 457 // KnownLevel class maintains the global list of all known levels. 458 // The API allows multiple custom Level instances of the same name/value 459 // be created. This class provides convenient methods to find a level 460 // by a given name, by a given value, or by a given localized name. 461 // 462 // KnownLevel wraps the following Level objects: 463 // 1. levelObject: standard Level object or custom Level object 464 // 2. mirroredLevel: Level object representing the level specified in the 465 // logging configuration. 466 // 467 // Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods 468 // are non-final but the name and resource bundle name are parameters to 469 // the Level constructor. Use the mirroredLevel object instead of the 470 // levelObject to prevent the logging framework to execute foreign code 471 // implemented by untrusted Level subclass. 472 // 473 // Implementation Notes: 474 // If Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods 475 // were final, the following KnownLevel implementation can be removed. 476 // Future API change should take this into consideration. 477 static final class KnownLevel { 478 private static Map<String, List<KnownLevel>> nameToLevels = new HashMap<>(); 479 private static Map<Integer, List<KnownLevel>> intToLevels = new HashMap<>(); 480 final Level levelObject; // instance of Level class or Level subclass 481 final Level mirroredLevel; // instance of Level class 482 KnownLevel(Level l) { 483 this.levelObject = l; 484 if (l.getClass() == Level.class) { 485 this.mirroredLevel = l; 486 } else { 487 this.mirroredLevel = new Level(l.name, l.value, l.resourceBundleName); 488 } 489 } 490 491 static synchronized void add(Level l) { 492 // the mirroredLevel object is always added to the list 493 // before the custom Level instance 494 KnownLevel o = new KnownLevel(l); 495 List<KnownLevel> list = nameToLevels.get(l.name); 496 if (list == null) { 497 list = new ArrayList<>(); 498 nameToLevels.put(l.name, list); 499 } 500 list.add(o); 501 502 list = intToLevels.get(l.value); 503 if (list == null) { 504 list = new ArrayList<>(); 505 intToLevels.put(l.value, list); 506 } 507 list.add(o); 508 } 509 510 // Returns a KnownLevel with the given non-localized name. 511 static synchronized KnownLevel findByName(String name) { 512 List<KnownLevel> list = nameToLevels.get(name); 513 if (list != null) { 514 return list.get(0); 515 } 516 return null; 517 } 518 519 // Returns a KnownLevel with the given value. 520 static synchronized KnownLevel findByValue(int value) { 521 List<KnownLevel> list = intToLevels.get(value); 522 if (list != null) { 523 return list.get(0); 524 } 525 return null; 526 } 527 528 // Returns a KnownLevel with the given localized name matching 529 // by calling the Level.getLocalizedLevelName() method (i.e. found 530 // from the resourceBundle associated with the Level object). 531 // This method does not call Level.getLocalizedName() that may 532 // be overridden in a subclass implementation 533 static synchronized KnownLevel findByLocalizedLevelName(String name) { 534 for (List<KnownLevel> levels : nameToLevels.values()) { 535 for (KnownLevel l : levels) { 536 String lname = l.levelObject.getLocalizedLevelName(); 537 if (name.equals(lname)) { 538 return l; 539 } 540 } 541 } 542 return null; 543 } 544 545 // Returns a KnownLevel with the given localized name matching 546 // by calling the Level.getLocalizedName() method 547 static synchronized KnownLevel findByLocalizedName(String name) { 548 for (List<KnownLevel> levels : nameToLevels.values()) { 549 for (KnownLevel l : levels) { 550 String lname = l.levelObject.getLocalizedName(); 551 if (name.equals(lname)) { 552 return l; 553 } 554 } 555 } 556 return null; 557 } 558 559 static synchronized KnownLevel matches(Level l) { 560 List<KnownLevel> list = nameToLevels.get(l.name); 561 if (list != null) { 562 for (KnownLevel level : list) { 563 Level other = level.mirroredLevel; 564 if (l.value == other.value && 565 (l.resourceBundleName == other.resourceBundleName || 566 (l.resourceBundleName != null && 567 l.resourceBundleName.equals(other.resourceBundleName)))) { 568 return level; 569 } 570 } 571 } 572 return null; 573 } 574 } 575 576} 577