写给开发者:记录日志的10个建议

尽管在写这篇博文的时候,我是在负责运维工作,不过本文主要是写给开发者的。

对我来说,明白如何记录日志和记录什么,是软件工程师必须明了的最艰巨的任务之一。之所以这么说,是因为这项任务与预测(divination)类似,你不知道当你要调试的时候需要些什么信息……我希望这10个建议能帮助你更好地在应用程序中记录日志,让运维工程师们受益。:)

 

1. 你不应自己写log

绝对不要,即便是用printf或者是自己写入到log文件,又或自己处理logrotate。请给你的运维同志们省省心,调用标准库或者系统API来完成它。

这样,你可以保证程序的运行与其他系统组件好好相处,把log写到正确的位置或者网络服务上,而不需要专门的系统配置。

假如你要使用系统API,也就是syslog(3),学习好怎么用它。

如果你更喜欢用logging库,在Java里面你有很多选择,例如Log4j,JCL,slf4j和logback。我最喜欢用slf4j和logback的组合,因为它们特别给力,而且相对地容易配置(还允许使用JMX进行配置或者重载配置文件)。

slf4j最好的是你可以修改logging控制台的位置。如果你在编写一个库,这会变得非常重要,因为这可以让库的使用者使用自己的logging控制台而不需要修改你的库。

其他语言当然也有多种logging库,例如ruby的Log4r,stdlib logger,和几近完美的Jordan Sissel’s Ruby-cabin。

如果你想纠结CPU占用问题,那么你不用看这篇文章了。还有,不要把log语句放在紧内部循环体内,否则你永远看不出区别来。

 

2. 你应在适当级别上进行log

如果你遵循了上述第一点的做法,接下来你要对你程序中每一个log语句使用不同的log级别。其中最困难的一个任务是找出这个log应该是什么级别

以下是我的一些建议:

  • TRACE level: 如果使用在生产环境中,这是一个代码异味(code smell)。它可以用于开发过程中追踪bug,但不要提交到你的版本控制系统

  • DEBUG level: 把一切东西都记录在这里。这在debug过程中最常用到。我主张在进入生产阶段前减少debug语句的数量,只留下最有意义的部分,在调试(troubleshooting)的时候激活。

  • INFO level: 把用户行为(user-driven)和系统的特定行为(例如计划任务…)

  • NOTICE level: 这是生产环境中使用的级别。把一切不认为是错误的,可以记录的事件都log起来

  • WARN level: 记录在这个级别的事件都有可能成为一个error。例如,一次调用数据库使用的时间超过了预设时间,或者内存缓存即将到达容量上限。这可以让你适当地发出警报,或者在调试时更好地理解系统在failure之前做了些什么

  • ERROR level: 把每一个错误条件都记录在这。例如API调用返回了错误,或是内部错误条件

  • FATAL level: 末日来了。它极少被用到,在实际程序中也不应该出现多少。在这个级别上进行log意味着程序要结束了。例如一个网络守护进程无法bind到socket上,那么它唯一能做的就只有log到这里,然后退出运行。

记住,在你的程序中,默认的运行级别是高度可变的。例如我通常用INFO运行我的服务端代码,但是我的桌面程序用的是DEBUG。这是因为你很难在一台你没有接入权限的机器上进行调试,但你在做用户服务时,比起教他们怎么修改log level再把生成的log发给你,我的做法可以让你轻松得多。当然你可以有其他的做法:)

 

3. honor the log category

我在第一点中提到的大部分logging库允许指定一个logging类别。它可以分类log信息,并基于logging框架的配置,在最后以某一形式进行log或是不进行。

通常,Java开发者在log语句处使用完整,合格的类名作为类别名。如果你的程序遵循单一职责原则(Single responsibility principle,原文有误),这种模式还不错。

在Java的logging库中,Log类别是按等级划分的,例如在com.daysofwonder.ranking.ELORankingComputation会匹配到顶级的com.daysofwonder.ranking。这可以让运营工程师配置一个对此类别下指定的所有ranking子系统作用的logging。如果需要的话,还可以同时生成子类别的logging配置。

拓展开来,我们讲解一下特定情况下的调试。假设你在做一个应答用户请求的服务端软件(如REST API)。它正在对my.service.api.<apitoken>进行log(其中apitoken用于识别用户)。那么你可以选择对my.service.api类别进行log,记录所有的api,或是对某违规API用户的my.service.api.<bad-user-api-token>进行log。当然这需要系统允许你在运行中修改logging配置。

 

4. 你应该写有意义的log

这可能是最重要的建议了。没有什么比你深刻理解程序内部,却写出含糊的log更糟了。

在你写日志信息之前,总要提醒自己,有突发事件的时候,你唯一拥有的只有来自log文件,你必须从中明白发生了什么。这可能就是被开除和升职之间的微妙的差距。

