2018-02-07 如何记录日志

2023-10-30


一、简介

在软件开发中,我们出于各种目的,需要将程序运行中的一些状态记录在日志中。
日志记录,并不是越多越好,也不是记录的越频繁越好,而是需要我们精心设计记录日志的时机、内容、格式(以方便后续解析、查询日志)等等。
本文简单介绍了记录日志的一些基本原则和注意事项,更具体的记日志的技巧、最佳实践、日志框架等请参考子文档 Go如何记录日志

二、记录日志的目的(why)

开发调试

目的是开发期调试程序使用,这种日志量比较大,且没有什么实质性的意义,只应该出现在开发期,而不应该在项目上线之后输出。

记录用户行为

这种类型的日志,记录用户的操作行为,用于大数据分析,比如监控、风控、推荐等等。这种日志,一般是给其他团队分析使用,而且可能是多个团队,因此一般会有一定的格式要求,开发者应该按照这个格式来记录,便于其他团队的使用。当然,要记录哪些行为、操作,一般也是约定好的,因此,开发者主要是执行的角色。

程序运行状况

记录程序的运行状况,特别是非预期的行为、异常情况,这种日志,主要是给开发、维护人员使用。什么时候记录,记录什么内容,完全取决于开发人员,开发者具有高度自主性。本文讨论的主要也是指这种类型的日志,因为作为一个服务端开发、运维人员,程序运行日志往往是解决线上问题的救命稻草。

系统、机器状况

比如网络请求、系统CPU、内存、IO使用情况等等,这种日志主要是给运维人员使用,生成各种更直观的展现形式,在系统出问题的时候报警。

三、日志的要素(what)

每条日志都可以被当作一个事件(event),纪录了该事件发生时各个信息:

时间

日志的时间可以包含多种含义,不同含义的时间传递不同的信息:
1. 指事件发生的时间,而不是日志被打印的时间。该时间附近范围内,结合该事件及服务器的网络、CPU、IO等状况,可以了解他们之间的相关性,有助于分析事件发生的原因。
2. 持续时间。例如网络请求的耗时、处理请求的各个阶段的耗时等。
3. 事件发生的顺序。通过时间戳的顺序,了解到一系列事件的发生顺序。对多进程、多线程、分布式系统有帮助。

位置

事件发生在哪个模块、哪个文件、哪个函数、哪一行代码里。

级别

日志的重要程度。用于:
1. 不同的环境(测试、生产)下,打印不同级别的日志
2. 不同级别的日志产生不同级别的监控报警

内容

简明扼要的描述发生了什么样的事情。目的是通过日志本身,而不是重新阅读相关代码来搞清楚发生了什么事情。
例如:logger.warn(‘user_login failed due to unvalid_username’)

唯一标识

不管是面向用户的服务、面向机器集群的服务,都需要一个唯一标识作为日志的主体,以方便查找该事件主体的其他信息。(很多元数据是不会记在日志里的,只会记一个唯一标识)
例如:logger.warn(‘user_login failed due to password, username %s’, username)

事件上下文

除了时间、位置、级别、内容,其他的一些有用的信息。
例如:
1. logger.warn(‘user_login failed due to wrong password, username %s’, username)
2. logger.warn(‘user_login failed due to invalid password, username %s’, username)
3. logger.warn(‘user_login failed due to empty password, username %s’, username)
为了获取更丰富的上下文,有些数据需要从 Nginx 那里获取并传给本服务,然后打印。参考:和日志相关的Nginx设置

格式化

将上述的各个信息按照固定的顺序打印出来,不仅方便查找(例如使用grep,sed,awk等),也方便收集日志的程序(ES)解析日志。

其他

根据各自的业务特点,还可以在日志中记录(包括、不限于):
1. 错误次数
2. 当前正在处理的请求数
3. 处理的进度(33%,50%,78%。。。)
4. IP
5. 问题出现时的请求链接

四、记录日志的一些原则和技巧

使用框架或模块

