源码整理,更新一些函数的命名方式。

This commit is contained in:
ZhanGSKen
2025-11-11 20:42:47 +08:00
parent 935dba200e
commit f332abaa05
4 changed files with 417 additions and 258 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Tue Nov 11 12:22:58 GMT 2025 #Tue Nov 11 12:40:49 GMT 2025
stageCount=10 stageCount=10
libraryProject=libappbase libraryProject=libappbase
baseVersion=15.10 baseVersion=15.10
publishVersion=15.10.9 publishVersion=15.10.9
buildCount=4 buildCount=5
baseBetaVersion=15.10.10 baseBetaVersion=15.10.10

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Tue Nov 11 12:22:58 GMT 2025 #Tue Nov 11 12:40:49 GMT 2025
stageCount=10 stageCount=10
libraryProject=libappbase libraryProject=libappbase
baseVersion=15.10 baseVersion=15.10
publishVersion=15.10.9 publishVersion=15.10.9
buildCount=4 buildCount=5
baseBetaVersion=15.10.10 baseBetaVersion=15.10.10

View File

@@ -1,10 +1,11 @@
package cc.winboll.studio.libappbase; package cc.winboll.studio.libappbase;
/** /**
* @Author ZhanGSKen<zhangsken@qq.com> * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2024/08/12 13:44:06 * @Date 2025/11/11 20:36
* @Describe LogUtils * @Describe WinBoLl 应用日志管理工具类(单例逻辑)
* @Describe 应用日志类 * 核心功能日志分级控制、日志文件读写、TAG 过滤配置、应用内所有 TAG 自动扫描
* 支持 Debug/Release 模式区分存储路径,日志持久化与清理
*/ */
import android.content.Context; import android.content.Context;
import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.GlobalApplication;
@@ -28,353 +29,511 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
public class LogUtils { public class LogUtils {
/** 当前工具类的日志 TAG */
public static final String TAG = "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; /** 是否初始化完成标志volatile 保证多线程可见性) */
static Context _mContext; private static volatile boolean sIsInited = false;
// 日志显示时间格式 /** 全局上下文(用于获取存储路径、包信息) */
static SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("[yyyyMMdd_HHmmss_SSS]", Locale.getDefault()); private static Context sContext;
// 应用日志文件夹 /** 日志时间格式化工具(格式:[yyyyMMdd_HHmmss_SSS],精确到毫秒) */
static File _mfLogCacheDir; private static SimpleDateFormat sSimpleDateFormat = new SimpleDateFormat("[yyyyMMdd_HHmmss_SSS]", Locale.getDefault());
static File _mfLogDataDir; /** 日志缓存文件夹Debug 模式下存储在外部缓存Release 存储在内部缓存) */
// 应用日志文件 private static File sLogCacheDir;
static File _mfLogCatchFile; /** 日志配置文件夹(存储 TAG 配置文件) */
static File _mfLogUtilsBeanFile; private static File sLogDataDir;
static LogUtilsBean _mLogUtilsBean; /** 日志存储文件(所有日志写入此文件) */
public static Map<String, Boolean> mapTAGList = new HashMap<String, Boolean>(); private static File sLogFile;
/** 日志配置文件存储日志级别、TAG 启用状态等配置) */
private static File sLogConfigFile;
/** 日志配置实体类(封装日志级别等配置) */
private static LogUtilsBean sLogConfigBean;
/** TAG 过滤映射表keyTAG 名称value是否启用该 TAG 的日志输出) */
public static Map<String, Boolean> sTagEnableMap = new HashMap<>();
// /**
// 初始化函数 * 初始化日志工具默认日志级别Off不输出日志
// * @param context 全局上下文(建议传入 Application 实例)
*/
public static void init(Context context) { public static void init(Context context) {
_mContext = context; sContext = context;
init(context, LOG_LEVEL.Off); init(context, LOG_LEVEL.Off);
} }
// /**
// 初始化函数 * 初始化日志工具(指定日志级别)
// * 1. 根据 Debug/Release 模式初始化日志存储路径;
* 2. 加载日志配置文件;
* 3. 扫描应用内所有类的 TAG 并初始化过滤映射表;
* 4. 标记初始化完成。
* @param context 全局上下文
* @param logLevel 初始日志级别
*/
public static void init(Context context, LOG_LEVEL logLevel) { public static void init(Context context, LOG_LEVEL logLevel) {
sContext = context;
// 根据 Debug 模式选择存储路径(外部/内部存储)
if (GlobalApplication.isDebugging()) { if (GlobalApplication.isDebugging()) {
// 初始化日志缓存文件路径 // Debug 模式:存储在外部缓存目录(可通过文件管理器查看)
_mfLogCacheDir = new File(context.getApplicationContext().getExternalCacheDir(), TAG); sLogCacheDir = new File(context.getApplicationContext().getExternalCacheDir(), TAG);
if (!_mfLogCacheDir.exists()) { sLogDataDir = context.getApplicationContext().getExternalFilesDir(TAG);
_mfLogCacheDir.mkdirs();
}
_mfLogCatchFile = new File(_mfLogCacheDir, "log.txt");
// 初始化日志配置文件路径
_mfLogDataDir = context.getApplicationContext().getExternalFilesDir(TAG);
if (!_mfLogDataDir.exists()) {
_mfLogDataDir.mkdirs();
}
_mfLogUtilsBeanFile = new File(_mfLogDataDir, TAG + ".json");
} else { } else {
// 初始化日志缓存文件路径 // Release 模式:存储在内部缓存目录(仅应用自身可访问)
_mfLogCacheDir = new File(context.getApplicationContext().getCacheDir(), TAG); sLogCacheDir = new File(context.getApplicationContext().getCacheDir(), TAG);
if (!_mfLogCacheDir.exists()) { sLogDataDir = new File(context.getApplicationContext().getFilesDir(), TAG);
_mfLogCacheDir.mkdirs(); }
// 创建日志文件夹(不存在则创建)
createDirIfNotExists(sLogCacheDir);
createDirIfNotExists(sLogDataDir);
// 初始化日志文件和配置文件路径
sLogFile = new File(sLogCacheDir, "log.txt");
sLogConfigFile = new File(sLogDataDir, TAG + ".json");
// 加载日志配置(从文件读取,读取失败则创建默认配置)
sLogConfigBean = LogUtilsBean.loadBeanFromFile(sLogConfigFile.getPath(), LogUtilsBean.class);
if (sLogConfigBean == null) {
sLogConfigBean = new LogUtilsBean();
sLogConfigBean.setLogLevel(logLevel);
// 保存默认配置到文件
sLogConfigBean.saveBeanToFile(sLogConfigFile.getPath(), sLogConfigBean);
}
// 扫描应用内所有类的 TAG 并添加到过滤映射表
scanAllClassTags();
// 加载已保存的 TAG 启用状态配置
loadTagEnableSettings();
// 标记初始化完成
sIsInited = true;
// 打印初始化日志(调试用)
d(TAG, String.format("TAG 过滤映射表初始化完成:%s", sTagEnableMap.toString()));
}
/**
* 获取 TAG 过滤映射表(外部可通过此方法获取所有 TAG 及其启用状态)
* @return TAG 名称与启用状态的映射
*/
public static Map<String, Boolean> getTagEnableMap() {
return sTagEnableMap;
}
/**
* 加载已保存的 TAG 启用状态配置
* 从 LogUtilsClassTAGBean 列表中读取每个 TAG 的启用状态,更新到映射表
*/
private static void loadTagEnableSettings() {
ArrayList<LogUtilsClassTAGBean> tagSettingList = new ArrayList<>();
// 从文件加载 TAG 配置列表
LogUtilsClassTAGBean.loadBeanList(sContext, tagSettingList, LogUtilsClassTAGBean.class);
// 遍历配置列表,更新 TAG 启用状态
for (LogUtilsClassTAGBean tagSetting : tagSettingList) {
String tag = tagSetting.getTag();
boolean isEnable = tagSetting.getEnable();
// 仅更新已存在的 TAG避免无效配置
if (sTagEnableMap.containsKey(tag)) {
sTagEnableMap.put(tag, isEnable);
} }
_mfLogCatchFile = new File(_mfLogCacheDir, "log.txt");
// 初始化日志配置文件路径
_mfLogDataDir = new File(context.getApplicationContext().getFilesDir(), TAG);
if (!_mfLogDataDir.exists()) {
_mfLogDataDir.mkdirs();
}
_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();
_mLogUtilsBean.saveBeanToFile(_mfLogUtilsBeanFile.getPath(), _mLogUtilsBean);
}
// 加载当前应用下的所有类的 TAG
addClassTAGList();
loadTAGBeanSettings();
_IsInited = true;
LogUtils.d(TAG, String.format("mapTAGList : %s", mapTAGList.toString()));
}
public static Map<String, Boolean> getMapTAGList() {
return mapTAGList;
}
static void loadTAGBeanSettings() {
ArrayList<LogUtilsClassTAGBean> list = new ArrayList<LogUtilsClassTAGBean>();
LogUtilsClassTAGBean.loadBeanList(_mContext, list, LogUtilsClassTAGBean.class);
for (int i = 0; i < list.size(); i++) {
LogUtilsClassTAGBean beanSetting = list.get(i);
for (Map.Entry<String, Boolean> entry : mapTAGList.entrySet()) {
if (entry.getKey().equals(beanSetting.getTag())) {
entry.setValue(beanSetting.getEnable());
}
}
} }
} }
static void saveTAGBeanSettings() { /**
ArrayList<LogUtilsClassTAGBean> list = new ArrayList<LogUtilsClassTAGBean>(); * 保存当前 TAG 启用状态配置到文件
for (Map.Entry<String, Boolean> entry : mapTAGList.entrySet()) { * 将映射表中的 TAG 及其启用状态转换为 LogUtilsClassTAGBean 列表,持久化到文件
list.add(new LogUtilsClassTAGBean(entry.getKey(), entry.getValue())); */
private static void saveTagEnableSettings() {
ArrayList<LogUtilsClassTAGBean> tagSettingList = new ArrayList<>();
// 遍历映射表,构建配置列表
for (Map.Entry<String, Boolean> entry : sTagEnableMap.entrySet()) {
tagSettingList.add(new LogUtilsClassTAGBean(entry.getKey(), entry.getValue()));
} }
LogUtilsClassTAGBean.saveBeanList(_mContext, list, LogUtilsClassTAGBean.class); // 保存配置列表到文件
LogUtilsClassTAGBean.saveBeanList(sContext, tagSettingList, LogUtilsClassTAGBean.class);
} }
static void addClassTAGList() { /**
//ClassLoader classLoader = getClass().getClassLoader(); * 扫描应用内所有类的 TAG 并添加到过滤映射表
* 1. 通过 DexFile 读取 APK 中所有类;
* 2. 过滤指定包名前缀cc.winboll.studio的类
* 3. 反射获取类中 public static final String TAG 字段的值;
* 4. 将 TAG 加入映射表默认禁用false
*/
private static void scanAllClassTags() {
try { try {
//String packageName = context.getPackageName(); // 应用 APK 路径(通过上下文获取)
String packageNamePrefix = "cc.winboll.studio"; String apkPath = sContext.getPackageCodePath();
List<String> classNames = new ArrayList<>(); d(TAG, String.format("APK 路径:%s", apkPath));
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"); // 读取 APK 中的所有类
DexFile dexfile = new DexFile(apkPath); DexFile dexFile = new DexFile(apkPath);
Enumeration<String> classNames = dexFile.entries();
int countTemp = 0; int totalClassCount = 0; // 总类数(调试用)
Enumeration<String> entries = dexfile.entries(); List<String> targetClassNames = new ArrayList<>(); // 目标包名下的类名列表
while (entries.hasMoreElements()) { String targetPackagePrefix = "cc.winboll.studio"; // 目标包名前缀
countTemp++;
String className = entries.nextElement(); // 过滤目标包名下的类
if (className.startsWith(packageNamePrefix)) { while (classNames.hasMoreElements()) {
classNames.add(className); totalClassCount++;
String className = classNames.nextElement();
if (className.startsWith(targetPackagePrefix)) {
targetClassNames.add(className);
} }
} }
LogUtils.d(TAG, String.format("countTemp : %d\nClassNames size : %d", countTemp, classNames.size())); // 打印扫描统计(调试用)
d(TAG, String.format("APK 总类数:%d目标包下类数%d", totalClassCount, targetClassNames.size()));
for (String className : classNames) { // 反射获取每个类的 TAG 字段
for (String className : targetClassNames) {
try { try {
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())) { // 过滤条件public static String 类型,且字段名是 "TAG"
if (Modifier.isStatic(field.getModifiers())
&& Modifier.isPublic(field.getModifiers())
&& field.getType() == String.class
&& "TAG".equals(field.getName())) {
// 获取 TAG 字段的值(静态字段,传入 null 即可)
String tagValue = (String) field.get(null); String tagValue = (String) field.get(null);
//Log.d("TAG_INFO", "Class: " + className + ", TAG value: " + tagValue); // 添加到映射表,默认禁用
//LogUtils.d(TAG, String.format("Tag Value : %s", tagValue)); sTagEnableMap.put(tagValue, false);
//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, Thread.currentThread().getStackTrace()); d(TAG, e.getMessage(), 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()); // 捕获 APK 读取异常
//Toast.makeText(context, TAG + " : " + e.getMessage(), Toast.LENGTH_SHORT).show(); d(TAG, e, Thread.currentThread().getStackTrace());
} }
} }
public static void setTAGListEnable(String tag, boolean isEnable) { /**
Iterator<Map.Entry<String, Boolean>> iterator = mapTAGList.entrySet().iterator(); * 设置单个 TAG 的启用状态
* @param tag TAG 名称
* @param isEnable 是否启用true输出该 TAG 的日志false不输出
*/
public static void setTagEnable(String tag, boolean isEnable) {
// 遍历映射表,更新目标 TAG 的状态
Iterator<Map.Entry<String, Boolean>> iterator = sTagEnableMap.entrySet().iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
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;
} }
} }
saveTAGBeanSettings(); // 保存配置到文件(持久化)
LogUtils.d(TAG, String.format("mapTAGList : %s", mapTAGList.toString())); saveTagEnableSettings();
d(TAG, String.format("TAG 配置更新:%s", sTagEnableMap.toString()));
} }
public static void setALlTAGListEnable(boolean isEnable) { /**
Iterator<Map.Entry<String, Boolean>> iterator = mapTAGList.entrySet().iterator(); * 设置所有 TAG 的启用状态(批量控制)
* @param isEnable 是否启用true所有 TAG 均输出日志false所有 TAG 均不输出)
*/
public static void setAllTagsEnable(boolean isEnable) {
// 遍历映射表,批量更新所有 TAG 的状态
Iterator<Map.Entry<String, Boolean>> iterator = sTagEnableMap.entrySet().iterator();
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(); // 保存配置到文件(持久化)
LogUtils.d(TAG, String.format("mapTAGList : %s", mapTAGList.toString())); saveTagEnableSettings();
d(TAG, String.format("所有 TAG 配置更新:%s", sTagEnableMap.toString()));
} }
/**
* 设置全局日志级别(控制日志输出的详细程度)
* @param logLevel 目标日志级别
*/
public static void setLogLevel(LOG_LEVEL logLevel) { public static void setLogLevel(LOG_LEVEL logLevel) {
LogUtils._mLogUtilsBean.setLogLevel(logLevel); if (sLogConfigBean != null) {
_mLogUtilsBean.saveBeanToFile(_mfLogUtilsBeanFile.getPath(), _mLogUtilsBean); sLogConfigBean.setLogLevel(logLevel);
// 保存配置到文件(持久化)
sLogConfigBean.saveBeanToFile(sLogConfigFile.getPath(), sLogConfigBean);
}
} }
/**
* 获取当前全局日志级别
* @return 当前日志级别
*/
public static LOG_LEVEL getLogLevel() { public static LOG_LEVEL getLogLevel() {
return LogUtils._mLogUtilsBean.getLogLevel(); return sLogConfigBean != null ? sLogConfigBean.getLogLevel() : LOG_LEVEL.Off;
} }
static boolean isLoggable(String tag, LOG_LEVEL logLevel) { /**
if (!_IsInited) { * 判断当前日志是否可输出校验初始化状态、TAG 启用状态、日志级别)
* @param tag 日志 TAG
* @param logLevel 日志级别
* @return true可输出false不可输出
*/
private static boolean isLoggable(String tag, LOG_LEVEL logLevel) {
// 未初始化:不输出
if (!sIsInited) {
return false; return false;
} }
if (mapTAGList.get(tag) == null // TAG 未配置或未启用:不输出
|| !mapTAGList.get(tag)) { if (sTagEnableMap.get(tag) == null || !sTagEnableMap.get(tag)) {
return false; return false;
} }
if (!isInTheLevel(logLevel)) { // 日志级别未达到:不输出
if (!isLevelMatched(logLevel)) {
return false; return false;
} }
return true; return true;
} }
static boolean isInTheLevel(LOG_LEVEL logLevel) { /**
return (LogUtils._mLogUtilsBean.getLogLevel().ordinal() == logLevel.ordinal() * 判断日志级别是否匹配(当前全局级别 >= 目标级别时可输出)
|| LogUtils._mLogUtilsBean.getLogLevel().ordinal() > logLevel.ordinal()); * 例:全局级别为 Debug4则 Error1、Warn2、Info3、Debug4均可输出
* @param logLevel 目标日志级别
* @return true级别匹配false不匹配
*/
private static boolean isLevelMatched(LOG_LEVEL logLevel) {
if (sLogConfigBean == null) {
return false;
}
// 枚举的 ordinal() 方法返回索引Off=0Error=1...Verbose=5
return sLogConfigBean.getLogLevel().ordinal() >= logLevel.ordinal();
} }
// /**
// 获取应用日志文件夹 * 获取日志缓存文件夹路径(外部可通过此方法获取日志存储目录)
// * @return 日志缓存文件夹
*/
public static File getLogCacheDir() { public static File getLogCacheDir() {
return _mfLogCacheDir; return sLogCacheDir;
} }
// /**
// 调试日志写入函数 * 输出 Error 级别日志
// * @param tag TAG 名称
public static void e(String szTAG, String szMessage) { * @param message 日志内容
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Error)) { */
saveLog(szTAG, LogUtils.LOG_LEVEL.Error, szMessage); public static void e(String tag, String message) {
if (isLoggable(tag, LOG_LEVEL.Error)) {
saveLog(tag, LOG_LEVEL.Error, message);
} }
} }
// /**
// 调试日志写入函数 * 输出 Warn 级别日志
// * @param tag TAG 名称
public static void w(String szTAG, String szMessage) { * @param message 日志内容
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Warn)) { */
saveLog(szTAG, LogUtils.LOG_LEVEL.Warn, szMessage); public static void w(String tag, String message) {
if (isLoggable(tag, LOG_LEVEL.Warn)) {
saveLog(tag, LOG_LEVEL.Warn, message);
} }
} }
// /**
// 调试日志写入函数 * 输出 Info 级别日志
// * @param tag TAG 名称
public static void i(String szTAG, String szMessage) { * @param message 日志内容
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Info)) { */
saveLog(szTAG, LogUtils.LOG_LEVEL.Info, szMessage); public static void i(String tag, String message) {
if (isLoggable(tag, LOG_LEVEL.Info)) {
saveLog(tag, LOG_LEVEL.Info, message);
} }
} }
// /**
// 调试日志写入函数 * 输出 Debug 级别日志(基础版)
// * @param tag TAG 名称
public static void d(String szTAG, String szMessage) { * @param message 日志内容
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) { */
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, szMessage); public static void d(String tag, String message) {
if (isLoggable(tag, LOG_LEVEL.Debug)) {
saveLog(tag, LOG_LEVEL.Debug, message);
} }
} }
// /**
// 调试日志写入函数 * 输出 Debug 级别日志(带调用栈信息)
// 包含线程调试堆栈信息 * 包含调用方法名、文件名、行号,便于调试定位
// * @param tag TAG 名称
public static void d(String szTAG, String szMessage, StackTraceElement[] listStackTrace) { * @param message 日志内容
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) { * @param stackTrace 线程调用栈(通常传入 Thread.currentThread().getStackTrace()
StringBuilder sbMessage = new StringBuilder(szMessage); */
sbMessage.append(" \nAt "); public static void d(String tag, String message, StackTraceElement[] stackTrace) {
sbMessage.append(listStackTrace[2].getMethodName()); if (isLoggable(tag, LOG_LEVEL.Debug)) {
sbMessage.append(" ("); StringBuilder sb = new StringBuilder(message);
sbMessage.append(listStackTrace[2].getFileName()); // 拼接调用栈信息stackTrace[2] 为实际调用处)
sbMessage.append(":"); sb.append(" \nAt ")
sbMessage.append(listStackTrace[2].getLineNumber()); .append(stackTrace[2].getMethodName())
sbMessage.append(")"); .append(" (")
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, sbMessage.toString()); .append(stackTrace[2].getFileName())
.append(":")
.append(stackTrace[2].getLineNumber())
.append(")");
saveLog(tag, LOG_LEVEL.Debug, sb.toString());
} }
} }
// /**
// 调试日志写入函数 * 输出 Debug 级别日志(带异常信息和调用栈)
// 包含异常信息和线程调试堆栈信息 * 包含异常类型、异常信息、调用位置,便于异常定位
// * @param tag TAG 名称
public static void d(String szTAG, Exception e, StackTraceElement[] listStackTrace) { * @param e 异常对象
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) { * @param stackTrace 线程调用栈
StringBuilder sbMessage = new StringBuilder(e.getClass().toGenericString()); */
sbMessage.append(" : "); public static void d(String tag, Exception e, StackTraceElement[] stackTrace) {
sbMessage.append(e.getMessage()); if (isLoggable(tag, LOG_LEVEL.Debug)) {
sbMessage.append(" \nAt "); StringBuilder sb = new StringBuilder();
sbMessage.append(listStackTrace[2].getMethodName()); // 拼接异常信息
sbMessage.append(" ("); sb.append(e.getClass().toGenericString())
sbMessage.append(listStackTrace[2].getFileName()); .append(" : ")
sbMessage.append(":"); .append(e.getMessage())
sbMessage.append(listStackTrace[2].getLineNumber()); // 拼接调用栈信息
sbMessage.append(")"); .append(" \nAt ")
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, sbMessage.toString()); .append(stackTrace[2].getMethodName())
.append(" (")
.append(stackTrace[2].getFileName())
.append(":")
.append(stackTrace[2].getLineNumber())
.append(")");
saveLog(tag, LOG_LEVEL.Debug, sb.toString());
} }
} }
// /**
// 调试日志写入函数 * 输出 Verbose 级别日志(最详细级别)
// * @param tag TAG 名称
public static void v(String szTAG, String szMessage) { * @param message 日志内容
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Verbose)) { */
saveLog(szTAG, LogUtils.LOG_LEVEL.Verbose, szMessage); public static void v(String tag, String message) {
if (isLoggable(tag, LOG_LEVEL.Verbose)) {
saveLog(tag, LOG_LEVEL.Verbose, message);
} }
} }
// /**
// 日志文件保存函数 * 核心日志保存方法(将日志写入文件)
// * 日志格式:[级别] [时间戳] [TAG]
static void saveLog(String szTAG, LogUtils.LOG_LEVEL logLevel, String szMessage) { * 日志内容
* @param tag TAG 名称
* @param logLevel 日志级别
* @param message 日志内容
*/
private static void saveLog(String tag, LOG_LEVEL logLevel, String message) {
BufferedWriter writer = null;
try { try {
BufferedWriter out = null; // 以追加模式打开日志文件UTF-8 编码
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(_mfLogCatchFile, true), "UTF-8")); writer = new BufferedWriter(
out.write("[" + logLevel + "] " + mSimpleDateFormat.format(System.currentTimeMillis()) + " [" + szTAG + "]\n" + szMessage + "\n"); new OutputStreamWriter(
out.close(); new FileOutputStream(sLogFile, true),
"UTF-8"
)
);
// 拼接日志内容(级别 + 时间 + TAG + 消息)
String logContent = String.format(
"[%s] %s [%s]\n%s\n",
logLevel.name(), // 日志级别(如 Debug
sSimpleDateFormat.format(System.currentTimeMillis()), // 时间戳
tag, // TAG 名称
message // 日志内容
);
// 写入文件
writer.write(logContent);
} catch (IOException e) { } catch (IOException e) {
LogUtils.d(TAG, "IOException : " + e.getMessage()); // 日志写入失败时,输出内部调试日志
} d(TAG, "日志写入失败:" + e.getMessage());
} } finally {
// 关闭流,避免资源泄漏
// if (writer != null) {
// 历史日志加载函数 try {
// writer.close();
public static String loadLog() { } catch (IOException e) {
if (_mfLogCatchFile.exists()) { e.printStackTrace();
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");
} }
} catch (IOException e) {
LogUtils.d(TAG, "IOException : " + e.getMessage());
} }
return sb.toString();
} }
return "";
} }
// /**
// 清理日志函数 * 加载历史日志(读取日志文件所有内容)
// * @return 历史日志字符串(空字符串表示文件不存在或读取失败)
public static void cleanLog() { */
if (_mfLogCatchFile.exists()) { public static String loadLog() {
try { // 日志文件不存在,返回空
UTF8FileUtils.writeStringToFile(_mfLogCatchFile.getPath(), ""); if (sLogFile == null || !sLogFile.exists()) {
//LogUtils.d(TAG, "cleanLog"); return "";
} catch (IOException e) { }
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
StringBuilder logContent = new StringBuilder();
BufferedReader reader = null;
try {
// 以 UTF-8 编码读取日志文件
reader = new BufferedReader(
new InputStreamReader(
new FileInputStream(sLogFile),
"UTF-8"
)
);
String line;
// 逐行读取并拼接
while ((line = reader.readLine()) != null) {
logContent.append(line).append("\n");
} }
} catch (IOException e) {
// 读取失败时,输出内部调试日志
d(TAG, "日志读取失败:" + e.getMessage());
} finally {
// 关闭流,避免资源泄漏
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return logContent.toString();
}
/**
* 清理历史日志(清空日志文件内容)
*/
public static void cleanLog() {
if (sLogFile == null || !sLogFile.exists()) {
return;
}
try {
// 写入空字符串到文件,实现清空
UTF8FileUtils.writeStringToFile(sLogFile.getPath(), "");
} catch (IOException e) {
// 清空失败时,输出内部调试日志(带调用栈)
d(TAG, e, Thread.currentThread().getStackTrace());
}
}
/**
* 辅助方法:创建文件夹(不存在则创建)
* @param dir 目标文件夹
*/
private static void createDirIfNotExists(File dir) {
if (dir != null && !dir.exists()) {
dir.mkdirs();
} }
} }
} }

