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
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.
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.
According to Jake Wharton:
The
tag
is null unless you calltag(String)
at the log site or extend fromDebugTree
(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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With