当开发者写log的时候,它(log语句)是直接写在代码环境中的,在各种条件中我们应该写入基于当前环境的信息。不幸的是,在log文件中并没有这些环境,这可能导致这些信息无法被理解。

解决这个情况(在写warn和error level时尤为重要)的一个方法是,添加辅助信息到log信息中,如果做不到,那么改为把这个操作的作用写下。

还有,不要让一个log信息的内容基于上一个。这是因为前面的信息可能由于(与当前信息)处于不同的类别或者level而没被写入。更坏的情况是,它因多线程或异步操作,在另一个地方(或是以另一方式)出现。

 

5. 日志信息应该用英语

这个建议可能有点奇怪,尤其是对法国佬(French guy)来说。我还是认为英语远比法语更简炼,更适应技术语言。如果一个信息里面包含超过50%的英语单词,你有什么理由去用法语写log呢

把英法之争丢一边,下面是这个建议背后的原因:

  • 英语意味着你的log是用ASCII编码的。这非常重要,因为你不会真正知道log信息会发生什么,或是它被归档前经过何种软件层和介质。如果你的信息里面使用了特殊字符集,乃至UTF-8,它可能并不会被正确地显示(render),更糟的是,它可能在传输过程中被损坏,变得不可读。不过这还有个问题,log用户输入时,可能有各种字符集或者编码。

  • 如果你的程序被大多数人使用,而你又没有足够的资源做国际化,英语会成为你的不二之选。如果你有国际化,那么让界面与终端用户更亲近(closer)(这通常不会是你的log)

  • 如果你国际化了你的log(例如所有的warning和error level信息),给他们一个特定的有意义的错误码。这样,用户做与语言无关的搜索,找到相关信息。这种良好的模式已经在虚拟内存(VMS)操作系统中应用了很久,而我必须承认它非常有用。如果你曾经设计过这种模式,你还可以试试这种模式: APP-S-CODE 或者 APP-S-SUB-CODE,它们分别代表:
    APP: 应用程序的3字缩写
    S: 严重程度的1字缩写(例如D代表debug,I代表info)
    SUB: 这个code所从属的应用程序的子部分
    CODE: 一个数字代号,指定这个问题中的错误

 

6. 你应该给log带上上下文

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

相关文章:

  • 免费开源分布式系统日志收集框架 Exceptionless

  • 使用 Exceptionless 作为 Log Server 搭配 NLog 记录系统日志

  • 使用Elasticsearch 与 NEST 库 构建 .NET 企业级搜索

  • 为elasticsearch集成一些实用 插件以及配置的开箱即用的版本


原文地址:http://blog.jobbole.com/52018/


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/328131.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)错误

解决org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)错误 2018年07月23日 16:55:42 犹抱琵琶半遮面 阅读数 29677 &#xff01;&#xff01;&#xff01;此类问题出现的原因是——不细心 一般的原因 Mapper interface和xml文件的定义对应…

Java 9 中的 GC 调优基础

转载自 Java 9 中的 GC 调优基础在经过了几次跳票之后&#xff0c;Java 9终于在原计划日期的整整一年之后发布了正式版。Java 9引入了很多新的特性&#xff0c;除了闪瞎眼的Module System和REPL&#xff0c;最重要的变化我认为是默认GC&#xff08;Garbage Collector&#xf…

mysql删除索引 增加索引

使用ALTER TABLE语句创建索引alter table table_name add index index_name (column_list) ; alter table table_name add unique (column_list) ; alter table table_name add primary key (column_list) ;删除索引drop index index_name on table_name ; alter table table_n…

Hibernate基本概念 (3)

一、hibernate配置&#xff1a;1.导jar包2.配置主文件a。数据库连接 url 驱动 用户名&#xff0c;密码&#xff0c;方言b。相关 显示sql&#xff0c;sql格式化c。映射文件3.映射文件表到实体 字段到属性查询&#xff1a;select dname from Dept d;参数&#xff1a;1.按位置&…

全自动迁移数据库的实现 (Fluent NHibernate, Entity Framework Core)

在开发涉及到数据库的程序时&#xff0c;常会遇到一开始设计的结构不能满足需求需要再添加新字段或新表的情况&#xff0c;这时就需要进行数据库迁移。实现数据库迁移有很多种办法&#xff0c;从手动管理各个版本的ddl脚本&#xff0c;到实现自己的migrator&#xff0c;或是使用…

ThreadLocal 内存泄露的实例分析

转载自 ThreadLocal 内存泄露的实例分析前言 昨天分享了一篇深入分析 ThreadLocal 内存泄漏问题是从理论上分析ThreadLocal的内存泄漏问题&#xff0c;这一篇文章我们来分析一下实际的内存泄漏案例。分析问题的过程比结果更重要&#xff0c;理论结合实际才能彻底分析出内存泄…

ZKWeb网站框架的动态编译的实现原理