View File

@@ -213,7 +213,7 @@ public class LogView extends RelativeLayout {
} }
// 加载标签列表 // 加载标签列表
Map<String, Boolean> mapTAGList = LogUtils.getMapTAGList(); Map<String, Boolean> mapTAGList = LogUtils.getTagEnableMap();
boolean isAllSelect = true; boolean isAllSelect = true;
for (Map.Entry<String, Boolean> entry : mapTAGList.entrySet()) { for (Map.Entry<String, Boolean> entry : mapTAGList.entrySet()) {
if (entry.getValue() == false) { if (entry.getValue() == false) {
@@ -237,7 +237,7 @@ public class LogView extends RelativeLayout {
mSelectAllTAGCheckBox.setOnClickListener(new View.OnClickListener(){ mSelectAllTAGCheckBox.setOnClickListener(new View.OnClickListener(){
@Override @Override
public void onClick(View v) { public void onClick(View v) {
LogUtils.setALlTAGListEnable(mSelectAllTAGCheckBox.isChecked()); LogUtils.setAllTagsEnable(mSelectAllTAGCheckBox.isChecked());
//LogUtils.setALlTAGListEnable(false); //LogUtils.setALlTAGListEnable(false);
//mTAGListAdapter.notifyDataSetChanged(); //mTAGListAdapter.notifyDataSetChanged();
mTAGListAdapter.reload(); mTAGListAdapter.reload();
@@ -483,7 +483,7 @@ public class LogView extends RelativeLayout {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
LogUtils.setTAGListEnable(item.getTag(), ((CheckBox)v).isChecked()); LogUtils.setTagEnable(item.getTag(), ((CheckBox)v).isChecked());
} }
}); });