嵌入式没仿真器真的就是只能打log了
内容提要
本文介绍了在Linux系统中打log的原则和log格式的原则,强调了打log的重要性以避免问题的发生和解决问题时的困难。
延伸解读
日志格式:定位问题的关键要素
文章强调日志必须包含时间戳(最好微秒级)、文件名和行号、进程ID、线程ID以及日志级别。这些要素共同作用:时间戳帮助还原问题场景,微秒级精度可辅助判断多线程死锁;文件名和行号直接定位代码位置,还能防止代码拷贝后的责任推诿;进程和线程ID在多进程或多线程环境下追溯行为;日志级别则便于按紧急程度过滤问题。缺少任一要素都可能增加排查难度。
内存分配失败:打印申请大小揭示碎片问题
作者通过亲身经历说明,仅记录“Failed to allocate memory”不足以定位问题。当进程运行一天后频繁内存失败,但系统空闲内存充足、进程占用也不高时,打印申请大小后发现请求的块从64K到16M不等,怀疑是内存碎片导致。将大数组改为指针后问题消失。这提示在内存失败日志中附加申请大小,能帮助判断是否因碎片或大块分配引发故障。
关键操作日志:为维护人员提供自诊断依据
文章列举了文件操作、socket连接、数据库操作、创建进程和解析文件等场景,要求失败时打印文件名、IP端口、SQL语句、程序参数、错误码等详细信息。这样在客户环境中,维护人员可自行验证问题,例如确认目标主机是否在线、端口配置是否正确、文件权限是否足够,从而减少对开发人员的依赖,降低沟通成本。作者认为可维护性比日志中暴露IP等细节的安全性更重要。
Q&A
在Linux下用C/C++开发,打log时应该遵循哪些基本原则?
打log有两个基本原则:一是在异常分支或错误处理时务必打log;二是在进行重大操作时务必打log。这样能避免问题发生时难以排查。
为什么日志格式里需要包含微秒级时间戳、文件名和行号?
微秒级时间戳主要用于多线程程序,如果进程停止响应,可以通过时间戳查看是否发生死锁等问题。文件名和行号用于定位问题根源,同时防止代码copy-paste后责任推诿。
多线程程序打log时为什么要加线程ID和进程ID?
加进程ID是因为有些log机制在进程重启时不会重新生成日志文件,而是直接追加,或者同一程序的多个进程同时运行可能写入同一个日志文件。加线程ID是因为在多线程程序中,如果不加线程ID,很难追溯程序的行为。
申请内存失败时,日志里除了打印失败信息还应该打印什么?为什么?
还应该把申请的内存大小打印出来。因为仅打印“Failed to allocate memory”无法定位问题,打印大小后可以发现是否是内存碎片导致,从而将大数组改为指针来进一步申请,避免问题复发。
操作socket失败时,日志应该记录哪些信息?
应该把IP、端口号(或Unix socket路径名)和错误码打印出来。这样在客户环境出错时,维护人员可以根据日志确认目标机器是否在线、服务是否开启或端口是否配置错误。
操作数据库时,日志中需要包含哪些详细信息?
需要把相应操作的IP、端口、库、用户名、SQL语句和错误打印出来。这样在客户环境下,维护人员可以通过命令行验证问题。虽然暴露这些信息可能涉及安全性,但可维护性比这种细节的安全性更重要。
创建新进程时,日志应该记录什么?为什么在客户环境特别重要?
需要把程序名、参数和错误码打印出来。在客户环境,由于运维人员水平参差不齐,可能误操作或漏操作导致文件缺失或权限出错,这种错误在公司的模拟环境根本不会出现,详细的日志能减少很多工作量。