67、日志系统:Log类使用、Logger库、自定义日志输出、日志文件保存
日志这东西,平时开发时觉得不起眼,一旦线上出了bug,它就是你的救命稻草。我见过太多项目,日志打得乱七八糟,出了问题根本没法定位。今天咱们就把日志系统彻底捋一遍。
Android原生Log类,你真的会用吗?
Android自带的android.util.Log,大家肯定都用过。但说实话,很多人只用了皮毛。
Log.v(TAG, "这是verbose日志");
Log.d(TAG, "这是debug日志");
Log.i(TAG, "这是info日志");
Log.w(TAG, "这是warning日志");
Log.e(TAG, "这是error日志");
五个级别,从低到高:VERBOSE、DEBUG、INFO、WARN、ERROR。我个人的习惯是:
- 开发调试用DEBUG,上线前全部关掉
- 关键业务流程用INFO
- 异常捕获用WARN
- 崩溃和严重错误用ERROR
- VERBOSE?说实话,我几乎不用,太啰嗦了
小技巧:TAG命名最好统一规范。我一般用类名做TAG,比如LoginActivity。这样看logcat时一眼就知道是哪个类输出的。
为什么需要Logger库?
原生Log有个硬伤——没法控制开关。你想想看,DEBUG日志上线后还在打印,不仅浪费性能,还可能泄露敏感信息。
所以业界普遍用Logger库来封装一层。我早期项目用过Logger、Timber,后来干脆自己写了一个。核心思路就一个:可配置、可开关、可扩展。
public class Logger {
private static boolean isDebug = BuildConfig.DEBUG;
private static String defaultTag = "AppLog";
public static void init(boolean debug) {
isDebug = debug;
}
public static void d(String tag, String msg) {
if (isDebug) {
Log.d(tag, msg);
}
}
public static void e(String tag, String msg) {
// 错误日志即使上线也保留
Log.e(tag, msg);
// 同时写入文件
writeToFile(tag, "ERROR", msg);
}
}
你看,这样一封装,DEBUG日志只在debug模式下打印,而ERROR日志无论什么模式都保留,并且自动写入文件。这就是Logger库的价值所在。
核心原则:日志系统要区分「开发用」和「运维用」。开发日志可以随意,运维日志必须持久化。
自定义日志输出,满足你的特殊需求
有时候,光打印到logcat不够。比如你需要:
- 把日志发送到远程服务器
- 把日志写入本地文件
- 把日志格式化后展示在调试界面
这时候就需要自定义输出策略。我常用的做法是定义一个接口:
public interface LogPrinter {
void print(int level, String tag, String message);
}
然后实现不同的Printer:
public class FilePrinter implements LogPrinter {
private File logFile;
public FilePrinter(File file) {
this.logFile = file;
}
@Override
public void print(int level, String tag, String message) {
String line = formatLog(level, tag, message);
writeLineToFile(line);
}
}
这样做的好处是啥?解耦。你想加什么输出方式,就加一个Printer实现类,完全不影响现有逻辑。
注意:文件写入操作一定要放在子线程。我曾经见过有人直接在UI线程写日志文件,结果卡到ANR。日志系统本身不能成为性能瓶颈。
日志文件保存,这些坑你踩过吗?
日志写到文件里,听起来简单,但细节很多。我总结了几条经验:
| 问题 | 解决方案 |
|---|---|
| 日志文件越来越大 | 按天分割,设置最大大小(比如5MB) |
| 磁盘空间被占满 | 自动清理7天前的日志 |
| 多进程同时写 | 加文件锁,或者用独立进程写日志 |
| 日志内容敏感 | 脱敏处理,比如手机号中间四位用****代替 |
嗯,这里要特别说一下文件分割。我习惯用日期+序号命名:
log_2024_01_15_001.txt
log_2024_01_15_002.txt
每个文件达到上限就新建一个。这样查找某一天的日志非常方便。
我的做法:日志文件保存在应用的私有目录下,不要放在SD卡公共目录。一方面安全,另一方面Android 11以后对公共目录访问限制更严了。
知识体系总览
下面这张图,把日志系统的核心脉络画出来了。你看一眼就能明白整体架构:
从图上能看出来,日志系统分四层:来源层、封装层、输出层、管理层。每一层各司其职,互不干扰。这就是我推崇的架构思路。
避坑指南
最后分享几个我踩过的坑:
- 不要打印敏感信息:我曾经在日志里打了用户密码,结果被安全团队通报批评。密码、token、身份证号,一律脱敏或直接不打。
- 控制日志频率:循环里打日志要小心。有一次我在for循环里打了DEBUG日志,结果一次操作生成了几百MB的日志文件。
- 异步写入要防丢失:应用崩溃时,内存中的日志可能来不及写入文件。我的做法是每写一条就flush一次,虽然性能差点,但数据安全更重要。
日志系统看似简单,但做好不容易。它就像飞机的黑匣子,平时没人关注,出事时才知道有多重要。希望今天的分享能帮你少走弯路。
公众号:蓝海资料掘金营,微信deep3321