Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tag is null when using Timber

When the DebugTree logs, I see the class name, however when I create a custom Tree, the tag is null. Here what my custom tree looks like:

public class CrashlyticsTree extends Timber.Tree {
private static final String CRASHLYTICS_KEY_PRIORITY = "priority";
private static final String CRASHLYTICS_KEY_TAG = "tag";
private static final String CRASHLYTICS_KEY_MESSAGE = "message";

@Override
protected boolean isLoggable(int priority) {
    if (priority == Log.VERBOSE || priority == Log.DEBUG || priority == Log.INFO) {
        return false;
    }

    // only log WARN(Timber.w), ERROR(Timber.e), or WTF(Timber.wtf)
    return true;
}

@Override
protected void log(int priority, @Nullable String tag, @Nullable String message, @Nullable Throwable t) {
    if(User.CurrentUser.isLoggedIn()){
        Crashlytics.setUserIdentifier(Long.toString(User.CurrentUser.getUserId()));
    }

    Crashlytics.setInt(CRASHLYTICS_KEY_PRIORITY, priority);
    Crashlytics.setString(CRASHLYTICS_KEY_TAG, tag);
    Crashlytics.setString(CRASHLYTICS_KEY_MESSAGE, message);

    if (t == null) {
        Crashlytics.logException(new Exception(message));
    } else {
        if(!TextUtils.isEmpty(message)){
            Crashlytics.log(priority, tag, message);
        }
        Crashlytics.logException(t);
    }
}
}

However even from the DebugTree, the tag that gets generated is BaseActivity because it does come from the BaseActivity however I was wondering if there was a way I can get the name of the class that extends BaseActivity

like image 271
Sree Avatar asked Jul 13 '16 22:07

Sree


3 Answers

There is a hack if you want to show/add tag in release apk. Instead of using Timber.Tree() use Timber.DebugTree() which will allow you to show tag in each and every logs like below:

class ReleaseTree : Timber.DebugTree() {

    override fun log(priority: Int, tag: String?, message: String, throwable: Throwable?) {
        // Don't log VERBOSE and DEBUG
        if (priority == Log.VERBOSE || priority == Log.DEBUG) {
            return
        }

        Log.println(priority, "Test->$tag", message)

    }
}

This way you can also add your custom tag as prefix with each log.

like image 74
0xAliHn Avatar answered Oct 20 '22 00:10

0xAliHn


I did something like this to get rid of the problem:

    public static class ReleaseTree extends Timber.Tree {

    private static final int MAX_LOG_LENGTH = 4000;
    private static final int MAX_TAG_LENGTH = 23;
    private static final int CALL_STACK_INDEX = 6;
    private static final Pattern ANONYMOUS_CLASS = Pattern.compile("(\\$\\d+)+$");

    @SuppressLint("LogNotTimber")
    @Override
    protected void log(int priority, @Nullable String tag, @NotNull String message, @Nullable Throwable t) {
        if (priority != Log.ERROR) {
            return;
        }

        String newTag = getTag(tag);
        if (message.length() < MAX_LOG_LENGTH) {
            Log.println(priority, newTag, message);
            return;
        }

        // Split by line, then ensure each line can fit into Log's maximum length.
        for (int i = 0, length = message.length(); i < length; i++) {
            int newline = message.indexOf('\n', i);
            newline = newline != -1 ? newline : length;
            do {
                int end = Math.min(newline, i + MAX_LOG_LENGTH);
                String part = message.substring(i, end);
                Log.println(priority, newTag, part);
                i = end;
            } while (i < newline);
        }
    }

    private String getTag(String tag) {
        if (tag != null) {
            return tag;
        }

        // DO NOT switch this to Thread.getCurrentThread().getStackTrace(). The test will pass
        // because Robolectric runs them on the JVM but on Android the elements are different.
        StackTraceElement[] stackTrace = new Throwable().getStackTrace();
        if (stackTrace.length <= CALL_STACK_INDEX) {
            throw new IllegalStateException(
                    "Synthetic stacktrace didn't have enough elements: are you using proguard?");
        }
        return createStackElementTag(stackTrace[CALL_STACK_INDEX]);
    }

    @Nullable
    protected String createStackElementTag(@NotNull StackTraceElement element) {
        String tag = element.getClassName();
        Matcher m = ANONYMOUS_CLASS.matcher(tag);
        if (m.find()) {
            tag = m.replaceAll("");
        }
        tag = tag.substring(tag.lastIndexOf('.') + 1);
        // Tag length limit was removed in API 24.
        if (tag.length() <= MAX_TAG_LENGTH || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return tag;
        }
        return tag.substring(0, MAX_TAG_LENGTH);
    }
}

It's nearly like the implementation of the DebugTree, except that it logs only errors and since error rate should be low we don't have too much overhead ;P

In this case we didn't use reflection in order to find the tag name, but some smart usage of a Throwable instance. For some useful information, you can have a look here.

like image 35
Kreshnik Avatar answered Oct 20 '22 01:10

Kreshnik


According to Jake Wharton:

The tag is null unless you call tag(String) at the log site or extend from DebugTree (which you shouldn't do for production logging).

Therefore you need to add Timber.tag([class name]) before each call.

See github.com/JakeWharton/timber/issues/122

like image 32
Don Wilson Avatar answered Oct 20 '22 00:10

Don Wilson