/****************************************************************************************
* Copyright (c) 2013 Flavio Lerda <flerda@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify it under *
* the terms of the GNU General Public License as published by the Free Software *
* Foundation; either version 3 of the License, or (at your option) any later *
* version. *
* *
* This program is distributed in the hope that it will be useful, but WITHOUT ANY *
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A *
* PARTICULAR PURPOSE. See the GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License along with *
* this program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************************/
package com.ichi2.utils;
import android.text.TextUtils;
import timber.log.Timber;
/**
* Helper class to log method invocation.
* <p>
* Use with moderation as it spans the logcat and reduces performances.
* <p>
* Consider guarding calls to this method with an if statement on a static final constant, as in:
*
* <pre>
* public static final boolean DEBUG = false; // Enable for debugging this class.
*
* public void methodName(int value, String name) {
* if (DEBUG) {
* MethodLogger.log(value, name);
* }
* ...
* }
* </pre>
*/
public class MethodLogger {
private MethodLogger() {
}
/**
* Logs the method being called.
*
* @param message to add to the logged statement
*/
public static void log(String message) {
logInternal(message);
}
/**
* Logs the method being called.
*/
public static void log() {
logInternal("");
}
/**
* Logs the method that made the call.
* <p>
* A helper method is needed to make sure the number of stack frames is the same on every path.
*
* @param message to be added to the logged message
*/
private static void logInternal(String message) {
// Get the name of the class and method.
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
// Look for the index of this method call in the stack trace.
//
// The task should something like:
// 0: dalvik.system.VMStack.getThreadStackTrace()
// 1: java.lang.Thread.getStackTrace()
// 2: com.ichi2.utils.MethodLogger.logInternal()
// 3: com.ichi2.utils.MethodLogger.log()
// 4: THE METHOD WE ARE LOOKING FOR
//
// But we cannot guarantee what the stack trace below this method will be, and it might be different on
// different versions of Android. Instead, we look for the call to our own method and we assume there is a
// single public method on this class above it before the call to logInternal.
int size = stack.length;
int logInternalIndex = 0;
for (; logInternalIndex < size; ++logInternalIndex) {
if (TextUtils.equals(stack[logInternalIndex].getClassName(), MethodLogger.class.getName())
&& TextUtils.equals(stack[logInternalIndex].getMethodName(), "logInternal")) {
break;
}
}
if (logInternalIndex + 2 >= size) {
throw new IllegalStateException("there should always be a caller for this method");
}
StackTraceElement caller = stack[logInternalIndex + 2];
String callerClass = caller.getClassName();
String callerMethod = caller.getMethodName();
if (TextUtils.isEmpty(message)) {
Timber.d("called: %s.%s()", callerClass, callerMethod);
} else {
Timber.d("called: %s.%s(): %s", callerClass, callerMethod, message);
}
}
}