关于代码中写日志的一些思考



在我工做过的公司里,有一家公司的代码里所有都是日志,而另一家则是一行日志记录都没有。css

可是在线上环境中,日志记录是很是重要的,它能帮咱们快速定位问题所在。整理以后,我发现下面几点关于日志的建议是很是有效的。html

没有提示就是最好的提示

用过 类 Unix 系统的人都知道,Unix 系统的提示哲学是没提示就是最好的提示。若是一个命令执行成功了,那么系统是不会提示的,只有当执行出错的时候才会报错。反观咱们的代码,通常状况下咱们的代码99%都是正常的执行路径,不会出现逻辑异常或代码异常,只有1%的时候才会出现异常。在这种状况下,咱们固然是选择在那1%的地方进行日志输出了。因此写日志咱们应该达成一个共识,那就是:只在逻辑异常或代码异常的地方加上日志提示。java

你应该考虑阅读者

在咱们讨论“没有提示就是最好的提示”时,咱们只是站在程序猿的角度来思考如何写提示,可是有时候阅读日志的人多是nginx

  • 一个在调试产品问题的系统管理员或者运维工程师
  • 一个尝试本身解决问题的终端用户(想象一个客户端或桌面程序)
  • 一个在开发中debug,或者在解决产品问题的开发者

对于不一样的”谁”,你将要写下的log信息的内容,上下文,类别和level会大不一样。开发者了解程序内部,因此给他的log信息能够比给终端用户的复杂得多。因此为了兼顾其余人对于程序的理解,有时候须要在一些重要的业务逻辑里增长一些与逻辑异常或代码异常无关的一些代码,如:在P2P项目中,当用户完成充值的时候,你须要打印出用户充值成功的信息。数据库

简单地说:咱们不只仅要在逻辑异常或代码异常的地方加上日志提示,并且也须要在每一个环节完成的时候输出日志提示,这样会便于咱们进行线上问题的定位。安全

你应在适当级别上进行log

若是你使用了开源日志框架,那么你要对你程序中每个log语句使用不一样的log级别。其中最困难的一个任务是找出这个log应该是什么级别。在 SLF4J 中经常使用的级别有这么几个,他们的日志级别从高到低是:ERROR > WARN > INFO > DEBUG。当代码中的级别大于设置文件中设置的级别时,对应的日志就会输出。markdown

如下是个人一些建议:多线程

  • DEBUG Level:主要用于开发人员进行代码调试的时候使用。好比当开发人员发现了一种异常状况,这时候咱们就须要输出一些日志信息,那么这时候就应该使用 log.debug() 进行日志记录。
  • INFO Level:重要的业务逻辑处理完成。在理想状况下, INFO的日志信息要能让高级用户和系统管理员理解, 并从日志信息中能知道系统当前的运行状态。好比对于一个机票预订系统来讲, 当一个用户完成一个机票预订操做以后, 提醒应该给出"谁预订了从A到B的机票"。另外一个须要输出INFO信息的地方就是一个系统操做引发系统的状态发生了重大变化(好比数据库更新, 过多的系统请求)。
  • WARN Level:系统能继续运行,可是必须引发关注。对于存在的问题通常能够分为两类: 一种系统存在明显的问题(好比数据不可用), 另外一种就是系统存在潜在的问题, 须要引发注意或者给出一些建议(好比,系统运行在安全模式或者访问当前系统的帐号存在安全隐患)。总之就是系统仍然可用,可是最好进行检查和调整。
  • ERROR Level:系统发生了严重的错误, 必须立刻进行处理, 不然系统将没法继续运行。好比:NullPointerException、内存不足等。

正确的使用输出模式

log 输出模式能够帮助咱们在日志中增长一些清晰的上下文信息,不过对添加的信息仍是要多加当心。好比说,若是你是每小时输出一个文件,这样你的日志文件名中已经包含了部分日期时间信息,所以就不必在日志中再包含这些信息。另外在多线程环境下也不要在本身在日志中包含线程名称, 由于这个也能够在模式中配置。框架

根据个人经验, 一个理想的日志模式将包含下列信息:运维

  • 当前时间(不须要包含日志, 精确到毫秒)
  • 日志级别(若是你关心这个)
  • 线程名称
  • 简单的日志名(非全限定名的那种)
  • 日志描述信息

根据实际状况,你还能够添加下面一些信息:

  • 文件名
  • 类名(我想这个应该是全限定名吧)
  • 代码行号

但要记得,不要用反射去本身获取文件名、类名等信息再去做为日志信息输出,这样会极大下降日志效率。若是须要,你完成能够在 SLF4J 的配置文件中进行配置。

日志信息应该用英语

若是你的程序被大多数人使用,而你又没有足够的资源作国际化,英语会成为你的不二之选。

你应该给log带上上下文

没有什么比这样的log信息更糟的了

Transaction failed

或者

User operation succeeds

又或是API异常时:

java.lang.IndexOutOfBoundsException

没有相应的上下文,这些信息不过是噪音,它们不会对调试过程当中有意义的数值或是空间起做用(add value and consume space)。带上上下文的信息要有价值得多,例如:

Transaction 234632 failed: cc number checksum incorrect

或是:

User 54543 successfully registered e-mail<a href="mailto:user@domain.com">user@domain.com</a>

又或是:

IndexOutOfBoundsException: index 12 is greater than collection size 10

在上面这一例子中的异常,若是你想把它传播开, 确保在处理的时候带上与当前level相应的上下文,让调试更简单,以下一个 Java 的例子:

public void storeUserRank(int userId,int rank,String game) {
     try {
          ...deal database ...
     } catch (DatabaseException de) {
          throw new RankingException("Can't store ranking for user "+userId+" in game "+ game + " because " + de.getMessage() );
     }
}

使用占位符而不是拼接字符串

使用占位符更有利于代码的阅读,而且能够提升性能。所以你应该写这样的日志记录:

logger.info("User {} login.", userId);

而不是这样的:

logger.info("User " + userId + " login.");

日志不能打断业务逻辑

在下面的日志记录中,若是 user 为空,那么将会抛出异常,那么就会直接致使程序中断。

logger.info("User {} login fail.", user.getUserInfo);

所以在日志记录中,要避免日志中发生异常而致使正常的业务逻辑受到影响,这是绝对不容许出现的。

纸上得来终觉浅,说了这么多,但仍是要实践时不断去应用,去提取适合本身的日志规则,这样才能写出好的代码。

 

出处:http://www.cnblogs.com/chanshuyi/p/how_to_write_log_in_code.html

相关文章
相关标签/搜索