Java : Log4jLog4j2Commons LoggingSlf4jLogback
Python 内置的 logging 模块
beego 框架里的 github.com/astaxie/beego/logs 模块
其他的一些Go的框架:
1. Logrus: github.com/Sirupsen/logrus (Docker use this)
2. github.com/op/go-logging
3. github.com/golang/glog (from Google, implementation of their C++ glog library in Go)
4. github.com/cihub/seelog

不能出错

记录日志的目的是为了方便发现问题,解决问题,那么就需要保证记录日志本身不能出错。
尤其是对于 Error、Fatal级别的日志,出现的概率低,所以要做好单元测试,以保证记录日志本身是没问题的,是不会影响正常业务的。

避免敏感信息

  1. 避免记录用户密码。
  2. 避免记录用户个人信息,如身份证号、手机号等。应该只在日志里记录该用户的唯一标识,然后根据该唯一标识去其他的系统(例如数据库)查看详细信息。

记录“不可能发生”的事件

虽然正常逻辑下,某些情况是永远都不可能发生的,但是还是需要给这些不可能发生的情况打印一条日志。
例如 条件语句里的 else,switch 里的 default,都需要进行防御式的编程,同时记录日志。

Lazy logging

日志本身是一个字符串,可以通过多种方式拼接而成。如果根据log level,该条日志不应被打印,那么就应该避免拼接这个操作,也应该避免字符串拼接里的函数的调用。
例如:

#coding=utf-8
import logging

logger = logging.getLogger('LazyLogging')
logger.setLevel(logging.DEBUG)
hander = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
hander.setFormatter(formatter) 
logger.addHandler(hander)

def getUserCount():
    logger.info('getUserCount is called')
    return 1

logger.debug("There are " + str(getUserCount()) + " users logged in now.")
  1. log level 设置为 DEBUG(第5行),那么会先打印第12行,再打印第15行
  2. log level 设置为 INFO,依然会打印第12行,说明:
    1. 问题1: 即便没有打印第15行,依然生成了一个字符串
    2. 问题2: 而且调用了 getUserCount 这个函数
  3. 解决上述的问题1,可以把第15行修改为:
logger.debug("There are %s users logged in now.", getUserCount())
  1. 为了解决上述的问题2,一个办法是:
class lazyEval:
    def __init__(self, f, *args):
        self.func = f
        self.args = args

    def __str__(self):
        return str(self.func(*self.args))

logger.debug("There are %s users logged in now.", lazyEval(getUserCount))

Java里的方式:

logger.debug(``"There are {} users logged in now."``, () -> getUserCount());

目前Golang里也存在这样的问题,但是还没找到怎么做到 lazyLogging。
但是,可以看出上面的方式也不优雅,所以应当避免在打印日志时调用函数。

异步打印日志

互联网应用程序中,高并发下的写日志会带来大量的IO操作,从而影响正常服务的性能。这时候的记日志就需要专门的服务去做,例如把日志打到消息队列里,然后再写到磁盘上。

设置缓存

默认的日志是随时 flush 到console、文件里的,通过设置缓存进行批量操作,可以一定程度上优化服务性能。

对日志归档、分类

打印到一个文件里,会使得该文件越来越大,不方便查找。
按日志属性分类:access log,error log,
按日期归档:lathspell-api.2018-01-17.log,lathspell-api.2018-01-18.log
按大小切分:lathspell-api.2018-01-17.log.1,lathspell-api.2018-01-17.log.2,lathspell-api.2018-01-17.log.3 (避免文件太大)

