https://github.com/golang/glog ,是 google 提供的一个不维护的日志库, glog 有其他语言的一些版本,对我当时使用 log 库有很大的影响。它包含如下日志级别: Info , Warning , Error , Fatal (会中断程序执行)
还有类似 log4go , loggo , zap 等其他第三方日志库,他们还提供了设置日志级别的可见行,一般提供日志级别: Trace , Debug , Info , Warning , Error , Critical .
# 日志级别
# Warning
没人看警告,因为从定义上讲,没有什么出错。也许将来会出问题,但这听起来像是别人的问题。我们尽可能的消除警告级别,它要么是一条信息性消息,要么是一个错误。我们参考 Go 语言设计额哲学,所有警告都是错误,其他语言的 warning 都可以忽略,除非 IDE 或者在 CICD 流程中强制他们为 error ,然后逼着程序员们尽可能去消除。同样的,如果想要最终消除 warning 可以记录为 error ,让代码作者重视起来。
# Fatal
记录消息后,直接调用 os.Exit(1) ,这意味着: 在其他 goroutine defer 语句不会被执行; 各种 buffers 不会被 flush ,包括日志的; 临时文件或者目录不会被移除; 不要使用 fatal 记录日志,而是向调用者返回错误。如果错误一直持续到 main.main 。 main.main 那就是在退出之前做处理任何清理操作的正确位置。
# Error
也有很多人,在错误发生的地方要立马记录日志,尤其要使用 error 级别记录。

# 处理 error
把 error 抛给调用者,在顶部打印日志;
如果您选择通过日志记录来处理错误,那么根据定义,它不再是一个错误 — 您已经处理了它。记录错误的行为会处理错误,因此不再适合将其记录为错误。

# DEBUG
相信只有两件事你应该记录:
- 开发人员在开发或调试软件时关心的事情。
- 用户在使用软件时关心的事情。
显然,它们分别是调试和信息级别。
log.Info 只需将该行写入日志输出。不应该有关闭它的选项,因为用户只应该被告知对他们有用的事情。如果发生了一个无法处理的错误,它就会抛出到 main.main 。 main.main 程序终止的地方。在最后的日志消息前面插入 fatal 前缀,或者直接写入 os.Stderr 。
log.Debug ,是完全不同的事情。它由开发人员或支持工程师控制。在开发过程中,调试语句应该是丰富的,而不必求助于 trace 或 debug2 (您知道自己是谁)级别。日志包应该支持细粒度控制,以启用或禁用调试,并且只在包或更精细的范围内启用或禁用调试语句。
B 站的 go 框架是如何设计和思考的:https://github.com/go-kratos/kratos/tree/v2.0.x/log
# 日志选型
一个完整的集中式日志系统,需要包含以下几个主要特点:
- 收集-能够采集多种来源的日志数据;
- 传输-能够稳定的把日志数据传输到中央系统;
- 存储-如何存储日志数据;
- 分析-可以支持
UI分析; - 警告-能够提供错误报告,监控机制;
开源界鼎鼎大名 ELK stack ,分别表示: Elasticsearch , Logstash , Kibana , 它们都是开源软件。新增了一个 FileBeat ,它是一个轻量级的日志收集处理工具 ( Agent ), Filebeat 占用资源少,适合于在各个服务器上搜集日志后传输给 Logstash ,官方也推荐此工具。
# ELK
此架构由 Logstash 分布于各个节点上搜集相关日志、数据,并经过分析、过滤后发送给远端服务器上的 Elasticsearch 进行存储。
Elasticsearch 将数据以分片的形式压缩存储并提供多种 API 供用户查询,操作。用户亦可以更直观的通过配置 Kibana Web 方便的对日志查询,并根据数据生成报表。
因为 logstash 属于 server 角色,必然出现流量集中式的热点问题,因此我们不建议使用这种部署方式,同时因为 还需要做大量 match 操作(格式化日志),消耗的 CPU 也很多,不利于 scale out 。


此种架构引入了消息队列机制,位于各个节点上的 Logstash Agent 先将数据 / 日志传递给 Kafka ,并将队列中消息或数据间接传递给 Logstash , Logstash 过滤、分析后将数据传递给 Elasticsearch 存储。最后由 Kibana 将日志和数据呈现给用户。因为引入了 Kafka ,所以即使远端 Logstash server 因故障停止运行,数据将会先被存储下来,从而避免数据丢失。
更进一步的: 将收集端 logstash 替换为 beats , 更灵活,消耗资源更少,扩展性更强。

# 日志系统的设计目标
- 接入方式收敛;
- 日志格式规范;
- 日志解析对日志系统透明;
- 系统高吞吐、低延迟;
- 系统高可用、容量可扩展、高可运维性;
# 日志系统的格式规范
JSON 作为日志的输出格式:
time : 日志产生时间, ISO8601 格式;
level : 日志等级, ERROR 、 WARN 、 INFO 、 DEBUG ;
app_id : 应用 id ,用于标示日志来源;
instance_id : 实例 id ,用于区分同一应用不同实例,即 hostname ;

# 日志系统的设计与实现
日志从产生到可检索,经历几个阶段:
- 生产 & 采集
- 传输 & 切分
- 存储 & 检索
# 采集

# logstash :
监听 tcp/udp
适用于通过网络上报日志的方式
# filebeat :
直接采集本地生成的日志文件
适用于日志无法定制化输出的应用
# logagent :
物理机部署,监听 unixsocket
日志系统提供各种语言 SDK
直接读取本地日志文件

# 传输
基于 flume + Kafka 统一传输平台
基于 LogID 做日志分流:
- 一般级别
- 低级别
- 高级别(
ERROR)
现在替换为 Flink + Kafka 的实现方式。

# 切分
从 kafka 消费日志,解析日志,写入 elasticsearch
bili-index : 自研, golang 开发,逻辑简单,性能 高,可定制化方便。
・日志规范产生的日志 ( log agent 收集)
logstash : es 官方组件,基于 jruby 开发,功能强大, 资源消耗高,性能低。
・处理未按照日志规范产生的日志 ( filebeat 、 logstash 收集),需配置各种日志解析规则。
# 存储和检索
elasticsearch 多集群架构:
日志分级、高可用
单数据集群内:
master node + data node(hot/stale) + client node
每日固定时间进行热 -> 冷迁移
Index 提前一天创建,基于 template 进行 mapping 管理
检索基于 kibana
# 存储 - 文件
- 使用自定义协议,对
SDK质量、版本升级都有比较高的要求,因此我们长期会使用 “本地文件” 的方案实现: - 采集本地日志文件:位置不限,容器内
or物理机 - 配置自描述:不做中心化配置,配置由
app/paas自身提供,agent读取配置并生效 - 日志不重不丢:多级队列,能够稳定地处理日志收集过程中各种异常
- 可监控:实时监控运行状态
- 完善的自我保护机制:限制自身对于宿主机资源的消耗,限制发送速度
# 最后
期望与你一起遇见更好的自己
