package com.support.design.common;
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;
/**
* Fans log
*
* @author choices
*/
public final class Flog {
/**
* 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);
}
/**
* A view into Timber's planted trees as a tree itself. This can be used for injecting a logger
* instance rather than using static methods or to facilitate testing.
*/
public static Tree asTree() {
return TREE_OF_SOULS;
}
/**
* 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 == null) {
throw new NullPointerException("tree == null");
}
if (tree == TREE_OF_SOULS) {
throw new IllegalArgumentException("Cannot plant Timber into itself.");
}
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 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) {
List<Tree> forest = FOREST;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = forest.size(); i < count; i++) {
forest.get(i).v(message, args);
}
}
@Override
public void v(Throwable t, String message, Object... args) {
List<Tree> forest = FOREST;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = forest.size(); i < count; i++) {
forest.get(i).v(t, message, args);
}
}
@Override
public void d(String message, Object... args) {
List<Tree> forest = FOREST;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = forest.size(); i < count; i++) {
forest.get(i).d(message, args);
}
}
@Override
public void d(Throwable t, String message, Object... args) {
List<Tree> forest = FOREST;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = forest.size(); i < count; i++) {
forest.get(i).d(t, message, args);
}
}
@Override
public void i(String message, Object... args) {
List<Tree> forest = FOREST;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = forest.size(); i < count; i++) {
forest.get(i).i(message, args);
}
}
@Override
public void i(Throwable t, String message, Object... args) {
List<Tree> forest = FOREST;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = forest.size(); i < count; i++) {
forest.get(i).i(t, message, args);
}
}
@Override
public void w(String message, Object... args) {
List<Tree> forest = FOREST;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = forest.size(); i < count; i++) {
forest.get(i).w(message, args);
}
}
@Override
public void w(Throwable t, String message, Object... args) {
List<Tree> forest = FOREST;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = forest.size(); i < count; i++) {
forest.get(i).w(t, message, args);
}
}
@Override
public void e(String message, Object... args) {
List<Tree> forest = FOREST;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = forest.size(); i < count; i++) {
forest.get(i).e(message, args);
}
}
@Override
public void e(Throwable t, String message, Object... args) {
List<Tree> forest = FOREST;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = forest.size(); i < count; i++) {
forest.get(i).e(t, message, args);
}
}
};
private Flog() {
throw new AssertionError("No instances.");
}
/**
* 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 Tree} for debug builds. Automatically infers the tag from the calling class.
*/
public static class DebugTree implements TaggedTree {
private boolean showLine = false;
private static final int MAX_LOG_LENGTH = 4000;
private static final Pattern ANONYMOUS_CLASS = Pattern.compile("\\$\\d+$");
private static final ThreadLocal<String> NEXT_TAG = new ThreadLocal<String>();
public void setShowLine(boolean canShowLine) {
this.showLine = canShowLine;
}
@Override
public final void tag(String tag) {
NEXT_TAG.set(tag);
}
/**
* Returns an explicitly set tag for the next log message or {@code null}. Calling this method
* clears any set tag so it may only be called once.
*/
protected final String nextTag() {
String tag = NEXT_TAG.get();
if (tag != null) {
NEXT_TAG.remove();
}
return tag;
}
/**
* Creates a tag for a log message.
* <p/>
* By default this method will check {@link #nextTag()} for an explicit tag. If there is no
* explicit tag, the class name of the caller will be used by inspecting the stack trace of the
* current thread.
* <p/>
* Note: Do not call {@code super.createTag()} if you override this method. It will produce
* incorrect results.
*/
protected String createTag() {
String tag = nextTag();
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 < 6) {
throw new IllegalStateException(
"Synthetic stacktrace didn't have enough elements: are you using proguard?");
}
tag = stackTrace[5].getClassName();
int lineNum = stackTrace[5].getLineNumber();
tag = tag.concat(":").concat(String.valueOf(lineNum));
Matcher m = ANONYMOUS_CLASS.matcher(tag);
if (m.find()) {
tag = m.replaceAll("");
}
return tag.substring(tag.lastIndexOf('.') + 1);
}
private static String maybeFormat(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 final void v(String message, Object... args) {
throwShade(Log.VERBOSE, maybeFormat(message, args), null);
}
@Override
public final void v(Throwable t, String message, Object... args) {
throwShade(Log.VERBOSE, maybeFormat(message, args), t);
}
@Override
public final void d(String message, Object... args) {
throwShade(Log.DEBUG, maybeFormat(message, args), null);
}
@Override
public final void d(Throwable t, String message, Object... args) {
throwShade(Log.DEBUG, maybeFormat(message, args), t);
}
@Override
public final void i(String message, Object... args) {
throwShade(Log.INFO, maybeFormat(message, args), null);
}
@Override
public final void i(Throwable t, String message, Object... args) {
throwShade(Log.INFO, maybeFormat(message, args), t);
}
@Override
public final void w(String message, Object... args) {
throwShade(Log.WARN, maybeFormat(message, args), null);
}
@Override
public final void w(Throwable t, String message, Object... args) {
throwShade(Log.WARN, maybeFormat(message, args), t);
}
@Override
public final void e(String message, Object... args) {
throwShade(Log.ERROR, maybeFormat(message, args), null);
}
@Override
public final void e(Throwable t, String message, Object... args) {
throwShade(Log.ERROR, maybeFormat(message, args), t);
}
private void throwShade(int priority, String message, Throwable t) {
if (message == null || message.length() == 0) {
if (t == null) {
return; // Swallow message if it's null and there's no throwable.
}
message = Log.getStackTraceString(t);
} else if (t != null) {
message += "\n" + Log.getStackTraceString(t);
}
if (showLine) {
logMessage(priority, createTag(), message);
} else {
logMessage(priority, "Log", message);
}
}
/**
* Log a message!
*/
protected void logMessage(int priority, String tag, String message) {
if (message.length() < MAX_LOG_LENGTH) {
Log.println(priority, tag, 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);
Log.println(priority, tag, message.substring(i, end));
i = end;
} while (i < newline);
}
}
}
/**
* A {@link 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) {
}
}
}