日志概述
日志就是记录程序的正常或者异常行为,或者用户发生的操作、操作的流程、结果等。
为什么要记录日志?
我们平时在写程序代码过程中,一般会把主要精力集中在功能实现上,然后自测代码的时候,可以通过多种手段调试、定位问题,往往会忽视日志的重要性。
然而在功能上线后一旦发生异常,第一件事就是要弄清楚当时发生了什么,用户当时做了什么操作,数据是怎么变化的,是否是环境的原因,是不是反复发生等,然后再进一步的确定大致是哪个方面的问题。确定是程序的问题之后再交由开发人员去重现并提出解决方案。
在这个过程中我们不能方便的打断点调试、删除或者还原错误数据重来等操作来定位线上问题,也不太方便让用户去回想操作的步骤或者让用户重新操作来定位线上问题。这时,日志就给我们提供了第一手的资料。
日志打印规范
记录日志的基本原则
- 不能影响系统的正常运行;
- 不允许因为打日志产生安全问题;
- 不允许在日志里输出密码等机密信息;
- 日志要采用合适的级别;
- 日志要采用统一的格式
- 日志要具有可读性,可供开发人员快速定位问题的真正原因;
- 日志要可以供监控系统自动监控与分析;
- 不要输出无意义的信息到日志,也不要什么信息都输出到日志(输出太多无意义的日志会导致日志文件很大,以至于把服务器磁盘空间占满,造成生产问题)
日志文件命名
通常来说日志文件的命名可包括以下几个关键信息:
- 类型标识(logTypeName)
- 日志级别(logLevel)
- 日志生成时间(logCreateTime)
- 日志备份编号(logBackupNum)
类型标识:指此日志文件的功能或者用途,比如一个web服务,记录http请求的日志通常命名为request.log或者access.log,request、access就是类型标识,而java的gc日志通常命名为gc.log,这样看一目了然;而通常用来记录服务的整体运行的日志一般用服务名称(serviceName、appKey)或者机器名(hostName)来命名,如 nginx.log;
日志级别:打印日志的时候直接通过文件来区分级别是一种比较推荐的方式,如果把所有级别的日志打到同一个日志文件中,在定位问题时,还需要去文件中进行查找操作,相对繁琐。
常用的日志级别一般包括DEBUG、INFO、WARN、ERROR这几个级别,在实际编写代码中,可以采取严格匹配模式或者非严格匹配模式,严格匹配模式即INFO日志文件中只打印INFO日志,ERROR日志文件只打印ERROR日志;非严格匹配模式即INFO日志文件可以打印INFO日志、WARN日志、ERROR日志、FATAL日志,WARN日志文件可以打印WARN日志、ERROR日志,以此类推。
日志生成时间:即在日志文件名称中附带上日志文件创建的时间,方便在查找日志文件时进行排序;
日志备份编号:当进行日志切割时,如果是以文件大小进行滚动,此时可以在日志文件名称末尾加上编号;
日志滚动
虽然日志中能够保存系统运行时的关键信息,但是由于服务器磁盘空间有限,所以我们不能无限制地保留日志,因此必须有日志滚动策略。日志滚动通常有以下几种模式:
第一种:按照时间滚动
第二种:按照单个日志文件大小滚动
第三种:同时按照时间和单个日志文件大小滚动。
- 按照时间滚动,即每隔一定的时间建立一个新的日志文件,通常可以按照小时级别滚动或者天级别滚动,具体采取哪种方式取决于系统日志的打印量。如果系统日志比较少,可以采取天级别滚动;而如果系统日常量比较大,则建议采取小时级别滚动。
- 按照单个日志文件大小滚动,即每当日志文件达到一定大小则建立一个新的日志文件,通常建议单个日志文件大小不要超过500M,日志文件过大的话,对于日志监控或者问题定位排查都可能会造成一定影响。
- 按照时间和单个日志文件大小滚动,这种模式通常适用于希望保留一定时间的日志,但是又不希望单个日志文件过大的场景。
注意:对于日志滚动策略来说,有2个比较关键的参数:最大保留日志数量和最大磁盘占用空间。这2个参数切记一定要设置,如果没有设置,则很有可能会出现把线上机器磁盘打满的情况。
注意:对于日志滚动来说可以采用多种方式来实现,常见的就是代码层面的控制以及服务器层面的控制。代码层面更灵活一点,你可以任意定制自己需要的格式、命名、大小等(自己写了个例子,仅供参考);服务器层面就是根据系统提供的工具或者其他插件之类的东西实现,如Linux提供的日志滚动工具logrotate。
日志的级别
ERROR:系统发生了错误事件,需要修复才能正常工作。这种级别的错误是任何系统都无法容忍的,必须马上解决。
WARN:系统在业务处理时触发了异常流程,但并不影响系统接下来的运行。其实在这里还应该有两种级别:一个是不需要人工修复的问题(比如:缓存数据不存在,从数据库中重新读取等),另一个是应用程序可以容忍这些问题,不过它们应该被检查和及时修复。
INFO:记录系统关键信息,旨在保留系统正常工作期间关键运行指标,开发人员可以将初始化系统配置、业务状态变化信息,或者用户业务流程中的核心处理记录到INFO日志中,方便日常运维工作以及错误回溯时上下文场景复现
DEBUG:可以将各类详细信息记录到DEBUG里,起到调试的作用,包括参数信息,调试细节信息,返回值信息等等。
TRACE:更详细的跟踪信息。
上述日志级别从高到低排列,是开发中最常用的五种。生产系统一般只打印INFO 级别以上的日志,对于 DEBUG 级别的日志,只在测试环境中打印。打印错误日志时,需要区分是业务异常(如:用户名不能为空),还是系统异常(如:操作数据库异常),业务异常使用 warn 级别记录,比如用户输入参数错误的情况;系统异常使用 error 记录。
日志的格式
每一行日志中记录的信息应该包含:时间、日志级别、请求的类名、方法名、具体描述、参数
# 我们在实际打印日志的时候,可以只打印具体描述和参数信息,其它的部分可以在日志方法底层封装
Log::error(“[错误信息描述] [参数。。。]”);
Log::error(sprintf('[用户无权操作] userid:[%s]', $userId));
什么时候该打印日志
-
系统初始化:系统初始化时会依赖一些关键配置,根据参数不同会提供不一样的服务。将系统的启动参数记录INFO日志,打印出参数以及启动完成态服务表述。
-
重要方法入口:建议记录方法调用、入参、返回值,对于排查问题会有很大帮助。
-
业务流程与预期不符:项目代码中结果与期望不符时也是日志场景之一,简单来说所有流程分支都可以加入考虑。取决于开发人员判断能否容忍情形发生。常见的合适场景包括外部参数不正确,数据处理问题导致返回码不在合理范围内等等。
-
系统核心的关键动作:系统中核心角色触发的业务动作是需要多加关注的,是衡量系统正常运行的重要指标,建议记录INFO级别日志,比如电商系统用户从登录到下单的整个流程;微服务各服务节点交互;核心数据表增删改等等。
-
系统异常:这类捕获的异常是系统告知开发人员需要加以关注的,是质量非常高的报错。应当适当记录日志,根据实际结合业务的情况使用warn或者error级别。catch中的异常记录必须打印堆栈信息,不要记录日志后又抛出异常。抛出去的异常,一般外层会处理。如果不处理,那为什么还要抛出去?另外一个原则是,无论是否发生异常,都不要在不同地方重复记录针对同一事件的日志消息, 输出Exceptions的全部Throwable信息,否则会丢失最重要的堆栈信息。
日志的可读性
我们一定要明白日志是给人读的,不仅仅是让自己明白,也要让没有接触过我们代码的其他程序员也能够一目了然。
有的同学在日志中打印特殊的标识符号,例如“++++++++++”, “===========”,“—————”,这些符号令人眼花缭乱。如果你打印了一行日志,没有具体的描述或者打印了一堆乱七八糟的东西读不明白,这个日志就是没有可读性的日志。因为这些,在你排查问题的时候不能一下帮你确定问题的原因,是无效的、没有意义的日志。这是一种不好的编程习惯。
日志一定要说明原因,带上当时的参数,让你一下就能明白当时发生了什么,是不是代码有问题,这样的日志才具有可读性。
另外,把日志分类输出到不同的文件也有利于我们排除干扰,迅速找到我们需要的信息。而且,最好在打印日志时输出英文,防止中文不支持而打印出乱码的情况。
日志的性能
无论我们把日志写到文件还是数据库,都需要消耗IO资源。适当的控制日志的输出也有利于提高程序的性能。例如:尽量避免在在大的循环中打印意义不大的日志内容。
占用磁盘空间
通常,我们都是把日志写入磁盘上的日志文件中。适当的使用滚动日志并且定时清除旧文件是有好处的。我见过这样一个例子,程序运行几次后就跑不起来了,前几次都是正常的。怎么都想不明白程序有什么问题,最后才发现居然是日志文件占满了磁盘空间。在实际的应用中出现上G的日志文件也往往不少见。要在这样规模的日志文件中找出对解决问题有用的信息也是一大挑战。
其它注意项
-
不打印无意义日志:不记录对于排查故障毫无意义的日志信息,日志信息一定要带有业务信息。
-
不推荐字符串拼接:如果信息本身需要计算或合并的,不推荐使用字符串拼接的方式打印日志,可读性和可维护性都比较差。建议使用占位符的方式。
-
循环体内不要打印 INFO 级别日志
-
打印日志的代码任何情况下都不允许失败
-
日志文件推荐至少保存15天,因为有些异常具备以“周”为频次发生的特点。
-
避免重复打印日志,浪费磁盘空间
-
最好在打印日志时输出英文,防止中文不支持而打印出乱码的情况
-
谨慎地记录日志。生产环境禁止输出debug日志;有选择地输出info日志;如果使用warn来记录刚上线时的业务行为信息,一定要注意日志输出量的问题,避免把服务器磁盘撑爆,并记得及时删除这些观察日志。
注意:大量地输出无效日志,不利于系统性能提升,也不利于快速定位错误点。记录日志时请思考:这些日志真的有人看吗?看到这条日志你能做什么?能不能给问题排查带来好处?不要认为日志记录不怎么消耗性能,有很多事无巨细式的日志把系统性能严重拖慢的案例
日志是记录程序行为和用户操作的关键,尤其在上线后用于定位异常。本文介绍了日志的重要性,日志打印的原则,如不影响系统运行,确保安全性,统一格式,保持可读性。讨论了日志文件命名、日志级别(如ERROR, WARN, INFO, DEBUG)、日志滚动策略、日志格式、何时打印日志以及日志的性能和磁盘空间管理。强调日志应具有可读性,避免无意义的日志输出,以提高系统性能和便于问题排查。"
79239112,7403700,Mini UI Datagrid 数据加载问题解析,"['前端开发', 'HTML', 'CSS', 'JavaScript', '框架']
8121

被折叠的 条评论
为什么被折叠?



