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 4c3c75a..4d1e897 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtils.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtils.java @@ -1,15 +1,12 @@ package cc.winboll.studio.libappbase; -/** - * @Author ZhanGSKen@QQ.COM - * @Date 2024/08/12 13:44:06 - * @Describe LogUtils - * @Describe 应用日志类(补全所有日志重载方法,适配不同调试场景) - */ import android.content.Context; import android.widget.Toast; +import android.util.Log; + import cc.winboll.studio.libappbase.GlobalApplication; import dalvik.system.DexFile; + import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; @@ -29,82 +26,126 @@ import java.util.List; import java.util.Locale; import java.util.Map; - +/** + * @Author 豆包&ZhanGSKen + * @CreateTime 2026/05/09 15:46:28 + * @LastEditTime 2026/05/09 17:28:45 + * @Describe 应用日志工具类 + * 补全多级别日志重载、自动日志文件裁剪、应用内TAG自动扫描管理 + * 支持日志本地持久化、异常堆栈格式化、TAG开关配置、线程/集合打印工具 + * 完全兼容Java7语法,严格遵循变量final编码规范 + * 重要说明:本类内部调试打印必须使用 android.util.Log,禁止使用LogUtils自身方法; + * 若内部使用LogUtils打印会造成递归嵌套调用,程序运行时会概率出现逻辑漩涡、循环无限制调用问题。 + */ public class LogUtils { + // ====================== 常量与枚举定义 ====================== public static final String TAG = "LogUtils"; - public static enum LOG_LEVEL { Off, Error, Warn, Info, Debug, Verbose } + /** + * 日志级别枚举 + */ + public static enum LOG_LEVEL { + Off, Error, Warn, Info, Debug, Verbose + } - static volatile boolean _IsInited = false; - static Context _mContext; - // 日志显示时间格式 - static SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("[yyyyMMdd_HHmmss_SSS]", Locale.getDefault()); - // 应用日志文件夹 - static File _mfLogCacheDir; - static File _mfLogDataDir; - // 应用日志文件 - static File _mfLogCatchFile; - static File _mfLogUtilsBeanFile; - static LogUtilsBean _mLogUtilsBean; - public static Map mapTAGList = new HashMap(); + // ====================== 全局成员变量 ====================== + 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 mapTAGList = new HashMap(); - // - // 初始化函数 - // - public static void init(Context context) { - _mContext = context; + // ====================== 初始化入口方法 ====================== + /** + * 初始化日志工具,默认级别Off + * @param context 上下文 + */ + public static void init(final Context context) { init(context, LOG_LEVEL.Off); } - // - // 初始化函数 - // - public static void init(Context context, LOG_LEVEL logLevel) { + /** + * 初始化日志工具,指定日志级别 + * @param context 上下文 + * @param logLevel 日志级别 + */ + public static void init(final Context context, final LOG_LEVEL logLevel) { + Log.d(TAG, "init 执行日志工具初始化"); + _mContext = context; + if (GlobalApplication.isDebugging()) { - // 初始化日志缓存文件路径(debug模式:外部存储) - _mfLogCacheDir = new File(context.getApplicationContext().getExternalCacheDir(), TAG); - if (!_mfLogCacheDir.exists()) { - _mfLogCacheDir.mkdirs(); - } - _mfLogCatchFile = new File(_mfLogCacheDir, "log.txt"); - - // 初始化日志配置文件路径 - _mfLogDataDir = context.getApplicationContext().getExternalFilesDir(TAG); - if (!_mfLogDataDir.exists()) { - _mfLogDataDir.mkdirs(); - } - _mfLogUtilsBeanFile = new File(_mfLogDataDir, TAG + ".json"); + initDebugDir(); } else { - // 初始化日志缓存文件路径(release模式:内部存储) - _mfLogCacheDir = new File(context.getApplicationContext().getCacheDir(), TAG); - if (!_mfLogCacheDir.exists()) { - _mfLogCacheDir.mkdirs(); - } - _mfLogCatchFile = new File(_mfLogCacheDir, "log.txt"); - - // 初始化日志配置文件路径 - _mfLogDataDir = new File(context.getApplicationContext().getFilesDir(), TAG); - if (!_mfLogDataDir.exists()) { - _mfLogDataDir.mkdirs(); - } - _mfLogUtilsBeanFile = new File(_mfLogDataDir, TAG + ".json"); + initReleaseDir(); } + initLogConfigBean(); + addClassTAGList(); + loadTAGBeanSettings(); + checkAndTrimLogFileSize(); + + _IsInited = true; + Log.d(TAG, "init 日志工具初始化完成"); + } + + // ====================== 目录初始化私有方法 ====================== + /** + * 调试模式初始化外部存储日志目录 + */ + private static void initDebugDir() { + final Context appContext = _mContext.getApplicationContext(); + _mfLogCacheDir = new File(appContext.getExternalCacheDir(), 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 initReleaseDir() { + final Context appContext = _mContext.getApplicationContext(); + _mfLogCacheDir = new File(appContext.getCacheDir(), TAG); + if (!_mfLogCacheDir.exists()) { + _mfLogCacheDir.mkdirs(); + } + _mfLogCatchFile = new File(_mfLogCacheDir, "log.txt"); + + _mfLogDataDir = new File(appContext.getFilesDir(), TAG); + if (!_mfLogDataDir.exists()) { + _mfLogDataDir.mkdirs(); + } + _mfLogUtilsBeanFile = new File(_mfLogDataDir, TAG + ".json"); + } + + /** + * 初始化日志配置Bean,无配置则自动创建默认配置 + */ + 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 自动创建默认日志配置文件"); } - - // 加载当前应用下的所有类的 TAG - addClassTAGList(); - loadTAGBeanSettings(); - checkAndTrimLogFileSize(); - _IsInited = true; - LogUtils.d(TAG, String.format("mapTAGList : %s", mapTAGList.toString())); } + // ====================== 日志文件裁剪(Java7 兼容) ====================== + /** + * 日志文件大小检查裁剪,超6MB保留最新3MB + */ private static void checkAndTrimLogFileSize() { if (_mfLogCatchFile == null || !_mfLogCatchFile.exists()) { return; @@ -112,484 +153,406 @@ public class LogUtils { final long MAX_FILE_SIZE = 6291456L; final long KEEP_FILE_SIZE = 3145728L; + final long fileSize = _mfLogCatchFile.length(); - long fileSize = _mfLogCatchFile.length(); if (fileSize <= MAX_FILE_SIZE) { return; } - long needSkip = fileSize - KEEP_FILE_SIZE; + final long needSkip = fileSize - KEEP_FILE_SIZE; + final String lineBreak = System.lineSeparator(); - try (FileInputStream fis = new FileInputStream(_mfLogCatchFile); - BufferedReader reader = new BufferedReader(new InputStreamReader(fis)); - FileOutputStream fos = new FileOutputStream(_mfLogCatchFile); - BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos))) { + FileInputStream fis = null; + BufferedReader reader = null; + FileOutputStream fos = null; + BufferedWriter writer = null; - StringBuilder sb = new StringBuilder(); + 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; - final String lineBreak = System.lineSeparator(); while ((line = reader.readLine()) != null) { - byte[] lineBytes = line.getBytes(); + 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) { - e.printStackTrace(); + 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 getMapTAGList() { return mapTAGList; } - static void loadTAGBeanSettings() { - ArrayList list = new ArrayList(); + /** + * 加载本地TAG启用配置 + */ + private static void loadTAGBeanSettings() { + final ArrayList list = new ArrayList(); LogUtilsClassTAGBean.loadBeanList(_mContext, list, LogUtilsClassTAGBean.class); - for (int i = 0; i < list.size(); i++) { - LogUtilsClassTAGBean beanSetting = list.get(i); - for (Map.Entry entry : mapTAGList.entrySet()) { - if (entry.getKey().equals(beanSetting.getTag())) { - entry.setValue(beanSetting.getEnable()); - } + + for (final LogUtilsClassTAGBean beanSetting : list) { + final String tag = beanSetting.getTag(); + if (mapTAGList.containsKey(tag)) { + mapTAGList.put(tag, beanSetting.getEnable()); } } } - static void saveTAGBeanSettings() { - ArrayList list = new ArrayList(); - for (Map.Entry entry : mapTAGList.entrySet()) { + /** + * 保存当前TAG开关配置到本地 + */ + private static void saveTAGBeanSettings() { + final ArrayList list = new ArrayList(); + for (final Map.Entry entry : mapTAGList.entrySet()) { list.add(new LogUtilsClassTAGBean(entry.getKey(), entry.getValue())); } LogUtilsClassTAGBean.saveBeanList(_mContext, list, LogUtilsClassTAGBean.class); } - static void addClassTAGList() { + /** + * 自动扫描应用内含 public static String TAG 的类 + */ + private static void addClassTAGList() { try { - // 包名前缀(过滤当前应用的类) - String packageNamePrefix = "cc.winboll.studio"; - List classNames = new ArrayList<>(); - String apkPath = _mContext.getPackageCodePath(); - LogUtils.d(TAG, String.format("apkPath : %s", apkPath)); + final String packageNamePrefix = "cc.winboll.studio"; + final List classNames = new ArrayList(); + final String apkPath = _mContext.getPackageCodePath(); - DexFile dexfile = new DexFile(apkPath); - int countTemp = 0; + final DexFile dexfile = new DexFile(apkPath); Enumeration entries = dexfile.entries(); + while (entries.hasMoreElements()) { - countTemp++; - String className = entries.nextElement(); + final String className = entries.nextElement(); if (className.startsWith(packageNamePrefix)) { classNames.add(className); } } - LogUtils.d(TAG, String.format("countTemp : %d\nClassNames size : %d", countTemp, classNames.size())); - - for (String className : classNames) { + for (final String className : classNames) { try { - Class clazz = Class.forName(className); - Field[] fields = clazz.getDeclaredFields(); - for (Field field : fields) { - // 过滤静态、公共、String类型的 TAG 字段 - if (Modifier.isStatic(field.getModifiers()) - && Modifier.isPublic(field.getModifiers()) - && field.getType() == String.class + 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())) { - String tagValue = (String) field.get(null); - mapTAGList.put(tagValue, false); // 默认禁用,可通过设置开启 + + final String tagValue = (String) field.get(null); + mapTAGList.put(tagValue, false); } } } catch (NoClassDefFoundError | ClassNotFoundException | IllegalAccessException e) { - LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace()); + Log.w(TAG, "addClassTAGList 解析类TAG失败"); } } } catch (IOException e) { - LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); + Log.e(TAG, "addClassTAGList 扫描Dex异常", e); } } - public static void setTAGListEnable(String tag, boolean isEnable) { - Iterator> iterator = mapTAGList.entrySet().iterator(); + /** + * 设置单个TAG日志开关 + * @param tag 日志TAG + * @param isEnable 是否启用 + */ + public static void setTAGListEnable(final String tag, final boolean isEnable) { + final Iterator> iterator = mapTAGList.entrySet().iterator(); while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); + final Map.Entry entry = iterator.next(); if (tag.equals(entry.getKey())) { entry.setValue(isEnable); break; } } saveTAGBeanSettings(); - LogUtils.d(TAG, String.format("mapTAGList : %s", mapTAGList.toString())); + Log.d(TAG, "setTAGListEnable 更新TAG开关配置"); } - public static void setALlTAGListEnable(boolean isEnable) { - Iterator> iterator = mapTAGList.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); + /** + * 批量设置所有TAG日志开关 + * @param isEnable 是否全量启用 + */ + public static void setALlTAGListEnable(final boolean isEnable) { + for (final Map.Entry entry : mapTAGList.entrySet()) { entry.setValue(isEnable); } saveTAGBeanSettings(); - LogUtils.d(TAG, String.format("mapTAGList : %s", mapTAGList.toString())); + Log.d(TAG, "setALlTAGListEnable 全量设置TAG开关"); } - public static void setLogLevel(LOG_LEVEL logLevel) { - LogUtils._mLogUtilsBean.setLogLevel(logLevel); + // ====================== 日志级别控制 ====================== + public static void setLogLevel(final LOG_LEVEL logLevel) { + _mLogUtilsBean.setLogLevel(logLevel); _mLogUtilsBean.saveBeanToFile(_mfLogUtilsBeanFile.getPath(), _mLogUtilsBean); } public static LOG_LEVEL getLogLevel() { - return LogUtils._mLogUtilsBean.getLogLevel(); + return _mLogUtilsBean.getLogLevel(); } - static boolean isLoggable(String tag, LOG_LEVEL logLevel) { + /** + * 判断当前TAG及级别是否允许输出日志 + * @param tag 日志标识 + * @param logLevel 日志级别 + * @return 是否允许打印 + */ + private static boolean isLoggable(final String tag, final LOG_LEVEL logLevel) { if (!_IsInited) { return false; - } - // TAG 未配置或禁用时,不打印日志 + } if (mapTAGList.get(tag) == null || !mapTAGList.get(tag)) { return false; - } - // 日志级别不符合时,不打印日志 - if (!isInTheLevel(logLevel)) { - return false; } - return true; + return isInTheLevel(logLevel); } - static boolean isInTheLevel(LOG_LEVEL logLevel) { - // 当前日志级别 >= 目标级别时,允许打印(级别顺序:Off < Error < Warn < Info < Debug < Verbose) - return LogUtils._mLogUtilsBean.getLogLevel().ordinal() >= logLevel.ordinal(); + /** + * 判断日志级别是否匹配输出等级 + * @param logLevel 待判断级别 + * @return 是否达标 + */ + private static boolean isInTheLevel(final LOG_LEVEL logLevel) { + return _mLogUtilsBean.getLogLevel().ordinal() >= logLevel.ordinal(); } - // - // 获取应用日志文件夹 - // + // ====================== 对外基础工具方法 ====================== public static File getLogCacheDir() { 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); + 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); } } - /** - * 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); + 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, LogUtils.LOG_LEVEL.Error, sb.toString()); + saveLog(szTAG, 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); + 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); } } - /** - * 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); + 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, LogUtils.LOG_LEVEL.Error, sb.toString()); + saveLog(szTAG, 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 日志重载 ====================== + public static void w(final String szTAG, final String szMessage) { + if (isLoggable(szTAG, LOG_LEVEL.Warn)) { + saveLog(szTAG, 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); + 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, LogUtils.LOG_LEVEL.Warn, sb.toString()); + saveLog(szTAG, 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); + 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 级别) ================================= - /** - * 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 日志重载 ====================== + public static void i(final String szTAG, final String szMessage) { + if (isLoggable(szTAG, LOG_LEVEL.Info)) { + saveLog(szTAG, 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); + 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, LogUtils.LOG_LEVEL.Info, sb.toString()); + saveLog(szTAG, 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 日志重载 ====================== + public static void d(final String szTAG, final String szMessage) { + if (isLoggable(szTAG, LOG_LEVEL.Debug)) { + saveLog(szTAG, 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); + 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, LogUtils.LOG_LEVEL.Debug, sbMessage.toString()); + saveLog(szTAG, 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); + 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, LogUtils.LOG_LEVEL.Debug, sb.toString()); + saveLog(szTAG, 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(); + 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, LogUtils.LOG_LEVEL.Debug, sbMessage.toString()); + saveLog(szTAG, 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); + 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, LogUtils.LOG_LEVEL.Debug, sb.toString()); + saveLog(szTAG, 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); + 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 级别) ================================= - /** - * 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 日志重载 ====================== + public static void v(final String szTAG, final String szMessage) { + if (isLoggable(szTAG, LOG_LEVEL.Verbose)) { + saveLog(szTAG, 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); + 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, LogUtils.LOG_LEVEL.Verbose, sb.toString()); + saveLog(szTAG, 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); + 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); } } - // ================================= 新增:通用日志工具方法(补充调试能力) ================================= - /** - * 打印当前线程信息(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); + // ====================== 扩展工具打印 ====================== + 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, LogUtils.LOG_LEVEL.Debug, sb.toString()); + saveLog(szTAG, 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(); + public static void printMap(final String szTAG, final String szMessage, final Map 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 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, LogUtils.LOG_LEVEL.Debug, sb.toString()); + saveLog(szTAG, 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("):"); + public static void printList(final String szTAG, final String szMessage, final List 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++) { - T item = list.get(i); - String itemStr = item == null ? "null" : item.toString(); + final T item = list.get(i); + final String itemStr = item == null ? "null" : item.toString(); sb.append("\n 索引").append(i).append(" = ").append(itemStr); } - saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, sb.toString()); + saveLog(szTAG, LOG_LEVEL.Debug, sb.toString()); } } - // ================================= 私有工具方法(异常/堆栈信息格式化) ================================= - /** - * 格式化异常信息(提取异常类型、消息、简化堆栈) - * @param e 异常对象 - * @return 格式化后的异常字符串 - */ - private static String getExceptionInfo(Exception e) { + // ====================== 私有格式化工具 ====================== + private static String getExceptionInfo(final 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(); + 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) { - sb.append("\n 简化堆栈(前5行):"); - int limit = Math.min(stackTrace.length, 5); + final int limit = Math.min(stackTrace.length, 5); for (int i = 0; i < limit; i++) { sb.append("\n ").append(stackTrace[i].toString()); } @@ -597,20 +560,13 @@ public class LogUtils { return sb.toString(); } - /** - * 格式化堆栈信息(提取关键调用链路) - * @param stackTrace 堆栈数组 - * @return 格式化后的堆栈字符串 - */ - private static String getStackTraceInfo(StackTraceElement[] stackTrace) { + private static String getStackTraceInfo(final StackTraceElement[] stackTrace) { if (stackTrace == null || stackTrace.length == 0) { return "堆栈信息为空"; } - StringBuilder sb = new StringBuilder(); - // 过滤 LogUtils 内部调用,取真实业务调用链路(前8行) + final StringBuilder sb = new StringBuilder(); int count = 0; - for (StackTraceElement element : stackTrace) { - // 跳过 LogUtils 自身的堆栈(避免冗余) + for (final StackTraceElement element : stackTrace) { if (element.getClassName().contains("cc.winboll.studio.libappbase.LogUtils")) { continue; } @@ -618,145 +574,103 @@ public class LogUtils { .append(element.getMethodName()).append("(") .append(element.getFileName()).append(":").append(element.getLineNumber()).append(")"); count++; - if (count >= 8) { // 限制堆栈长度,避免日志过大 + if (count >= 8) { break; } } return sb.toString(); } - // ================================= 原有核心方法(保留并优化) ================================= - /** - * 日志文件保存函数(优化:增加异常捕获完整性,避免流泄漏) - * @param szTAG 标签 - * @param logLevel 日志级别 - * @param szMessage 日志消息 - */ - static void saveLog(String szTAG, LogUtils.LOG_LEVEL logLevel, String szMessage) { + // ====================== 日志持久化读写 ====================== + private static void saveLog(final String szTAG, final LogUtils.LOG_LEVEL logLevel, final String szMessage) { BufferedWriter out = null; try { - // 确保日志文件存在(创建父目录 + 文件) if (!_mfLogCatchFile.exists()) { - File parentDir = _mfLogCatchFile.getParentFile(); + final 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"; // 增加空行,区分不同日志 + final String logLine = "[" + logLevel + "] " + + mSimpleDateFormat.format(System.currentTimeMillis()) + + " [" + szTAG + "]\n" + + szMessage + "\n\n"; out.write(logLine); - out.flush(); // 强制刷新,确保日志及时写入 + out.flush(); } catch (IOException e) { - // 日志写入失败时,打印系统日志(避免递归调用) - android.util.Log.e(TAG, "日志写入失败: " + e.getMessage()); + Log.e(TAG, "saveLog 日志写入失败", e); } finally { - // 关闭流,避免资源泄漏(Java 7 手动关闭,无 try-with-resources) if (out != null) { try { out.close(); } catch (IOException e) { - android.util.Log.e(TAG, "流关闭失败: " + e.getMessage()); + Log.e(TAG, "saveLog 流关闭失败", e); } } } } - /** - * 历史日志加载函数(优化:增加流关闭,避免内存泄漏) - * @return 日志内容字符串(空串表示无日志) - */ public static String loadLog() { if (_mfLogCatchFile == null || !_mfLogCatchFile.exists()) { return "日志文件不存在"; } - StringBuffer sb = new StringBuffer(); + final StringBuffer sb = new StringBuffer(); BufferedReader in = null; try { in = new BufferedReader(new InputStreamReader( new FileInputStream(_mfLogCatchFile), "UTF-8")); - String line = ""; + String line; while ((line = in.readLine()) != null) { - sb.append(line); - sb.append("\n"); + sb.append(line).append("\n"); } } catch (IOException e) { - sb.append("日志加载失败: ").append(e.getMessage()); - LogUtils.e(TAG, "日志加载异常", e); + sb.append("日志加载失败"); + Log.e(TAG, "loadLog 读取日志异常", e); } finally { - // 关闭流,避免资源泄漏 if (in != null) { try { in.close(); } catch (IOException e) { - LogUtils.e(TAG, "流关闭异常", e); + Log.e(TAG, "loadLog 读取流关闭异常", e); } } } return sb.toString(); } - /** - * 清理日志函数(优化:支持清空日志,避免文件过大) - */ public static void cleanLog() { if (_mfLogCatchFile == null) { - LogUtils.d(TAG, "日志文件未初始化,无需清理"); + Log.d(TAG, "cleanLog 日志文件未初始化"); return; } try { - // 清空文件内容(覆盖写入空字符串) - BufferedWriter out = new BufferedWriter(new OutputStreamWriter( - new FileOutputStream(_mfLogCatchFile, false), "UTF-8")); + final BufferedWriter out = new BufferedWriter(new OutputStreamWriter( + new FileOutputStream(_mfLogCatchFile, false), "UTF-8")); out.write(""); out.flush(); out.close(); - LogUtils.d(TAG, "日志已清空,文件路径: " + _mfLogCatchFile.getPath()); + Log.d(TAG, "cleanLog 日志已清空"); } catch (IOException e) { - LogUtils.e(TAG, "日志清空失败", e); + Log.e(TAG, "cleanLog 清空日志失败", e); } } - /** - * 检查日志文件大小(新增:避免日志文件过大占用内存) - * @param maxSizeMB 最大允许大小(MB) - * @return true:文件超过限制;false:文件大小正常 - */ - public static boolean checkLogFileSize(int maxSizeMB) { + public static boolean checkLogFileSize(final 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)); + final long maxSizeByte = 1048576L * maxSizeMB; + final long fileSize = _mfLogCatchFile.length(); 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 { @@ -767,8 +681,6 @@ public class LogUtils { } }); } - // 同时写入日志 - LogUtils.d(TAG, "Toast提示: " + message); } }