关于程序打印log的方式研究
曾有人这么评价我的代码:“你这代码关键步骤都没有log输出,怎么知道程序运行到哪儿了呢,至少也应该在控制台输出一句话吧"
仔细一想,这话说得十分中肯,也确实正确,掐指算算我的码龄,也确实该把打log的方式给统一 一下了,但是请注意,本篇文章仅从最实用的角度来介绍一下打印log的方式,当然还有更专业的,这里暂不涉及。
1. 为什么要打log?
如果项目上过线的话,那你一定知道Log是多么重要。
别人的代码跑起来控制台上会不断输出各种语句,一旦程序卡住了瞬间就知道哪个地方出的错,而你的程序从头到尾运行起来控制台安安静静,一旦卡住,满脸疑惑,然后开启一天写代码,一周来检错的过程,唉~。
事实上,事情并没有上边说的那么简单,当项目即将上线,突然却蹦出来几个问题,这时打一手好Log的重要性绝不亚于写一手好代码。
同时,如果一个项目是分工合作的,当项目出问题时,还能拿出Log证明自己负责的部分没有问题,即使是自己的问题,也能从Log里快速找出错误原因。
这就是为什么要打log的原因。
2. 我们是如何打log的?
纵观九年义务教务+七年自费教育,目前没发现哪位老师来讲解如何打一手漂亮的log,于是,我们普遍打log的方式是这样的:
(1)数字型
printf("0000000");
这种怎么用呢?
就是发现控制台出现了000000的时候,说明了程序运行到这里是没问题的。
优点:简便、快速;
缺点:1天过后鬼知道这是什么东西;
(2) 字符型
printf("hello world");
printf("here is main function");
这样写仿佛就聪明的多,因为可以简单说明一下这是什么;
优点:直观;
缺点:3天后鬼知道这是什么东西;
(3) 特殊字符型
printf(">>> attention:here is main function")
这个写的已经初步具备大师的水准了,因为有些大师就是这么写的,只是没有这么简陋罢了;
优点:清晰,直观
缺点:关键信息不足,如行号之类
这里只简单汇总几种大家普遍使用的方式,当然还有更离谱的,比如说随便敲一段字母上去,或者说这个地方这样打,那个地方那样打,最终程序运行起来乱七八糟。
当然,只要尝试去打log的人都是值得肯定的,比不打log的人已经前进了一大步。
那么如何打一手好看、统一、有条理的log呢?
3. 如何打一手漂亮的log?
(1)首先给自己log设计一个漂亮的格式
根据经验,有些地方log是这么打的:
printf("-test_1 function make done")
- test_2 function make done
- test_3 function make done
明眼人一眼看出,这不就是cmake打log的方式?哈哈,是的。
这里分享一下我的打log方式:
printf("[message] test_1 function make done")
printf("[message] test_2 function make done")
printf("[message] test_3 function make done")
其中中括号里填写log的分类,后边写上要写的内容,分类的话主要有四种:message、error、debug、warning;
这里我细说一下这四种log常用的地方:
- message: 输出提示信息,比如某个重要函数的执行,某个循环变量的数值;
- error: 输出错误信息,这个经常配合异常抛出进行使用,可以在抛出时加上自己备注的一些话;
- debug: 有一些输出语句是不适合在产品上线的时候输出的,那么可以加在debug里,产品上线时debug开关一键关闭,所有备注有[debug]的语句都会消失;
- warning:在逻辑判断时如果输入的信息不符合要求但是又没有导致出错的地步,可以输出一句warning语句,一旦出现错误可以进行回溯一下,避免手足无措;
(2)准确把握输出log的时机
初学打log的时候经常会出现这样的问题:log确实是打了,但是出现问题后翻log翻了半天,这是为什么呢?
因为没有把握住输出log的时机,有的甚至在循环里不停的输出无关简要的log,或者是每写一句话就输出一句log,那最终的结果当然是log满天飞,没一个重要的。
那么该怎么做呢?我这里总结了一下:
- 函数开始结束处:在重要函数的开始结束出应该打上log ,这样在看log时会比较直观,什么时候开始什么时候结束就会一目了然,万一中间出异常导致程序退出了,也知道是在哪个函数突然中断的,也同样适用于一个重要逻辑块的开始结束;
- 返回结果:尽量在重要函数或接口的每个返回分支打印返回结果,在出现不好分析的异常时,从细节下手,这时log会派上用场;
- 成功失败标志:如果某个函数是做一件比较关键的事情,那么这件事情成功还是失败了,要打印log;
- 关于耗时:访问一个第三方接口、上传下载文件等可能耗时的操作,都要记录完成这个操作所耗的时间,否则程序性能出了问题,你不知道是网络原因呢,还是你调用的第三方接口性能出现问题呢,还是你自己程序的问题呢;
- 关于数量:涉及到数量的操作要打印log,比如查询数据库和批量拷贝文件、上传下载、批量格式转换等批量操作,设计到的数量要打印出来;
(3)准确把握应该输出的内容
应该输出什么内容上边我也说到了一些,这里还是归纳一下:
运行时间、类型或函数名、行号(error和warning很需要这个)、重要提示(如果是异常需要完善异常信息,比如堆栈信息,也可以是自己的备注)、前后log的顺序信息;
番外篇:
如果想让自己的log输出有些不一样,可以使用以下方式进行操作:
cout << "\033[30;1m这是黑色\033[0m" << endl;
cout << "\033[31;1m这是红色\033[0m" << endl;
cout << "\033[32;1m这是绿色\033[0m" << endl;
cout << "\033[33;1m这是黄色\033[0m" << endl;
cout << "\033[34;1m这是蓝色\033[0m" << endl;
cout << "\033[35;1m这是紫色\033[0m" << endl;
cout << "\033[36;1m这是青色\033[0m" << endl;
cout << "\033[37;1m这是白色\033[0m" << endl;
学海无涯,不一定要学完所有的东西,实用最重要,这就是本次的经验分享,希望对你有所帮助。
(不用点赞,你要是担心下次找不到了,可以收藏一下,当然,有补充的话评论区留言最好,感谢感谢)