Home | History | Annotate | Download | only in logging
      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