blob: 4c79a4d507f3f5c255c5df224a12ea0282f0a992 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.base;
import android.text.TextUtils;
import org.chromium.base.annotations.NoSideEffects;
import java.util.Locale;
/**
* Utility class for Logging.
*
* <p>
* Defines logging access points for each feature. They format and forward the logs to
* {@link android.util.Log}, allowing to standardize the output, to make it easy to identify
* the origin of logs, and enable or disable logging in different parts of the code.
* </p>
* <p>
* Please make use of the formatting capability of the logging methods rather than doing
* concatenations in the calling code. In the release builds of Chrome, debug and verbose log
* calls will be stripped out of the binary. Concatenations and method calls however will still
* remain and be executed. If they can't be avoided, try to generate the logs in a method annotated
* with {@link NoSideEffects}. Another possibility is to use
* {@link android.util.Log#isLoggable(String, int)} to guard those calls.
* </p>
*
* Usage:
* <pre>
* private static final String TAG = Log.makeTag("Group");
*
* private void myMethod(String awesome) {
* Log.i(TAG, "My %s message.", awesome);
* Log.d(TAG, "My debug message");
* }
* </pre>
*
* Logcat output:
* <pre>
* I/cr.Group (999): My awesome message
* D/cr.Group (999): [MyClass.java:42] My debug message
* </pre>
*
* Set the log level for a given group:
* <pre>
* $ adb shell setprop log.tag.chromium.Group VERBOSE
* </pre>
*/
public class Log {
private static final String BASE_TAG = "cr";
/** Convenience property, same as {@link android.util.Log#ASSERT}. */
public static final int ASSERT = android.util.Log.ASSERT;
/** Convenience property, same as {@link android.util.Log#DEBUG}. */
public static final int DEBUG = android.util.Log.DEBUG;
/** Convenience property, same as {@link android.util.Log#ERROR}. */
public static final int ERROR = android.util.Log.ERROR;
/** Convenience property, same as {@link android.util.Log#INFO}. */
public static final int INFO = android.util.Log.INFO;
/** Convenience property, same as {@link android.util.Log#VERBOSE}. */
public static final int VERBOSE = android.util.Log.VERBOSE;
/** Convenience property, same as {@link android.util.Log#WARN}. */
public static final int WARN = android.util.Log.WARN;
private Log() {
// Static only access
}
/** Returns a formatted log message, using the supplied format and arguments.*/
private static String formatLog(String messageTemplate, Object... params) {
if (params != null && params.length != 0) {
messageTemplate = String.format(Locale.US, messageTemplate, params);
}
return messageTemplate;
}
/**
* Returns a formatted log message, using the supplied format and arguments.
* The message will be prepended with the filename and line number of the call.
*/
private static String formatLogWithStack(String messageTemplate, Object... params) {
return "[" + getCallOrigin() + "] " + formatLog(messageTemplate, params);
}
/**
* Returns a full tag for the provided group tag. Full tags longer than 23 characters
* will cause a runtime exception.
*
* @param groupTag {@code null} and empty string are allowed.
*
* @see android.util.Log#isLoggable(String, int)
* @throws IllegalArgumentException if the tag is too long.
*/
public static String makeTag(String groupTag) {
if (TextUtils.isEmpty(groupTag)) return BASE_TAG;
String tag = BASE_TAG + "." + groupTag;
if (tag.length() > 23) {
throw new IllegalArgumentException(
"The full tag (" + tag + ") is longer than 23 characters.");
}
return tag;
}
/** Convenience function, forwards to {@link android.util.Log#isLoggable(String, int)}. */
public static boolean isLoggable(String tag, int level) {
return android.util.Log.isLoggable(tag, level);
}
/**
* Sends a {@link android.util.Log#VERBOSE} log message.
*
* For optimization purposes, only the fixed parameters versions are visible. If you need more
* than 7 parameters, consider building your log message using a function annotated with
* {@link NoSideEffects}.
*
* @param tag Used to identify the source of a log message. Should be created using
* {@link #makeTag(String)}.
*
* @param messageTemplate The message you would like logged. It is to be specified as a format
* string.
* @param args Arguments referenced by the format specifiers in the format string. If the last
* one is a {@link Throwable}, its trace will be printed.
*/
private static void verbose(String tag, String messageTemplate, Object... args) {
if (android.util.Log.isLoggable(tag, android.util.Log.VERBOSE)) {
String message = formatLogWithStack(messageTemplate, args);
Throwable tr = getThrowableToLog(args);
if (tr != null) {
android.util.Log.v(tag, message, tr);
} else {
android.util.Log.v(tag, message);
}
}
}
/** Sends a {@link android.util.Log#VERBOSE} log message. 0 arg version. */
public static void v(String tag, String message) {
verbose(tag, message);
}
/** Sends a {@link android.util.Log#VERBOSE} log message. 1 arg version. */
public static void v(String tag, String messageTemplate, Object arg1) {
verbose(tag, messageTemplate, arg1);
}
/** Sends a {@link android.util.Log#VERBOSE} log message. 2 args version */
public static void v(String tag, String messageTemplate, Object arg1, Object arg2) {
verbose(tag, messageTemplate, arg1, arg2);
}
/** Sends a {@link android.util.Log#VERBOSE} log message. 3 args version */
public static void v(
String tag, String messageTemplate, Object arg1, Object arg2, Object arg3) {
verbose(tag, messageTemplate, arg1, arg2, arg3);
}
/** Sends a {@link android.util.Log#VERBOSE} log message. 4 args version */
public static void v(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
Object arg4) {
verbose(tag, messageTemplate, arg1, arg2, arg3, arg4);
}
/** Sends a {@link android.util.Log#VERBOSE} log message. 5 args version */
public static void v(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
Object arg4, Object arg5) {
verbose(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5);
}
/** Sends a {@link android.util.Log#VERBOSE} log message. 6 args version */
public static void v(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
Object arg4, Object arg5, Object arg6) {
verbose(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5, arg6);
}
/** Sends a {@link android.util.Log#VERBOSE} log message. 7 args version */
public static void v(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
Object arg4, Object arg5, Object arg6, Object arg7) {
verbose(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}
/**
* Sends a {@link android.util.Log#DEBUG} log message.
*
* For optimization purposes, only the fixed parameters versions are visible. If you need more
* than 7 parameters, consider building your log message using a function annotated with
* {@link NoSideEffects}.
*
* @param tag Used to identify the source of a log message. Should be created using
* {@link #makeTag(String)}.
*
* @param messageTemplate The message you would like logged. It is to be specified as a format
* string.
* @param args Arguments referenced by the format specifiers in the format string. If the last
* one is a {@link Throwable}, its trace will be printed.
*/
private static void debug(String tag, String messageTemplate, Object... args) {
if (android.util.Log.isLoggable(tag, android.util.Log.VERBOSE)) {
String message = formatLogWithStack(messageTemplate, args);
Throwable tr = getThrowableToLog(args);
if (tr != null) {
android.util.Log.d(tag, message, tr);
} else {
android.util.Log.d(tag, message);
}
}
}
/** Sends a {@link android.util.Log#DEBUG} log message. 0 arg version. */
public static void d(String tag, String message) {
debug(tag, message);
}
/** Sends a {@link android.util.Log#DEBUG} log message. 1 arg version. */
public static void d(String tag, String messageTemplate, Object arg1) {
debug(tag, messageTemplate, arg1);
}
/** Sends a {@link android.util.Log#DEBUG} log message. 2 args version */
public static void d(String tag, String messageTemplate, Object arg1, Object arg2) {
debug(tag, messageTemplate, arg1, arg2);
}
/** Sends a {@link android.util.Log#DEBUG} log message. 3 args version */
public static void d(
String tag, String messageTemplate, Object arg1, Object arg2, Object arg3) {
debug(tag, messageTemplate, arg1, arg2, arg3);
}
/** Sends a {@link android.util.Log#DEBUG} log message. 4 args version */
public static void d(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
Object arg4) {
debug(tag, messageTemplate, arg1, arg2, arg3, arg4);
}
/** Sends a {@link android.util.Log#DEBUG} log message. 5 args version */
public static void d(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
Object arg4, Object arg5) {
debug(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5);
}
/** Sends a {@link android.util.Log#DEBUG} log message. 6 args version */
public static void d(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
Object arg4, Object arg5, Object arg6) {
debug(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5, arg6);
}
/** Sends a {@link android.util.Log#DEBUG} log message. 7 args version */
public static void d(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
Object arg4, Object arg5, Object arg6, Object arg7) {
debug(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}
/**
* Sends an {@link android.util.Log#INFO} log message.
*
* @param tag Used to identify the source of a log message. Should be created using
* {@link #makeTag(String)}.
* @param messageTemplate The message you would like logged. It is to be specified as a format
* string.
* @param args Arguments referenced by the format specifiers in the format string. If the last
* one is a {@link Throwable}, its trace will be printed.
*/
public static void i(String tag, String messageTemplate, Object... args) {
if (android.util.Log.isLoggable(tag, android.util.Log.INFO)) {
String message = formatLog(messageTemplate, args);
Throwable tr = getThrowableToLog(args);
if (tr != null) {
android.util.Log.i(tag, message, tr);
} else {
android.util.Log.i(tag, message);
}
}
}
/**
* Sends a {@link android.util.Log#WARN} log message.
*
* @param tag Used to identify the source of a log message. Should be created using
* {@link #makeTag(String)}.
* @param messageTemplate The message you would like logged. It is to be specified as a format
* string.
* @param args Arguments referenced by the format specifiers in the format string. If the last
* one is a {@link Throwable}, its trace will be printed.
*/
public static void w(String tag, String messageTemplate, Object... args) {
if (android.util.Log.isLoggable(tag, android.util.Log.WARN)) {
String message = formatLog(messageTemplate, args);
Throwable tr = getThrowableToLog(args);
if (tr != null) {
android.util.Log.w(tag, message, tr);
} else {
android.util.Log.w(tag, message);
}
}
}
/**
* Sends an {@link android.util.Log#ERROR} log message.
*
* @param tag Used to identify the source of a log message. Should be created using
* {@link #makeTag(String)}.
* @param messageTemplate The message you would like logged. It is to be specified as a format
* string.
* @param args Arguments referenced by the format specifiers in the format string. If the last
* one is a {@link Throwable}, its trace will be printed.
*/
public static void e(String tag, String messageTemplate, Object... args) {
if (android.util.Log.isLoggable(tag, android.util.Log.ERROR)) {
String message = formatLog(messageTemplate, args);
Throwable tr = getThrowableToLog(args);
if (tr != null) {
android.util.Log.e(tag, message, tr);
} else {
android.util.Log.e(tag, message);
}
}
}
/**
* What a Terrible Failure: Used for conditions that should never happen, and logged at
* the {@link android.util.Log#ASSERT} level. Depending on the configuration, it might
* terminate the process.
*
* @see android.util.Log#wtf(String, String, Throwable)
*
* @param tag Used to identify the source of a log message. Should be created using
* {@link #makeTag(String)}.
* @param messageTemplate The message you would like logged. It is to be specified as a format
* string.
* @param args Arguments referenced by the format specifiers in the format string. If the last
* one is a {@link Throwable}, its trace will be printed.
*/
public static void wtf(String tag, String messageTemplate, Object... args) {
if (android.util.Log.isLoggable(tag, android.util.Log.ERROR)) {
String message = formatLog(messageTemplate, args);
Throwable tr = getThrowableToLog(args);
if (tr != null) {
android.util.Log.wtf(tag, message, tr);
} else {
android.util.Log.wtf(tag, message);
}
}
}
private static Throwable getThrowableToLog(Object[] args) {
if (args == null || args.length == 0) return null;
Object lastArg = args[args.length - 1];
if (!(lastArg instanceof Throwable)) return null;
return (Throwable) lastArg;
}
/** Returns a string form of the origin of the log call, to be used as secondary tag.*/
private static String getCallOrigin() {
StackTraceElement[] st = Thread.currentThread().getStackTrace();
// The call stack should look like:
// n [a variable number of calls depending on the vm used]
// +0 getCallOrigin()
// +1 privateLogFunction: verbose or debug
// +2 formatLogWithStack()
// +3 logFunction: v or d
// +4 caller
int callerStackIndex;
String logClassName = Log.class.getName();
for (callerStackIndex = 0; callerStackIndex < st.length; callerStackIndex++) {
if (st[callerStackIndex].getClassName().equals(logClassName)) {
callerStackIndex += 4;
break;
}
}
return st[callerStackIndex].getFileName() + ":" + st[callerStackIndex].getLineNumber();
}
}