From 2a31658cf88bf159e184c9f0dea3ad0bed162a3a Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Sat, 29 Nov 2025 02:37:47 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=A8=E6=97=A5=E5=BF=97=E8=B0=83?= =?UTF-8?q?=E8=AF=95=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- appbase/build.properties | 4 +- libappbase/build.properties | 4 +- .../winboll/studio/libappbase/LogUtils.java | 576 ++++++++++++++---- 3 files changed, 468 insertions(+), 116 deletions(-) diff --git a/appbase/build.properties b/appbase/build.properties index 401228c5..9da6a9ee 100644 --- a/appbase/build.properties +++ b/appbase/build.properties @@ -1,8 +1,8 @@ #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 libraryProject=libappbase baseVersion=15.11 publishVersion=15.11.1 -buildCount=25 +buildCount=28 baseBetaVersion=15.11.2 diff --git a/libappbase/build.properties b/libappbase/build.properties index 401228c5..9da6a9ee 100644 --- a/libappbase/build.properties +++ b/libappbase/build.properties @@ -1,8 +1,8 @@ #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 libraryProject=libappbase baseVersion=15.11 publishVersion=15.11.1 -buildCount=25 +buildCount=28 baseBetaVersion=15.11.2 diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtils.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtils.java index a5de0115..0cabf7a3 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtils.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtils.java @@ -4,9 +4,10 @@ package cc.winboll.studio.libappbase; * @Author ZhanGSKen@QQ.COM * @Date 2024/08/12 13:44:06 * @Describe LogUtils - * @Describe 应用日志类 + * @Describe 应用日志类(补全所有日志重载方法,适配不同调试场景) */ import android.content.Context; +import android.widget.Toast; import cc.winboll.studio.libappbase.GlobalApplication; import dalvik.system.DexFile; import java.io.BufferedReader; @@ -61,7 +62,7 @@ public class LogUtils { // public static void init(Context context, LOG_LEVEL logLevel) { if (GlobalApplication.isDebugging()) { - // 初始化日志缓存文件路径 + // 初始化日志缓存文件路径(debug模式:外部存储) _mfLogCacheDir = new File(context.getApplicationContext().getExternalCacheDir(), TAG); if (!_mfLogCacheDir.exists()) { _mfLogCacheDir.mkdirs(); @@ -75,7 +76,7 @@ public class LogUtils { } _mfLogUtilsBeanFile = new File(_mfLogDataDir, TAG + ".json"); } else { - // 初始化日志缓存文件路径 + // 初始化日志缓存文件路径(release模式:内部存储) _mfLogCacheDir = new File(context.getApplicationContext().getCacheDir(), TAG); if (!_mfLogCacheDir.exists()) { _mfLogCacheDir.mkdirs(); @@ -90,11 +91,6 @@ public class LogUtils { _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); if (_mLogUtilsBean == null) { _mLogUtilsBean = new LogUtilsBean(); @@ -122,7 +118,6 @@ public class LogUtils { entry.setValue(beanSetting.getEnable()); } } - } } @@ -135,19 +130,14 @@ public class LogUtils { } static void addClassTAGList() { - //ClassLoader classLoader = getClass().getClassLoader(); try { - //String packageName = context.getPackageName(); + // 包名前缀(过滤当前应用的类) String packageNamePrefix = "cc.winboll.studio"; List classNames = new ArrayList<>(); String apkPath = _mContext.getPackageCodePath(); - //Log.d("APK_PATH", "The APK path is: " + 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); - int countTemp = 0; Enumeration entries = dexfile.entries(); while (entries.hasMoreElements()) { @@ -165,23 +155,21 @@ public class LogUtils { Class clazz = Class.forName(className); Field[] fields = clazz.getDeclaredFields(); 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); - //Log.d("TAG_INFO", "Class: " + className + ", TAG value: " + tagValue); - //LogUtils.d(TAG, String.format("Tag Value : %s", tagValue)); - //mapTAGList.put(tagValue, true); - mapTAGList.put(tagValue, false); + mapTAGList.put(tagValue, false); // 默认禁用,可通过设置开启 } } } catch (NoClassDefFoundError | ClassNotFoundException | IllegalAccessException e) { 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) { 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 entry = iterator.next(); if (tag.equals(entry.getKey())) { entry.setValue(isEnable); - //System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue()); break; } } @@ -204,7 +191,6 @@ public class LogUtils { while (iterator.hasNext()) { Map.Entry entry = iterator.next(); entry.setValue(isEnable); - //System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue()); } saveTAGBeanSettings(); LogUtils.d(TAG, String.format("mapTAGList : %s", mapTAGList.toString())); @@ -223,10 +209,11 @@ public class LogUtils { if (!_IsInited) { return false; } - if (mapTAGList.get(tag) == null - || !mapTAGList.get(tag)) { + // TAG 未配置或禁用时,不打印日志 + if (mapTAGList.get(tag) == null || !mapTAGList.get(tag)) { return false; } + // 日志级别不符合时,不打印日志 if (!isInTheLevel(logLevel)) { return false; } @@ -234,8 +221,8 @@ public class LogUtils { } static boolean isInTheLevel(LOG_LEVEL logLevel) { - return (LogUtils._mLogUtilsBean.getLogLevel().ordinal() == logLevel.ordinal() - || LogUtils._mLogUtilsBean.getLogLevel().ordinal() > logLevel.ordinal()); + // 当前日志级别 >= 目标级别时,允许打印(级别顺序:Off < Error < Warn < Info < Debug < Verbose) + return LogUtils._mLogUtilsBean.getLogLevel().ordinal() >= logLevel.ordinal(); } // @@ -245,136 +232,501 @@ public class LogUtils { return _mfLogCacheDir; } - // - // 调试日志写入函数 - // + // ================================= 补全所有日志重载方法(Error 级别) ================================= + /** + * Error 级别日志(仅消息) + * @param szTAG 标签 + * @param szMessage 日志消息 + */ public static void e(String szTAG, String szMessage) { if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Error)) { 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) { if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Warn)) { 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) { if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Info)) { 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) { if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) { saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, szMessage); } } - // - // 调试日志写入函数 - // 包含线程调试堆栈信息 - // + /** + * Debug 级别日志(消息 + 堆栈) + * @param szTAG 标签 + * @param szMessage 日志消息 + * @param listStackTrace 堆栈信息 + */ public static void d(String szTAG, String szMessage, StackTraceElement[] listStackTrace) { if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) { StringBuilder sbMessage = new StringBuilder(szMessage); - sbMessage.append(" \nAt "); - sbMessage.append(listStackTrace[2].getMethodName()); - sbMessage.append(" ("); - sbMessage.append(listStackTrace[2].getFileName()); - sbMessage.append(":"); - sbMessage.append(listStackTrace[2].getLineNumber()); - sbMessage.append(")"); + sbMessage.append("\n【调用堆栈】: ").append(getStackTraceInfo(listStackTrace)); 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) { if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) { - StringBuilder sbMessage = new StringBuilder(e.getClass().toGenericString()); - sbMessage.append(" : "); - sbMessage.append(e.getMessage()); - sbMessage.append(" \nAt "); - sbMessage.append(listStackTrace[2].getMethodName()); - sbMessage.append(" ("); - sbMessage.append(listStackTrace[2].getFileName()); - sbMessage.append(":"); - sbMessage.append(listStackTrace[2].getLineNumber()); - sbMessage.append(")"); + StringBuilder sbMessage = new StringBuilder(); + sbMessage.append("【异常信息】: ").append(getExceptionInfo(e)); + sbMessage.append("\n【调用堆栈】: ").append(getStackTraceInfo(listStackTrace)); 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) { if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Verbose)) { 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 void printMap(String szTAG, String szMessage, Map 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 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 void printList(String szTAG, String szMessage, List 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) { + BufferedWriter out = null; try { - BufferedWriter out = null; - out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(_mfLogCatchFile, true), "UTF-8")); - out.write("[" + logLevel + "] " + mSimpleDateFormat.format(System.currentTimeMillis()) + " [" + szTAG + "]\n" + szMessage + "\n"); - out.close(); - } catch (IOException e) { - LogUtils.d(TAG, "IOException : " + e.getMessage()); - } - } - - // - // 历史日志加载函数 - // - public static String loadLog() { - if (_mfLogCatchFile.exists()) { - StringBuffer sb = new StringBuffer(); - try { - BufferedReader in = null; - in = new BufferedReader(new InputStreamReader(new FileInputStream(_mfLogCatchFile), "UTF-8")); - String line = ""; - while ((line = in.readLine()) != null) { - sb.append(line); - sb.append("\n"); + // 确保日志文件存在(创建父目录 + 文件) + if (!_mfLogCatchFile.exists()) { + File parentDir = _mfLogCatchFile.getParentFile(); + if (parentDir != null && !parentDir.exists()) { + parentDir.mkdirs(); + } + _mfLogCatchFile.createNewFile(); + } + // 追加写入日志(UTF-8编码,避免中文乱码) + out = new BufferedWriter(new OutputStreamWriter( + new FileOutputStream(_mfLogCatchFile, true), "UTF-8")); + String logLine = "[" + logLevel + "] " + + mSimpleDateFormat.format(System.currentTimeMillis()) + + " [" + szTAG + "]\n" + + szMessage + "\n\n"; // 增加空行,区分不同日志 + out.write(logLine); + out.flush(); // 强制刷新,确保日志及时写入 + } catch (IOException e) { + // 日志写入失败时,打印系统日志(避免递归调用) + android.util.Log.e(TAG, "日志写入失败: " + e.getMessage()); + } finally { + // 关闭流,避免资源泄漏(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); + } } +