681 lines
26 KiB
Java
681 lines
26 KiB
Java
package cc.winboll.studio.libappbase;
|
||
|
||
import android.content.Context;
|
||
import android.util.Log;
|
||
import android.widget.Toast;
|
||
|
||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||
import dalvik.system.DexFile;
|
||
|
||
import java.io.BufferedReader;
|
||
import java.io.BufferedWriter;
|
||
import java.io.File;
|
||
import java.io.FileInputStream;
|
||
import java.io.FileOutputStream;
|
||
import java.io.IOException;
|
||
import java.io.InputStreamReader;
|
||
import java.io.OutputStreamWriter;
|
||
import java.lang.reflect.Field;
|
||
import java.lang.reflect.Modifier;
|
||
import java.text.SimpleDateFormat;
|
||
import java.util.ArrayList;
|
||
import java.util.Enumeration;
|
||
import java.util.HashMap;
|
||
import java.util.Iterator;
|
||
import java.util.List;
|
||
import java.util.Locale;
|
||
import java.util.Map;
|
||
|
||
/**
|
||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||
* @CreateTime 2026-05-09 15:46:28
|
||
* @EditTime 2026-05-11 15:36:12
|
||
* @Describe 应用日志工具类
|
||
* 补全多级别日志重载、自动日志文件裁剪、应用内TAG自动扫描管理;
|
||
* 支持日志本地持久化、异常堆栈格式化、TAG开关配置、线程与集合打印工具;
|
||
* 完全兼容Java7语法,严格遵循变量final编码规范;
|
||
* 重要说明:本类内部调试打印必须使用 android.util.Log,禁止使用LogUtils自身方法,
|
||
* 避免递归嵌套调用、逻辑漩涡与无限循环调用问题。
|
||
*/
|
||
public class LogUtils {
|
||
|
||
// ====================== 常量与枚举 ======================
|
||
public static final String TAG = "LogUtils";
|
||
|
||
/**
|
||
* 日志级别枚举
|
||
*/
|
||
public static enum LOG_LEVEL {
|
||
Off,
|
||
Error,
|
||
Warn,
|
||
Info,
|
||
Debug,
|
||
Verbose
|
||
}
|
||
|
||
// ====================== 全局静态成员 ======================
|
||
private static volatile boolean _IsInited = false;
|
||
private static Context _mContext;
|
||
private static final SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("[yyyyMMdd_HHmmss_SSS]", Locale.getDefault());
|
||
private static File _mfLogCacheDir;
|
||
private static File _mfLogDataDir;
|
||
private static File _mfLogCatchFile;
|
||
private static File _mfLogUtilsBeanFile;
|
||
private static LogUtilsBean _mLogUtilsBean;
|
||
public static final Map<String, Boolean> mapTAGList = new HashMap<String, Boolean>();
|
||
|
||
// ====================== 初始化入口 ======================
|
||
public static void init(final Context context) {
|
||
init(context, LOG_LEVEL.Off);
|
||
}
|
||
|
||
public static void init(final Context context, final LOG_LEVEL logLevel) {
|
||
if (!GlobalApplication.isDebugging()) {
|
||
return;
|
||
}
|
||
Log.d(TAG, "init 执行日志工具初始化");
|
||
_mContext = context;
|
||
|
||
initLogUtilsDir();
|
||
|
||
initLogConfigBean();
|
||
addClassTAGList();
|
||
loadTAGBeanSettings();
|
||
checkAndTrimLogFileSize();
|
||
|
||
_IsInited = true;
|
||
Log.d(TAG, "init 执行日志工具初始化完成");
|
||
}
|
||
|
||
// ====================== 目录初始化 ======================
|
||
private static void initLogUtilsDir() {
|
||
final Context appContext = _mContext.getApplicationContext();
|
||
_mfLogCacheDir = new File(appContext.getCacheDir(), TAG);
|
||
if (!_mfLogCacheDir.exists()) {
|
||
_mfLogCacheDir.mkdirs();
|
||
}
|
||
_mfLogCatchFile = new File(_mfLogCacheDir, "log.txt");
|
||
|
||
_mfLogDataDir = appContext.getExternalFilesDir(TAG);
|
||
if (!_mfLogDataDir.exists()) {
|
||
_mfLogDataDir.mkdirs();
|
||
}
|
||
_mfLogUtilsBeanFile = new File(_mfLogDataDir, TAG + ".json");
|
||
}
|
||
|
||
private static void initLogConfigBean() {
|
||
_mLogUtilsBean = LogUtilsBean.loadBeanFromFile(_mfLogUtilsBeanFile.getPath(), LogUtilsBean.class);
|
||
if (_mLogUtilsBean == null) {
|
||
_mLogUtilsBean = new LogUtilsBean();
|
||
_mLogUtilsBean.saveBeanToFile(_mfLogUtilsBeanFile.getPath(), _mLogUtilsBean);
|
||
Log.d(TAG, "initLogConfigBean 自动创建默认日志配置文件");
|
||
}
|
||
}
|
||
|
||
// ====================== 日志文件裁剪 ======================
|
||
private static void checkAndTrimLogFileSize() {
|
||
if (_mfLogCatchFile == null || !_mfLogCatchFile.exists()) {
|
||
return;
|
||
}
|
||
|
||
final long KEEP_FILE_SIZE = 25000L; // ~25KB 确保剪贴板可完整复制
|
||
final long MAX_FILE_SIZE = 2 * KEEP_FILE_SIZE;
|
||
final long fileSize = _mfLogCatchFile.length();
|
||
|
||
if (fileSize <= MAX_FILE_SIZE) {
|
||
return;
|
||
}
|
||
|
||
final long needSkip = fileSize - KEEP_FILE_SIZE;
|
||
final String lineBreak = System.lineSeparator();
|
||
|
||
FileInputStream fis = null;
|
||
BufferedReader reader = null;
|
||
FileOutputStream fos = null;
|
||
BufferedWriter writer = null;
|
||
|
||
try {
|
||
fis = new FileInputStream(_mfLogCatchFile);
|
||
reader = new BufferedReader(new InputStreamReader(fis));
|
||
fos = new FileOutputStream(_mfLogCatchFile);
|
||
writer = new BufferedWriter(new OutputStreamWriter(fos));
|
||
|
||
final StringBuilder sb = new StringBuilder();
|
||
String line;
|
||
long skippedTotal = 0;
|
||
|
||
while ((line = reader.readLine()) != null) {
|
||
final byte[] lineBytes = line.getBytes();
|
||
skippedTotal += lineBytes.length + lineBreak.getBytes().length;
|
||
if (skippedTotal > needSkip) {
|
||
sb.append(line).append(lineBreak);
|
||
}
|
||
}
|
||
writer.write(sb.toString());
|
||
Log.d(TAG, "checkAndTrimLogFileSize 日志文件裁剪完成");
|
||
} catch (IOException e) {
|
||
Log.e(TAG, "checkAndTrimLogFileSize 日志文件裁剪失败", e);
|
||
} finally {
|
||
try {
|
||
if (reader != null) {
|
||
reader.close();
|
||
}
|
||
} catch (IOException e) {}
|
||
try {
|
||
if (fis != null) {
|
||
fis.close();
|
||
}
|
||
} catch (IOException e) {}
|
||
try {
|
||
if (writer != null) {
|
||
writer.close();
|
||
}
|
||
} catch (IOException e) {}
|
||
try {
|
||
if (fos != null) {
|
||
fos.close();
|
||
}
|
||
} catch (IOException e) {}
|
||
}
|
||
}
|
||
|
||
// ====================== TAG 配置管理 ======================
|
||
public static Map<String, Boolean> getMapTAGList() {
|
||
return mapTAGList;
|
||
}
|
||
|
||
private static void loadTAGBeanSettings() {
|
||
final ArrayList<LogUtilsClassTAGBean> list = new ArrayList<LogUtilsClassTAGBean>();
|
||
LogUtilsClassTAGBean.loadBeanList(_mContext, list, LogUtilsClassTAGBean.class);
|
||
|
||
for (final LogUtilsClassTAGBean beanSetting : list) {
|
||
final String tag = beanSetting.getTag();
|
||
if (mapTAGList.containsKey(tag)) {
|
||
mapTAGList.put(tag, beanSetting.getEnable());
|
||
}
|
||
}
|
||
}
|
||
|
||
private static void saveTAGBeanSettings() {
|
||
final ArrayList<LogUtilsClassTAGBean> list = new ArrayList<LogUtilsClassTAGBean>();
|
||
for (final Map.Entry<String, Boolean> entry : mapTAGList.entrySet()) {
|
||
list.add(new LogUtilsClassTAGBean(entry.getKey(), entry.getValue()));
|
||
}
|
||
LogUtilsClassTAGBean.saveBeanList(_mContext, list, LogUtilsClassTAGBean.class);
|
||
}
|
||
|
||
private static void addClassTAGList() {
|
||
try {
|
||
final String packageNamePrefix = "cc.winboll.studio";
|
||
final List<String> classNames = new ArrayList<String>();
|
||
final String apkPath = _mContext.getPackageCodePath();
|
||
|
||
final DexFile dexfile = new DexFile(apkPath);
|
||
Enumeration<String> entries = dexfile.entries();
|
||
|
||
while (entries.hasMoreElements()) {
|
||
final String className = entries.nextElement();
|
||
if (className.startsWith(packageNamePrefix)) {
|
||
classNames.add(className);
|
||
}
|
||
}
|
||
|
||
for (final String className : classNames) {
|
||
try {
|
||
final Class<?> clazz = Class.forName(className);
|
||
final Field[] fields = clazz.getDeclaredFields();
|
||
|
||
for (final Field field : fields) {
|
||
if (Modifier.isStatic(field.getModifiers())
|
||
&& Modifier.isPublic(field.getModifiers())
|
||
&& field.getType() == String.class
|
||
&& "TAG".equals(field.getName())) {
|
||
|
||
final String tagValue = (String) field.get(null);
|
||
mapTAGList.put(tagValue, false);
|
||
}
|
||
}
|
||
} catch (NoClassDefFoundError e) {
|
||
Log.w(TAG, "addClassTAGList 解析类TAG失败 NoClassDefFoundError");
|
||
} catch (ClassNotFoundException e) {
|
||
Log.w(TAG, "addClassTAGList 解析类TAG失败 ClassNotFoundException");
|
||
} catch (IllegalAccessException e) {
|
||
Log.w(TAG, "addClassTAGList 解析类TAG失败 IllegalAccessException");
|
||
}
|
||
}
|
||
} catch (IOException e) {
|
||
Log.e(TAG, "addClassTAGList 扫描Dex异常", e);
|
||
}
|
||
}
|
||
|
||
public static void setTAGListEnable(final String tag, final boolean isEnable) {
|
||
if (!_IsInited) {
|
||
return;
|
||
}
|
||
final Iterator<Map.Entry<String, Boolean>> iterator = mapTAGList.entrySet().iterator();
|
||
while (iterator.hasNext()) {
|
||
final Map.Entry<String, Boolean> entry = iterator.next();
|
||
if (tag.equals(entry.getKey())) {
|
||
entry.setValue(isEnable);
|
||
break;
|
||
}
|
||
}
|
||
saveTAGBeanSettings();
|
||
Log.d(TAG, "setTAGListEnable 更新TAG开关配置");
|
||
}
|
||
|
||
public static void setALlTAGListEnable(final boolean isEnable) {
|
||
if (!_IsInited) {
|
||
return;
|
||
}
|
||
for (final Map.Entry<String, Boolean> entry : mapTAGList.entrySet()) {
|
||
entry.setValue(isEnable);
|
||
}
|
||
saveTAGBeanSettings();
|
||
Log.d(TAG, "setALlTAGListEnable 全量设置TAG开关");
|
||
}
|
||
|
||
// ====================== 日志级别控制 ======================
|
||
public static void setLogLevel(final LOG_LEVEL logLevel) {
|
||
if (_mLogUtilsBean == null) {
|
||
Log.d(TAG, "setLogLevel LogUtils未初始化,忽略设置日志级别");
|
||
return;
|
||
}
|
||
_mLogUtilsBean.setLogLevel(logLevel);
|
||
_mLogUtilsBean.saveBeanToFile(_mfLogUtilsBeanFile.getPath(), _mLogUtilsBean);
|
||
}
|
||
|
||
public static LOG_LEVEL getLogLevel() {
|
||
return _mLogUtilsBean == null ?LOG_LEVEL.Off: _mLogUtilsBean.getLogLevel();
|
||
}
|
||
|
||
private static boolean isLoggable(final String tag, final LOG_LEVEL logLevel) {
|
||
if (!GlobalApplication.isDebugging()) {
|
||
return false;
|
||
}
|
||
if (!_IsInited) {
|
||
return false;
|
||
}
|
||
if (mapTAGList.get(tag) == null || !mapTAGList.get(tag)) {
|
||
return false;
|
||
}
|
||
return isInTheLevel(logLevel);
|
||
}
|
||
|
||
private static boolean isInTheLevel(final LOG_LEVEL logLevel) {
|
||
return _mLogUtilsBean.getLogLevel().ordinal() >= logLevel.ordinal();
|
||
}
|
||
|
||
// ====================== 基础对外方法 ======================
|
||
public static File getLogCacheDir() {
|
||
return _mfLogCacheDir;
|
||
}
|
||
|
||
public static boolean isInited() {
|
||
return _IsInited;
|
||
}
|
||
|
||
// ====================== Error 日志重载(补齐缺失方法) ======================
|
||
public static void e(final String szTAG, final String szMessage) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Error)) {
|
||
saveLog(szTAG, LOG_LEVEL.Error, szMessage);
|
||
}
|
||
}
|
||
|
||
public static void e(final String szTAG, final String szMessage, final Exception e) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Error)) {
|
||
final StringBuilder sb = new StringBuilder(szMessage);
|
||
sb.append("\n【异常信息】: ").append(getExceptionInfo(e));
|
||
saveLog(szTAG, LOG_LEVEL.Error, sb.toString());
|
||
}
|
||
}
|
||
|
||
public static void e(final String szTAG, final Exception e) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Error)) {
|
||
final String message = "【异常信息】: " + getExceptionInfo(e);
|
||
saveLog(szTAG, LOG_LEVEL.Error, message);
|
||
}
|
||
}
|
||
|
||
public static void e(final String szTAG, final String szMessage, final Exception e, final StackTraceElement[] listStackTrace) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Error)) {
|
||
final StringBuilder sb = new StringBuilder(szMessage);
|
||
sb.append("\n【异常信息】: ").append(getExceptionInfo(e));
|
||
sb.append("\n【堆栈信息】: ").append(getStackTraceInfo(listStackTrace));
|
||
saveLog(szTAG, LOG_LEVEL.Error, sb.toString());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 补齐你需要的:LogUtils.e(TAG, "uncaughtException: 崩溃处理异常", e)
|
||
*/
|
||
public static void e(final String szTAG, final String szMessage, final Throwable e) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Error)) {
|
||
final StringBuilder sb = new StringBuilder(szMessage);
|
||
sb.append("\n【异常信息】: ").append(getThrowableInfo(e));
|
||
saveLog(szTAG, LOG_LEVEL.Error, sb.toString());
|
||
}
|
||
}
|
||
|
||
// ====================== Warn 日志重载 ======================
|
||
public static void w(final String szTAG, final String szMessage) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Warn)) {
|
||
saveLog(szTAG, LOG_LEVEL.Warn, szMessage);
|
||
}
|
||
}
|
||
|
||
public static void w(final String szTAG, final String szMessage, final Exception e) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Warn)) {
|
||
final StringBuilder sb = new StringBuilder(szMessage);
|
||
sb.append("\n【异常信息】: ").append(getExceptionInfo(e));
|
||
saveLog(szTAG, LOG_LEVEL.Warn, sb.toString());
|
||
}
|
||
}
|
||
|
||
public static void w(final String szTAG, final Exception e) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Warn)) {
|
||
final String message = "【异常信息】: " + getExceptionInfo(e);
|
||
saveLog(szTAG, LOG_LEVEL.Warn, message);
|
||
}
|
||
}
|
||
|
||
// ====================== Info 日志重载 ======================
|
||
public static void i(final String szTAG, final String szMessage) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Info)) {
|
||
saveLog(szTAG, LOG_LEVEL.Info, szMessage);
|
||
}
|
||
}
|
||
|
||
public static void i(final String szTAG, final String szMessage, final Object obj) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Info)) {
|
||
final String objStr = obj == null ? "null" : obj.toString();
|
||
final StringBuilder sb = new StringBuilder(szMessage);
|
||
sb.append("\n【数据对象】: ").append(objStr);
|
||
saveLog(szTAG, LOG_LEVEL.Info, sb.toString());
|
||
}
|
||
}
|
||
|
||
// ====================== Debug 日志重载 ======================
|
||
public static void d(final String szTAG, final String szMessage) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Debug)) {
|
||
saveLog(szTAG, LOG_LEVEL.Debug, szMessage);
|
||
}
|
||
}
|
||
|
||
public static void d(final String szTAG, final String szMessage, final StackTraceElement[] listStackTrace) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Debug)) {
|
||
final StringBuilder sbMessage = new StringBuilder(szMessage);
|
||
sbMessage.append("\n【调用堆栈】: ").append(getStackTraceInfo(listStackTrace));
|
||
saveLog(szTAG, LOG_LEVEL.Debug, sbMessage.toString());
|
||
}
|
||
}
|
||
|
||
public static void d(final String szTAG, final String szMessage, final Exception e) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Debug)) {
|
||
final StringBuilder sb = new StringBuilder(szMessage);
|
||
sb.append("\n【异常信息】: ").append(getExceptionInfo(e));
|
||
saveLog(szTAG, LOG_LEVEL.Debug, sb.toString());
|
||
}
|
||
}
|
||
|
||
public static void d(final String szTAG, final Exception e, final StackTraceElement[] listStackTrace) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Debug)) {
|
||
final StringBuilder sbMessage = new StringBuilder();
|
||
sbMessage.append("【异常信息】: ").append(getExceptionInfo(e));
|
||
sbMessage.append("\n【调用堆栈】: ").append(getStackTraceInfo(listStackTrace));
|
||
saveLog(szTAG, LOG_LEVEL.Debug, sbMessage.toString());
|
||
}
|
||
}
|
||
|
||
public static void d(final String szTAG, final String szMessage, final Object obj) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Debug)) {
|
||
final String objStr = obj == null ? "null" : obj.toString();
|
||
final StringBuilder sb = new StringBuilder(szMessage);
|
||
sb.append("\n【数据对象】: ").append(objStr);
|
||
saveLog(szTAG, LOG_LEVEL.Debug, sb.toString());
|
||
}
|
||
}
|
||
|
||
public static void d(final String szTAG, final Object obj) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Debug)) {
|
||
final String objStr = obj == null ? "null" : obj.toString();
|
||
final String message = "【数据对象】: " + objStr;
|
||
saveLog(szTAG, LOG_LEVEL.Debug, message);
|
||
}
|
||
}
|
||
|
||
// ====================== Verbose 日志重载 ======================
|
||
public static void v(final String szTAG, final String szMessage) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Verbose)) {
|
||
saveLog(szTAG, LOG_LEVEL.Verbose, szMessage);
|
||
}
|
||
}
|
||
|
||
public static void v(final String szTAG, final String szMessage, final Object obj) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Verbose)) {
|
||
final String objStr = obj == null ? "null" : obj.toString();
|
||
final StringBuilder sb = new StringBuilder(szMessage);
|
||
sb.append("\n【数据对象】: ").append(objStr);
|
||
saveLog(szTAG, LOG_LEVEL.Verbose, sb.toString());
|
||
}
|
||
}
|
||
|
||
public static void v(final String szTAG, final Object obj) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Verbose)) {
|
||
final String objStr = obj == null ? "null" : obj.toString();
|
||
final String message = "【数据对象】: " + objStr;
|
||
saveLog(szTAG, LOG_LEVEL.Verbose, message);
|
||
}
|
||
}
|
||
|
||
// ====================== 扩展工具打印 ======================
|
||
public static void printThreadInfo(final String szTAG, final String szMessage) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Debug)) {
|
||
final Thread currentThread = Thread.currentThread();
|
||
final StringBuilder sb = new StringBuilder(szMessage);
|
||
sb.append("\n【线程信息】: ")
|
||
.append("线程名=").append(currentThread.getName())
|
||
.append(", 线程ID=").append(currentThread.getId())
|
||
.append(", 线程状态=").append(currentThread.getState().name());
|
||
saveLog(szTAG, LOG_LEVEL.Debug, sb.toString());
|
||
}
|
||
}
|
||
|
||
public static <K, V> void printMap(final String szTAG, final String szMessage, final Map<K, V> map) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Debug) && map != null) {
|
||
final StringBuilder sb = new StringBuilder(szMessage);
|
||
sb.append("\n【Map 数据】size=").append(map.size());
|
||
for (final Map.Entry<K, V> entry : map.entrySet()) {
|
||
final String keyStr = entry.getKey() == null ? "null" : entry.getKey().toString();
|
||
final String valueStr = entry.getValue() == null ? "null" : entry.getValue().toString();
|
||
sb.append("\n ").append(keyStr).append(" = ").append(valueStr);
|
||
}
|
||
saveLog(szTAG, LOG_LEVEL.Debug, sb.toString());
|
||
}
|
||
}
|
||
|
||
public static <T> void printList(final String szTAG, final String szMessage, final List<T> list) {
|
||
if (isLoggable(szTAG, LOG_LEVEL.Debug) && list != null) {
|
||
final StringBuilder sb = new StringBuilder(szMessage);
|
||
sb.append("\n【List 数据】size=").append(list.size());
|
||
for (int i = 0; i < list.size(); i++) {
|
||
final T item = list.get(i);
|
||
final String itemStr = item == null ? "null" : item.toString();
|
||
sb.append("\n 索引").append(i).append(" = ").append(itemStr);
|
||
}
|
||
saveLog(szTAG, LOG_LEVEL.Debug, sb.toString());
|
||
}
|
||
}
|
||
|
||
// ====================== 私有格式化工具 ======================
|
||
private static String getExceptionInfo(final Exception e) {
|
||
if (e == null) {
|
||
return "异常对象为null";
|
||
}
|
||
final StringBuilder sb = new StringBuilder();
|
||
sb.append(e.getClass().getSimpleName()).append(" : ")
|
||
.append(e.getMessage() == null ? "无异常消息" : e.getMessage());
|
||
|
||
final StackTraceElement[] stackTrace = e.getStackTrace();
|
||
if (stackTrace != null && stackTrace.length > 0) {
|
||
final int limit = Math.min(stackTrace.length, 5);
|
||
for (int i = 0; i < limit; i++) {
|
||
sb.append("\n ").append(stackTrace[i].toString());
|
||
}
|
||
}
|
||
return sb.toString();
|
||
}
|
||
|
||
private static String getThrowableInfo(final Throwable e) {
|
||
if (e == null) {
|
||
return "异常对象为null";
|
||
}
|
||
final StringBuilder sb = new StringBuilder();
|
||
sb.append(e.getClass().getSimpleName()).append(" : ")
|
||
.append(e.getMessage() == null ? "无异常消息" : e.getMessage());
|
||
|
||
final StackTraceElement[] stackTrace = e.getStackTrace();
|
||
if (stackTrace != null && stackTrace.length > 0) {
|
||
final int limit = Math.min(stackTrace.length, 5);
|
||
for (int i = 0; i < limit; i++) {
|
||
sb.append("\n ").append(stackTrace[i].toString());
|
||
}
|
||
}
|
||
return sb.toString();
|
||
}
|
||
|
||
private static String getStackTraceInfo(final StackTraceElement[] stackTrace) {
|
||
if (stackTrace == null || stackTrace.length == 0) {
|
||
return "堆栈信息为空";
|
||
}
|
||
final StringBuilder sb = new StringBuilder();
|
||
int count = 0;
|
||
for (final StackTraceElement element : stackTrace) {
|
||
if (element.getClassName().contains("cc.winboll.studio.libappbase.LogUtils")) {
|
||
continue;
|
||
}
|
||
sb.append("\n ").append(element.getClassName()).append(".")
|
||
.append(element.getMethodName()).append("(")
|
||
.append(element.getFileName()).append(":").append(element.getLineNumber()).append(")");
|
||
count++;
|
||
if (count >= 8) {
|
||
break;
|
||
}
|
||
}
|
||
return sb.toString();
|
||
}
|
||
|
||
// ====================== 日志持久化读写 ======================
|
||
private static void saveLog(final String szTAG, final LogUtils.LOG_LEVEL logLevel, final String szMessage) {
|
||
BufferedWriter out = null;
|
||
try {
|
||
if (!_mfLogCatchFile.exists()) {
|
||
final File parentDir = _mfLogCatchFile.getParentFile();
|
||
if (parentDir != null && !parentDir.exists()) {
|
||
parentDir.mkdirs();
|
||
}
|
||
_mfLogCatchFile.createNewFile();
|
||
}
|
||
out = new BufferedWriter(new OutputStreamWriter(
|
||
new FileOutputStream(_mfLogCatchFile, true), "UTF-8"));
|
||
final String logLine = "[" + logLevel + "] "
|
||
+ mSimpleDateFormat.format(System.currentTimeMillis())
|
||
+ " [" + szTAG + "]\n"
|
||
+ szMessage + "\n\n";
|
||
out.write(logLine);
|
||
out.flush();
|
||
} catch (IOException e) {
|
||
Log.e(TAG, "saveLog 日志写入失败", e);
|
||
} finally {
|
||
if (out != null) {
|
||
try {
|
||
out.close();
|
||
} catch (IOException e) {
|
||
Log.e(TAG, "saveLog 流关闭失败", e);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public static String loadLog() {
|
||
if (_mfLogCatchFile == null || !_mfLogCatchFile.exists()) {
|
||
return "日志文件不存在";
|
||
}
|
||
final StringBuffer sb = new StringBuffer();
|
||
BufferedReader in = null;
|
||
try {
|
||
in = new BufferedReader(new InputStreamReader(
|
||
new FileInputStream(_mfLogCatchFile), "UTF-8"));
|
||
String line;
|
||
while ((line = in.readLine()) != null) {
|
||
sb.append(line).append("\n");
|
||
}
|
||
} catch (IOException e) {
|
||
sb.append("日志加载失败");
|
||
Log.e(TAG, "loadLog 读取日志异常", e);
|
||
} finally {
|
||
if (in != null) {
|
||
try {
|
||
in.close();
|
||
} catch (IOException e) {
|
||
Log.e(TAG, "loadLog 读取流关闭异常", e);
|
||
}
|
||
}
|
||
}
|
||
return sb.toString();
|
||
}
|
||
|
||
public static void cleanLog() {
|
||
if (_mfLogCatchFile == null) {
|
||
Log.d(TAG, "cleanLog 日志文件未初始化");
|
||
return;
|
||
}
|
||
BufferedWriter out = null;
|
||
try {
|
||
out = new BufferedWriter(new OutputStreamWriter(
|
||
new FileOutputStream(_mfLogCatchFile, false), "UTF-8"));
|
||
out.write("");
|
||
out.flush();
|
||
Log.d(TAG, "cleanLog 日志已清空");
|
||
} catch (IOException e) {
|
||
Log.e(TAG, "cleanLog 清空日志失败", e);
|
||
} finally {
|
||
if (out != null) {
|
||
try {
|
||
out.close();
|
||
} catch (IOException e) {
|
||
Log.e(TAG, "cleanLog 流关闭异常", e);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public static boolean checkLogFileSize(final int maxSizeMB) {
|
||
if (_mfLogCatchFile == null || !_mfLogCatchFile.exists()) {
|
||
return false;
|
||
}
|
||
final long maxSizeByte = 1048576L * maxSizeMB;
|
||
final long fileSize = _mfLogCatchFile.length();
|
||
return fileSize > maxSizeByte;
|
||
}
|
||
|
||
public static void showShortToast(final Context context, final String message) {
|
||
if (context == null || message == null) {
|
||
return;
|
||
}
|
||
if (Thread.currentThread().getId() == android.os.Process.myTid()) {
|
||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
|
||
} else {
|
||
final Context uiContext = context;
|
||
((android.app.Activity) uiContext).runOnUiThread(new Runnable() {
|
||
@Override
|
||
public void run() {
|
||
Toast.makeText(uiContext, message, Toast.LENGTH_SHORT).show();
|
||
}
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|