package com.marshalchen.common.commonUtils.logUtils;
import android.util.Log;
import android.util.SparseBooleanArray;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** Logging for lazy people. */
public final class Timber {
/** Log a verbose message with optional format args. */
public static void v(String message, Object... args) {
TREE_OF_SOULS.v(message, args);
}
/** Log a verbose exception and a message with optional format args. */
public static void v(Throwable t, String message, Object... args) {
TREE_OF_SOULS.v(t, message, args);
}
/** Log a debug message with optional format args. */
public static void d(String message, Object... args) {
TREE_OF_SOULS.d(message, args);
}
/** Log a debug exception and a message with optional format args. */
public static void d(Throwable t, String message, Object... args) {
TREE_OF_SOULS.d(t, message, args);
}
/** Log an info message with optional format args. */
public static void i(String message, Object... args) {
TREE_OF_SOULS.i(message, args);
}
/** Log an info exception and a message with optional format args. */
public static void i(Throwable t, String message, Object... args) {
TREE_OF_SOULS.i(t, message, args);
}
/** Log a warning message with optional format args. */
public static void w(String message, Object... args) {
TREE_OF_SOULS.w(message, args);
}
/** Log a warning exception and a message with optional format args. */
public static void w(Throwable t, String message, Object... args) {
TREE_OF_SOULS.w(t, message, args);
}
/** Log an error message with optional format args. */
public static void e(String message, Object... args) {
TREE_OF_SOULS.e(message, args);
}
/** Log an error exception and a message with optional format args. */
public static void e(Throwable t, String message, Object... args) {
TREE_OF_SOULS.e(t, message, args);
}
/** Set a one-time tag for use on the next logging call. */
public static Tree tag(String tag) {
for (int index = 0, size = TAGGED_TREES.size(); index < size; index++) {
((TaggedTree) FOREST.get(TAGGED_TREES.keyAt(index))).tag(tag);
}
return TREE_OF_SOULS;
}
/** Add a new logging tree. */
public static void plant(Tree tree) {
if (tree instanceof TaggedTree) {
TAGGED_TREES.append(FOREST.size(), true);
}
FOREST.add(tree);
}
/** Remove a planted tree. */
public static void uproot(Tree tree) {
for (int i = 0, size = FOREST.size(); i < size; i++) {
if (FOREST.get(i) == tree) {
TAGGED_TREES.delete(i);
FOREST.remove(i);
return;
}
}
throw new IllegalArgumentException("Cannot uproot tree which is not planted: " + tree);
}
/** Remove all planted trees. */
public static void uprootAll() {
TAGGED_TREES.clear();
FOREST.clear();
}
static final List<Tree> FOREST = new CopyOnWriteArrayList<Tree>();
static final SparseBooleanArray TAGGED_TREES = new SparseBooleanArray();
/** A {@link com.marshalchen.common.commonUtils.logUtils.Timber.Tree} that delegates to all planted trees in the {@link #FOREST forest}. */
private static final Tree TREE_OF_SOULS = new Tree() {
@Override public void v(String message, Object... args) {
for (Tree tree : FOREST) {
tree.v(message, args);
}
}
@Override public void v(Throwable t, String message, Object... args) {
for (Tree tree : FOREST) {
tree.v(t, message, args);
}
}
@Override public void d(String message, Object... args) {
for (Tree tree : FOREST) {
tree.d(message, args);
}
}
@Override public void d(Throwable t, String message, Object... args) {
for (Tree tree : FOREST) {
tree.d(t, message, args);
}
}
@Override public void i(String message, Object... args) {
for (Tree tree : FOREST) {
tree.i(message, args);
}
}
@Override public void i(Throwable t, String message, Object... args) {
for (Tree tree : FOREST) {
tree.i(t, message, args);
}
}
@Override public void w(String message, Object... args) {
for (Tree tree : FOREST) {
tree.w(message, args);
}
}
@Override public void w(Throwable t, String message, Object... args) {
for (Tree tree : FOREST) {
tree.w(t, message, args);
}
}
@Override public void e(String message, Object... args) {
for (Tree tree : FOREST) {
tree.e(message, args);
}
}
@Override public void e(Throwable t, String message, Object... args) {
for (Tree tree : FOREST) {
tree.e(t, message, args);
}
}
};
private Timber() {
}
/** A facade for handling logging calls. Install instances via {@link #plant}. */
public interface Tree {
/** Log a verbose message with optional format args. */
void v(String message, Object... args);
/** Log a verbose exception and a message with optional format args. */
void v(Throwable t, String message, Object... args);
/** Log a debug message with optional format args. */
void d(String message, Object... args);
/** Log a debug exception and a message with optional format args. */
void d(Throwable t, String message, Object... args);
/** Log an info message with optional format args. */
void i(String message, Object... args);
/** Log an info exception and a message with optional format args. */
void i(Throwable t, String message, Object... args);
/** Log a warning message with optional format args. */
void w(String message, Object... args);
/** Log a warning exception and a message with optional format args. */
void w(Throwable t, String message, Object... args);
/** Log an error message with optional format args. */
void e(String message, Object... args);
/** Log an error exception and a message with optional format args. */
void e(Throwable t, String message, Object... args);
}
/** A facade for attaching tags to logging calls. Install instances via {@link #plant} */
public interface TaggedTree extends Tree {
/** Set a one-time tag for use on the next logging call. */
void tag(String tag);
}
/** A {@link com.marshalchen.common.commonUtils.logUtils.Timber.Tree} for debug builds. Automatically infers the tag from the calling class. */
public static class DebugTree implements TaggedTree {
private static final Pattern ANONYMOUS_CLASS = Pattern.compile("\\$\\d+$");
private static final ThreadLocal<String> NEXT_TAG = new ThreadLocal<String>();
private static String createTag() {
String tag = NEXT_TAG.get();
if (tag != null) {
NEXT_TAG.remove();
return tag;
}
StackTraceElement[] stackTrace = new Throwable().getStackTrace();
if (stackTrace.length < 6) {
throw new IllegalStateException(
"Synthetic stacktrace didn't have enough elements: are you using proguard?");
}
tag = stackTrace[5].getClassName();
Matcher m = ANONYMOUS_CLASS.matcher(tag);
if (m.find()) {
tag = m.replaceAll("");
}
return tag.substring(tag.lastIndexOf('.') + 1);
}
static String formatString(String message, Object... args) {
// If no varargs are supplied, treat it as a request to log the string without formatting.
return args.length == 0 ? message : String.format(message, args);
}
@Override public void v(String message, Object... args) {
throwShade(Log.VERBOSE, formatString(message, args), null);
}
@Override public void v(Throwable t, String message, Object... args) {
throwShade(Log.VERBOSE, formatString(message, args), t);
}
@Override public void d(String message, Object... args) {
throwShade(Log.DEBUG, formatString(message, args), null);
}
@Override public void d(Throwable t, String message, Object... args) {
throwShade(Log.DEBUG, formatString(message, args), t);
}
@Override public void i(String message, Object... args) {
throwShade(Log.INFO, formatString(message, args), null);
}
@Override public void i(Throwable t, String message, Object... args) {
throwShade(Log.INFO, formatString(message, args), t);
}
@Override public void w(String message, Object... args) {
throwShade(Log.WARN, formatString(message, args), null);
}
@Override public void w(Throwable t, String message, Object... args) {
throwShade(Log.WARN, formatString(message, args), t);
}
@Override public void e(String message, Object... args) {
throwShade(Log.ERROR, formatString(message, args), null);
}
@Override public void e(Throwable t, String message, Object... args) {
throwShade(Log.ERROR, formatString(message, args), t);
}
private void throwShade(int priority, String message, Throwable t) {
if (message == null || message.length() == 0) {
if (t != null) {
message = Log.getStackTraceString(t);
} else {
// Swallow message if it's null and there's no throwable.
return;
}
} else if (t != null) {
message += "\n" + Log.getStackTraceString(t);
}
String tag = createTag();
if (message.length() < 4000) {
Log.println(priority, tag, message);
} else {
// It's rare that the message will be this large, so we're ok with the perf hit of splitting
// and calling Log.println N times. It's possible but unlikely that a single line will be
// longer than 4000 characters: we're explicitly ignoring this case here.
String[] lines = message.split("\n");
for (String line : lines) {
Log.println(priority, tag, line);
}
}
}
@Override public void tag(String tag) {
NEXT_TAG.set(tag);
}
}
/** A {@link com.marshalchen.common.commonUtils.logUtils.Timber.Tree} which does nothing. Useful for extending. */
public static class HollowTree implements Tree {
@Override public void v(String message, Object... args) {
}
@Override public void v(Throwable t, String message, Object... args) {
}
@Override public void d(String message, Object... args) {
}
@Override public void d(Throwable t, String message, Object... args) {
}
@Override public void i(String message, Object... args) {
}
@Override public void i(Throwable t, String message, Object... args) {
}
@Override public void w(String message, Object... args) {
}
@Override public void w(Throwable t, String message, Object... args) {
}
@Override public void e(String message, Object... args) {
}
@Override public void e(Throwable t, String message, Object... args) {
}
}
}