浅谈程序日志

简介: 日志系统在软件程序中占有非常重要的地位,日志文件是排查程序问题的主要工具,是程序调试的利器。日志编写的总体原则是简单清晰、便于排查问题。作为一名合格的软件开发工程师,一定要学会日志函数的灵活调用及准确通过日志文件来定位程序问题。

如果世界上有一个人能够保证一次写出来的代码是百分之百正确的,那么毫无疑问,他一定是世界上最优秀的程序员,没有之一。为什么要求代码写好过后要进行充分的自测 ( 包括单元测试和集成测试 ) ?就因为是人皆会犯错,使程序就会有 bug 。作为一名软件开发人员,必须要学会对程序进行测试,也就是要学会程序的调试。

代码调试方法
一般而言,对代码的调试有以下几种方法:

第一,凭肉眼看 。在开发阶段,我们编写的每一行代码都需要用我们的“火眼金睛”多审查几遍。如果要问,最好的代码调试工具是什么?我认为是人眼。不管是代码还是文档,在用工具检查之前,都需要先过了我们眼睛这一关。

第二,对代码进行编译,以发现语法错误 。编译器能够帮助我们发现代码中存在的语法错误,但对于那些隐蔽性的错误 ( 如逻辑错误等 ) 无能为力。

第三,用代码检查工具 ( 如 Pclint 等 ) 来走查代码 。如果代码编译通过,并不表示它就没有问题了。在学校的时候,我们一般认为只要程序能够运行就可以了。但在实际的软件开发项目中,程序能够跑起来,只是“万里长征走完了第一步”。用代码检查工具可以发现很多编译器无法发现的错误,如变量定义了未引用、不同数据类型之间相互赋值、函数未声明便被调用等。

第四,对代码进行调试 。对于运行正常而输出结果不正确的程序,我们可以用设置断点并进行单步跟踪调试的方法来发现其中存在的问题。例如,在 VC++ 6.0 里面,可实现对代码的单步调试,并输出变量在某一步产生的值,可据此判断程序的逻辑的正确与否。

第五,对程序的日志文件进行分析 。对代码的单步调试只在代码行数较少的时候比较适用,如学校教材上面的程序。但在实际的软件项目中,代码少则几千行,多则数万行,用单步调试的方法显然不恰当。为了跟踪某一变量值的变化,用该方法可能要花费几个小时,这对工作效率产生了严重影响。为了解决大程序文件代码调试问题,日志系统应运而生。在程序中的重要地方打印日志,之后对产生的日志进行分析,可找到对应代码的问题。因此,日志文件分析成了大型软件项目中代码调试的主要手段。

什么是日志文件?
看过电视剧《神探狄仁杰》的朋友可能都会对狄仁杰的断案能力极为叹服,他会将一个外人看来非常诡异的案件查得水落石出,连武则天都不得不说他是“神乎其技”。实际上,狄仁杰也是凡人,他只是通过极为细小的线索来顺藤摸瓜发现幕后的黑手。对应到软件开发上,很多厉害的程序员都是通过分析程序运行过程中产生的少量的异常日志来发现软件问题的。因此,程序日志就像断案线索一样重要。

在业务软件系统中大量使用日志,日志能够起到“按图索骥”的作用,它对于故障定位和系统正常运行维护具有举足轻重的作用。

日志文件是程序中写日志函数产生的记录程序执行情况的文件。写日志函数可以用多种编程语言编写,可以像普通的函数一样被调用。在恰当的地方调用该函数,可对整个程序的运行状况有一个全面的了解,方便对程序的跟踪调试。

日志等级
事有轻重缓急,日志信息也有重要与不重要之分。一般按照重要程度,将日志等级分为几类。在作者参与过的软件开发项目中,共有 7 个等级,用宏定义表示如下:

// 日志等级定义
#define LOG_FATAL         (int)1     // 严重错误
#define LOG_ERROR         (int)2     // 一般错误
#define LOG_WARN          (int)3     // 警告信息
#define LOG_INFO          (int)4     // 一般信息
#define LOG_TRACE         (int)5     // 跟踪信息
#define LOG_DEBUG         (int)6     // 调试信息
#define LOG_ALL           (int)7     // 全部

开发人员根据所要打印的日志的具体情况采用不同的日志等级。

