package com.socks.jiandan.utils.logger;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Logger is a wrapper of {@link android.util.Log}
* But more pretty, simple and powerful
*
* @author Orhan Obut
*/
public final class Logger {
/**
* Android's max limit for a log entry is ~4076 bytes,
* so 4000 bytes is used as chunk size since default charset
* is UTF-8
*/
private static final int CHUNK_SIZE = 4000;
/**
* It is used for json pretty print
*/
private static final int JSON_INDENT = 4;
/**
* In order to prevent readability, max method count is restricted with 5
*/
private static final int MAX_METHOD_COUNT = 5;
/**
* It is used to determine log settings such as method count, thread info visibility
*/
private static final Settings settings = new Settings();
/**
* Drawing toolbox
*/
private static final char TOP_LEFT_CORNER = '╔';
private static final char BOTTOM_LEFT_CORNER = '╚';
private static final char MIDDLE_CORNER = '╟';
private static final char HORIZONTAL_DOUBLE_LINE = '║';
private static final String DOUBLE_DIVIDER = "════════════════════════════════════════════";
private static final String SINGLE_DIVIDER = "────────────────────────────────────────────";
private static final String TOP_BORDER = TOP_LEFT_CORNER + DOUBLE_DIVIDER + DOUBLE_DIVIDER;
private static final String BOTTOM_BORDER = BOTTOM_LEFT_CORNER + DOUBLE_DIVIDER + DOUBLE_DIVIDER;
private static final String MIDDLE_BORDER = MIDDLE_CORNER + SINGLE_DIVIDER + SINGLE_DIVIDER;
/**
* TAG is used for the Log, the name is a little different
* in order to differentiate the logs easily with the filter
*/
private static String TAG = "debugTAG";
//no instance
private Logger() {
}
/**
* It is used to get the settings object in order to change settings
*
* @return the settings object
*/
public static Settings init() {
return settings;
}
/**
* It is used to change the tag
*
* @param tag is the given string which will be used in Logger
*/
public static Settings init(String tag) {
if (tag == null) {
throw new NullPointerException("tag may not be null");
}
if (tag.trim().length() == 0) {
throw new IllegalStateException("tag may not be empty");
}
Logger.TAG = tag;
return settings;
}
public static void d(String message) {
d(TAG, message);
}
public static void d(String tag, String message) {
d(tag, message, settings.methodCount);
}
public static void d(String message, int methodCount) {
d(TAG, message, methodCount);
}
public static void d(String tag, String message, int methodCount) {
validateMethodCount(methodCount);
log(Log.DEBUG, tag, message, methodCount);
}
public static void e(String message) {
e(TAG, message);
}
public static void e(String tag, String message) {
e(tag, message, null, settings.methodCount);
}
public static void e(Exception e) {
e(TAG, null, e, settings.methodCount);
}
public static void e(String tag, Exception e) {
e(tag, null, e, settings.methodCount);
}
public static void e(String message, int methodCount) {
validateMethodCount(methodCount);
e(message, null, methodCount);
}
public static void e(String tag, String message, int methodCount) {
validateMethodCount(methodCount);
e(tag, message, null, methodCount);
}
public static void e(String tag, String message, Exception e) {
e(tag, message, e, settings.methodCount);
}
public static void e(String tag, String message, Exception e, int methodCount) {
validateMethodCount(methodCount);
if (e != null && message != null) {
message += " : " + e.toString();
}
if (e != null && message == null) {
message = e.toString();
}
if (message == null) {
message = "No message/exception is set";
}
log(Log.ERROR, tag, message, methodCount);
}
public static void w(String message) {
w(TAG, message);
}
public static void w(String tag, String message) {
w(tag, message, settings.methodCount);
}
public static void w(String message, int methodCount) {
w(TAG, message, methodCount);
}
public static void w(String tag, String message, int methodCount) {
validateMethodCount(methodCount);
log(Log.WARN, tag, message, methodCount);
}
public static void i(String message) {
i(TAG, message);
}
public static void i(String tag, String message) {
i(tag, message, settings.methodCount);
}
public static void i(String message, int methodCount) {
i(TAG, message, methodCount);
}
public static void i(String tag, String message, int methodCount) {
validateMethodCount(methodCount);
log(Log.INFO, tag, message, methodCount);
}
public static void v(String message) {
v(TAG, message);
}
public static void v(String tag, String message) {
v(tag, message, settings.methodCount);
}
public static void v(String message, int methodCount) {
v(TAG, message, methodCount);
}
public static void v(String tag, String message, int methodCount) {
validateMethodCount(methodCount);
log(Log.VERBOSE, tag, message, methodCount);
}
public static void wtf(String message) {
wtf(TAG, message);
}
public static void wtf(String tag, String message) {
wtf(tag, message, settings.methodCount);
}
public static void wtf(String message, int methodCount) {
wtf(TAG, message, methodCount);
}
public static void wtf(String tag, String message, int methodCount) {
validateMethodCount(methodCount);
log(Log.ASSERT, tag, message, methodCount);
}
/**
* Formats the json content and print it
*
* @param json the json content
*/
public static void json(String json) {
json(TAG, json);
}
public static void json(String tag, String json) {
json(tag, json, settings.methodCount);
}
public static void json(String json, int methodCount) {
json(TAG, json, methodCount);
}
/**
* Formats the json content and print it
*
* @param json the json content
* @param methodCount number of the method that will be printed
*/
public static void json(String tag, String json, int methodCount) {
validateMethodCount(methodCount);
if (TextUtils.isEmpty(json)) {
d(tag, "Empty/Null json content", methodCount);
return;
}
try {
if (json.startsWith("{")) {
JSONObject jsonObject = new JSONObject(json);
String message = jsonObject.toString(JSON_INDENT);
d(tag, message, methodCount);
return;
}
if (json.startsWith("[")) {
JSONArray jsonArray = new JSONArray(json);
String message = jsonArray.toString(JSON_INDENT);
d(tag, message, methodCount);
}
} catch (JSONException e) {
d(tag, e.getCause().getMessage() + "\n" + json, methodCount);
}
}
/**
* This method is synchronized in order to avoid messy of logs' order.
*/
private synchronized static void log(int logType, String tag, String message, int methodCount) {
if (settings.logLevel == LogLevel.NONE) {
return;
}
logTopBorder(logType, tag);
logHeaderContent(logType, tag, methodCount);
//get bytes of message with system's default charset (which is UTF-8 for Android)
byte[] bytes = message.getBytes();
int length = bytes.length;
if (length <= CHUNK_SIZE) {
if (methodCount > 0) {
logDivider(logType, tag);
}
logContent(logType, tag, message);
logBottomBorder(logType, tag);
return;
}
if (methodCount > 0) {
logDivider(logType, tag);
}
for (int i = 0; i < length; i += CHUNK_SIZE) {
int count = Math.min(length - i, CHUNK_SIZE);
//create a new String with system's default charset (which is UTF-8 for Android)
logContent(logType, tag, new String(bytes, i, count));
}
logBottomBorder(logType, tag);
}
private static void logTopBorder(int logType, String tag) {
logChunk(logType, tag, TOP_BORDER);
}
private static void logHeaderContent(int logType, String tag, int methodCount) {
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
if (settings.showThreadInfo) {
logChunk(logType, tag, HORIZONTAL_DOUBLE_LINE + " Thread: " + Thread.currentThread().getName());
logDivider(logType, tag);
}
String level = "";
for (int i = methodCount; i > 0; i--) {
int stackIndex = i + 5;
StringBuilder builder = new StringBuilder();
builder.append("║ ")
.append(level)
.append(getSimpleClassName(trace[stackIndex].getClassName()))
.append(".")
.append(trace[stackIndex].getMethodName())
.append(" ")
.append(" (")
.append(trace[stackIndex].getFileName())
.append(":")
.append(trace[stackIndex].getLineNumber())
.append(")");
level += " ";
logChunk(logType, tag, builder.toString());
}
}
private static void logBottomBorder(int logType, String tag) {
logChunk(logType, tag, BOTTOM_BORDER);
}
private static void logDivider(int logType, String tag) {
logChunk(logType, tag, MIDDLE_BORDER);
}
private static void logContent(int logType, String tag, String chunk) {
String[] lines = chunk.split(System.getProperty("line.separator"));
for (String line : lines) {
logChunk(logType, tag, HORIZONTAL_DOUBLE_LINE + " " + line);
}
}
private static void logChunk(int logType, String tag, String chunk) {
String finalTag = formatTag(tag);
switch (logType) {
case Log.ERROR:
Log.e(finalTag, chunk);
break;
case Log.INFO:
Log.i(finalTag, chunk);
break;
case Log.VERBOSE:
Log.v(finalTag, chunk);
break;
case Log.WARN:
Log.w(finalTag, chunk);
break;
case Log.ASSERT:
Log.wtf(finalTag, chunk);
break;
case Log.DEBUG:
// Fall through, log debug by default
default:
Log.d(finalTag, chunk);
break;
}
}
private static String getSimpleClassName(String name) {
int lastIndex = name.lastIndexOf(".");
return name.substring(lastIndex + 1);
}
private static void validateMethodCount(int methodCount) {
if (methodCount < 0 || methodCount > MAX_METHOD_COUNT) {
throw new IllegalStateException("methodCount must be > 0 and < 5");
}
}
private static String formatTag(String tag) {
if (!TextUtils.isEmpty(tag) && !TextUtils.equals(TAG, tag)) {
return TAG + "-" + tag;
}
return TAG;
}
public static class Settings {
int methodCount = 2;
boolean showThreadInfo = true;
/**
* Determines how logs will printed
*/
LogLevel logLevel = LogLevel.FULL;
public Settings hideThreadInfo() {
showThreadInfo = false;
return this;
}
public Settings setMethodCount(int methodCount) {
validateMethodCount(methodCount);
this.methodCount = methodCount;
return this;
}
public Settings setLogLevel(LogLevel logLevel) {
this.logLevel = logLevel;
return this;
}
}
}