Been using a lot of Log.d(TAG, "message");
when working on Android projects. Problem with TAG
is that it needs to be explicitly declared, e.g. protected String TAG = MyClass.class.getSimpleName();
instead of being inferred. Unlike PHP, Java has no magic constants such as __CLASS__
or __FUNCTION__
that can resolve automagically to the class/method being used at the point of call.
Did up a simple Java logging class that infers TAG
from the stack trace, with a few assumptions of course.
package sg.zion.testapp; import android.util.Log; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; public class Z { public static void log(String message, String... extras) { Log.d(getTag(), message + getAppendString(extras)); } public static void log(int number, String... extras) { Log.d(getTag(), String.valueOf(number) + getAppendString(extras)); } public static void log(Object obj, String... extras) { Log.d(getTag(), getObjectAsString(obj) + getAppendString(extras)); } public static void log(List items, String... extras) { String tag = getTag(); Log.d(tag, getAppendString(extras)); for (Object item : items) { Log.d(tag, getObjectAsString(item)); } } protected static String getTag() { // Get trace for caller which called Z.log() which called Z.getTag() StackTraceElement trace = Thread.currentThread().getStackTrace()[4]; return trace.getClassName() + "." + trace.getMethodName() + ":L" + trace.getLineNumber(); // this is useful for callbacks } protected static String getAppendString(String... extras) { StringBuilder result = new StringBuilder(); // recommended for loops String tmp; for (int i = 0; i < extras.length; i++) { tmp = (0 == i ? "" : " ") + extras[i]; result.append(tmp); } return result.toString().trim(); } protected static String getObjectAsString(Object obj) { StringBuilder result = new StringBuilder(); // recommended for loops String tmp; Class<?> objClass = obj.getClass(); // <?> removes compiler warning Field field; Method method; for (String fieldName : Arrays.asList("id", "name")) { try { field = objClass.getField(fieldName); tmp = fieldName + ":" + String.valueOf(field.get(obj)) + "; "; result.append(tmp); } catch (Exception e) { Log.d(getTag(), e.getMessage()); } } for (String methodName : Arrays.asList("getId", "getName")) { try { method = objClass.getMethod(methodName); tmp = methodName + ":" + String.valueOf(method.invoke(obj)) + "; "; result.append(tmp); } catch (Exception e) { Log.d(getTag(), e.getMessage()); } } result.append(obj.toString()); return result.toString(); } }
To use, just call Z.log()
. The user does not need to cast the arguments to String
first. The 2nd parameter, extras
, is optional but useful when logging numbers. Line numbers included in the computed tag are useful for tracing output from callbacks. For objects, public properties id
/name
and public methods getId()
/getName()
are used if they exist. Below is an example of how it is used.
package sg.zion.testapp; /** * Search for "D/.+MainActivity" when tracing Z.log() output in Logcat window */ public class MainActivity extends AppCompatActivity { public void test() { // D/sg.zion.testapp.MainActivity.test:L9: Hello World Z.log("Hello World"); // this is line 9 of the code, hence L9 in output // D/sg.zion.testapp.MainActivity.test:L12: 10 items Z.log(10, " items"); // D/sg.zion.testapp.MainActivity.test:L17: id:1; getName:A; sg.zion.testapp.Person@60b42c31 // Assume Person class with property id and getter getName(). // This works on List<Person> as well. Z.log(new Person(1, "A")); // D/sg.zion.testapp.MainActivity.test:L25: [Strings] // D/sg.zion.testapp.MainActivity.test:L25: alpha // D/sg.zion.testapp.MainActivity.test:L25: beta List<String> items = new ArrayList<>(); items.add("alpha"); items.add("beta"); Z.log(items, "[Strings]"); } }