最近在考虑后端日志规范拟定,参考了一些别的同志的经验,加上自己的一些思考,初步整理如下。
一、日志规范
1. 日志严格分级
-
DEBUG:该级别日志的主要作用是对系统每一步的运行状态进行精确的记录。可以将各类详细信息记录到DEBUG里,起到调试的作用,包括
参数信息、调试细节信息、返回值信息
等。 -
INFO:该种日志记录系统的正常运行状态,通过查看INFO级别的日志,可以很快地对系统中出现的 WARN,ERROR,FATAL错误进行定位。可以将
初始化系统配置、业务状态变化信息
,或者用户业务流程中的核心处理记录
到INFO日志中,方便日常运维工作以及错误回溯时上下文场景复现。 -
WARN:该日志表示
系统可能出现问题
,也可能没有。 -
ERROR:
该级别的错误也需要马上被处理
,但是紧急程度要低于FATAL级别。ERROR应该尽量详细记录。 -
FATAL:
需要立即被处理的系统级错误
。系统需要将错误相关痕迹以及错误细节记录FATAL日志中,方便后续人工回溯解决。
必须严格按照日志的级别给出日志。
2. 日志记录时机
a) 程序设计语言异常位置
:C++提示异常的位置。结合实际业务使用WARN及以上级别
日志。
b) 业务流程与预期不符
:如外部参数不正确等问题,取决于开发人员的经验。结合实际业务,使用WARN和ERROR级别
日志。
c) 系统核心角色,组件关键动作
:如用户从登陆到交易完成的整个流程,各个服务节点间交互的位置,核心组件运行过程等。建议记录INFO级别
日志。
d) 系统及各模块初始化
:各个服务组件的启动参数,状态信息。建议INFO级别
日志记录。
3. 日志记录规范
在出现问题之后,需要立即根据日志定位问题。对于INFO及以上级别的日志,要求按照一定顺序,输出以下必要的信息。(最好各个级别日志都遵循以下规范,否则有些日志可能仍无法快速定位。)
- 当前服务器IP
- 当前服务或应用名
- (实例ID):若是不同实例不同名,可忽略,参考应用名。
- 当前类名(如有)
- 当前函数名
- 当前行号
- 内容信息:包含参数信息,调试信息等等。可以有多项。
例子:
PJ_LOG_INFO("Host:{},App:{},Class:{},Function:{},Line:{},Content:{}",localhost,app_example,class_example,func_example,1234,content);
TODO:
- 如果定位问题花费了很长时间,说明系统日志还存在问题,需要进一步完善和优化。
- 日志集中,如何集中?哪些日志集中在一起?同一个在线系统的日志?所有在线系统的所有日志?若存在多个在线的系统?如果日志集中后,需要考虑日志融合和区分。
二、日志追踪定位
TODO:可以尝试使用zipkin或者基于Google dapper搭建。
参考
- Trace:跟踪,表示一条链路,以一个唯一的TraceID标识。一次完整调用需要传递并记录唯一的traceID,需要参考一下zipkin代码。
- Span:跨度,dapper中的基本工作单元,以一个至少Trace内的SpanID标识。每个span中包含traceid,spanid,parentid,spanname,下面提到的annotationsh和binaryAnnotations等。
- Annotation:基本标注列表,一个标注可以理解成span生命周期中重要时刻的数据快照。
"annotations": [
{
"timestamp":1476197067420000,
"value": "cs",
"endpoint": {
"serviceName":"gateway",
"ipv4": "xxx.xxx.xxx.110"
}
},
{
"timestamp":1476197072114000,
"value": "cr",
"endpoint": {
"serviceName":"gateway",
"ipv4": "xxx.xxx.xxx.110"
}
}
],
- BinaryAnnotation:业务标注列表,如果某些跟踪埋点需要带上部分业务数据(比如url地址、返回码和异常信息等),可以将需要的数据以键值对的形式放入到这个字段中。
"binaryAnnotations": [
{
"key":"http.url",
"value": "http://localhost:8080/service1",
"endpoint": {
"serviceName":"gateway",
"ipv4": "xxx.xxx.xxx.110"
}
}
]
还有很多没有完成的工作和不太成熟的地方,欢迎交流指正。