前言

在软件开发过程中,log往往是不太引人注意的环节,大部分的log都只是开发人员为了调试bug临时加上的。这样出来的软件,最后的log往往杂乱无章没有系统性。我们判断一个软件系统的log写的好不好,可以通过以下几点:

  • 当软件交付给其他人使用出现bug,他们是否能够凭借log对问题有一个比较准确的推断。
  • 当开发者解决bug的时候,通过更改不同的log级别是否可以快速准确的定位具体问题所在。
  • 当软件生产环境运行时,运维人员能否通过看log就大致了解系统状况和工作情况。

好的log往往各不相同,但是不好的log基本特点都是一样的:

  • 描述不清,杂乱无章,大量临时添加的log语句在其中。比如web server log里常见的一行"conntected",这往往是开发人员开发时为了验证连接是否成功加上的。而且在这个log前后往往还会出现"trying to connect", "connecting ...",以及"connected to ..."之类的重复。开发人员为了着急完成功能,往往会加入大量这样的log,但是这些log在系统中留下了杂乱的痕迹。

    我们应该尽可能准确丰富的用log表达系统行为,对于刚刚的例子,应该在刚开始连接时加入一个"debug"级别的"connecting Database/Web Admin Server/.. ...",这里不仅描述了连接url,还描述了连接的server的业务功能,这样如果有很多个连接建立时,log里可以清晰分辨,定义为"debug"级别是因为我们只需要在开发过程和调试bug时知道有这个行为即可,在生产环境中不需要反复提示。然后我们在连接的错误处理中打连接失败的log,"connection to Database/Web Admin Server .. failed, due to ..."。通常情况下,连接成功不用再打log了,因为系统会进行下一步操作。当然也可以加一个“debug”级别的“connection to Database/Web Admin Server .. established"

  • 在真正调试bug时,又需要东加一行log,西删一行log。当开发的软件出现问题时,开发人员往往发现以前写的log都用不上,解决问题需要添加很多log才行,而且还要删除很多其它的log免得log太杂乱看不清。这个其实是开发人员的经验不足,在开发过程中预计不到可能出问题的点和解决问题可能需要的条件。比如在写一个与http server以json格式通信的模块时,我们可以先用”debug“级别写一个”send [function] request to Database/Admin server", 这里用“debug”级别同样因为在生产环境时我们可以方便的关掉它,而在debug级别我们又充分描述了系统的行为。但是这样往往是不够的,我们经常会需要了解发送的具体内容进行问题诊断, 所以我们可以在后面加入一个log语句,如果有,最好用更细的级别比如"trival"之类的,记录下log("trival", "request content : " + request.body)。这样我们可以随时把log级别调到trival来详细诊断问题。等等,这样就够了么?其实还不够,如果这么写log,真出先问题的时候你会发现request content非常长,根本不能直观的来观察内容。最好的办法,我们在这里把request.body按照一定的缩进格式打印,变成JSON.stringify(request.body, null, 4),这样的log就非常直观明了了。

  • 层叠的Exception。首先明确一点,Exception是用来帮助解决未知问题的。所以它会有展开的调用栈让我们知道问题出在哪里。而如果一个已经写好的软件系统,是不应该还有未知问题出现的,如果出现了,应该作为bug尽快fix。对于一个完成的软件系统,所有的Exception都应该是被有预计的catch并且处理,然后在处理这个Exception的那一层,用简单明了的log记录下来。

      注:接下来的内容为个人见解,请每个读者独立思考合理和不合理之处。

在写log的时候,我们需要结合以下三点来考虑:

  • 这个log在系统工作过程中的出现次数
  • 这个log适合的级别(生产级别,开发级别和调试级别),对整体log的影响。
  • 这个log能表达的信息量,是否跟其它log有重复,是否可以在其它位置写表达更丰富的信息

接下来我们以一个Web系统为例子讲一下log的写法

启动

启动时候,系统需要读取一些配置文件,这个时候需要打印一些读取到的参数之类的,结合上面三点,它的出现次数一般就一次,因此对于级别没有什么要求,就写成info级别即可,表达信息时注意描述准确。

收到请求

在收到请求时,我们可以考虑打一条log表示收到,但是如果系统通讯量很大,这个log的出现次数太多,因此不适合于info级别(info一般是用在生产环境中),通常会定义成debug级别。另外,根据刚刚讲到的,我们经常会需要把收到的request内容打在log中,但是如果把内容整个打印出来会很占篇幅,如果我们是在定位问题和解决其它问题时,这个log干扰有点大。因此我们用debug级别来仅仅描述收到了request,另外再用一个更精细的级别来把请求内容打印出来。

业务逻辑

收到请求后,我们通常要开始业务逻辑,这里参考上面的办法,用”debug"来简要描述各个业务的关键点,而用更细的级别来描述每个点所涉及到的具体数据。注意,info级别因为用在生产环境中,我们通常只需要用info级别描述结果即可,不要用info去描述过程。

错误处理

错误处理有一条很基本的原则:谁处理错误谁打log。这样可以避免大量重复内容的log出现,并且按照前面所说,错误的栈信息通常是不用打印的,处理错误的函数应该能用准确的语言归纳描述错误原因和处理办法。

后记

软件系统好比一个妹子,功能,UI之类的是妹子的外表,往往会第一时间吸引人。但是用户体验(...为毛我有奇怪的联想),log这些东西是内在的品性。这些是真正能让人觉得好用(....呃)的点,也是能真正体现出设计者内功差异的地方。

