来自应用层的日志是你的应用对你唯一的反馈。让日志合理化并具有良好的检索能力,就像在一个黑暗的房间里点亮了一个火把。当任何问题出现时,“充分利用日志”这一做法带来的效果常常被我们低估。作为软件工程师,我们能利用应用层日志解决问题,以及了解应用整体的健康状况。
日志还可以帮助分析应用程序的性能。
时间戳(timestamp) + 数据(data) = 日志(log)
log 单词的愿意是“原木,航海日志”
古代的航海家经常把非常结实的原木做成桅杆,而最古老的记事工具根本不是纸笔等,如何记事呢? 很简单,太阳东升西落一次,航海家就在桅杆上刻下一个刻痕,代表一天已经过去了,于是 log 就从“原木”演化到“航海日志”。
记录有帮助的信息
什么时候记录日志
操作日志
安全相关日志
监管/标准要强制(外部系统访问)
业务相关日志
日志消息分解
记录的一行日志,就像讲述一个故事,when, what, who, and why
在日志行中固定使用一个结构,拥有这些简单的结构化后,日志信息就会变得易于检索。非常推荐在日志行中记录上下文信息,例如:当记录订单无法正常发货的日志时,可以同时添加订单的详细信息到日志中(当然是指订单中不敏感的信息)。
2020-04-23 20:50:06.392 INFO 66668 --- [1] [127.0.0.1-1544498251.152-3835-1940] logger.go:145 : {"key1":"value10","key2":"value20"} read ~/.otterbeat/TableStats.lastTime got no new items
它包含以下结构化部分,每个部分以一个空格分开:
yyyy-MM-dd HH:mm:ss.SSS
ERROR
WARN
/WARNING
INFO
DEBUG
logger:line
GO语言 %-20s
JAVA %-40s
2020-04-27 17:39:04.070 WARNING 71370 --- [19] [] log_test.go:30 : errors error github.com/bingoohuang/gou/lo.TestSetupLog /Users/bingoobjca/github/gou/lo/log_test.go:26 testing.tRunner /usr/local/go/src/testing/testing.go:991 runtime.goexit /usr/local/go/src/runtime/asm_amd64.s:1373 open failed github.com/bingoohuang/gou/lo.TestSetupLog /Users/bingoobjca/github/gou/lo/log_test.go:27 testing.tRunner /usr/local/go/src/testing/testing.go:991 runtime.goexit /usr/local/go/src/runtime/asm_amd64.s:1373 read config failed github.com/bingoohuang/gou/lo.TestSetupLog /Users/bingoobjca/github/gou/lo/log_test.go:28 testing.tRunner /usr/local/go/src/testing/testing.go:991 runtime.goexit /usr/local/go/src/runtime/asm_amd64.s:13732020-04-27 17:39:04.070 INFO 71370 --- [19] [] log_test.go:33 : {"key1":"value10","key2":"value\n20"} abcefg
$HOME/logs/{appName}/
目录下面,例如:/Users/bingoobjca/logs/otterbeat/
{appName}.log
,不带日期后缀,例如:otterbeat.log
{appName}.log.{YYYY-MM-DD}
,日期作为文件名后缀,例如:otterbeat.log.2020-04-24
====
,$$$$
等开源的日志库非常多,基本每个语言都有数十种,选择一个符合公司/业务需求的日志库需要精挑细选,有一个简单的指导原则是尽可能使用比较流行的日志库的稳定版本,入坑的几率要小一点。例如:
在虚拟机/物理机的场景中,绝大部分应用都以文件的形式输出日志(只有一些系统应用输出到 syslog/journal);
在容器场景中,多了一个标准输出的方式,应用把日志打到 stdout 或 stderr 上,日志会自动进入到 docker 的日志模块,可以通过 docker logs 或 kubectl logs 直接查看。
容器的标准输出只适应于比较单一的应用,例如 K8s 中的一些系统组件,线上的服务类应用通常都会涉及到多个层级(中间件)、和各种服务交互,一般日志都会分为好几类,如果全部打印到容器的标准输出,很难区分处理。
同时容器标准输出对于 DockerEngine 的性能消耗特别大,实测 10W/s 的日志量会额外占用 DockerEngine 1个核心的 CPU(单核 100%)。
日志等级是用来区分日志对应事件严重程度的说明,这是所有日志中必须具备的一个选项。通常日志会分为6个不同的等级:
FATAL
(致命):用来输出非常严重或预期中不会发生的错误。ERROR
(错误):非预期中的错误,此种错误可能导致部分系统异常但不会影响核心业务和系统正常运行。WARN
(警告):潜在的危险或值得关注的信息(比较核心的路径)。INFO
(信息):应用执行过程中的详细信息,一般通过该信息可以看到每个请求的主要执行过程。DEBUG
(调试):用于线下调试的日志信息,用于分析应用执行逻辑,线上应用切勿开启。TRACE
(跟踪):输出最细致的运行轨迹,可能包含涉及的数据内容。没有新的数据
,有输出时打印 发现新的数据,共10条
发现0条新数据
,或者 发现10条新数据
,这样难于搜索 发现新数据
日志是暂存的事物,而非永久存在的
。因为它们没有被存储在数据库中,往往只存在几天到几周的时间github.com/pkg/errors
库的 errors.Wrapf(err, "open file %s", filename)
来形成错误堆栈%+v
来打印日志堆栈 logrus.Warnf("errors %+v", err)