补全日志调试函数
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Fri Nov 28 18:28:36 GMT 2025
|
#Fri Nov 28 18:37:12 GMT 2025
|
||||||
stageCount=2
|
stageCount=2
|
||||||
libraryProject=libappbase
|
libraryProject=libappbase
|
||||||
baseVersion=15.11
|
baseVersion=15.11
|
||||||
publishVersion=15.11.1
|
publishVersion=15.11.1
|
||||||
buildCount=25
|
buildCount=28
|
||||||
baseBetaVersion=15.11.2
|
baseBetaVersion=15.11.2
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Fri Nov 28 18:28:36 GMT 2025
|
#Fri Nov 28 18:37:12 GMT 2025
|
||||||
stageCount=2
|
stageCount=2
|
||||||
libraryProject=libappbase
|
libraryProject=libappbase
|
||||||
baseVersion=15.11
|
baseVersion=15.11
|
||||||
publishVersion=15.11.1
|
publishVersion=15.11.1
|
||||||
buildCount=25
|
buildCount=28
|
||||||
baseBetaVersion=15.11.2
|
baseBetaVersion=15.11.2
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ package cc.winboll.studio.libappbase;
|
|||||||
* @Author ZhanGSKen@QQ.COM
|
* @Author ZhanGSKen@QQ.COM
|
||||||
* @Date 2024/08/12 13:44:06
|
* @Date 2024/08/12 13:44:06
|
||||||
* @Describe LogUtils
|
* @Describe LogUtils
|
||||||
* @Describe 应用日志类
|
* @Describe 应用日志类(补全所有日志重载方法,适配不同调试场景)
|
||||||
*/
|
*/
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.widget.Toast;
|
||||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||||
import dalvik.system.DexFile;
|
import dalvik.system.DexFile;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
@@ -61,7 +62,7 @@ public class LogUtils {
|
|||||||
//
|
//
|
||||||
public static void init(Context context, LOG_LEVEL logLevel) {
|
public static void init(Context context, LOG_LEVEL logLevel) {
|
||||||
if (GlobalApplication.isDebugging()) {
|
if (GlobalApplication.isDebugging()) {
|
||||||
// 初始化日志缓存文件路径
|
// 初始化日志缓存文件路径(debug模式:外部存储)
|
||||||
_mfLogCacheDir = new File(context.getApplicationContext().getExternalCacheDir(), TAG);
|
_mfLogCacheDir = new File(context.getApplicationContext().getExternalCacheDir(), TAG);
|
||||||
if (!_mfLogCacheDir.exists()) {
|
if (!_mfLogCacheDir.exists()) {
|
||||||
_mfLogCacheDir.mkdirs();
|
_mfLogCacheDir.mkdirs();
|
||||||
@@ -75,7 +76,7 @@ public class LogUtils {
|
|||||||
}
|
}
|
||||||
_mfLogUtilsBeanFile = new File(_mfLogDataDir, TAG + ".json");
|
_mfLogUtilsBeanFile = new File(_mfLogDataDir, TAG + ".json");
|
||||||
} else {
|
} else {
|
||||||
// 初始化日志缓存文件路径
|
// 初始化日志缓存文件路径(release模式:内部存储)
|
||||||
_mfLogCacheDir = new File(context.getApplicationContext().getCacheDir(), TAG);
|
_mfLogCacheDir = new File(context.getApplicationContext().getCacheDir(), TAG);
|
||||||
if (!_mfLogCacheDir.exists()) {
|
if (!_mfLogCacheDir.exists()) {
|
||||||
_mfLogCacheDir.mkdirs();
|
_mfLogCacheDir.mkdirs();
|
||||||
@@ -90,11 +91,6 @@ public class LogUtils {
|
|||||||
_mfLogUtilsBeanFile = new File(_mfLogDataDir, TAG + ".json");
|
_mfLogUtilsBeanFile = new File(_mfLogDataDir, TAG + ".json");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toast.makeText(context,
|
|
||||||
// "_mfLogUtilsBeanFile : " + _mfLogUtilsBeanFile
|
|
||||||
// + "\n_mfLogCatchFile : " + _mfLogCatchFile,
|
|
||||||
// Toast.LENGTH_SHORT).show();
|
|
||||||
//
|
|
||||||
_mLogUtilsBean = LogUtilsBean.loadBeanFromFile(_mfLogUtilsBeanFile.getPath(), LogUtilsBean.class);
|
_mLogUtilsBean = LogUtilsBean.loadBeanFromFile(_mfLogUtilsBeanFile.getPath(), LogUtilsBean.class);
|
||||||
if (_mLogUtilsBean == null) {
|
if (_mLogUtilsBean == null) {
|
||||||
_mLogUtilsBean = new LogUtilsBean();
|
_mLogUtilsBean = new LogUtilsBean();
|
||||||
@@ -122,7 +118,6 @@ public class LogUtils {
|
|||||||
entry.setValue(beanSetting.getEnable());
|
entry.setValue(beanSetting.getEnable());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,19 +130,14 @@ public class LogUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void addClassTAGList() {
|
static void addClassTAGList() {
|
||||||
//ClassLoader classLoader = getClass().getClassLoader();
|
|
||||||
try {
|
try {
|
||||||
//String packageName = context.getPackageName();
|
// 包名前缀(过滤当前应用的类)
|
||||||
String packageNamePrefix = "cc.winboll.studio";
|
String packageNamePrefix = "cc.winboll.studio";
|
||||||
List<String> classNames = new ArrayList<>();
|
List<String> classNames = new ArrayList<>();
|
||||||
String apkPath = _mContext.getPackageCodePath();
|
String apkPath = _mContext.getPackageCodePath();
|
||||||
//Log.d("APK_PATH", "The APK path is: " + apkPath);
|
|
||||||
LogUtils.d(TAG, String.format("apkPath : %s", apkPath));
|
LogUtils.d(TAG, String.format("apkPath : %s", apkPath));
|
||||||
//String apkPath = "/data/app/" + packageName + "-";
|
|
||||||
|
|
||||||
//DexFile dexfile = new DexFile(apkPath + "1/base.apk");
|
|
||||||
DexFile dexfile = new DexFile(apkPath);
|
DexFile dexfile = new DexFile(apkPath);
|
||||||
|
|
||||||
int countTemp = 0;
|
int countTemp = 0;
|
||||||
Enumeration<String> entries = dexfile.entries();
|
Enumeration<String> entries = dexfile.entries();
|
||||||
while (entries.hasMoreElements()) {
|
while (entries.hasMoreElements()) {
|
||||||
@@ -165,23 +155,21 @@ public class LogUtils {
|
|||||||
Class<?> clazz = Class.forName(className);
|
Class<?> clazz = Class.forName(className);
|
||||||
Field[] fields = clazz.getDeclaredFields();
|
Field[] fields = clazz.getDeclaredFields();
|
||||||
for (Field field : fields) {
|
for (Field field : fields) {
|
||||||
if (Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers()) && field.getType() == String.class && "TAG".equals(field.getName())) {
|
// 过滤静态、公共、String类型的 TAG 字段
|
||||||
|
if (Modifier.isStatic(field.getModifiers())
|
||||||
|
&& Modifier.isPublic(field.getModifiers())
|
||||||
|
&& field.getType() == String.class
|
||||||
|
&& "TAG".equals(field.getName())) {
|
||||||
String tagValue = (String) field.get(null);
|
String tagValue = (String) field.get(null);
|
||||||
//Log.d("TAG_INFO", "Class: " + className + ", TAG value: " + tagValue);
|
mapTAGList.put(tagValue, false); // 默认禁用,可通过设置开启
|
||||||
//LogUtils.d(TAG, String.format("Tag Value : %s", tagValue));
|
|
||||||
//mapTAGList.put(tagValue, true);
|
|
||||||
mapTAGList.put(tagValue, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (NoClassDefFoundError | ClassNotFoundException | IllegalAccessException e) {
|
} catch (NoClassDefFoundError | ClassNotFoundException | IllegalAccessException e) {
|
||||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||||
//LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
|
||||||
//Toast.makeText(context, TAG + " : " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||||
//Toast.makeText(context, TAG + " : " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +179,6 @@ public class LogUtils {
|
|||||||
Map.Entry<String, Boolean> entry = iterator.next();
|
Map.Entry<String, Boolean> entry = iterator.next();
|
||||||
if (tag.equals(entry.getKey())) {
|
if (tag.equals(entry.getKey())) {
|
||||||
entry.setValue(isEnable);
|
entry.setValue(isEnable);
|
||||||
//System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,7 +191,6 @@ public class LogUtils {
|
|||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
Map.Entry<String, Boolean> entry = iterator.next();
|
Map.Entry<String, Boolean> entry = iterator.next();
|
||||||
entry.setValue(isEnable);
|
entry.setValue(isEnable);
|
||||||
//System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
|
|
||||||
}
|
}
|
||||||
saveTAGBeanSettings();
|
saveTAGBeanSettings();
|
||||||
LogUtils.d(TAG, String.format("mapTAGList : %s", mapTAGList.toString()));
|
LogUtils.d(TAG, String.format("mapTAGList : %s", mapTAGList.toString()));
|
||||||
@@ -223,10 +209,11 @@ public class LogUtils {
|
|||||||
if (!_IsInited) {
|
if (!_IsInited) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (mapTAGList.get(tag) == null
|
// TAG 未配置或禁用时,不打印日志
|
||||||
|| !mapTAGList.get(tag)) {
|
if (mapTAGList.get(tag) == null || !mapTAGList.get(tag)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// 日志级别不符合时,不打印日志
|
||||||
if (!isInTheLevel(logLevel)) {
|
if (!isInTheLevel(logLevel)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -234,8 +221,8 @@ public class LogUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static boolean isInTheLevel(LOG_LEVEL logLevel) {
|
static boolean isInTheLevel(LOG_LEVEL logLevel) {
|
||||||
return (LogUtils._mLogUtilsBean.getLogLevel().ordinal() == logLevel.ordinal()
|
// 当前日志级别 >= 目标级别时,允许打印(级别顺序:Off < Error < Warn < Info < Debug < Verbose)
|
||||||
|| LogUtils._mLogUtilsBean.getLogLevel().ordinal() > logLevel.ordinal());
|
return LogUtils._mLogUtilsBean.getLogLevel().ordinal() >= logLevel.ordinal();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -245,136 +232,501 @@ public class LogUtils {
|
|||||||
return _mfLogCacheDir;
|
return _mfLogCacheDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// ================================= 补全所有日志重载方法(Error 级别) =================================
|
||||||
// 调试日志写入函数
|
/**
|
||||||
//
|
* Error 级别日志(仅消息)
|
||||||
|
* @param szTAG 标签
|
||||||
|
* @param szMessage 日志消息
|
||||||
|
*/
|
||||||
public static void e(String szTAG, String szMessage) {
|
public static void e(String szTAG, String szMessage) {
|
||||||
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Error)) {
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Error)) {
|
||||||
saveLog(szTAG, LogUtils.LOG_LEVEL.Error, szMessage);
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Error, szMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
/**
|
||||||
// 调试日志写入函数
|
* Error 级别日志(消息 + 异常)
|
||||||
//
|
* @param szTAG 标签
|
||||||
|
* @param szMessage 日志消息
|
||||||
|
* @param e 异常对象
|
||||||
|
*/
|
||||||
|
public static void e(String szTAG, String szMessage, Exception e) {
|
||||||
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Error)) {
|
||||||
|
StringBuilder sb = new StringBuilder(szMessage);
|
||||||
|
sb.append("\n【异常信息】: ").append(getExceptionInfo(e));
|
||||||
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Error, sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error 级别日志(仅异常)
|
||||||
|
* @param szTAG 标签
|
||||||
|
* @param e 异常对象
|
||||||
|
*/
|
||||||
|
public static void e(String szTAG, Exception e) {
|
||||||
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Error)) {
|
||||||
|
String message = "【异常信息】: " + getExceptionInfo(e);
|
||||||
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Error, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error 级别日志(消息 + 异常 + 堆栈)
|
||||||
|
* @param szTAG 标签
|
||||||
|
* @param szMessage 日志消息
|
||||||
|
* @param e 异常对象
|
||||||
|
* @param listStackTrace 堆栈信息
|
||||||
|
*/
|
||||||
|
public static void e(String szTAG, String szMessage, Exception e, StackTraceElement[] listStackTrace) {
|
||||||
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Error)) {
|
||||||
|
StringBuilder sb = new StringBuilder(szMessage);
|
||||||
|
sb.append("\n【异常信息】: ").append(getExceptionInfo(e));
|
||||||
|
sb.append("\n【堆栈信息】: ").append(getStackTraceInfo(listStackTrace));
|
||||||
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Error, sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================= 补全所有日志重载方法(Warn 级别) =================================
|
||||||
|
/**
|
||||||
|
* Warn 级别日志(仅消息)
|
||||||
|
* @param szTAG 标签
|
||||||
|
* @param szMessage 日志消息
|
||||||
|
*/
|
||||||
public static void w(String szTAG, String szMessage) {
|
public static void w(String szTAG, String szMessage) {
|
||||||
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Warn)) {
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Warn)) {
|
||||||
saveLog(szTAG, LogUtils.LOG_LEVEL.Warn, szMessage);
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Warn, szMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
/**
|
||||||
// 调试日志写入函数
|
* Warn 级别日志(消息 + 异常)
|
||||||
//
|
* @param szTAG 标签
|
||||||
|
* @param szMessage 日志消息
|
||||||
|
* @param e 异常对象
|
||||||
|
*/
|
||||||
|
public static void w(String szTAG, String szMessage, Exception e) {
|
||||||
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Warn)) {
|
||||||
|
StringBuilder sb = new StringBuilder(szMessage);
|
||||||
|
sb.append("\n【异常信息】: ").append(getExceptionInfo(e));
|
||||||
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Warn, sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warn 级别日志(仅异常)
|
||||||
|
* @param szTAG 标签
|
||||||
|
* @param e 异常对象
|
||||||
|
*/
|
||||||
|
public static void w(String szTAG, Exception e) {
|
||||||
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Warn)) {
|
||||||
|
String message = "【异常信息】: " + getExceptionInfo(e);
|
||||||
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Warn, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================= 补全所有日志重载方法(Info 级别) =================================
|
||||||
|
/**
|
||||||
|
* Info 级别日志(仅消息)
|
||||||
|
* @param szTAG 标签
|
||||||
|
* @param szMessage 日志消息
|
||||||
|
*/
|
||||||
public static void i(String szTAG, String szMessage) {
|
public static void i(String szTAG, String szMessage) {
|
||||||
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Info)) {
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Info)) {
|
||||||
saveLog(szTAG, LogUtils.LOG_LEVEL.Info, szMessage);
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Info, szMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
/**
|
||||||
// 调试日志写入函数
|
* Info 级别日志(消息 + 数据对象)
|
||||||
//
|
* @param szTAG 标签
|
||||||
|
* @param szMessage 日志消息
|
||||||
|
* @param obj 数据对象(自动转为字符串)
|
||||||
|
*/
|
||||||
|
public static void i(String szTAG, String szMessage, Object obj) {
|
||||||
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Info)) {
|
||||||
|
String objStr = obj == null ? "null" : obj.toString();
|
||||||
|
StringBuilder sb = new StringBuilder(szMessage);
|
||||||
|
sb.append("\n【数据对象】: ").append(objStr);
|
||||||
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Info, sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================= 补全所有日志重载方法(Debug 级别) =================================
|
||||||
|
/**
|
||||||
|
* Debug 级别日志(仅消息)
|
||||||
|
* @param szTAG 标签
|
||||||
|
* @param szMessage 日志消息
|
||||||
|
*/
|
||||||
public static void d(String szTAG, String szMessage) {
|
public static void d(String szTAG, String szMessage) {
|
||||||
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) {
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) {
|
||||||
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, szMessage);
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, szMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
/**
|
||||||
// 调试日志写入函数
|
* Debug 级别日志(消息 + 堆栈)
|
||||||
// 包含线程调试堆栈信息
|
* @param szTAG 标签
|
||||||
//
|
* @param szMessage 日志消息
|
||||||
|
* @param listStackTrace 堆栈信息
|
||||||
|
*/
|
||||||
public static void d(String szTAG, String szMessage, StackTraceElement[] listStackTrace) {
|
public static void d(String szTAG, String szMessage, StackTraceElement[] listStackTrace) {
|
||||||
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) {
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) {
|
||||||
StringBuilder sbMessage = new StringBuilder(szMessage);
|
StringBuilder sbMessage = new StringBuilder(szMessage);
|
||||||
sbMessage.append(" \nAt ");
|
sbMessage.append("\n【调用堆栈】: ").append(getStackTraceInfo(listStackTrace));
|
||||||
sbMessage.append(listStackTrace[2].getMethodName());
|
|
||||||
sbMessage.append(" (");
|
|
||||||
sbMessage.append(listStackTrace[2].getFileName());
|
|
||||||
sbMessage.append(":");
|
|
||||||
sbMessage.append(listStackTrace[2].getLineNumber());
|
|
||||||
sbMessage.append(")");
|
|
||||||
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, sbMessage.toString());
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, sbMessage.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
/**
|
||||||
// 调试日志写入函数
|
* Debug 级别日志(消息 + 异常)
|
||||||
// 包含异常信息和线程调试堆栈信息
|
* @param szTAG 标签
|
||||||
//
|
* @param szMessage 日志消息
|
||||||
|
* @param e 异常对象
|
||||||
|
*/
|
||||||
|
public static void d(String szTAG, String szMessage, Exception e) {
|
||||||
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) {
|
||||||
|
StringBuilder sb = new StringBuilder(szMessage);
|
||||||
|
sb.append("\n【异常信息】: ").append(getExceptionInfo(e));
|
||||||
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug 级别日志(异常 + 堆栈)
|
||||||
|
* @param szTAG 标签
|
||||||
|
* @param e 异常对象
|
||||||
|
* @param listStackTrace 堆栈信息
|
||||||
|
*/
|
||||||
public static void d(String szTAG, Exception e, StackTraceElement[] listStackTrace) {
|
public static void d(String szTAG, Exception e, StackTraceElement[] listStackTrace) {
|
||||||
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) {
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) {
|
||||||
StringBuilder sbMessage = new StringBuilder(e.getClass().toGenericString());
|
StringBuilder sbMessage = new StringBuilder();
|
||||||
sbMessage.append(" : ");
|
sbMessage.append("【异常信息】: ").append(getExceptionInfo(e));
|
||||||
sbMessage.append(e.getMessage());
|
sbMessage.append("\n【调用堆栈】: ").append(getStackTraceInfo(listStackTrace));
|
||||||
sbMessage.append(" \nAt ");
|
|
||||||
sbMessage.append(listStackTrace[2].getMethodName());
|
|
||||||
sbMessage.append(" (");
|
|
||||||
sbMessage.append(listStackTrace[2].getFileName());
|
|
||||||
sbMessage.append(":");
|
|
||||||
sbMessage.append(listStackTrace[2].getLineNumber());
|
|
||||||
sbMessage.append(")");
|
|
||||||
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, sbMessage.toString());
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, sbMessage.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
/**
|
||||||
// 调试日志写入函数
|
* Debug 级别日志(消息 + 数据对象)
|
||||||
//
|
* @param szTAG 标签
|
||||||
|
* @param szMessage 日志消息
|
||||||
|
* @param obj 数据对象(自动转为字符串)
|
||||||
|
*/
|
||||||
|
public static void d(String szTAG, String szMessage, Object obj) {
|
||||||
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) {
|
||||||
|
String objStr = obj == null ? "null" : obj.toString();
|
||||||
|
StringBuilder sb = new StringBuilder(szMessage);
|
||||||
|
sb.append("\n【数据对象】: ").append(objStr);
|
||||||
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug 级别日志(仅数据对象)
|
||||||
|
* @param szTAG 标签
|
||||||
|
* @param obj 数据对象(自动转为字符串)
|
||||||
|
*/
|
||||||
|
public static void d(String szTAG, Object obj) {
|
||||||
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) {
|
||||||
|
String objStr = obj == null ? "null" : obj.toString();
|
||||||
|
String message = "【数据对象】: " + objStr;
|
||||||
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================= 补全所有日志重载方法(Verbose 级别) =================================
|
||||||
|
/**
|
||||||
|
* Verbose 级别日志(仅消息)
|
||||||
|
* @param szTAG 标签
|
||||||
|
* @param szMessage 日志消息
|
||||||
|
*/
|
||||||
public static void v(String szTAG, String szMessage) {
|
public static void v(String szTAG, String szMessage) {
|
||||||
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Verbose)) {
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Verbose)) {
|
||||||
saveLog(szTAG, LogUtils.LOG_LEVEL.Verbose, szMessage);
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Verbose, szMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
/**
|
||||||
// 日志文件保存函数
|
* Verbose 级别日志(消息 + 数据对象)
|
||||||
//
|
* @param szTAG 标签
|
||||||
|
* @param szMessage 日志消息
|
||||||
|
* @param obj 数据对象(自动转为字符串)
|
||||||
|
*/
|
||||||
|
public static void v(String szTAG, String szMessage, Object obj) {
|
||||||
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Verbose)) {
|
||||||
|
String objStr = obj == null ? "null" : obj.toString();
|
||||||
|
StringBuilder sb = new StringBuilder(szMessage);
|
||||||
|
sb.append("\n【数据对象】: ").append(objStr);
|
||||||
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Verbose, sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verbose 级别日志(仅数据对象)
|
||||||
|
* @param szTAG 标签
|
||||||
|
* @param obj 数据对象(自动转为字符串)
|
||||||
|
*/
|
||||||
|
public static void v(String szTAG, Object obj) {
|
||||||
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Verbose)) {
|
||||||
|
String objStr = obj == null ? "null" : obj.toString();
|
||||||
|
String message = "【数据对象】: " + objStr;
|
||||||
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Verbose, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================= 新增:通用日志工具方法(补充调试能力) =================================
|
||||||
|
/**
|
||||||
|
* 打印当前线程信息(Debug 级别)
|
||||||
|
* @param szTAG 标签
|
||||||
|
* @param szMessage 日志消息
|
||||||
|
*/
|
||||||
|
public static void printThreadInfo(String szTAG, String szMessage) {
|
||||||
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) {
|
||||||
|
Thread currentThread = Thread.currentThread();
|
||||||
|
StringBuilder sb = new StringBuilder(szMessage);
|
||||||
|
sb.append("\n【线程信息】: ")
|
||||||
|
.append("线程名=").append(currentThread.getName())
|
||||||
|
.append(", 线程ID=").append(currentThread.getId())
|
||||||
|
.append(", 线程状态=").append(currentThread.getState().name());
|
||||||
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印 Map 数据(Debug 级别,格式化输出,便于查看)
|
||||||
|
* @param szTAG 标签
|
||||||
|
* @param szMessage 日志消息
|
||||||
|
* @param map 要打印的 Map 数据
|
||||||
|
*/
|
||||||
|
public static <K, V> void printMap(String szTAG, String szMessage, Map<K, V> map) {
|
||||||
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug) && map != null) {
|
||||||
|
StringBuilder sb = new StringBuilder(szMessage);
|
||||||
|
sb.append("\n【Map 数据】(size=").append(map.size()).append("):");
|
||||||
|
for (Map.Entry<K, V> entry : map.entrySet()) {
|
||||||
|
String keyStr = entry.getKey() == null ? "null" : entry.getKey().toString();
|
||||||
|
String valueStr = entry.getValue() == null ? "null" : entry.getValue().toString();
|
||||||
|
sb.append("\n ").append(keyStr).append(" = ").append(valueStr);
|
||||||
|
}
|
||||||
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印 List 数据(Debug 级别,格式化输出)
|
||||||
|
* @param szTAG 标签
|
||||||
|
* @param szMessage 日志消息
|
||||||
|
* @param list 要打印的 List 数据
|
||||||
|
*/
|
||||||
|
public static <T> void printList(String szTAG, String szMessage, List<T> list) {
|
||||||
|
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug) && list != null) {
|
||||||
|
StringBuilder sb = new StringBuilder(szMessage);
|
||||||
|
sb.append("\n【List 数据】(size=").append(list.size()).append("):");
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
T item = list.get(i);
|
||||||
|
String itemStr = item == null ? "null" : item.toString();
|
||||||
|
sb.append("\n 索引").append(i).append(" = ").append(itemStr);
|
||||||
|
}
|
||||||
|
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================= 私有工具方法(异常/堆栈信息格式化) =================================
|
||||||
|
/**
|
||||||
|
* 格式化异常信息(提取异常类型、消息、简化堆栈)
|
||||||
|
* @param e 异常对象
|
||||||
|
* @return 格式化后的异常字符串
|
||||||
|
*/
|
||||||
|
private static String getExceptionInfo(Exception e) {
|
||||||
|
if (e == null) {
|
||||||
|
return "异常对象为null";
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
// 异常类型 + 异常消息
|
||||||
|
sb.append(e.getClass().getSimpleName()).append(" : ").append(e.getMessage() == null ? "无异常消息" : e.getMessage());
|
||||||
|
// 简化堆栈(取前5行,避免日志过长)
|
||||||
|
StackTraceElement[] stackTrace = e.getStackTrace();
|
||||||
|
if (stackTrace != null && stackTrace.length > 0) {
|
||||||
|
sb.append("\n 简化堆栈(前5行):");
|
||||||
|
int limit = Math.min(stackTrace.length, 5);
|
||||||
|
for (int i = 0; i < limit; i++) {
|
||||||
|
sb.append("\n ").append(stackTrace[i].toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化堆栈信息(提取关键调用链路)
|
||||||
|
* @param stackTrace 堆栈数组
|
||||||
|
* @return 格式化后的堆栈字符串
|
||||||
|
*/
|
||||||
|
private static String getStackTraceInfo(StackTraceElement[] stackTrace) {
|
||||||
|
if (stackTrace == null || stackTrace.length == 0) {
|
||||||
|
return "堆栈信息为空";
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
// 过滤 LogUtils 内部调用,取真实业务调用链路(前8行)
|
||||||
|
int count = 0;
|
||||||
|
for (StackTraceElement element : stackTrace) {
|
||||||
|
// 跳过 LogUtils 自身的堆栈(避免冗余)
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================= 原有核心方法(保留并优化) =================================
|
||||||
|
/**
|
||||||
|
* 日志文件保存函数(优化:增加异常捕获完整性,避免流泄漏)
|
||||||
|
* @param szTAG 标签
|
||||||
|
* @param logLevel 日志级别
|
||||||
|
* @param szMessage 日志消息
|
||||||
|
*/
|
||||||
static void saveLog(String szTAG, LogUtils.LOG_LEVEL logLevel, String szMessage) {
|
static void saveLog(String szTAG, LogUtils.LOG_LEVEL logLevel, String szMessage) {
|
||||||
|
BufferedWriter out = null;
|
||||||
try {
|
try {
|
||||||
BufferedWriter out = null;
|
// 确保日志文件存在(创建父目录 + 文件)
|
||||||
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(_mfLogCatchFile, true), "UTF-8"));
|
if (!_mfLogCatchFile.exists()) {
|
||||||
out.write("[" + logLevel + "] " + mSimpleDateFormat.format(System.currentTimeMillis()) + " [" + szTAG + "]\n" + szMessage + "\n");
|
File parentDir = _mfLogCatchFile.getParentFile();
|
||||||
out.close();
|
if (parentDir != null && !parentDir.exists()) {
|
||||||
} catch (IOException e) {
|
parentDir.mkdirs();
|
||||||
LogUtils.d(TAG, "IOException : " + e.getMessage());
|
}
|
||||||
}
|
_mfLogCatchFile.createNewFile();
|
||||||
}
|
}
|
||||||
|
// 追加写入日志(UTF-8编码,避免中文乱码)
|
||||||
//
|
out = new BufferedWriter(new OutputStreamWriter(
|
||||||
// 历史日志加载函数
|
new FileOutputStream(_mfLogCatchFile, true), "UTF-8"));
|
||||||
//
|
String logLine = "[" + logLevel + "] "
|
||||||
public static String loadLog() {
|
+ mSimpleDateFormat.format(System.currentTimeMillis())
|
||||||
if (_mfLogCatchFile.exists()) {
|
+ " [" + szTAG + "]\n"
|
||||||
StringBuffer sb = new StringBuffer();
|
+ szMessage + "\n\n"; // 增加空行,区分不同日志
|
||||||
try {
|
out.write(logLine);
|
||||||
BufferedReader in = null;
|
out.flush(); // 强制刷新,确保日志及时写入
|
||||||
in = new BufferedReader(new InputStreamReader(new FileInputStream(_mfLogCatchFile), "UTF-8"));
|
} catch (IOException e) {
|
||||||
String line = "";
|
// 日志写入失败时,打印系统日志(避免递归调用)
|
||||||
while ((line = in.readLine()) != null) {
|
android.util.Log.e(TAG, "日志写入失败: " + e.getMessage());
|
||||||
sb.append(line);
|
} finally {
|
||||||
sb.append("\n");
|
// 关闭流,避免资源泄漏(Java 7 手动关闭,无 try-with-resources)
|
||||||
|
if (out != null) {
|
||||||
|
try {
|
||||||
|
out.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
android.util.Log.e(TAG, "流关闭失败: " + e.getMessage());
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
|
||||||
LogUtils.d(TAG, "IOException : " + e.getMessage());
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// 清理日志函数
|
|
||||||
//
|
|
||||||
public static void cleanLog() {
|
|
||||||
if (_mfLogCatchFile.exists()) {
|
|
||||||
try {
|
|
||||||
UTF8FileUtils.writeStringToFile(_mfLogCatchFile.getPath(), "");
|
|
||||||
//LogUtils.d(TAG, "cleanLog");
|
|
||||||
} catch (IOException e) {
|
|
||||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 历史日志加载函数(优化:增加流关闭,避免内存泄漏)
|
||||||
|
* @return 日志内容字符串(空串表示无日志)
|
||||||
|
*/
|
||||||
|
public static String loadLog() {
|
||||||
|
if (_mfLogCatchFile == null || !_mfLogCatchFile.exists()) {
|
||||||
|
return "日志文件不存在";
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
sb.append("日志加载失败: ").append(e.getMessage());
|
||||||
|
LogUtils.e(TAG, "日志加载异常", e);
|
||||||
|
} finally {
|
||||||
|
// 关闭流,避免资源泄漏
|
||||||
|
if (in != null) {
|
||||||
|
try {
|
||||||
|
in.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.e(TAG, "流关闭异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理日志函数(优化:支持清空日志,避免文件过大)
|
||||||
|
*/
|
||||||
|
public static void cleanLog() {
|
||||||
|
if (_mfLogCatchFile == null) {
|
||||||
|
LogUtils.d(TAG, "日志文件未初始化,无需清理");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 清空文件内容(覆盖写入空字符串)
|
||||||
|
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
|
||||||
|
new FileOutputStream(_mfLogCatchFile, false), "UTF-8"));
|
||||||
|
out.write("");
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
LogUtils.d(TAG, "日志已清空,文件路径: " + _mfLogCatchFile.getPath());
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.e(TAG, "日志清空失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查日志文件大小(新增:避免日志文件过大占用内存)
|
||||||
|
* @param maxSizeMB 最大允许大小(MB)
|
||||||
|
* @return true:文件超过限制;false:文件大小正常
|
||||||
|
*/
|
||||||
|
public static boolean checkLogFileSize(int maxSizeMB) {
|
||||||
|
if (_mfLogCatchFile == null || !_mfLogCatchFile.exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 转换为字节(1MB = 1024*1024 字节)
|
||||||
|
long maxSizeByte = maxSizeMB * 1024 * 1024;
|
||||||
|
long fileSize = _mfLogCatchFile.length();
|
||||||
|
LogUtils.d(TAG, String.format("日志文件大小: %.2f MB(限制: %d MB)",
|
||||||
|
fileSize / (1024.0 * 1024), maxSizeMB));
|
||||||
|
return fileSize > maxSizeByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化检查(新增:快速判断 LogUtils 是否初始化完成)
|
||||||
|
* @return true:已初始化;false:未初始化
|
||||||
|
*/
|
||||||
|
public static boolean isInited() {
|
||||||
|
return _IsInited;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示短提示(新增:日志+Toast联动,调试时快速提示)
|
||||||
|
* @param context 上下文
|
||||||
|
* @param message 提示内容
|
||||||
|
*/
|
||||||
|
public static void showShortToast(final Context context, final String message) {
|
||||||
|
if (context == null || message == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 主线程显示Toast,避免子线程崩溃
|
||||||
|
if (Thread.currentThread().getId() == android.os.Process.myTid()) {
|
||||||
|
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
((android.app.Activity) context).runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 同时写入日志
|
||||||
|
LogUtils.d(TAG, "Toast提示: " + message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user