参考文档

  1. 《程序那些事:日志记录的作用和方法》 http://www.infoq.com/cn/articles/why-and-how-log#
  2. 日志的艺术http://www.cnblogs.com/xybaby/p/7954610.html
  3. 《日志的设计》http://blog.csdn.net/songbaojie/article/details/1867502
  4. 《日志规范》http://blog.csdn.net/hzhxxx/article/details/10590907
  5. 《Log 日志规范》http://www.cnblogs.com/kofxxf/p/3713472.html
  6. 《我的编程习惯 - 日志建议》https://zhuanlan.zhihu.com/p/28629319
  7. 《程序调试的利器 - 日志》https://www.tuicool.com/articles/V7z26j
  8. 《日志处理》https://beego.me/docs/module/logs.md
  9. 《写给开发者:记录日志的10个建议》http://blog.jobbole.com/52018/
  10. 《The Art of Logging: Advanced message formatting》https://garygregory.wordpress.com/2015/10/08/the-art-of-logging-advanced-message-formatting/
  11. 《Twelve Go Best Practices》https://talks.golang.org/2013/bestpractices.slide#1
  12. 《Go Best Practices 2016》https://peter.bourgon.org/go-best-practices-2016/#logging-and-instrumentation
  13. 《Think differently about what to log in go best practices》https://www.loggly.com/blog/think-differently-about-what-to-log-in-go-best-practices-examined/
  14. 《7 Good Rules to Log Exceptions》http://codemonkeyism.com/7-good-rules-to-log-exceptions/
  15. 《Logging in Java》http://hugozhu.myalert.info/2013/02/28/logging-in-java.html
  16. 《Apache Commons Logging - JCL Best Practices》http://commons.apache.org/proper/commons-logging//guide.html#JCL_Best_Practices
  17. 《The Art of Logging》https://www.codeproject.com/Articles/42354/The-Art-of-Logging
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

2018-02-07 如何记录日志 的相关文章

