blob: 6c7dabb2b3178bf594f264a627dd0492564f0152 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.util.logging;
// BEGIN android-note
// this file contains cleaned up documentation and style for contribution
// upstream
// It also contains Android-specific optimizations that aren't appropriate for
// general purpose platforms
// END android-note
import dalvik.system.DalvikLogHandler;
import dalvik.system.DalvikLogging;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Loggers are used to log records to a variety of destinations such as log files or
* the console. They use instances of {@link Handler} to actually do the destination-specific
* operations.
*
* <p>Client applications can get named loggers by calling the {@code getLogger}
* methods. They can also get anonymous loggers by calling the
* {@code getAnonymousLogger} methods. Named loggers are organized in a
* namespace hierarchy managed by a log manager. The naming convention is
* usually the Java package naming convention. Anonymous loggers do not belong to any namespace.
*
* <p>Developers should use named loggers to enable logging to be controlled on a
* per-{@code Logger} granularity. The recommended idiom is to create and assign the logger to
* a {@code static final} field. This ensures that there's always a strong reference to the logger,
* preventing it from being garbage collected. In particular, {@link LogManager#addLogger(Logger)}
* will <i>not</i> keep your logger live.
*
* <p>Loggers "inherit" log level setting from their parent if their own level is
* set to {@code null}. This is also true for the resource bundle. The logger's
* resource bundle is used to localize the log messages if no resource bundle
* name is given when a log method is called. If {@code getUseParentHandlers()}
* returns {@code true}, loggers also inherit their parent's handlers. In this
* context, "inherit" only means that "behavior" is inherited. The internal
* field values will not change, for example, {@code getLevel()} still returns
* {@code null}.
* <p>
* When loading a given resource bundle, the logger first tries to use the
* context {@code ClassLoader}. If that fails, it tries the system {@code ClassLoader}. And if
* that still fails, it searches up the class stack and uses each class's
* {@code ClassLoader} to try to locate the resource bundle.
* <p>
* Some log methods accept log requests that do not specify the source class and
* source method. In these cases, the logging framework will automatically infer
* the calling class and method, but this is not guaranteed to be accurate.
* <p>
* Once a {@code LogRecord} object has been passed into the logging framework,
* it is owned by the logging framework and the client applications should not
* use it any longer.
* <p>
* All methods of this class are thread-safe.
*
* @see LogManager
*/
public class Logger {
// BEGIN android-only
/** A handler for use when no handler optimization is possible. */
private static final DalvikLogHandler GENERAL_LOG_HANDLER = new DalvikLogHandler() {
public void publish(Logger source, String tag, Level level, String message) {
LogRecord record = new LogRecord(level, message);
record.setLoggerName(source.name);
source.setResourceBundle(record);
source.log(record);
}
};
// END android-only
/**
* The name of the global logger. Before using this, see the discussion of how to use
* {@code Logger} in the class documentation.
* @since 1.6
*/
public static final String GLOBAL_LOGGER_NAME = "global";
/**
* The global logger is provided as convenience for casual use.
* @deprecated deadlock-prone. Use {@code Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)} as
* a direct replacement, but see the discussion of how to use {@code Logger} in the class
* documentation.
*/
@Deprecated
public final static Logger global = new Logger(GLOBAL_LOGGER_NAME, null);
/**
* When converting the concurrent collection of handlers to an array, we
* always pass a zero-length array to avoid size miscalculations. Passing
* properly-sized arrays is non-atomic, and risks a null element in the
* result.
*/
private static final Handler[] EMPTY_HANDLERS_ARRAY = new Handler[0];
/** The name of this logger. */
private volatile String name;
/** The parent logger of this logger. */
Logger parent;
/** The logging level of this logger, or null if none is set. */
volatile Level levelObjVal;
/**
* The effective logging level of this logger. In order of preference this
* is the first applicable of:
* <ol>
* <li>the int value of this logger's {@link #levelObjVal}
* <li>the logging level of the parent
* <li>the default level ({@link Level#INFO})
* </ol>
*/
volatile int levelIntVal = Level.INFO.intValue();
/** The filter. */
private Filter filter;
/**
* The resource bundle used to localize logging messages. If null, no
* localization will be performed.
*/
private volatile String resourceBundleName;
/** The loaded resource bundle according to the specified name. */
private volatile ResourceBundle resourceBundle;
/**
* The handlers attached to this logger. Eagerly initialized and
* concurrently modified.
*/
private final List<Handler> handlers = new CopyOnWriteArrayList<Handler>();
/** True to notify the parent's handlers of each log message. */
private boolean notifyParentHandlers = true;
/**
* Indicates whether this logger is named. Only {@link #getAnonymousLogger
* anonymous loggers} are unnamed.
*/
private boolean isNamed = true;
/**
* Child loggers. Should be accessed only while synchronized on {@code
* LogManager.getLogManager()}.
*/
final List<Logger> children = new ArrayList<Logger>();
// BEGIN android-only
/** the tag used for optimized logging. Derived from the logger name. */
private final String androidTag;
/** Handler delegate for either optimized or standard logging. */
private volatile DalvikLogHandler dalvikLogHandler = GENERAL_LOG_HANDLER;
/**
* We've optimized for the common case: logging to a single handler that
* implements {@link DalvikLogHandler}. This is how Android framework
* applications are configured by default.
*
* <p>This optimization has been measured to show a 2.75x improvement in
* throughput in the common case: 154ns vs. 56ns per message on a Cortex-A8.
* Direct use of {@code android.util.Log} takes 29ns per message.
*
* <p>Each time the handler configuration changes, either directly or
* indirectly, it's necessary to either turn on or off this optimization.
* When the optimization is off, {@link #dalvikLogHandler} is assigned to
* {@link #GENERAL_LOG_HANDLER} which can satisfy arbitrary configuration.
* When the optimization is possible, {@link #dalvikLogHandler} is assigned
* to the user's efficient implementation. In pratice this is usually the
* {@code com.android.internal.logging.AndroidHandler}.
*/
void updateDalvikLogHandler() {
DalvikLogHandler newLogHandler = GENERAL_LOG_HANDLER;
Logger parent = this.parent;
if (getClass() != Logger.class) {
/*
* Do nothing. Subclasses aren't eligible for the optimization
* because they may override methods like getHandlers() or
* log(LogRecord).
*/
} else if (parent == null) {
// we use an iterator rather than size()+get() for safe concurrency
Iterator<Handler> h = handlers.iterator();
if (h.hasNext()) {
Handler firstHandler = h.next();
if (!h.hasNext() && firstHandler instanceof DalvikLogHandler) {
/*
* At this point, we're eligible for the optimization. We've
* satisfied these constraints:
* 1. This is not a subclass of logger
* 2. This is a root logger (no parent)
* 3. There is exactly one handler installed
* 4. That handler is a DalvikLogHandler
*/
newLogHandler = (DalvikLogHandler) firstHandler;
}
}
} else if (handlers.isEmpty() && notifyParentHandlers) {
/*
* At this point, we're eligible for the optimization if our parent
* logger is eligible. We've satisfied these constraints:
* 1. This is not a subclass of logger
* 2. our parent exists
* 3. we have no handlers of our own
* 4. we notify our parent's handlers
*/
newLogHandler = parent.dalvikLogHandler;
}
if (newLogHandler == this.dalvikLogHandler) {
return;
}
this.dalvikLogHandler = newLogHandler;
for (Logger logger : children) {
logger.updateDalvikLogHandler();
}
}
// END android-only
/**
* Constructs a {@code Logger} object with the supplied name and resource
* bundle name; {@code notifiyParentHandlers} is set to {@code true}.
* <p>
* Notice : Loggers use a naming hierarchy. Thus "z.x.y" is a child of "z.x".
*
* @param name
* the name of this logger, may be {@code null} for anonymous
* loggers.
* @param resourceBundleName
* the name of the resource bundle used to localize logging
* messages, may be {@code null}.
* @throws MissingResourceException
* if the specified resource bundle can not be loaded.
*/
protected Logger(String name, String resourceBundleName) {
this.name = name;
initResourceBundle(resourceBundleName);
// BEGIN android-only
this.androidTag = DalvikLogging.loggerNameToTag(name);
updateDalvikLogHandler();
// END android-only
}
/**
* Load the specified resource bundle, use privileged code.
*
* @param resourceBundleName
* the name of the resource bundle to load, cannot be {@code null}.
* @return the loaded resource bundle.
* @throws MissingResourceException
* if the specified resource bundle can not be loaded.
*/
static ResourceBundle loadResourceBundle(String resourceBundleName) {
// try context class loader to load the resource
ClassLoader cl = AccessController
.doPrivileged(new PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return Thread.currentThread().getContextClassLoader();
}
});
if (cl != null) {
try {
return ResourceBundle.getBundle(resourceBundleName, Locale
.getDefault(), cl);
} catch (MissingResourceException e) {
// Failed to load using context classloader, ignore
}
}
// try system class loader to load the resource
cl = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return ClassLoader.getSystemClassLoader();
}
});
if (cl != null) {
try {
return ResourceBundle.getBundle(resourceBundleName, Locale
.getDefault(), cl);
} catch (MissingResourceException e) {
// Failed to load using system classloader, ignore
}
}
// try all class loaders up the class stack
final Class<?>[] classes = AccessController
.doPrivileged(new PrivilegedAction<Class<?>[]>() {
public Class<?>[] run() {
return (new PrivateSecurityManager())
.privateGetClassContext();
}
});
// the first class, which is PrivateSecurityManager, is skipped
for (int i = 1; i < classes.length; i++) {
final int index = i;
try {
cl = AccessController
.doPrivileged(new PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return classes[index].getClassLoader();
}
});
if (cl == null) {
continue;
}
return ResourceBundle.getBundle(resourceBundleName, Locale
.getDefault(), cl);
} catch (MissingResourceException e) {
// Failed to load using the current class's classloader, ignore
}
}
throw new MissingResourceException("Failed to load the specified resource bundle \"" +
resourceBundleName + "\"", resourceBundleName, null);
}
/**
* Gets an anonymous logger to use internally in a thread. Anonymous loggers
* are not registered in the log manager's namespace. No security checks
* will be performed when updating an anonymous logger's control settings.
* <p>
* The anonymous loggers' parent is set to be the root logger. This way it
* inherits the default logging level and handlers from the root logger.
*
* @return a new instance of anonymous logger.
*/
public static Logger getAnonymousLogger() {
return getAnonymousLogger(null);
}
/**
* Gets an anonymous logger to use internally in a thread. Anonymous loggers
* are not registered in the log manager's namespace. No security checks
* will be performed when updating an anonymous logger's control settings.
* <p>
* The anonymous loggers' parent is set to be the root logger. This way it
* inherits default logging level and handlers from the root logger.
*
* @param resourceBundleName
* the name of the resource bundle used to localize log messages.
* @return a new instance of anonymous logger.
* @throws MissingResourceException
* if the specified resource bundle can not be loaded.
*/
public static Logger getAnonymousLogger(String resourceBundleName) {
Logger result = new Logger(null, resourceBundleName);
result.isNamed = false;
LogManager logManager = LogManager.getLogManager();
logManager.setParent(result, logManager.getLogger(""));
return result;
}
/**
* Initializes this logger's resource bundle.
*
* @throws IllegalArgumentException if this logger's resource bundle already
* exists and is different from the resource bundle specified.
*/
private synchronized void initResourceBundle(String resourceBundleName) {
String current = this.resourceBundleName;
if (current != null) {
if (current.equals(resourceBundleName)) {
return;
} else {
throw new IllegalArgumentException("Resource bundle name '" + resourceBundleName + "' is inconsistent with the existing '" + current + "'");
}
}
if (resourceBundleName != null) {
this.resourceBundle = loadResourceBundle(resourceBundleName);
this.resourceBundleName = resourceBundleName;
}
}
/**
* Gets a named logger. The returned logger may already exist or may be
* newly created. In the latter case, its level will be set to the
* configured level according to the {@code LogManager}'s properties.
*
* @param name
* the name of the logger to get, cannot be {@code null}.
* @return a named logger.
* @throws MissingResourceException
* If the specified resource bundle can not be loaded.
*/
public static Logger getLogger(String name) {
return LogManager.getLogManager().getOrCreate(name, null);
}
/**
* Gets a named logger associated with the supplied resource bundle. The
* resource bundle will be used to localize logging messages.
*
* @param name
* the name of the logger to get, cannot be {@code null}.
* @param resourceBundleName
* the name of the resource bundle, may be {@code null}.
* @throws IllegalArgumentException
* if the logger identified by {@code name} is associated with a
* resource bundle and its name is not equal to
* {@code resourceBundleName}.
* @throws MissingResourceException
* if the name of the resource bundle cannot be found.
* @return a named logger.
*/
public static Logger getLogger(String name, String resourceBundleName) {
Logger result = LogManager.getLogManager()
.getOrCreate(name, resourceBundleName);
result.initResourceBundle(resourceBundleName);
return result;
}
/**
* Adds a handler to this logger. The {@code name} will be fed with log
* records received by this logger.
*
* @param handler
* the handler object to add, cannot be {@code null}.
* @throws SecurityException
* if a security manager determines that the caller does not
* have the required permission.
*/
public void addHandler(Handler handler) {
if (handler == null) {
throw new NullPointerException("handler == null");
}
// Anonymous loggers can always add handlers
if (this.isNamed) {
LogManager.getLogManager().checkAccess();
}
this.handlers.add(handler);
updateDalvikLogHandler(); // android-only
}
/**
* Set the logger's manager and initializes its configuration from the
* manager's properties.
*/
@SuppressWarnings("nls")
void setManager(LogManager manager) {
String levelProperty = manager.getProperty(name + ".level");
if (levelProperty != null) {
try {
manager.setLevelRecursively(Logger.this, Level.parse(levelProperty));
} catch (IllegalArgumentException invalidLevel) {
invalidLevel.printStackTrace();
}
}
String handlersPropertyName = name.isEmpty() ? "handlers" : name + ".handlers";
String handlersProperty = manager.getProperty(handlersPropertyName);
if (handlersProperty != null) {
for (String handlerName : handlersProperty.split(",|\\s")) {
if (handlerName.isEmpty()) {
continue;
}
final Handler handler;
try {
handler = (Handler) LogManager.getInstanceByClass(handlerName);
} catch (Exception invalidHandlerName) {
invalidHandlerName.printStackTrace();
continue;
}
try {
String level = manager.getProperty(handlerName + ".level");
if (level != null) {
handler.setLevel(Level.parse(level));
}
} catch (Exception invalidLevel) {
invalidLevel.printStackTrace();
}
handlers.add(handler);
}
}
updateDalvikLogHandler(); // android-only
}
/**
* Gets all the handlers associated with this logger.
*
* @return an array of all the handlers associated with this logger.
*/
public Handler[] getHandlers() {
return handlers.toArray(EMPTY_HANDLERS_ARRAY);
}
/**
* Removes a handler from this logger. If the specified handler does not
* exist then this method has no effect.
*
* @param handler
* the handler to be removed.
* @throws SecurityException
* if a security manager determines that the caller does not
* have the required permission.
*/
public void removeHandler(Handler handler) {
// Anonymous loggers can always remove handlers
if (this.isNamed) {
LogManager.getLogManager().checkAccess();
}
if (handler == null) {
return;
}
this.handlers.remove(handler);
updateDalvikLogHandler(); // android-only
}
/**
* Gets the filter used by this logger.
*
* @return the filter used by this logger, may be {@code null}.
*/
public Filter getFilter() {
return this.filter;
}
/**
* Sets the filter used by this logger.
*
* @param newFilter
* the filter to set, may be {@code null}.
* @throws SecurityException
* if a security manager determines that the caller does not
* have the required permission.
*/
public void setFilter(Filter newFilter) {
// Anonymous loggers can always set the filter
if (this.isNamed) {
LogManager.getLogManager().checkAccess();
}
filter = newFilter;
}
/**
* Gets the logging level of this logger. A {@code null} level indicates
* that this logger inherits its parent's level.
*
* @return the logging level of this logger.
*/
public Level getLevel() {
return levelObjVal;
}
/**
* Sets the logging level for this logger. A {@code null} level indicates
* that this logger will inherit its parent's level.
*
* @param newLevel
* the logging level to set.
* @throws SecurityException
* if a security manager determines that the caller does not
* have the required permission.
*/
public void setLevel(Level newLevel) {
// Anonymous loggers can always set the level
LogManager logManager = LogManager.getLogManager();
if (this.isNamed) {
logManager.checkAccess();
}
logManager.setLevelRecursively(this, newLevel);
}
/**
* Gets the flag which indicates whether to use the handlers of this
* logger's parent to publish incoming log records, potentially recursively
* up the namespace.
*
* @return {@code true} if set to use parent's handlers, {@code false}
* otherwise.
*/
public boolean getUseParentHandlers() {
return this.notifyParentHandlers;
}
/**
* Sets the flag which indicates whether to use the handlers of this
* logger's parent, potentially recursively up the namespace.
*
* @param notifyParentHandlers
* the new flag indicating whether to use the parent's handlers.
* @throws SecurityException
* if a security manager determines that the caller does not
* have the required permission.
*/
public void setUseParentHandlers(boolean notifyParentHandlers) {
// Anonymous loggers can always set the useParentHandlers flag
if (this.isNamed) {
LogManager.getLogManager().checkAccess();
}
this.notifyParentHandlers = notifyParentHandlers;
updateDalvikLogHandler(); // android-only
}
/**
* Gets the nearest parent of this logger in the namespace, a {@code null}
* value will be returned if called on the root logger.
*
* @return the parent of this logger in the namespace.
*/
public Logger getParent() {
return parent;
}
/**
* Sets the parent of this logger in the namespace. This method should be
* used by the {@code LogManager} object only.
*
* @param parent
* the parent logger to set.
* @throws SecurityException
* if a security manager determines that the caller does not
* have the required permission.
*/
public void setParent(Logger parent) {
if (parent == null) {
throw new NullPointerException("parent == null");
}
// even anonymous loggers are checked
LogManager logManager = LogManager.getLogManager();
logManager.checkAccess();
logManager.setParent(this, parent);
}
/**
* Gets the name of this logger, {@code null} for anonymous loggers.
*
* @return the name of this logger.
*/
public String getName() {
return this.name;
}
/**
* Gets the loaded resource bundle used by this logger to localize logging
* messages. If the value is {@code null}, the parent's resource bundle will be
* inherited.
*
* @return the loaded resource bundle used by this logger.
*/
public ResourceBundle getResourceBundle() {
return this.resourceBundle;
}
/**
* Gets the name of the loaded resource bundle used by this logger to
* localize logging messages. If the value is {@code null}, the parent's resource
* bundle name will be inherited.
*
* @return the name of the loaded resource bundle used by this logger.
*/
public String getResourceBundleName() {
return this.resourceBundleName;
}
/**
* This method is for compatibility. Tests written to the reference
* implementation API imply that the isLoggable() method is not called
* directly. This behavior is important because subclass may override
* isLoggable() method, so that affect the result of log methods.
*/
private boolean internalIsLoggable(Level l) {
int effectiveLevel = levelIntVal;
if (effectiveLevel == Level.OFF.intValue()) {
// always return false if the effective level is off
return false;
}
return l.intValue() >= effectiveLevel;
}
/**
* Determines whether this logger will actually log messages of the
* specified level. The effective level used to do the determination may be
* inherited from its parent. The default level is {@code Level.INFO}.
*
* @param l
* the level to check.
* @return {@code true} if this logger will actually log this level,
* otherwise {@code false}.
*/
public boolean isLoggable(Level l) {
return internalIsLoggable(l);
}
/**
* Sets the resource bundle and its name for a supplied LogRecord object.
* This method first tries to use this logger's resource bundle if any,
* otherwise try to inherit from this logger's parent, recursively up the
* namespace.
*/
private void setResourceBundle(LogRecord record) {
for (Logger p = this; p != null; p = p.parent) {
String resourceBundleName = p.resourceBundleName;
if (resourceBundleName != null) {
record.setResourceBundle(p.resourceBundle);
record.setResourceBundleName(resourceBundleName);
return;
}
}
}
/**
* Logs a message indicating that a method has been entered. A log record
* with log level {@code Level.FINER}, log message "ENTRY", the specified
* source class name and source method name is submitted for logging.
*
* @param sourceClass
* the calling class name.
* @param sourceMethod
* the method name.
*/
public void entering(String sourceClass, String sourceMethod) {
if (!internalIsLoggable(Level.FINER)) {
return;
}
LogRecord record = new LogRecord(Level.FINER, "ENTRY");
record.setLoggerName(this.name);
record.setSourceClassName(sourceClass);
record.setSourceMethodName(sourceMethod);
setResourceBundle(record);
log(record);
}
/**
* Logs a message indicating that a method has been entered. A log record
* with log level {@code Level.FINER}, log message "ENTRY", the specified
* source class name, source method name and one parameter is submitted for
* logging.
*
* @param sourceClass
* the source class name.
* @param sourceMethod
* the source method name.
* @param param
* the parameter for the method call.
*/
public void entering(String sourceClass, String sourceMethod, Object param) {
if (!internalIsLoggable(Level.FINER)) {
return;
}
LogRecord record = new LogRecord(Level.FINER, "ENTRY" + " {0}");
record.setLoggerName(this.name);
record.setSourceClassName(sourceClass);
record.setSourceMethodName(sourceMethod);
record.setParameters(new Object[] { param });
setResourceBundle(record);
log(record);
}
/**
* Logs a message indicating that a method has been entered. A log record
* with log level {@code Level.FINER}, log message "ENTRY", the specified
* source class name, source method name and array of parameters is
* submitted for logging.
*
* @param sourceClass
* the source class name.
* @param sourceMethod
* the source method name.
* @param params
* an array of parameters for the method call.
*/
@SuppressWarnings("nls")
public void entering(String sourceClass, String sourceMethod,
Object[] params) {
if (!internalIsLoggable(Level.FINER)) {
return;
}
String msg = "ENTRY";
if (params != null) {
StringBuilder msgBuffer = new StringBuilder("ENTRY");
for (int i = 0; i < params.length; i++) {
msgBuffer.append(" {").append(i).append("}");
}
msg = msgBuffer.toString();
}
LogRecord record = new LogRecord(Level.FINER, msg);
record.setLoggerName(this.name);
record.setSourceClassName(sourceClass);
record.setSourceMethodName(sourceMethod);
record.setParameters(params);
setResourceBundle(record);
log(record);
}
/**
* Logs a message indicating that a method is exited. A log record with log
* level {@code Level.FINER}, log message "RETURN", the specified source
* class name and source method name is submitted for logging.
*
* @param sourceClass
* the calling class name.
* @param sourceMethod
* the method name.
*/
public void exiting(String sourceClass, String sourceMethod) {
if (!internalIsLoggable(Level.FINER)) {
return;
}
LogRecord record = new LogRecord(Level.FINER, "RETURN");
record.setLoggerName(this.name);
record.setSourceClassName(sourceClass);
record.setSourceMethodName(sourceMethod);
setResourceBundle(record);
log(record);
}
/**
* Logs a message indicating that a method is exited. A log record with log
* level {@code Level.FINER}, log message "RETURN", the specified source
* class name, source method name and return value is submitted for logging.
*
* @param sourceClass
* the source class name.
* @param sourceMethod
* the source method name.
* @param result
* the return value of the method call.
*/
public void exiting(String sourceClass, String sourceMethod, Object result) {
if (!internalIsLoggable(Level.FINER)) {
return;
}
LogRecord record = new LogRecord(Level.FINER, "RETURN" + " {0}");
record.setLoggerName(this.name);
record.setSourceClassName(sourceClass);
record.setSourceMethodName(sourceMethod);
record.setParameters(new Object[] { result });
setResourceBundle(record);
log(record);
}
/**
* Logs a message indicating that an exception is thrown. A log record with
* log level {@code Level.FINER}, log message "THROW", the specified source
* class name, source method name and the {@code Throwable} object is
* submitted for logging.
*
* @param sourceClass
* the source class name.
* @param sourceMethod
* the source method name.
* @param thrown
* the {@code Throwable} object.
*/
public void throwing(String sourceClass, String sourceMethod,
Throwable thrown) {
if (!internalIsLoggable(Level.FINER)) {
return;
}
LogRecord record = new LogRecord(Level.FINER, "THROW");
record.setLoggerName(this.name);
record.setSourceClassName(sourceClass);
record.setSourceMethodName(sourceMethod);
record.setThrown(thrown);
setResourceBundle(record);
log(record);
}
/**
* Logs a message of level {@code Level.SEVERE}; the message is transmitted
* to all subscribed handlers.
*
* @param msg
* the message to log.
*/
public void severe(String msg) {
log(Level.SEVERE, msg);
}
/**
* Logs a message of level {@code Level.WARNING}; the message is
* transmitted to all subscribed handlers.
*
* @param msg
* the message to log.
*/
public void warning(String msg) {
log(Level.WARNING, msg);
}
/**
* Logs a message of level {@code Level.INFO}; the message is transmitted
* to all subscribed handlers.
*
* @param msg
* the message to log.
*/
public void info(String msg) {
log(Level.INFO, msg);
}
/**
* Logs a message of level {@code Level.CONFIG}; the message is transmitted
* to all subscribed handlers.
*
* @param msg
* the message to log.
*/
public void config(String msg) {
log(Level.CONFIG, msg);
}
/**
* Logs a message of level {@code Level.FINE}; the message is transmitted
* to all subscribed handlers.
*
* @param msg
* the message to log.
*/
public void fine(String msg) {
log(Level.FINE, msg);
}
/**
* Logs a message of level {@code Level.FINER}; the message is transmitted
* to all subscribed handlers.
*
* @param msg
* the message to log.
*/
public void finer(String msg) {
log(Level.FINER, msg);
}
/**
* Logs a message of level {@code Level.FINEST}; the message is transmitted
* to all subscribed handlers.
*
* @param msg
* the message to log.
*/
public void finest(String msg) {
log(Level.FINEST, msg);
}
/**
* Logs a message of the specified level. The message is transmitted to all
* subscribed handlers.
*
* @param logLevel
* the level of the specified message.
* @param msg
* the message to log.
*/
public void log(Level logLevel, String msg) {
if (!internalIsLoggable(logLevel)) {
return;
}
// BEGIN android-only
dalvikLogHandler.publish(this, androidTag, logLevel, msg);
// END android-only
}
/**
* Logs a message of the specified level with the supplied parameter. The
* message is then transmitted to all subscribed handlers.
*
* @param logLevel
* the level of the given message.
* @param msg
* the message to log.
* @param param
* the parameter associated with the event that is logged.
*/
public void log(Level logLevel, String msg, Object param) {
if (!internalIsLoggable(logLevel)) {
return;
}
LogRecord record = new LogRecord(logLevel, msg);
record.setLoggerName(this.name);
record.setParameters(new Object[] { param });
setResourceBundle(record);
log(record);
}
/**
* Logs a message of the specified level with the supplied parameter array.
* The message is then transmitted to all subscribed handlers.
*
* @param logLevel
* the level of the given message
* @param msg
* the message to log.
* @param params
* the parameter array associated with the event that is logged.
*/
public void log(Level logLevel, String msg, Object[] params) {
if (!internalIsLoggable(logLevel)) {
return;
}
LogRecord record = new LogRecord(logLevel, msg);
record.setLoggerName(this.name);
record.setParameters(params);
setResourceBundle(record);
log(record);
}
/**
* Logs a message of the specified level with the supplied {@code Throwable}
* object. The message is then transmitted to all subscribed handlers.
*
* @param logLevel
* the level of the given message.
* @param msg
* the message to log.
* @param thrown
* the {@code Throwable} object associated with the event that is
* logged.
*/
public void log(Level logLevel, String msg, Throwable thrown) {
if (!internalIsLoggable(logLevel)) {
return;
}
LogRecord record = new LogRecord(logLevel, msg);
record.setLoggerName(this.name);
record.setThrown(thrown);
setResourceBundle(record);
log(record);
}
/**
* Logs a given log record. Only records with a logging level that is equal
* or greater than this logger's level will be submitted to this logger's
* handlers for logging. If {@code getUseParentHandlers()} returns {@code
* true}, the log record will also be submitted to the handlers of this
* logger's parent, potentially recursively up the namespace.
* <p>
* Since all other log methods call this method to actually perform the
* logging action, subclasses of this class can override this method to
* catch all logging activities.
* </p>
*
* @param record
* the log record to be logged.
*/
public void log(LogRecord record) {
if (!internalIsLoggable(record.getLevel())) {
return;
}
// apply the filter if any
Filter f = filter;
if (f != null && !f.isLoggable(record)) {
return;
}
/*
* call the handlers of this logger, throw any exception that occurs
*/
Handler[] allHandlers = getHandlers();
for (Handler element : allHandlers) {
element.publish(record);
}
// call the parent's handlers if set useParentHandlers
Logger temp = this;
Logger theParent = temp.parent;
while (theParent != null && temp.getUseParentHandlers()) {
Handler[] ha = theParent.getHandlers();
for (Handler element : ha) {
element.publish(record);
}
temp = theParent;
theParent = temp.parent;
}
}
/**
* Logs a message of the given level with the specified source class name
* and source method name.
*
* @param logLevel
* the level of the given message.
* @param sourceClass
* the source class name.
* @param sourceMethod
* the source method name.
* @param msg
* the message to be logged.
*/
public void logp(Level logLevel, String sourceClass, String sourceMethod,
String msg) {
if (!internalIsLoggable(logLevel)) {
return;
}
LogRecord record = new LogRecord(logLevel, msg);
record.setLoggerName(this.name);
record.setSourceClassName(sourceClass);
record.setSourceMethodName(sourceMethod);
setResourceBundle(record);
log(record);
}
/**
* Logs a message of the given level with the specified source class name,
* source method name and parameter.
*
* @param logLevel
* the level of the given message
* @param sourceClass
* the source class name
* @param sourceMethod
* the source method name
* @param msg
* the message to be logged
* @param param
* the parameter associated with the event that is logged.
*/
public void logp(Level logLevel, String sourceClass, String sourceMethod,
String msg, Object param) {
if (!internalIsLoggable(logLevel)) {
return;
}
LogRecord record = new LogRecord(logLevel, msg);
record.setLoggerName(this.name);
record.setSourceClassName(sourceClass);
record.setSourceMethodName(sourceMethod);
record.setParameters(new Object[] { param });
setResourceBundle(record);
log(record);
}
/**
* Logs a message of the given level with the specified source class name,
* source method name and parameter array.
*
* @param logLevel
* the level of the given message.
* @param sourceClass
* the source class name.
* @param sourceMethod
* the source method name.
* @param msg
* the message to be logged.
* @param params
* the parameter array associated with the event that is logged.
*/
public void logp(Level logLevel, String sourceClass, String sourceMethod,
String msg, Object[] params) {
if (!internalIsLoggable(logLevel)) {
return;
}
LogRecord record = new LogRecord(logLevel, msg);
record.setLoggerName(this.name);
record.setSourceClassName(sourceClass);
record.setSourceMethodName(sourceMethod);
record.setParameters(params);
setResourceBundle(record);
log(record);
}
/**
* Logs a message of the given level with the specified source class name,
* source method name and {@code Throwable} object.
*
* @param logLevel
* the level of the given message.
* @param sourceClass
* the source class name.
* @param sourceMethod
* the source method name.
* @param msg
* the message to be logged.
* @param thrown
* the {@code Throwable} object.
*/
public void logp(Level logLevel, String sourceClass, String sourceMethod,
String msg, Throwable thrown) {
if (!internalIsLoggable(logLevel)) {
return;
}
LogRecord record = new LogRecord(logLevel, msg);
record.setLoggerName(this.name);
record.setSourceClassName(sourceClass);
record.setSourceMethodName(sourceMethod);
record.setThrown(thrown);
setResourceBundle(record);
log(record);
}
/**
* Logs a message of the given level with the specified source class name
* and source method name, using the given resource bundle to localize the
* message. If {@code bundleName} is null, the empty string or not valid then
* the message is not localized.
*
* @param logLevel
* the level of the given message.
* @param sourceClass
* the source class name.
* @param sourceMethod
* the source method name.
* @param bundleName
* the name of the resource bundle used to localize the message.
* @param msg
* the message to be logged.
*/
public void logrb(Level logLevel, String sourceClass, String sourceMethod,
String bundleName, String msg) {
if (!internalIsLoggable(logLevel)) {
return;
}
LogRecord record = new LogRecord(logLevel, msg);
if (bundleName != null) {
try {
record.setResourceBundle(loadResourceBundle(bundleName));
} catch (MissingResourceException e) {
// ignore
}
record.setResourceBundleName(bundleName);
}
record.setLoggerName(this.name);
record.setSourceClassName(sourceClass);
record.setSourceMethodName(sourceMethod);
log(record);
}
/**
* Logs a message of the given level with the specified source class name,
* source method name and parameter, using the given resource bundle to
* localize the message. If {@code bundleName} is null, the empty string
* or not valid then the message is not localized.
*
* @param logLevel
* the level of the given message.
* @param sourceClass
* the source class name.
* @param sourceMethod
* the source method name.
* @param bundleName
* the name of the resource bundle used to localize the message.
* @param msg
* the message to be logged.
* @param param
* the parameter associated with the event that is logged.
*/
public void logrb(Level logLevel, String sourceClass, String sourceMethod,
String bundleName, String msg, Object param) {
if (!internalIsLoggable(logLevel)) {
return;
}
LogRecord record = new LogRecord(logLevel, msg);
if (bundleName != null) {
try {
record.setResourceBundle(loadResourceBundle(bundleName));
} catch (MissingResourceException e) {
// ignore
}
record.setResourceBundleName(bundleName);
}
record.setLoggerName(this.name);
record.setSourceClassName(sourceClass);
record.setSourceMethodName(sourceMethod);
record.setParameters(new Object[] { param });
log(record);
}
/**
* Logs a message of the given level with the specified source class name,
* source method name and parameter array, using the given resource bundle
* to localize the message. If {@code bundleName} is null, the empty string
* or not valid then the message is not localized.
*
* @param logLevel
* the level of the given message.
* @param sourceClass
* the source class name.
* @param sourceMethod
* the source method name.
* @param bundleName
* the name of the resource bundle used to localize the message.
* @param msg
* the message to be logged.
* @param params
* the parameter array associated with the event that is logged.
*/
public void logrb(Level logLevel, String sourceClass, String sourceMethod,
String bundleName, String msg, Object[] params) {
if (!internalIsLoggable(logLevel)) {
return;
}
LogRecord record = new LogRecord(logLevel, msg);
if (bundleName != null) {
try {
record.setResourceBundle(loadResourceBundle(bundleName));
} catch (MissingResourceException e) {
// ignore
}
record.setResourceBundleName(bundleName);
}
record.setLoggerName(this.name);
record.setSourceClassName(sourceClass);
record.setSourceMethodName(sourceMethod);
record.setParameters(params);
log(record);
}
/**
* Logs a message of the given level with the specified source class name,
* source method name and {@code Throwable} object, using the given resource
* bundle to localize the message. If {@code bundleName} is null, the empty
* string or not valid then the message is not localized.
*
* @param logLevel
* the level of the given message
* @param sourceClass
* the source class name
* @param sourceMethod
* the source method name
* @param bundleName
* the name of the resource bundle used to localize the message.
* @param msg
* the message to be logged.
* @param thrown
* the {@code Throwable} object.
*/
public void logrb(Level logLevel, String sourceClass, String sourceMethod,
String bundleName, String msg, Throwable thrown) {
if (!internalIsLoggable(logLevel)) {
return;
}
LogRecord record = new LogRecord(logLevel, msg);
if (bundleName != null) {
try {
record.setResourceBundle(loadResourceBundle(bundleName));
} catch (MissingResourceException e) {
// ignore
}
record.setResourceBundleName(bundleName);
}
record.setLoggerName(this.name);
record.setSourceClassName(sourceClass);
record.setSourceMethodName(sourceMethod);
record.setThrown(thrown);
log(record);
}
/*
* This security manager is used to access the class context.
*/
static class PrivateSecurityManager extends SecurityManager {
public Class<?>[] privateGetClassContext() {
return super.getClassContext();
}
}
void reset() {
levelObjVal = null;
levelIntVal = Level.INFO.intValue();
for (Handler handler : handlers) {
try {
if (handlers.remove(handler)) {
handler.close();
}
} catch (Exception ignored) {
}
}
updateDalvikLogHandler(); // android-only
}
}