import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.ConcurrentModificationException;
import java.util.Locale;
public class XLogUtil {
private static final String TAG = "XLogUtil";
public static final String APP_VERSION = "";
private static final String LOGPRE_SPLIT = "|";
private static final String LOGPRE_APPVER = APP_VERSION + LOGPRE_SPLIT;
private static final String LOGPRE_TIME_FORMAT = "yyyyMMdd-HH:mm:ss:SSS" + LOGPRE_SPLIT;
/* 每次打印字符串的最大长度 */
private static final int LOGCAT_MESSAGE_MAX_LENTH = 4000;
/* 限制每个log文件最大3M */
private static final int MAX_LOGFILE_LENGTH = 1 * 1024 * 1024;
/* 限制log文件个数 */
private static final int MAX_LOGFILE_NUMBER = 5;
public static final String APP_NAME = "ahisense";
private static String LOGFILE_PATH = null;
private static final String FILECONTENT_ENCODE = "UTF-8";
/* logfile缓存大小 128k */
private static final long LOGFILE_BUFFER_MAX = 128 * 1024;
/* 6sec */
private static final long LOGFILE_DELAY = 6 * 1000;
/* 5mins */
private static final long LOGFILE_THREAD_EXIT_DELAY = 5 * 60 * 1000;
/* 处理消息 */
private static final int MSG_SAVE_LOG_FILE = 1;
private static final int MSG_LOG_FILE_THREAD_EXIT = 2;
/* 内存泄露检测开关 */
public static final boolean DEBUG_MEMORYWATCHER = true;
/* STRICT_MODE开关 */
public static final boolean DEBUG_STRICT_MODE = false;
private static StringBuilder logFileBuffer;
private static Handler logFileHandler;
private LogUtil() {
}
/**
* 打印debug级别的log
*
* @param tag
* @param msg
*/
public static void d(String tag, Object... msg) {
String text = buildMessage(msg, LOGPRE_APPVER);
if (text != null) {
int len = text.length();
if (len <= LOGCAT_MESSAGE_MAX_LENTH) {
Log.d(tag, text);
} else {
do {
Log.d(tag, text.substring(0, LOGCAT_MESSAGE_MAX_LENTH));
text = text.substring(LOGCAT_MESSAGE_MAX_LENTH);
len = text.length();
} while (len > LOGCAT_MESSAGE_MAX_LENTH);
if (len > 0) {
Log.d(tag, text);
}
}
}
f(tag, msg);
}
/**
* 打印debug级别的log
*
* @param tag
* @param msg
*/
public static void d(boolean isSaveLog, String tag, Object... msg) {
String text = buildMessage(msg, LOGPRE_APPVER);
if (text != null) {
int len = text.length();
if (len <= LOGCAT_MESSAGE_MAX_LENTH) {
Log.d(tag, text);
} else {
do {
Log.d(tag, text.substring(0, LOGCAT_MESSAGE_MAX_LENTH));
text = text.substring(LOGCAT_MESSAGE_MAX_LENTH);
len = text.length();
} while (len > LOGCAT_MESSAGE_MAX_LENTH);
if (len > 0) {
Log.d(tag, text);
}
}
}
if (isSaveLog) {
f(tag, msg);
}
}
/**
* 打印warn级别的log
*
* @param tag
* @param msg
*/
public static void w(String tag, Object... msg) {
String text = buildMessage(msg, LOGPRE_APPVER);
if (text != null) {
int len = text.length();
if (len <= LOGCAT_MESSAGE_MAX_LENTH) {
Log.w(tag, text);
} else {
do {
Log.w(tag, text.substring(0, LOGCAT_MESSAGE_MAX_LENTH));
text = text.substring(LOGCAT_MESSAGE_MAX_LENTH);
len = text.length();
} while (len > LOGCAT_MESSAGE_MAX_LENTH);
if (len > 0) {
Log.w(tag, text);
}
}
}
}
/**
* 打印error级别的log
*
* @param tag
* @param msg
*/
public static void e(String tag, Object... msg) {
String text = buildMessage(msg, LOGPRE_APPVER);
if (text != null) {
int len = text.length();
if (len <= LOGCAT_MESSAGE_MAX_LENTH) {
Log.e(tag, text);
} else {
do {
Log.e(tag, text.substring(0, LOGCAT_MESSAGE_MAX_LENTH));
text = text.substring(LOGCAT_MESSAGE_MAX_LENTH);
len = text.length();
} while (len > LOGCAT_MESSAGE_MAX_LENTH);
if (len > 0) {
Log.e(tag, text);
}
}
}
f(tag, msg);
}
/**
* 打印error级别的log
*
* @param tag
* @param msg
*/
public static void e(boolean isSaveLog, String tag, Object... msg) {
String text = buildMessage(msg, LOGPRE_APPVER);
if (text != null) {
int len = text.length();
if (len <= LOGCAT_MESSAGE_MAX_LENTH) {
Log.e(tag, text);
} else {
do {
Log.e(tag, text.substring(0, LOGCAT_MESSAGE_MAX_LENTH));
text = text.substring(LOGCAT_MESSAGE_MAX_LENTH);
len = text.length();
} while (len > LOGCAT_MESSAGE_MAX_LENTH);
if (len > 0) {
Log.e(tag, text);
}
}
}
if (isSaveLog) {
f(tag, msg);
}
}
/**
* 打印敏感信息,用户信息级别的log
*
* @param tag 标签
* @param msg 日志内容
*/
public static void s(boolean isSaveLog, String tag, Object... msg) {
String text = buildMessage(msg, LOGPRE_APPVER);
if (text != null) {
int len = text.length();
if (len <= LOGCAT_MESSAGE_MAX_LENTH) {
Log.d(tag, text);
} else {
do {
Log.d(tag, text.substring(0, LOGCAT_MESSAGE_MAX_LENTH));
text = text.substring(LOGCAT_MESSAGE_MAX_LENTH);
len = text.length();
} while (len > LOGCAT_MESSAGE_MAX_LENTH);
if (len > 0) {
Log.d(tag, text);
}
}
}
if (isSaveLog) {
f(tag, msg);
}
}
/**
* 保存log到文件
*
* @param tag
* @param msg
*/
public static void f(String tag, Object... msg) {
String text = buildMessage(msg, getTimePrefix(), tag + LOGPRE_SPLIT, LOGPRE_APPVER);
if (text != null) {
putLogFileBuffer(text);
}
}
private static String getTimePrefix() {
SimpleDateFormat df = new SimpleDateFormat(LOGPRE_TIME_FORMAT, Locale.ENGLISH);
return df.format(System.currentTimeMillis());
}
private static String buildMessage(Object[] msg, String... prefix) {
if (msg == null || msg.length == 0) {
return null;
}
StringBuilder sb = new StringBuilder();
try {
if (prefix != null) {
for (String p : prefix) {
sb.append(p);
}
}
for (Object m : msg) {
sb.append(m != null ? m : "");
}
} catch (ConcurrentModificationException e) {
Log.e(TAG, e.getMessage());
return "";
}
return sb.toString();
}
/**
* 向文件中写文本内容
*
* @param file
* @param content
* @param append
*/
public static void writeFile(File file, String content, boolean append) {
if (null == file || null == content) {
return;
}
byte[] bytes = null;
try {
bytes = content.getBytes(FILECONTENT_ENCODE);
} catch (UnsupportedEncodingException e) {
w(TAG, "write file failed: ", e.getMessage());
}
if (bytes != null) {
writeFile(file, bytes, append);
}
}
/**
* 向文件中写二进制内容
*
* @param file
* @param content
* @param append
*/
public static void writeFile(File file, byte[] content, boolean append) {
FileOutputStream out = null;
try {
out = new FileOutputStream(file, append);
out.write(content);
out.flush();
} catch (IOException e) {
w(TAG, "write file failed: ", e.getMessage());
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
}
}
}
}
private static File getLogFile() {
if (LOGFILE_PATH == null) {
try {
String sdcard = Environment.getExternalStorageDirectory().getCanonicalPath();
LOGFILE_PATH = sdcard + "/" + APP_NAME + "/";
} catch (IOException e) {
Log.e(TAG, "getExternalStorageDirectory failed!");
}
}
File path = new File(LOGFILE_PATH);
if (!path.exists()) {
if (!path.mkdirs()) {
Log.e(TAG, "create log directory failed!");
return null;
}
}
File file = new File(LOGFILE_PATH + "log.0");
if (file.exists() && file.length() > MAX_LOGFILE_LENGTH) {
File tmp = new File(LOGFILE_PATH + "log." + (MAX_LOGFILE_NUMBER - 1));
if (tmp.exists()) {
if (!tmp.delete()) {
Log.e(TAG, "delete log file failed");
return null;
}
}
for (int i = MAX_LOGFILE_NUMBER - 2; i >= 0; i--) {
tmp = new File(LOGFILE_PATH + "log." + i);
if (tmp.exists()) {
if (!tmp.renameTo(new File(LOGFILE_PATH + "log." + (i + 1)))) {
Log.e(TAG, "rename log file failed");
return null;
}
}
}
}
return file;
}
private static void putLogFileBuffer(String text) {
synchronized (LogUtil.class) {
if (logFileBuffer == null) {
logFileBuffer = new StringBuilder(text);
} else {
logFileBuffer.append(text);
}
logFileBuffer.append("\n");
boolean saveNow = logFileBuffer.length() >= LOGFILE_BUFFER_MAX;
// schedule log file buffer saving
if (logFileHandler == null) {
HandlerThread thread = new HandlerThread("logfile_thread");
thread.start();
logFileHandler = new LogFileHandler(thread.getLooper());
logFileHandler.sendEmptyMessageDelayed(MSG_SAVE_LOG_FILE, saveNow ? 0 : LOGFILE_DELAY);
} else {
logFileHandler.removeMessages(MSG_LOG_FILE_THREAD_EXIT);
if (saveNow) {
logFileHandler.removeMessages(MSG_SAVE_LOG_FILE);
logFileHandler.sendEmptyMessage(MSG_SAVE_LOG_FILE);
} else if (!logFileHandler.hasMessages(MSG_SAVE_LOG_FILE)) {
logFileHandler.sendEmptyMessageDelayed(MSG_SAVE_LOG_FILE, LOGFILE_DELAY);
}
}
}
}
private static void flushLogFileBufferLocked() {
synchronized (LogUtil.class) {
if (logFileBuffer == null) {
return;
}
String text = logFileBuffer.toString();
logFileBuffer = null;
File file = getLogFile();
if (file != null) {
writeFile(file, text, true);
} else {
w(TAG, "get log file failed.");
}
}
}
private static class LogFileHandler extends Handler {
public LogFileHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
if (null == msg) {
return;
}
switch (msg.what) {
case MSG_SAVE_LOG_FILE:
synchronized (LogUtil.class) {
logFileHandler.removeMessages(MSG_SAVE_LOG_FILE);
flushLogFileBufferLocked();
logFileHandler.sendEmptyMessageDelayed(MSG_LOG_FILE_THREAD_EXIT, LOGFILE_THREAD_EXIT_DELAY);
}
break;
case MSG_LOG_FILE_THREAD_EXIT:
synchronized (LogUtil.class) {
if (!logFileHandler.hasMessages(MSG_SAVE_LOG_FILE)) {
logFileHandler.getLooper().quit();
logFileHandler = null;
}
}
break;
default:
break;
}
}
}
}