随机推荐

  • Simpsons’ Hidden Talents【KMP模板题】

    Homer Marge I just figured out a way to discover some of the talents we weren t aware we had Marge Yeah what is it Homer
  • 区块链的数据是存储在链上,还是在数据库中?(答案是这个问题并不成立,来一起了解一下吧)

    很多人都想了解区块链的数据到底什么时候是存储在链上 什么时候又储存在相应节点的数据库中间呢 今天我们就来解决这个有趣的问题 首先我们必须了解清楚两个概念 区块链数据 链上数据 首先 区块链数据包括区块数据和状态数据两者 区块数据描述的实际是
  • hive分区表详细介绍

    一 什么是分区表以及作用 数据分区的概念以及存在很久了 通常使用分区来水平分散压力 将数据从物理上移到和使用最频繁的用户更近的地方 以及实现其目的 hive中有分区表的概念 我们可以看到分区具重要性能优势 而且分区表还可以将数据以一种符合逻
  • Datalore安装使用教程

    发现一个jetbrain出的好东西 使用体验完爆jupyter notebook以及jupyter lab的软件 就是安装有点复杂 官网写得有点不清楚 这里简单介绍一下 首先他只能在linux运行 其他环境暂时不支持 首先 去https w
  • react简要分析

    一 简介 前段时间看到一个用33行代码就实现了一个非常基本的react代码 感觉还是蛮有趣的 代码如下 其主要实现了两大功能 生成虚拟DOM 根据虚拟DOM渲染出真实的DOM 无注释版 https github com leontrolsk
  • linux下查看物理CPU个数、核数、逻辑CPU个数

    cat proc cpuinfo中的信息 processor 逻辑处理器的id physical id 物理封装的处理器的id core id 每个核心的id cpu cores 位于相同物理封装的处理器中的内核数量 siblings 位于
  • 消息队列中间件 - 详解RabbitMQ6种模式

    RabbitMQ 6种工作模式 对RabbitMQ 6种工作模式 简单模式 工作模式 订阅模式 路由模式 主题模式 RPC模式 进行场景和参数进行讲解 PHP代码作为实例 安装 客户端实现 添加扩展 执行composer phar inst
  • 《计算机网络—自顶向下方法》 Wireshark实验(七):以太网与ARP协议分析

    1 以太网 1 1 介绍 以太网是现实世界中最普遍的一种计算机网络 以太网有两类 第一类是经典以太网 第二类是交换式以太网 使用了一种称为交换机的设备连接不同的计算机 经典以太网 是以太网的原始形式 运行速度从 3 10 Mbps 不等 交
  • 51单片机实战 1 --四个独立按键控制四位数码管

    本文基于普中51开发板 在其例程代码稍加改动而成的 单片机的入门小项目也很益智 启动单片机 四位数码管显示0000 按下s1并松开 显示1000 再按下s1并松开显示2000 连续10次按下并松开s2 数码管显示2100 2200 2300
  • WSL安装图形界面

    效果如下 1 下载并安装VcXsrv 链接如下 https sourceforge net projects vcxsrv 下载完安装一路next即可 或者自行选择安装路径 2 安装桌面环境 安装xfce4 terminalsudo apt
  • mysql的锁

    锁 锁机制用于管理对共享资源的并发访问 用来实现事务的隔离级别 锁类型 共享锁和排他锁都是行级锁 MySQL当中事务采用的是粒度锁 针对表 B 树 页 B 树叶子 节点 行 B 树叶子节点当中某一段记录行 三种粒度加锁 共享锁 S 可理解为
  • Python进阶-----面向对象2.0(特有属性和方法与私有属性和方法)

    目录 前言 1 添加特有属性 方法 示例1 添加特有属性 示例2 添加特有方法 2 私有属性 方法 1 私有化示例 2 私有化属性 方法可以在类的内部使用 3 强制访问私有化属性 方法 4 property装饰器去操作私有属性 方法 总结
  • 【测试入门】测试用例经典设计方法 —— 因果图法

    01 因果图设计测试用例的步骤 1 分析需求 阅读需求文档 如果User Case很复杂 尽量将它分解成若干个简单的部分 这样做的好处是 不必在一次处理过程中考虑所有的原因 没有固定的流程说明究竟分解到何种程度才算简单 需要测试人员根据自己
  • 【LeetCode-面试经典150题-day24】

    目录 35 搜索插入位置 74 搜索二维矩阵 162 寻找峰值 33 搜索旋转排序数组 35 搜索插入位置 题意 给定一个排序数组和一个目标值 在数组中找到目标值 并返回其索引 如果目标值不存在于数组中 返回它将会被按顺序插入的位置 请必须
  • 详解UART、I2C、SPI常用通信协议(全是细节)

    前言 UART I2C和SPI是我们在嵌入式开发中比较常见的通信协议了 没有最好的通信协议 每个通信协议都有自己的优缺点 如果想要通信速度快 SPI 将是理想的选择 如果用户想要连接多个设备而不是过于复杂 I2C 将是理想的选择 因为它最多
  • Java fail-fast与fail-safe

    fail fast和fail safe比较 java util包下面的所有的集合类都是快速失败的 而java util concurrent包下面的所有的类都是安全失败的 快速失败的迭代器会抛出ConcurrentModificationE
  • mac如何添加新的字体格式(以word中仿宋_GB2312为例)

    注意 字体中必须出现GB 2312格式的选项 才算成功
  • C#创建TCP服务器与客户端互传消息实例

    本项目使用C 语言建立一个TCP通讯实例 并可以互相传递消息 传送一下传智播客赵老师的视频课程 关键词解释 1 TCP协议 一种可以用于网络通信的数据传输协议 传输安全可靠不会有信息丢失 重点理解三次握手与四次分手 2 线程Thread 我
  • SQL2008 附加数据库提示 5120错误

    前几天在附加数据库时 出现了这个错误 在win7 x64系统上使用sql2008进行附加数据库 包括在x86系统正在使用的数据库文件 直接拷贝附加在X64系统中 时 提示无法打开文件 5120错误 这个错误是因为没有操作权限 所以附加的时候
  • 2018-02-07 如何记录日志

    一 简介 二 记录日志的目的 why 开发调试 记录用户行为 程序运行状况 系统 机器状况 三 日志的要素 what 时间 位置 级别 内容 唯一标识 事件上下文 格式化 其他 四 记录日志的一些原则和技巧 使用框架或模块 不能出错 避免敏