1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.util.logging; 19 20 import dalvik.system.DalvikLogHandler; 21 import dalvik.system.DalvikLogging; 22 import java.util.ArrayList; 23 import java.util.Iterator; 24 import java.util.List; 25 import java.util.Locale; 26 import java.util.MissingResourceException; 27 import java.util.ResourceBundle; 28 import java.util.concurrent.CopyOnWriteArrayList; 29 30 /** 31 * Loggers are used to log records to a variety of destinations such as log files or 32 * the console. They use instances of {@link Handler} to actually do the destination-specific 33 * operations. 34 * 35 * <p>Client applications can get named loggers by calling the {@code getLogger} 36 * methods. They can also get anonymous loggers by calling the 37 * {@code getAnonymousLogger} methods. Named loggers are organized in a 38 * namespace hierarchy managed by a log manager. The naming convention is 39 * usually the Java package naming convention. Anonymous loggers do not belong to any namespace. 40 * 41 * <p>Developers should use named loggers to enable logging to be controlled on a 42 * per-{@code Logger} granularity. The recommended idiom is to create and assign the logger to 43 * a {@code static final} field. This ensures that there's always a strong reference to the logger, 44 * preventing it from being garbage collected. In particular, {@link LogManager#addLogger(Logger)} 45 * will <i>not</i> keep your logger live. 46 * 47 * <p>Loggers "inherit" log level setting from their parent if their own level is 48 * set to {@code null}. This is also true for the resource bundle. The logger's 49 * resource bundle is used to localize the log messages if no resource bundle 50 * name is given when a log method is called. If {@code getUseParentHandlers()} 51 * returns {@code true}, loggers also inherit their parent's handlers. In this 52 * context, "inherit" only means that "behavior" is inherited. The internal 53 * field values will not change, for example, {@code getLevel()} still returns 54 * {@code null}. 55 * <p> 56 * When loading a given resource bundle, the logger first tries to use the 57 * context {@code ClassLoader}. If that fails, it tries the system {@code ClassLoader}. And if 58 * that still fails, it searches up the class stack and uses each class's 59 * {@code ClassLoader} to try to locate the resource bundle. 60 * <p> 61 * Some log methods accept log requests that do not specify the source class and 62 * source method. In these cases, the logging framework will automatically infer 63 * the calling class and method, but this is not guaranteed to be accurate. 64 * <p> 65 * Once a {@code LogRecord} object has been passed into the logging framework, 66 * it is owned by the logging framework and the client applications should not 67 * use it any longer. 68 * <p> 69 * All methods of this class are thread-safe. 70 * 71 * @see LogManager 72 */ 73 public class Logger { 74 75 /** A handler for use when no handler optimization is possible. */ 76 private static final DalvikLogHandler GENERAL_LOG_HANDLER = new DalvikLogHandler() { 77 public void publish(Logger source, String tag, Level level, String message) { 78 LogRecord record = new LogRecord(level, message); 79 record.setLoggerName(source.name); 80 source.setResourceBundle(record); 81 source.log(record); 82 } 83 }; 84 85 /** 86 * The name of the global logger. Before using this, see the discussion of how to use 87 * {@code Logger} in the class documentation. 88 * @since 1.6 89 */ 90 public static final String GLOBAL_LOGGER_NAME = "global"; 91 92 /** 93 * The global logger is provided as convenience for casual use. 94 * @deprecated deadlock-prone. Use {@code Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)} as 95 * a direct replacement, but see the discussion of how to use {@code Logger} in the class 96 * documentation. 97 */ 98 @Deprecated 99 public static final Logger global = new Logger(GLOBAL_LOGGER_NAME, null); 100 101 /** 102 * When converting the concurrent collection of handlers to an array, we 103 * always pass a zero-length array to avoid size miscalculations. Passing 104 * properly-sized arrays is non-atomic, and risks a null element in the 105 * result. 106 */ 107 private static final Handler[] EMPTY_HANDLERS_ARRAY = new Handler[0]; 108 109 /** The name of this logger. */ 110 private volatile String name; 111 112 /** The parent logger of this logger. */ 113 Logger parent; 114 115 /** The logging level of this logger, or null if none is set. */ 116 volatile Level levelObjVal; 117 118 /** 119 * The effective logging level of this logger. In order of preference this 120 * is the first applicable of: 121 * <ol> 122 * <li>the int value of this logger's {@link #levelObjVal} 123 * <li>the logging level of the parent 124 * <li>the default level ({@link Level#INFO}) 125 * </ol> 126 */ 127 volatile int levelIntVal = Level.INFO.intValue(); 128 129 /** The filter. */ 130 private Filter filter; 131 132 /** 133 * The resource bundle used to localize logging messages. If null, no 134 * localization will be performed. 135 */ 136 private volatile String resourceBundleName; 137 138 /** The loaded resource bundle according to the specified name. */ 139 private volatile ResourceBundle resourceBundle; 140 141 /** 142 * The handlers attached to this logger. Eagerly initialized and 143 * concurrently modified. 144 */ 145 private final List<Handler> handlers = new CopyOnWriteArrayList<Handler>(); 146 147 /** True to notify the parent's handlers of each log message. */ 148 private boolean notifyParentHandlers = true; 149 150 /** 151 * Indicates whether this logger is named. Only {@link #getAnonymousLogger 152 * anonymous loggers} are unnamed. 153 */ 154 private boolean isNamed = true; 155 156 /** 157 * Child loggers. Should be accessed only while synchronized on {@code 158 * LogManager.getLogManager()}. 159 */ 160 final List<Logger> children = new ArrayList<Logger>(); 161 162 /** the tag used for optimized logging. Derived from the logger name. */ 163 private final String androidTag; 164 165 /** Handler delegate for either optimized or standard logging. */ 166 private volatile DalvikLogHandler dalvikLogHandler = GENERAL_LOG_HANDLER; 167 168 /** 169 * We've optimized for the common case: logging to a single handler that 170 * implements {@link DalvikLogHandler}. This is how Android framework 171 * applications are configured by default. 172 * 173 * <p>This optimization has been measured to show a 2.75x improvement in 174 * throughput in the common case: 154ns vs. 56ns per message on a Cortex-A8. 175 * Direct use of {@code android.util.Log} takes 29ns per message. 176 * 177 * <p>Each time the handler configuration changes, either directly or 178 * indirectly, it's necessary to either turn on or off this optimization. 179 * When the optimization is off, {@link #dalvikLogHandler} is assigned to 180 * {@link #GENERAL_LOG_HANDLER} which can satisfy arbitrary configuration. 181 * When the optimization is possible, {@link #dalvikLogHandler} is assigned 182 * to the user's efficient implementation. In pratice this is usually the 183 * {@code com.android.internal.logging.AndroidHandler}. 184 */ 185 void updateDalvikLogHandler() { 186 DalvikLogHandler newLogHandler = GENERAL_LOG_HANDLER; 187 188 Logger parent = this.parent; 189 190 if (getClass() != Logger.class) { 191 /* 192 * Do nothing. Subclasses aren't eligible for the optimization 193 * because they may override methods like getHandlers() or 194 * log(LogRecord). 195 */ 196 197 } else if (parent == null) { 198 // we use an iterator rather than size()+get() for safe concurrency 199 Iterator<Handler> h = handlers.iterator(); 200 if (h.hasNext()) { 201 Handler firstHandler = h.next(); 202 if (!h.hasNext() && firstHandler instanceof DalvikLogHandler) { 203 /* 204 * At this point, we're eligible for the optimization. We've 205 * satisfied these constraints: 206 * 1. This is not a subclass of logger 207 * 2. This is a root logger (no parent) 208 * 3. There is exactly one handler installed 209 * 4. That handler is a DalvikLogHandler 210 */ 211 newLogHandler = (DalvikLogHandler) firstHandler; 212 } 213 } 214 } else if (handlers.isEmpty() && notifyParentHandlers) { 215 /* 216 * At this point, we're eligible for the optimization if our parent 217 * logger is eligible. We've satisfied these constraints: 218 * 1. This is not a subclass of logger 219 * 2. our parent exists 220 * 3. we have no handlers of our own 221 * 4. we notify our parent's handlers 222 */ 223 newLogHandler = parent.dalvikLogHandler; 224 } 225 226 if (newLogHandler == this.dalvikLogHandler) { 227 return; 228 } 229 230 this.dalvikLogHandler = newLogHandler; 231 232 for (Logger logger : children) { 233 logger.updateDalvikLogHandler(); 234 } 235 } 236 237 /** 238 * Constructs a {@code Logger} object with the supplied name and resource 239 * bundle name; {@code notifiyParentHandlers} is set to {@code true}. 240 * <p> 241 * Notice : Loggers use a naming hierarchy. Thus "z.x.y" is a child of "z.x". 242 * 243 * @param name 244 * the name of this logger, may be {@code null} for anonymous 245 * loggers. 246 * @param resourceBundleName 247 * the name of the resource bundle used to localize logging 248 * messages, may be {@code null}. 249 * @throws MissingResourceException 250 * if the specified resource bundle can not be loaded. 251 */ 252 protected Logger(String name, String resourceBundleName) { 253 this.name = name; 254 initResourceBundle(resourceBundleName); 255 this.androidTag = DalvikLogging.loggerNameToTag(name); 256 updateDalvikLogHandler(); 257 } 258 259 /** 260 * Load the specified resource bundle, use privileged code. 261 * 262 * @param resourceBundleName 263 * the name of the resource bundle to load, cannot be {@code null}. 264 * @return the loaded resource bundle. 265 * @throws MissingResourceException 266 * if the specified resource bundle can not be loaded. 267 */ 268 static ResourceBundle loadResourceBundle(String resourceBundleName) { 269 // try context class loader to load the resource 270 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 271 if (cl != null) { 272 try { 273 return ResourceBundle.getBundle(resourceBundleName, Locale.getDefault(), cl); 274 } catch (MissingResourceException ignored) { 275 // Failed to load using context class loader, ignore 276 } 277 } 278 // try system class loader to load the resource 279 cl = ClassLoader.getSystemClassLoader(); 280 if (cl != null) { 281 try { 282 return ResourceBundle.getBundle(resourceBundleName, Locale.getDefault(), cl); 283 } catch (MissingResourceException ignored) { 284 // Failed to load using system class loader, ignore 285 } 286 } 287 throw new MissingResourceException("Failed to load the specified resource bundle \"" + 288 resourceBundleName + "\"", resourceBundleName, null); 289 } 290 291 /** 292 * Gets an anonymous logger to use internally in a thread. Anonymous loggers 293 * are not registered in the log manager's namespace. No security checks 294 * will be performed when updating an anonymous logger's control settings. 295 * <p> 296 * The anonymous loggers' parent is set to be the root logger. This way it 297 * inherits the default logging level and handlers from the root logger. 298 * 299 * @return a new instance of anonymous logger. 300 */ 301 public static Logger getAnonymousLogger() { 302 return getAnonymousLogger(null); 303 } 304 305 /** 306 * Gets an anonymous logger to use internally in a thread. Anonymous loggers 307 * are not registered in the log manager's namespace. No security checks 308 * will be performed when updating an anonymous logger's control settings. 309 * <p> 310 * The anonymous loggers' parent is set to be the root logger. This way it 311 * inherits default logging level and handlers from the root logger. 312 * 313 * @param resourceBundleName 314 * the name of the resource bundle used to localize log messages. 315 * @return a new instance of anonymous logger. 316 * @throws MissingResourceException 317 * if the specified resource bundle can not be loaded. 318 */ 319 public static Logger getAnonymousLogger(String resourceBundleName) { 320 Logger result = new Logger(null, resourceBundleName); 321 result.isNamed = false; 322 LogManager logManager = LogManager.getLogManager(); 323 logManager.setParent(result, logManager.getLogger("")); 324 return result; 325 } 326 327 /** 328 * Initializes this logger's resource bundle. 329 * 330 * @throws IllegalArgumentException if this logger's resource bundle already 331 * exists and is different from the resource bundle specified. 332 */ 333 private synchronized void initResourceBundle(String resourceBundleName) { 334 String current = this.resourceBundleName; 335 336 if (current != null) { 337 if (current.equals(resourceBundleName)) { 338 return; 339 } else { 340 throw new IllegalArgumentException("Resource bundle name '" + resourceBundleName + "' is inconsistent with the existing '" + current + "'"); 341 } 342 } 343 344 if (resourceBundleName != null) { 345 this.resourceBundle = loadResourceBundle(resourceBundleName); 346 this.resourceBundleName = resourceBundleName; 347 } 348 } 349 350 /** 351 * Gets a named logger. The returned logger may already exist or may be 352 * newly created. In the latter case, its level will be set to the 353 * configured level according to the {@code LogManager}'s properties. 354 * 355 * @param name 356 * the name of the logger to get, cannot be {@code null}. 357 * @return a named logger. 358 * @throws MissingResourceException 359 * If the specified resource bundle can not be loaded. 360 */ 361 public static Logger getLogger(String name) { 362 return LogManager.getLogManager().getOrCreate(name, null); 363 } 364 365 /** 366 * Gets a named logger associated with the supplied resource bundle. The 367 * resource bundle will be used to localize logging messages. 368 * 369 * @param name 370 * the name of the logger to get, cannot be {@code null}. 371 * @param resourceBundleName 372 * the name of the resource bundle, may be {@code null}. 373 * @throws IllegalArgumentException 374 * if the logger identified by {@code name} is associated with a 375 * resource bundle and its name is not equal to 376 * {@code resourceBundleName}. 377 * @throws MissingResourceException 378 * if the name of the resource bundle cannot be found. 379 * @return a named logger. 380 */ 381 public static Logger getLogger(String name, String resourceBundleName) { 382 Logger result = LogManager.getLogManager() 383 .getOrCreate(name, resourceBundleName); 384 result.initResourceBundle(resourceBundleName); 385 return result; 386 } 387 388 /** 389 * Returns the global {@code Logger}. 390 * @since 1.7 391 * @hide 1.7 392 */ 393 public static Logger getGlobal() { 394 return global; 395 } 396 397 /** 398 * Adds a handler to this logger. The {@code name} will be fed with log 399 * records received by this logger. 400 * 401 * @param handler 402 * the handler object to add, cannot be {@code null}. 403 */ 404 public void addHandler(Handler handler) { 405 if (handler == null) { 406 throw new NullPointerException("handler == null"); 407 } 408 // Anonymous loggers can always add handlers 409 if (this.isNamed) { 410 LogManager.getLogManager().checkAccess(); 411 } 412 this.handlers.add(handler); 413 updateDalvikLogHandler(); 414 } 415 416 /** 417 * Set the logger's manager and initializes its configuration from the 418 * manager's properties. 419 */ 420 void setManager(LogManager manager) { 421 String levelProperty = manager.getProperty(name + ".level"); 422 if (levelProperty != null) { 423 try { 424 manager.setLevelRecursively(Logger.this, Level.parse(levelProperty)); 425 } catch (IllegalArgumentException invalidLevel) { 426 invalidLevel.printStackTrace(); 427 } 428 } 429 430 String handlersPropertyName = name.isEmpty() ? "handlers" : name + ".handlers"; 431 String handlersProperty = manager.getProperty(handlersPropertyName); 432 if (handlersProperty != null) { 433 for (String handlerName : handlersProperty.split(",|\\s")) { 434 if (handlerName.isEmpty()) { 435 continue; 436 } 437 438 final Handler handler; 439 try { 440 handler = (Handler) LogManager.getInstanceByClass(handlerName); 441 } catch (Exception invalidHandlerName) { 442 invalidHandlerName.printStackTrace(); 443 continue; 444 } 445 446 try { 447 String level = manager.getProperty(handlerName + ".level"); 448 if (level != null) { 449 handler.setLevel(Level.parse(level)); 450 } 451 } catch (Exception invalidLevel) { 452 invalidLevel.printStackTrace(); 453 } 454 455 handlers.add(handler); 456 } 457 } 458 459 updateDalvikLogHandler(); 460 } 461 462 /** 463 * Gets all the handlers associated with this logger. 464 * 465 * @return an array of all the handlers associated with this logger. 466 */ 467 public Handler[] getHandlers() { 468 return handlers.toArray(EMPTY_HANDLERS_ARRAY); 469 } 470 471 /** 472 * Removes a handler from this logger. If the specified handler does not 473 * exist then this method has no effect. 474 * 475 * @param handler 476 * the handler to be removed. 477 */ 478 public void removeHandler(Handler handler) { 479 // Anonymous loggers can always remove handlers 480 if (this.isNamed) { 481 LogManager.getLogManager().checkAccess(); 482 } 483 if (handler == null) { 484 return; 485 } 486 this.handlers.remove(handler); 487 updateDalvikLogHandler(); 488 } 489 490 /** 491 * Gets the filter used by this logger. 492 * 493 * @return the filter used by this logger, may be {@code null}. 494 */ 495 public Filter getFilter() { 496 return this.filter; 497 } 498 499 /** 500 * Sets the filter used by this logger. 501 * 502 * @param newFilter 503 * the filter to set, may be {@code null}. 504 */ 505 public void setFilter(Filter newFilter) { 506 // Anonymous loggers can always set the filter 507 if (this.isNamed) { 508 LogManager.getLogManager().checkAccess(); 509 } 510 filter = newFilter; 511 } 512 513 /** 514 * Gets the logging level of this logger. A {@code null} level indicates 515 * that this logger inherits its parent's level. 516 * 517 * @return the logging level of this logger. 518 */ 519 public Level getLevel() { 520 return levelObjVal; 521 } 522 523 /** 524 * Sets the logging level for this logger. A {@code null} level indicates 525 * that this logger will inherit its parent's level. 526 * 527 * @param newLevel 528 * the logging level to set. 529 */ 530 public void setLevel(Level newLevel) { 531 // Anonymous loggers can always set the level 532 LogManager logManager = LogManager.getLogManager(); 533 if (this.isNamed) { 534 logManager.checkAccess(); 535 } 536 logManager.setLevelRecursively(this, newLevel); 537 } 538 539 /** 540 * Gets the flag which indicates whether to use the handlers of this 541 * logger's parent to publish incoming log records, potentially recursively 542 * up the namespace. 543 * 544 * @return {@code true} if set to use parent's handlers, {@code false} 545 * otherwise. 546 */ 547 public boolean getUseParentHandlers() { 548 return this.notifyParentHandlers; 549 } 550 551 /** 552 * Sets the flag which indicates whether to use the handlers of this 553 * logger's parent, potentially recursively up the namespace. 554 * 555 * @param notifyParentHandlers 556 * the new flag indicating whether to use the parent's handlers. 557 */ 558 public void setUseParentHandlers(boolean notifyParentHandlers) { 559 // Anonymous loggers can always set the useParentHandlers flag 560 if (this.isNamed) { 561 LogManager.getLogManager().checkAccess(); 562 } 563 this.notifyParentHandlers = notifyParentHandlers; 564 updateDalvikLogHandler(); 565 } 566 567 /** 568 * Gets the nearest parent of this logger in the namespace, a {@code null} 569 * value will be returned if called on the root logger. 570 * 571 * @return the parent of this logger in the namespace. 572 */ 573 public Logger getParent() { 574 return parent; 575 } 576 577 /** 578 * Sets the parent of this logger in the namespace. This method should be 579 * used by the {@code LogManager} object only. 580 * 581 * @param parent 582 * the parent logger to set. 583 */ 584 public void setParent(Logger parent) { 585 if (parent == null) { 586 throw new NullPointerException("parent == null"); 587 } 588 589 // even anonymous loggers are checked 590 LogManager logManager = LogManager.getLogManager(); 591 logManager.checkAccess(); 592 logManager.setParent(this, parent); 593 } 594 595 /** 596 * Gets the name of this logger, {@code null} for anonymous loggers. 597 * 598 * @return the name of this logger. 599 */ 600 public String getName() { 601 return this.name; 602 } 603 604 /** 605 * Gets the loaded resource bundle used by this logger to localize logging 606 * messages. If the value is {@code null}, the parent's resource bundle will be 607 * inherited. 608 * 609 * @return the loaded resource bundle used by this logger. 610 */ 611 public ResourceBundle getResourceBundle() { 612 return this.resourceBundle; 613 } 614 615 /** 616 * Gets the name of the loaded resource bundle used by this logger to 617 * localize logging messages. If the value is {@code null}, the parent's resource 618 * bundle name will be inherited. 619 * 620 * @return the name of the loaded resource bundle used by this logger. 621 */ 622 public String getResourceBundleName() { 623 return this.resourceBundleName; 624 } 625 626 /** 627 * This method is for compatibility. Tests written to the reference 628 * implementation API imply that the isLoggable() method is not called 629 * directly. This behavior is important because subclass may override 630 * isLoggable() method, so that affect the result of log methods. 631 */ 632 private boolean internalIsLoggable(Level l) { 633 int effectiveLevel = levelIntVal; 634 if (effectiveLevel == Level.OFF.intValue()) { 635 // always return false if the effective level is off 636 return false; 637 } 638 return l.intValue() >= effectiveLevel; 639 } 640 641 /** 642 * Determines whether this logger will actually log messages of the 643 * specified level. The effective level used to do the determination may be 644 * inherited from its parent. The default level is {@code Level.INFO}. 645 * 646 * @param l 647 * the level to check. 648 * @return {@code true} if this logger will actually log this level, 649 * otherwise {@code false}. 650 */ 651 public boolean isLoggable(Level l) { 652 return internalIsLoggable(l); 653 } 654 655 /** 656 * Sets the resource bundle and its name for a supplied LogRecord object. 657 * This method first tries to use this logger's resource bundle if any, 658 * otherwise try to inherit from this logger's parent, recursively up the 659 * namespace. 660 */ 661 private void setResourceBundle(LogRecord record) { 662 for (Logger p = this; p != null; p = p.parent) { 663 String resourceBundleName = p.resourceBundleName; 664 if (resourceBundleName != null) { 665 record.setResourceBundle(p.resourceBundle); 666 record.setResourceBundleName(resourceBundleName); 667 return; 668 } 669 } 670 } 671 672 /** 673 * Logs a message indicating that a method has been entered. A log record 674 * with log level {@code Level.FINER}, log message "ENTRY", the specified 675 * source class name and source method name is submitted for logging. 676 * 677 * @param sourceClass 678 * the calling class name. 679 * @param sourceMethod 680 * the method name. 681 */ 682 public void entering(String sourceClass, String sourceMethod) { 683 if (!internalIsLoggable(Level.FINER)) { 684 return; 685 } 686 687 LogRecord record = new LogRecord(Level.FINER, "ENTRY"); 688 record.setLoggerName(this.name); 689 record.setSourceClassName(sourceClass); 690 record.setSourceMethodName(sourceMethod); 691 setResourceBundle(record); 692 log(record); 693 } 694 695 /** 696 * Logs a message indicating that a method has been entered. A log record 697 * with log level {@code Level.FINER}, log message "ENTRY", the specified 698 * source class name, source method name and one parameter is submitted for 699 * logging. 700 * 701 * @param sourceClass 702 * the source class name. 703 * @param sourceMethod 704 * the source method name. 705 * @param param 706 * the parameter for the method call. 707 */ 708 public void entering(String sourceClass, String sourceMethod, Object param) { 709 if (!internalIsLoggable(Level.FINER)) { 710 return; 711 } 712 713 LogRecord record = new LogRecord(Level.FINER, "ENTRY" + " {0}"); 714 record.setLoggerName(this.name); 715 record.setSourceClassName(sourceClass); 716 record.setSourceMethodName(sourceMethod); 717 record.setParameters(new Object[] { param }); 718 setResourceBundle(record); 719 log(record); 720 } 721 722 /** 723 * Logs a message indicating that a method has been entered. A log record 724 * with log level {@code Level.FINER}, log message "ENTRY", the specified 725 * source class name, source method name and array of parameters is 726 * submitted for logging. 727 * 728 * @param sourceClass 729 * the source class name. 730 * @param sourceMethod 731 * the source method name. 732 * @param params 733 * an array of parameters for the method call. 734 */ 735 public void entering(String sourceClass, String sourceMethod, 736 Object[] params) { 737 if (!internalIsLoggable(Level.FINER)) { 738 return; 739 } 740 741 String msg = "ENTRY"; 742 if (params != null) { 743 StringBuilder msgBuffer = new StringBuilder("ENTRY"); 744 for (int i = 0; i < params.length; i++) { 745 msgBuffer.append(" {").append(i).append("}"); 746 } 747 msg = msgBuffer.toString(); 748 } 749 LogRecord record = new LogRecord(Level.FINER, msg); 750 record.setLoggerName(this.name); 751 record.setSourceClassName(sourceClass); 752 record.setSourceMethodName(sourceMethod); 753 record.setParameters(params); 754 setResourceBundle(record); 755 log(record); 756 } 757 758 /** 759 * Logs a message indicating that a method is exited. A log record with log 760 * level {@code Level.FINER}, log message "RETURN", the specified source 761 * class name and source method name is submitted for logging. 762 * 763 * @param sourceClass 764 * the calling class name. 765 * @param sourceMethod 766 * the method name. 767 */ 768 public void exiting(String sourceClass, String sourceMethod) { 769 if (!internalIsLoggable(Level.FINER)) { 770 return; 771 } 772 773 LogRecord record = new LogRecord(Level.FINER, "RETURN"); 774 record.setLoggerName(this.name); 775 record.setSourceClassName(sourceClass); 776 record.setSourceMethodName(sourceMethod); 777 setResourceBundle(record); 778 log(record); 779 } 780 781 /** 782 * Logs a message indicating that a method is exited. A log record with log 783 * level {@code Level.FINER}, log message "RETURN", the specified source 784 * class name, source method name and return value is submitted for logging. 785 * 786 * @param sourceClass 787 * the source class name. 788 * @param sourceMethod 789 * the source method name. 790 * @param result 791 * the return value of the method call. 792 */ 793 public void exiting(String sourceClass, String sourceMethod, Object result) { 794 if (!internalIsLoggable(Level.FINER)) { 795 return; 796 } 797 798 LogRecord record = new LogRecord(Level.FINER, "RETURN" + " {0}"); 799 record.setLoggerName(this.name); 800 record.setSourceClassName(sourceClass); 801 record.setSourceMethodName(sourceMethod); 802 record.setParameters(new Object[] { result }); 803 setResourceBundle(record); 804 log(record); 805 } 806 807 /** 808 * Logs a message indicating that an exception is thrown. A log record with 809 * log level {@code Level.FINER}, log message "THROW", the specified source 810 * class name, source method name and the {@code Throwable} object is 811 * submitted for logging. 812 * 813 * @param sourceClass 814 * the source class name. 815 * @param sourceMethod 816 * the source method name. 817 * @param thrown 818 * the {@code Throwable} object. 819 */ 820 public void throwing(String sourceClass, String sourceMethod, 821 Throwable thrown) { 822 if (!internalIsLoggable(Level.FINER)) { 823 return; 824 } 825 826 LogRecord record = new LogRecord(Level.FINER, "THROW"); 827 record.setLoggerName(this.name); 828 record.setSourceClassName(sourceClass); 829 record.setSourceMethodName(sourceMethod); 830 record.setThrown(thrown); 831 setResourceBundle(record); 832 log(record); 833 } 834 835 /** 836 * Logs a message of level {@code Level.SEVERE}; the message is transmitted 837 * to all subscribed handlers. 838 * 839 * @param msg 840 * the message to log. 841 */ 842 public void severe(String msg) { 843 log(Level.SEVERE, msg); 844 } 845 846 /** 847 * Logs a message of level {@code Level.WARNING}; the message is 848 * transmitted to all subscribed handlers. 849 * 850 * @param msg 851 * the message to log. 852 */ 853 public void warning(String msg) { 854 log(Level.WARNING, msg); 855 } 856 857 /** 858 * Logs a message of level {@code Level.INFO}; the message is transmitted 859 * to all subscribed handlers. 860 * 861 * @param msg 862 * the message to log. 863 */ 864 public void info(String msg) { 865 log(Level.INFO, msg); 866 } 867 868 /** 869 * Logs a message of level {@code Level.CONFIG}; the message is transmitted 870 * to all subscribed handlers. 871 * 872 * @param msg 873 * the message to log. 874 */ 875 public void config(String msg) { 876 log(Level.CONFIG, msg); 877 } 878 879 /** 880 * Logs a message of level {@code Level.FINE}; the message is transmitted 881 * to all subscribed handlers. 882 * 883 * @param msg 884 * the message to log. 885 */ 886 public void fine(String msg) { 887 log(Level.FINE, msg); 888 } 889 890 /** 891 * Logs a message of level {@code Level.FINER}; the message is transmitted 892 * to all subscribed handlers. 893 * 894 * @param msg 895 * the message to log. 896 */ 897 public void finer(String msg) { 898 log(Level.FINER, msg); 899 } 900 901 /** 902 * Logs a message of level {@code Level.FINEST}; the message is transmitted 903 * to all subscribed handlers. 904 * 905 * @param msg 906 * the message to log. 907 */ 908 public void finest(String msg) { 909 log(Level.FINEST, msg); 910 } 911 912 /** 913 * Logs a message of the specified level. The message is transmitted to all 914 * subscribed handlers. 915 * 916 * @param logLevel 917 * the level of the specified message. 918 * @param msg 919 * the message to log. 920 */ 921 public void log(Level logLevel, String msg) { 922 if (!internalIsLoggable(logLevel)) { 923 return; 924 } 925 dalvikLogHandler.publish(this, androidTag, logLevel, msg); 926 } 927 928 /** 929 * Logs a message of the specified level with the supplied parameter. The 930 * message is then transmitted to all subscribed handlers. 931 * 932 * @param logLevel 933 * the level of the given message. 934 * @param msg 935 * the message to log. 936 * @param param 937 * the parameter associated with the event that is logged. 938 */ 939 public void log(Level logLevel, String msg, Object param) { 940 if (!internalIsLoggable(logLevel)) { 941 return; 942 } 943 944 LogRecord record = new LogRecord(logLevel, msg); 945 record.setLoggerName(this.name); 946 record.setParameters(new Object[] { param }); 947 setResourceBundle(record); 948 log(record); 949 } 950 951 /** 952 * Logs a message of the specified level with the supplied parameter array. 953 * The message is then transmitted to all subscribed handlers. 954 * 955 * @param logLevel 956 * the level of the given message 957 * @param msg 958 * the message to log. 959 * @param params 960 * the parameter array associated with the event that is logged. 961 */ 962 public void log(Level logLevel, String msg, Object[] params) { 963 if (!internalIsLoggable(logLevel)) { 964 return; 965 } 966 967 LogRecord record = new LogRecord(logLevel, msg); 968 record.setLoggerName(this.name); 969 record.setParameters(params); 970 setResourceBundle(record); 971 log(record); 972 } 973 974 /** 975 * Logs a message of the specified level with the supplied {@code Throwable} 976 * object. The message is then transmitted to all subscribed handlers. 977 * 978 * @param logLevel 979 * the level of the given message. 980 * @param msg 981 * the message to log. 982 * @param thrown 983 * the {@code Throwable} object associated with the event that is 984 * logged. 985 */ 986 public void log(Level logLevel, String msg, Throwable thrown) { 987 if (!internalIsLoggable(logLevel)) { 988 return; 989 } 990 991 LogRecord record = new LogRecord(logLevel, msg); 992 record.setLoggerName(this.name); 993 record.setThrown(thrown); 994 setResourceBundle(record); 995 log(record); 996 } 997 998 /** 999 * Logs a given log record. Only records with a logging level that is equal 1000 * or greater than this logger's level will be submitted to this logger's 1001 * handlers for logging. If {@code getUseParentHandlers()} returns {@code 1002 * true}, the log record will also be submitted to the handlers of this 1003 * logger's parent, potentially recursively up the namespace. 1004 * <p> 1005 * Since all other log methods call this method to actually perform the 1006 * logging action, subclasses of this class can override this method to 1007 * catch all logging activities. 1008 * </p> 1009 * 1010 * @param record 1011 * the log record to be logged. 1012 */ 1013 public void log(LogRecord record) { 1014 if (!internalIsLoggable(record.getLevel())) { 1015 return; 1016 } 1017 1018 // apply the filter if any 1019 Filter f = filter; 1020 if (f != null && !f.isLoggable(record)) { 1021 return; 1022 } 1023 1024 /* 1025 * call the handlers of this logger, throw any exception that occurs 1026 */ 1027 Handler[] allHandlers = getHandlers(); 1028 for (Handler element : allHandlers) { 1029 element.publish(record); 1030 } 1031 // call the parent's handlers if set useParentHandlers 1032 Logger temp = this; 1033 Logger theParent = temp.parent; 1034 while (theParent != null && temp.getUseParentHandlers()) { 1035 Handler[] ha = theParent.getHandlers(); 1036 for (Handler element : ha) { 1037 element.publish(record); 1038 } 1039 temp = theParent; 1040 theParent = temp.parent; 1041 } 1042 } 1043 1044 /** 1045 * Logs a message of the given level with the specified source class name 1046 * and source method name. 1047 * 1048 * @param logLevel 1049 * the level of the given message. 1050 * @param sourceClass 1051 * the source class name. 1052 * @param sourceMethod 1053 * the source method name. 1054 * @param msg 1055 * the message to be logged. 1056 */ 1057 public void logp(Level logLevel, String sourceClass, String sourceMethod, 1058 String msg) { 1059 if (!internalIsLoggable(logLevel)) { 1060 return; 1061 } 1062 1063 LogRecord record = new LogRecord(logLevel, msg); 1064 record.setLoggerName(this.name); 1065 record.setSourceClassName(sourceClass); 1066 record.setSourceMethodName(sourceMethod); 1067 setResourceBundle(record); 1068 log(record); 1069 } 1070 1071 /** 1072 * Logs a message of the given level with the specified source class name, 1073 * source method name and parameter. 1074 * 1075 * @param logLevel 1076 * the level of the given message 1077 * @param sourceClass 1078 * the source class name 1079 * @param sourceMethod 1080 * the source method name 1081 * @param msg 1082 * the message to be logged 1083 * @param param 1084 * the parameter associated with the event that is logged. 1085 */ 1086 public void logp(Level logLevel, String sourceClass, String sourceMethod, 1087 String msg, Object param) { 1088 if (!internalIsLoggable(logLevel)) { 1089 return; 1090 } 1091 1092 LogRecord record = new LogRecord(logLevel, msg); 1093 record.setLoggerName(this.name); 1094 record.setSourceClassName(sourceClass); 1095 record.setSourceMethodName(sourceMethod); 1096 record.setParameters(new Object[] { param }); 1097 setResourceBundle(record); 1098 log(record); 1099 } 1100 1101 /** 1102 * Logs a message of the given level with the specified source class name, 1103 * source method name and parameter array. 1104 * 1105 * @param logLevel 1106 * the level of the given message. 1107 * @param sourceClass 1108 * the source class name. 1109 * @param sourceMethod 1110 * the source method name. 1111 * @param msg 1112 * the message to be logged. 1113 * @param params 1114 * the parameter array associated with the event that is logged. 1115 */ 1116 public void logp(Level logLevel, String sourceClass, String sourceMethod, 1117 String msg, Object[] params) { 1118 if (!internalIsLoggable(logLevel)) { 1119 return; 1120 } 1121 1122 LogRecord record = new LogRecord(logLevel, msg); 1123 record.setLoggerName(this.name); 1124 record.setSourceClassName(sourceClass); 1125 record.setSourceMethodName(sourceMethod); 1126 record.setParameters(params); 1127 setResourceBundle(record); 1128 log(record); 1129 } 1130 1131 /** 1132 * Logs a message of the given level with the specified source class name, 1133 * source method name and {@code Throwable} object. 1134 * 1135 * @param logLevel 1136 * the level of the given message. 1137 * @param sourceClass 1138 * the source class name. 1139 * @param sourceMethod 1140 * the source method name. 1141 * @param msg 1142 * the message to be logged. 1143 * @param thrown 1144 * the {@code Throwable} object. 1145 */ 1146 public void logp(Level logLevel, String sourceClass, String sourceMethod, 1147 String msg, Throwable thrown) { 1148 if (!internalIsLoggable(logLevel)) { 1149 return; 1150 } 1151 1152 LogRecord record = new LogRecord(logLevel, msg); 1153 record.setLoggerName(this.name); 1154 record.setSourceClassName(sourceClass); 1155 record.setSourceMethodName(sourceMethod); 1156 record.setThrown(thrown); 1157 setResourceBundle(record); 1158 log(record); 1159 } 1160 1161 /** 1162 * Logs a message of the given level with the specified source class name 1163 * and source method name, using the given resource bundle to localize the 1164 * message. If {@code bundleName} is null, the empty string or not valid then 1165 * the message is not localized. 1166 * 1167 * @param logLevel 1168 * the level of the given message. 1169 * @param sourceClass 1170 * the source class name. 1171 * @param sourceMethod 1172 * the source method name. 1173 * @param bundleName 1174 * the name of the resource bundle used to localize the message. 1175 * @param msg 1176 * the message to be logged. 1177 */ 1178 public void logrb(Level logLevel, String sourceClass, String sourceMethod, 1179 String bundleName, String msg) { 1180 if (!internalIsLoggable(logLevel)) { 1181 return; 1182 } 1183 1184 LogRecord record = new LogRecord(logLevel, msg); 1185 if (bundleName != null) { 1186 try { 1187 record.setResourceBundle(loadResourceBundle(bundleName)); 1188 } catch (MissingResourceException e) { 1189 // ignore 1190 } 1191 record.setResourceBundleName(bundleName); 1192 } 1193 record.setLoggerName(this.name); 1194 record.setSourceClassName(sourceClass); 1195 record.setSourceMethodName(sourceMethod); 1196 log(record); 1197 } 1198 1199 /** 1200 * Logs a message of the given level with the specified source class name, 1201 * source method name and parameter, using the given resource bundle to 1202 * localize the message. If {@code bundleName} is null, the empty string 1203 * or not valid then the message is not localized. 1204 * 1205 * @param logLevel 1206 * the level of the given message. 1207 * @param sourceClass 1208 * the source class name. 1209 * @param sourceMethod 1210 * the source method name. 1211 * @param bundleName 1212 * the name of the resource bundle used to localize the message. 1213 * @param msg 1214 * the message to be logged. 1215 * @param param 1216 * the parameter associated with the event that is logged. 1217 */ 1218 public void logrb(Level logLevel, String sourceClass, String sourceMethod, 1219 String bundleName, String msg, Object param) { 1220 if (!internalIsLoggable(logLevel)) { 1221 return; 1222 } 1223 1224 LogRecord record = new LogRecord(logLevel, msg); 1225 if (bundleName != null) { 1226 try { 1227 record.setResourceBundle(loadResourceBundle(bundleName)); 1228 } catch (MissingResourceException e) { 1229 // ignore 1230 } 1231 record.setResourceBundleName(bundleName); 1232 } 1233 record.setLoggerName(this.name); 1234 record.setSourceClassName(sourceClass); 1235 record.setSourceMethodName(sourceMethod); 1236 record.setParameters(new Object[] { param }); 1237 log(record); 1238 } 1239 1240 /** 1241 * Logs a message of the given level with the specified source class name, 1242 * source method name and parameter array, using the given resource bundle 1243 * to localize the message. If {@code bundleName} is null, the empty string 1244 * or not valid then the message is not localized. 1245 * 1246 * @param logLevel 1247 * the level of the given message. 1248 * @param sourceClass 1249 * the source class name. 1250 * @param sourceMethod 1251 * the source method name. 1252 * @param bundleName 1253 * the name of the resource bundle used to localize the message. 1254 * @param msg 1255 * the message to be logged. 1256 * @param params 1257 * the parameter array associated with the event that is logged. 1258 */ 1259 public void logrb(Level logLevel, String sourceClass, String sourceMethod, 1260 String bundleName, String msg, Object[] params) { 1261 if (!internalIsLoggable(logLevel)) { 1262 return; 1263 } 1264 1265 LogRecord record = new LogRecord(logLevel, msg); 1266 if (bundleName != null) { 1267 try { 1268 record.setResourceBundle(loadResourceBundle(bundleName)); 1269 } catch (MissingResourceException e) { 1270 // ignore 1271 } 1272 record.setResourceBundleName(bundleName); 1273 } 1274 record.setLoggerName(this.name); 1275 record.setSourceClassName(sourceClass); 1276 record.setSourceMethodName(sourceMethod); 1277 record.setParameters(params); 1278 log(record); 1279 } 1280 1281 /** 1282 * Logs a message of the given level with the specified source class name, 1283 * source method name and {@code Throwable} object, using the given resource 1284 * bundle to localize the message. If {@code bundleName} is null, the empty 1285 * string or not valid then the message is not localized. 1286 * 1287 * @param logLevel 1288 * the level of the given message 1289 * @param sourceClass 1290 * the source class name 1291 * @param sourceMethod 1292 * the source method name 1293 * @param bundleName 1294 * the name of the resource bundle used to localize the message. 1295 * @param msg 1296 * the message to be logged. 1297 * @param thrown 1298 * the {@code Throwable} object. 1299 */ 1300 public void logrb(Level logLevel, String sourceClass, String sourceMethod, 1301 String bundleName, String msg, Throwable thrown) { 1302 if (!internalIsLoggable(logLevel)) { 1303 return; 1304 } 1305 1306 LogRecord record = new LogRecord(logLevel, msg); 1307 if (bundleName != null) { 1308 try { 1309 record.setResourceBundle(loadResourceBundle(bundleName)); 1310 } catch (MissingResourceException e) { 1311 // ignore 1312 } 1313 record.setResourceBundleName(bundleName); 1314 } 1315 record.setLoggerName(this.name); 1316 record.setSourceClassName(sourceClass); 1317 record.setSourceMethodName(sourceMethod); 1318 record.setThrown(thrown); 1319 log(record); 1320 } 1321 1322 void reset() { 1323 levelObjVal = null; 1324 levelIntVal = Level.INFO.intValue(); 1325 1326 for (Handler handler : handlers) { 1327 try { 1328 if (handlers.remove(handler)) { 1329 handler.close(); 1330 } 1331 } catch (Exception ignored) { 1332 } 1333 } 1334 1335 updateDalvikLogHandler(); 1336 } 1337 } 1338