好吧,你就是只看外表,不求朝朝暮暮就要一晌之欢?。。呃......

有关写log的思考的更多相关文章

  1. 关于Quartz.NET作业调度框架的一点小小的封装,实现伪AOP写LOG功能

    Quartz.NET是一个非常强大的作业调度框架,适用于各种定时执行的业务处理等,类似于WINDOWS自带的任务计划程序,其中运用Cron表达式来实现各种定时触发条件是我认为最为惊喜的地方. Quar ...

  2. 基于.Net Framework 4.0 Web API开发(3):ASP.NET Web APIs 异常的统一处理Attribute 和统一写Log 的Attribute的实现

    概述:  ASP.NET Web API 的好用使用过的都知道,没有复杂的配置文件,一个简单的ApiController加上需要的Action就能工作.但是项目,总有异常发生,本节就来谈谈API的异常 ...

  3. Java 写 Log

    . 一个最基本的例子 使用Logging框架写Log基本上就三个步骤 引入loggerg类和logger工厂类 声明logger 记录日志 下面看一个例子 //1. 引入slf4j接口的Logger和 ...

  4. PHP实现写LOG日志的代码

    这篇文章给大家介绍的内容是关于PHP实现写LOG日志的代码,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. public function write_log(){ //设置目录时间 ...

  5. Windows计划任务无法写Log的问题

    参照:https://www.cnblogs.com/jonezzz/p/10364153.html 使用WIndows计划任务去执行Exe文件时无法写Log,而Exe双击执行就能写Log,这是由于计 ...

  6. 现象:当指定logback的FileNamePattern为日期2020-01-15后,如果有线程不断的往里写log,过了零点文件不会变成下一日2020-01-16,还是会在2020-01-15里继续写 结论:写log的线程不停,文件不会按日子更换。

    logback版本:1.1.11 这个是我实验验证的,昨天我配置了一个logback,然后用两个线程不断往里写log,结果发现到了今天2020-01-16日,log文件还是昨天的logbackCfg. ...

  7. Nginx 配置日志路径(nginx.conf没有写log路径,所以debug的时候找不到日志)

    缘由:nginx.conf没有写log路径,所以debug的时候找不到日志,遂在conf文件里写入了log路径 Setp1.nginx默认日志路径: /var/log/nginx Setp2.conf ...

  8. 使用Mono Cecil 动态获取运行时数据 (Atribute形式 进行注入 用于写Log) [此文报考 xxx is declared in another module and needs to be imported的解决方法]-摘自网络

    目录 一:普通写法 二:注入定义 三:Weave函数 四:参数构造 五:业务编写 六:注入调用 7.  怎么调用别的程序集的方法示例 8. [is declared in another module ...

  9. svn强制commit写log

    https://tortoisesvn.net/docs/nightly/TortoiseSVN_en/tsvn-howto-minlogmsgsize.html Force users to ent ...

随机推荐

  1. java集合 stream 相关用法(1)

    java8新增一种流式数据,让操作集合数据更简单方便. 定义基本对象: public class Peo { private String name; private String id; publi ...

  2. AtomicInteger保证线程安全的全局变量

    现有业务场景需要做一个线程间的全局变量,并且实现自增效果. 初始使用了volatile 来保证count的安全性,如下: import java.util.concurrent.ExecutorSer ...

  3. Minecraft Forge编程入门二 “工艺和食谱”

    从现在开始我们就要开始真正写代码了,还没有来得及配置环境的同学可以参考Minecraft Forge编程入门一 "环境搭建"这篇文章来进行环境搭建. 工艺(Craft)和食谱(Re ...

  4. 微信公众号 订单 待发货-配送中-已收货 logic

    w function logistics_sameorder($logistics) { $arr = array(); $tmp_wxout_trade_no = ''; $w = 0; $wi = ...

  5. 好难忘又伤心一个非常好的学习js群解散了

    不知道为什么看到web前端之天天向上这个群解散,好难过.应该可以说,这个群是我见过比较靠谱的群,大家都非常热情帮助.那些管理员管理的非常好,突然解散了.觉得好可惜,也不会因为你是菜鸟不让你加,感觉好可 ...

  6. NPOI 操作office、word、excel

    下载地址为:http://npoi.codeplex.com/releases/view/616131 可以操作excel表,行,单元格内家及样式等.   使用示例:             usin ...

  7. Linux下如何安装Anaconda?

    下载 从https://repo.continuum.io/archive/index.html上下载对应版本的Anaconda. 或者到官网:https://www.anaconda.com/dow ...

  8. DRF(4) - 认证、权限组件

    一.引入 通过前面三节课的学习,我们已经详细了解了DRF提供的几个重要的工具,DRF充分利用了面向对象编程的思想,对Django的View类进行了继承,并封装了其as_view方法和dispatch方 ...

  9. 初级dba学习之路参考

    今天周一拖着疲惫的身躯 11点才离开公司,回到家估计写完这篇博客就要17号了. 一个人走在回家的路上,很黑,突然很多感触,一个人在北京拼搏,不敢停止学习的脚步,因为只要停下来就会感觉到孤独. 回顾一下 ...

  10. HDU1165: Eddy's research II(递推)

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=1165 果断不擅长找规律啊,做这种题静不下心来. Ackermann function can be def ...