日志配置
由于不同产品程序行数、部署情况、实现功能等的差别,对日志打印的要求也不尽相同,因此需要有配置来控制日志的产生数量和显示情况。

例如,在笔者所参与的开发项目的配置文件中,有一个专门的 [LOG] 配置段,其中的配置项如下:

 [ LOG ]
; 日志等级 , 0-Fatal 1-Error 2-Warn 3-Info 4-Trace 5-Debug 6-All
LogLevel =
; 每个日志文件的最大容量
LogMaxSize =
; 是否输出该条日志在代码中的行数 , 1-Yes 0-No
LogPosition =

其中, LogLevel 用于控制打印日志的等级,代码中日志等级比配置值大的日志信息均不在日志文件中显示; LogMaxSize 用于控制生成一个日志文件的大小的上限,超过该值后,便重新生成文件; LogPosition 用于控制是否在日志文件中显示代码行数,方便将日志与代码对应起来。

日志函数的调用
日志函数的调用遵循一般函数的调用规则。例如,在笔者所参与的开发项目中,有两类写日志函数,如下所示:

(1) 第一类形如: WriteLog(LogLevel, LogInfo) 。其中,参数 LogLevel 指日志等级 ( 见第 2 节中的说明 ) ;参数 LogInfo 是具体要打印的日志信息,我们据此信息来检查程序的运行情况。该函数的调用示例如: WriteLog(LOG_INFO, "The value of this integer is 3.") ,日志等级为 LOG_INFO ,日志信息为“ The value of this integer is 3. ” ( 该信息会输出到日志文件中 ) 。

(2) 第二类形如: WriteLogEx(LogLevel, LogInfo, ParaInfo) 。这是扩展的日志函数,不但能够输出日志信息,还能够在日志信息中显示变量的值。该函数的调用示例如: WriteLogEx(LOG_INFO, "The value of integer iInt is %d.", iInt) ,该日志要输出整型变量 iInt 的值,可以将该函数的调用与 printf 函数的调用比较起来看 ( 可以认为 WriteLogEx 函数只是在 printf 函数中增加了一个日志等级参数 ) 。

日志编写基本原则
1) 显式输出,关键信息必须输出;

2) 在编码时使用正确的日志级别, error 错误和 warning 错误必须反应出实在的含义,不是特别严重的问题不能将日志等级定义为 LOG_FATAL ;

3) 在写日志描述时,要使用正常简单易懂的语言,不能使用晦涩难懂的语言或某些专业术语;

4) 在极少数特殊情况不希望用户知道时,可使用特殊日志标记;

5) 为了写出优美的代码,在自己修改或添加代码的地方,都要正确的打上标记 ( 包括作者、日期信息等 ) ,方便追踪版本的演进情况。

日志编写基本要求
1) 分多条信息分别输出,不要企图一次将所有信息打印出来;

2) 分时输出;

3) 必须分日志级别,这样可根据等级迅速对日志进行分析;

4) 控制日志信息的条数,不重要的信息尽量不要打印日志。

输出日志位置要求
1) 所有的输入输出,包括收消息和发消息都要求输出日志;

2) 关键控制点必须输出日志;

3) 调用底层或第三方软件,必须输出日志,而且对不可靠底层,必须加上 begin/end 两行日志;

4) 对方系统处理时间必须输出日志,以利以后维护时快速定位性能问题。

一些注意事项
1) 在编写日志时需要注重日志细节,目标是为了方便以后维护,在遇到问题时,可以快速定位问题;

2) 不要在同一行中写意思重复的日志;

3) 日志需要足够的精简,不要随意换行;

4) 日志中字段之间可以用空格或其它符号分断,不能将日志一直连续而不将其分断,尽量使日志本身具备进行“识文断句”的能力;

5) 对于日志中的特殊信息 (如会话号、 IP 地址等) ,用特殊的符号进行标识,其主要目的是为了便于搜索。

