前言

在软件开发过程中,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. 《Sqlserver》Javaweb项目链接sqlserver 2008R2时出现的一系列的错误

    好久没有弄java,玩eclipse了,最近因为小小的原因,参加一个比赛,不得不把javaweb的东西又捡起来,所以不熟悉,再加上之前链接数据库都是用Oracle的,现在公司的电脑上又只是安装了sql ...

  2. bunoj 13124(数位dp)

    数位dp每次都给我一种繁琐的感觉.. A - Palindromic Numbers Time Limit:2000MS     Memory Limit:32768KB     64bit IO F ...

  3. vue+node+mongoDB 火车票H5(一)---准备工作,基本配置

    前端菜鸟一枚,由于公司项目用到了vue,我虽然参与了,但是很多环境配置和流程还不是特别清楚,就想自己个人业余做个webapp看看, 对于完全新手而言,很多坑会纠结很久,所以想借此机会自己做的同时记录各 ...

  4. 【IDEA】项目中引入Spring MVC

    一.原文说明: IntelliJ idea创建Spring MVC的Maven项目 - winner_0715 - 博客园 https://images2015.cnblogs.com/blog/82 ...

  5. python框架Scrapy中crawlSpider的使用

    一.创建Scrapy工程 #scrapy startproject 工程名 scrapy startproject demo3 二.进入工程目录,根据爬虫模板生成爬虫文件 #scrapy genspi ...

  6. monggodb 模糊查询

    MongoDB的模糊查询其实很简单:      11.LIKE模糊查询userName包含A字母的数据(%A%)       SQL:SELECT * FROM UserInfo WHERE user ...

  7. ios cocos2d 使用 sneakyInput 插件

    昨晚看了篇使用sneakyInput插件实现模拟手柄的代码,不过我加上后出现了很多问题.最后只看如何实现,没有自己动手去操作.今天终于吧问题都解决了.记录下来.也供别人参考. 首先要先加入libz.d ...

  8. Pandas常用操作方法

    Pandas pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的. Pandas 纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具. pandas提 ...

  9. java正则匹配

    java正则提取需要用到Matcher类,下面给出案例示例供参考 需要提取车牌号中最后一个数字,比如说:苏A7865提取5,苏A876X提取6import java.util.regex.Matche ...

  10. Java基础—数组(转载)

    Java 语言中提供的数组是用来存储固定大小的同类型元素.其实数组就是一个容器. 创建数组 Java 中声明数组的语法有两种: dataType[] arrayRefVar; // 首选的方法 dat ...