package com.youth.xframe.utils.log;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.List;
import java.util.Map;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
final class LoggerPrinter implements Printer {
/**
* Android一个日志条目不能超过4076字节,
* 这里设置以4000字节来计算
*/
private static final int CHUNK_SIZE = 4000;
/**
* log settings
*/
private static final XLogConfig config = new XLogConfig();
/**
* 样式
*/
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;
private static final String BOTTOM_BORDER = BOTTOM_LEFT_CORNER + DOUBLE_DIVIDER;
private static final String MIDDLE_BORDER = MIDDLE_CORNER + SINGLE_DIVIDER;
public static String LINE_SEPARATOR = System.getProperty("line.separator");
private StringBuilder logStr=new StringBuilder();
/**
* 初始化
*/
@Override
public XLogConfig init() {
return config;
}
/**
* 返回最后一次格式化的打印结果样式
* @return
*/
@Override
public String getFormatLog() {
return logStr.toString();
}
@Override
public void d(String message, Object... args) {
log(Log.DEBUG, message, args);
}
@Override
public void e(String message, Object... args) {
e(null, message, args);
}
@Override
public void e(Throwable throwable, String message, Object... args) {
if (throwable != null && message != null) {
message += " : " + throwable.toString();
}
if (throwable != null && message == null) {
message = throwable.toString();
}
if (message == null) {
message = "message/exception 为空!";
}
log(Log.ERROR, message, args);
}
@Override
public void w(String message, Object... args) {
log(Log.WARN, message, args);
}
@Override
public void i(String message, Object... args) {
log(Log.INFO, message, args);
}
@Override
public void v(String message, Object... args) {
log(Log.VERBOSE, message, args);
}
@Override
public void wtf(String message, Object... args) {
log(Log.ASSERT, message, args);
}
/**
* 格式化json
*/
@Override
public void json(String json) {
if (TextUtils.isEmpty(json)) {
d("json 数据为空!");
return ;
}
try {
String message="";
if (json.startsWith("{")) {
JSONObject jo = new JSONObject(json);
message = jo.toString(4);
} else if (json.startsWith("[")) {
JSONArray ja = new JSONArray(json);
message = ja.toString(4);
}
d(message);
} catch (Exception e) {
e(e.getCause().getMessage() + LINE_SEPARATOR + json);
}
}
/**
* 格式化xml
*/
@Override
public void xml(String xml) {
if (TextUtils.isEmpty(xml)) {
d("xml 数据为空!");
return;
}
try {
Source xmlInput = new StreamSource(new StringReader(xml));
StreamResult xmlOutput = new StreamResult(new StringWriter());
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
transformer.transform(xmlInput, xmlOutput);
String message=xmlOutput.getWriter().toString().replaceFirst(">", ">" + LINE_SEPARATOR);
d(message);
} catch (TransformerException e) {
e(e.getCause().getMessage() + LINE_SEPARATOR + xml);
}
}
/**
* 格式化Map集合
*/
@Override
public void map(Map map) {
if (map != null) {
StringBuilder stringBuilder = new StringBuilder();
for (Object entry : map.entrySet()) {
stringBuilder.append("[key] → ");
stringBuilder.append(((Map.Entry) entry).getKey());
stringBuilder.append(",[value] → ");
stringBuilder.append(((Map.Entry) entry).getValue());
stringBuilder.append(LINE_SEPARATOR);
}
d(stringBuilder.toString());
}
}
/**
* 格式化List集合
*/
@Override
public void list(List list) {
if (list != null) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
stringBuilder.append("[" + i + "] → ");
stringBuilder.append(list.get(i));
stringBuilder.append(LINE_SEPARATOR);
}
d(stringBuilder.toString());
}
}
/**
* 同步日志打印顺序
*/
private synchronized void log(int priority, String msg, Object... args) {
if (!config.isDebug()) {
return;
}
logStr.delete(0,logStr.length());
String message = args.length == 0 ? msg : String.format(msg, args);
logChunk(priority, TOP_BORDER);
if (config.isShowThreadInfo()) {
//打印线程
getStackInfo(priority);
}
//得到系统的默认字符集的信息字节(UTF-8)
byte[] bytes = message.getBytes();
int length = bytes.length;
if (length <= CHUNK_SIZE) {
logContent(priority, message);
logChunk(priority, BOTTOM_BORDER);
return;
}
for (int i = 0; i < length; i += CHUNK_SIZE) {
int count = Math.min(length - i, CHUNK_SIZE);
//创建系统的默认字符集的一个新的字符串(UTF-8)
logContent(priority, new String(bytes, i, count));
}
logChunk(priority, BOTTOM_BORDER);
}
private void logContent(int priority, String chunk) {
String[] lines = chunk.split(LINE_SEPARATOR);
for (String line : lines) {
logChunk(priority, HORIZONTAL_DOUBLE_LINE + " " + line);
}
}
private void logChunk(int priority, String chunk) {
logStr.append(LINE_SEPARATOR);
logStr.append(chunk);
String TAG = config.getTag();
switch (priority) {
case Log.ERROR:
Log.e(TAG, chunk);
break;
case Log.INFO:
Log.i(TAG, chunk);
break;
case Log.VERBOSE:
Log.v(TAG, chunk);
break;
case Log.WARN:
Log.w(TAG, chunk);
break;
case Log.ASSERT:
Log.wtf(TAG, chunk);
break;
case Log.DEBUG:
default:
Log.d(TAG, chunk);
break;
}
}
/**
* 打印堆栈信息.
*/
private void getStackInfo(int priority) {
logChunk(priority, HORIZONTAL_DOUBLE_LINE + "[Thread] → " + Thread.currentThread().getName());
logChunk(priority, MIDDLE_BORDER);
String str="";
StackTraceElement[] traces = Thread.currentThread().getStackTrace();
for (int i = 0; i < traces.length; i++) {
StackTraceElement element = traces[i];
StringBuilder perTrace = new StringBuilder(str);
if (element.isNativeMethod()) {
continue;
}
String className=element.getClassName();
if (className.startsWith("android.")
||className.contains("com.android")
||className.contains("java.lang")
||className.contains("com.youth.xframe")) {
continue;
}
perTrace.append(element.getClassName())
.append('.')
.append(element.getMethodName())
.append(" (")
.append(element.getFileName())
.append(':')
.append(element.getLineNumber())
.append(")");
str+=" ";
logContent(priority, perTrace.toString());
}
logChunk(priority, MIDDLE_BORDER);
}
}