总结
日志系统在软件程序中占有非常重要的地位,日志文件是排查程序问题的主要工具,是程序调试的利器。日志编写的总体原则是简单清晰、便于排查问题。作为一名合格的软件开发工程师,一定要学会日志函数的灵活调用及准确通过日志文件来定位程序问题。
“实践出真知”,只有通过不断的积累和总结,才会对日志有更全面的认识。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
JSON 应用服务中间件 nginx
filebeat收集json格式的nginx程序日志(二)
filebeat收集json格式的nginx日志 1.为什么要收集json格式的日志类型 由于nginx普通日志收集过来的日志内容都是存在一个字段中的值,我们想单独对日志中的某一项进行查询统计,比如我只想查看某个IP请求了我那些页面,一共访问了多少次,在普通的日志中是无法过滤的,不是很满意
776 0
filebeat收集json格式的nginx程序日志(二)
|
6月前
实际案例分析 - 根据应用程序日志的记录,反查出哪一行 ABAP 代码产生的这条日志试读版
实际案例分析 - 根据应用程序日志的记录,反查出哪一行 ABAP 代码产生的这条日志试读版
70 0
|
9月前
|
C++
VS-2019-.NET-C#使用log4net打日志,程序日志记录
VS-2019-.NET-C#使用log4net打日志,程序日志记录
105 0
|
9月前
|
存储 消息中间件 Java
SpringBoot程序日志极简教程
Slf4j简介 Java的简单日志记录外观(Simple Logging Facade for Java )可作为各种日志记录框架(例如java.util.logging,logback,log4j,log4j2)的简单外观或抽象,允许终端用户在开发时插拔所需的日志记录框架。简单来说,Slf4j定义了一种规范,java程序在记录日志时候的规范,这种规范是一个空壳,在实际开发中需要集成具体的日志框架来干活,这种具体的日志框架需要满足一些标准:符合Slf4j定义的标准;能够提供日志记录的功能。 Logback简介 一个“可靠、通用、快速而又灵活的Java日志框架”。logba
53 0
SpringBoot程序日志极简教程
|
存储 缓存 Go
第四十二章 构建数据库应用程序 - 在ISCLOG中启用日志
第四十二章 构建数据库应用程序 - 在ISCLOG中启用日志
|
消息中间件 Java 中间件
订阅 OceanBase CLog 日志(Java程序)
简介: 随着 OceanBase 数据库的开源,越来越多的企业开始使用 OceanBase,也有很多个人、机构开始学习 OceanBase,我也是其中之一。后续计划将自己的学习经验陆续总结出来,欢迎大家一起讨论。考虑到数据库是一个博大精深的领域,如有写的不对的地方欢迎指正。 本文主要通过 OceanBase-Mini 版本、LogProxy、已经 Java 程序,实现实时订阅 OB CLog 日志,仅限学习场景使用,不适合生产。
641 0
|
边缘计算 监控 前端开发
利用阿里云Eventbridge在CDN边缘应用程序中访问日志服务SLS
在Web前端领域,追求极致性能是个永恒的话题。这些年的一些新兴理念都是为了提升站点访问性能而提出,无论是Jamstack技术理念或者或者 ESR (边缘渲染),都是Client侧进行性能优化的基础上,进一步拓展到了网络Infra层面,就是我们现在经常讨论的边缘计算。而阿里云的CDN EdgeRoutine 就是为广大客户提供可自由编程的边缘计算能力,我们可以用他来构建边缘节点的网关应用,来访问静态资源或者后端服务。当这个边缘网关在业务上承担的角色越来越重要的时候,我们就对他内部的业务逻辑产生了可观测的诉求,希望能够记录日志到日志服务上,然而日志服务提供的SDK,在ER的环境中暂时不被支持,这个
282 0
|
安全 应用服务中间件 nginx
Nginx代理浏览器可实时查看程序日志
我们经常需要在页面上实时查看nginx的日志输出,并且能在页面上显示,那么如何通过Nginx实现浏览器可实时查看访问日志呢?
Nginx代理浏览器可实时查看程序日志
|
Linux Python
【Linux】nohup后台运行程序并打印日志
在/home/coggle目录下在你英文昵称(中间不要有空格哦)的文件夹中创建一个sleep.py文件,该文件需要完成以下功能:程序一直运行每10秒输出当前时间
542 0
【Linux】nohup后台运行程序并打印日志
|
设计模式 JSON Java
在云环境上使用SLF4J对Java程序进行日志记录
在云环境上使用SLF4J对Java程序进行日志记录
148 0
在云环境上使用SLF4J对Java程序进行日志记录