Simple Java logging class with inferred TAG

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]");
    }
}