ZKWeb网站框架是一个自主开发的网页框架&#xff0c;实现了动态插件和自动编译功能。ZKWeb把一个文件夹当成是一个插件&#xff0c;无需使用csproj或xproj等形式的项目文件管理&#xff0c;并且支持修改插件代码后自动重新编译加载。 下面将说明ZKWeb如何实现这个功能&#xff…

Hibernate基本概念 (4)

一、缓存&#xff1a;提高性能1.一级缓存&#xff1a;session级别 一个session共享2.二级缓存&#xff1a;进程或群集级别 不同session可以共享步骤&#xff1a;1.导jar包 2.添加xml放到src3.配置hibernate.cfg.xmla.开启二级缓存b。缓存管理类4.配置持久化类使用二级缓存 3…

Java中的内存泄露的几种可能

转载自 Java中的内存泄露的几种可能Java内存泄漏引起的原因&#xff1a;内存泄漏是指无用对象&#xff08;不再使用的对象&#xff09;持续占有内存或无用对象的内存得不到及时释放&#xff0c;从而造成内存空间的浪费称为内存泄漏。长生命周期的对象持有短生命周期对象的引用…

Visual Studio“15”启动速度提升

在Visual Studio“15”开发工作的技术预览阶段&#xff0c;微软称自己的主要目标之一是改善性能。他们已经对这些改进进行过一定程度的介绍&#xff0c;最近又通过更全面的信息进一步介绍了这些变化。本文将介绍这些让VS“15”启动速度更快的改进。 更快速地启动VS“15” 微软项…

介绍Java中的内存泄漏

转载自 介绍Java中的内存泄漏Java语言的一个关键的优势就是它的内存管理机制。你只管创建对象&#xff0c;Java的垃圾回收器帮你分配以及回收内存。然而&#xff0c;实际的情况并没有那么简单&#xff0c;因为内存泄漏在Java应用程序中还是时有发生的。 下面就解释下什么是内存…

2-6 基于SpringBoot的SpringSecurity环境快速搭建与验证

springboot是基于spring的一套全新的框架&#xff0c;其目的是为了简化spring的初始搭建和开发过程&#xff0c;不需要再做样板话的配置了 https://start.spring.io/ 上面这些都不想做权限拦截

DotLiquid模板引擎简介

DotLiquid是一个在.Net Framework上运行的模板引擎&#xff0c;采用Ruby的Liquid语法&#xff0c;这个语法广泛的用在Ruby on rails和Django等网页框架中。DotLiquid相比于Mvc默认模板引擎Razor的好处有&#xff1a; 因为不需要编译到程序集再载入首次渲染速度很快不会导致内存…

Hibernate基本概念 (5)

-----模板1.一对多(set)<set name"属性"><key column"关系外键"/><one-to-many class"实体类全名称"/></set>2.多对一<many-to-one name"" class"" column"关系外键"/>3.多对多(s…

Vue 阻止事件冒泡

转载自 Vue2学习笔记:事件对象、事件冒泡、默认行为1.事情对象<!DOCTYPE html> <html> <head><title></title><meta charset"utf-8"><script src"http://unpkg.com/vue/dist/vue.js"></script><scrip…

Windows Server 2016及System Center 2016正式商用

Windows Server 2016 及 System Center 2016 现已正式商用。作为微软全新一代的服务器操作系统和数据中心管理平台&#xff0c;它们将为企业 IT 带来全面的性能与安全性提升&#xff1b;为数据中心、私有云及公有云环境提供一致的混合云管理平台&#xff1b;并为在本地和云端开…

2-7 SpringBoot常用注解讲解

首先&#xff0c;讲解一下RestController RestController RestController是Controller和ResponseBody的结合。 RnableAutoConfiguration EnableAutoConfiguration springboot建议只能有一个有该注解的类 这个注解的作用是 根据你配置的依赖自动配置 根据jar包的配置…

vue-beauty UI库

vue-beauty UI库文档地址 一、全局配置全局CSS样式Polyfill二、组件&#xff08;1&#xff09;普通Button 按钮Icon 图标&#xff08;2&#xff09;布局Grid 栅格Layout 布局MorePanel 更多条件&#xff08;3&#xff09;导航Affix 固钉Breadcrumb …

ArrayList整理

ArrayList整理1&#xff0c;ArrayList特性2,ArrayList底层实现的特征1)&#xff0c;ArrayList初始化2)&#xff0c;初始容量3)&#xff0c;ArrayList的添加元素的add()方法4&#xff09;&#xff0c;ArrayList的删除方法remove(int index)其他的一些方法的操作其实都差不多&…

ASP.NET Core CORS 简单使用

CORS 全称"跨域资源共享"&#xff08;Cross-origin resource sharing&#xff09;。 跨域就是不同域之间进行数据访问&#xff0c;比如 a.sample.com 访问 b.sample.com 中的数据&#xff0c;我们如果不做任何处理的话&#xff0c;就会出现下面的错误&#xff1a; XM…