不管运行程序的平台是 docker 还是 k8s, 采集日志有两种方案:

  • 方案一:采集程序的标准输出
  • 方案二:程序把日志写文件中,然后采集日志文件

在先来分析一下方案一和二的好坏

方案一的坏处就是:『多行日志采集』

不管是用『行首正则表达式』还是『json化』都不是优雅的解决方案

『行首正则表达式』,不是所有模块的日志都有固定格式,当然,想要强制一致也有办法;『json化』:没有格式化的 json 是人类不可读的,『json化』方便了程序,但是恶心了程序员的眼睛

所以,pass

方案二:没有缺点

我怎么用的:程序,即打印日志到标准输出,又写入日志文件

  • 打印日志到标准输出: 这是给人看到,可以直接使用 docker logs、docker-compose logs、kubectl logs 直接看,方便
  • 写入日志文件:使用 loguru 的 serialize='json' 将单条日志写入日志文件

所以这些日志输出方案,及满足了程序员直接查看日志,也方便程序的采集。

那具体如何实现呢?直接上代码

from loguru import logger
from mark import BASE_DIR
import settings
import os
import json
from loguru._handler import Handler
from loguru._recattrs import RecordException

log_path = BASE_DIR/'logs'

if not os.path.exists(log_path):
    os.makedirs(log_path)

def _serialize_record(text: str, record: dict):
    exception: RecordException = record["exception"]

    if exception is not None:
        exception = {
            "type": None if exception.type is None else exception.type.__name__,
            "value": exception.value,
            "traceback": bool(exception.traceback),
        }

    serializable = {
        "text": text,
        "record": {
            "extra": record["extra"],
        },
    }

    return json.dumps(serializable, default=str, ensure_ascii=False) + "\n"


Handler._serialize_record = staticmethod(_serialize_record)

logger.add(
    log_path/'run.log',
    serialize='json',
    rotation='100 MB',
    retention=1
)

最重要的就是下面这段,我们添加一个日志处理器,用于将日志写到文件中
第一个参数指定,写哪里
第二个参数指定,用什么格式写,这里用 json,因为这样可以避免『打印堆栈错误会多行』的问题
第三、四个参数指定,表示日志轮换规则,写慢 100 MB,就备份一下,然后从 0MB 重新开始,总共有 1 份

logger.add(
    log_path/'run.log',
    serialize='json',
    rotation='100 MB',
    retention=1
)

loguru 使用 serialize='json' 默认输出的字段太多了,所以我们使用猴子补丁,替换 _serialize_record 函数,去掉不需要的字段,节约日志存储成本

参考文章:
loguru 如何轮换日志
loguru serialize 减少字段
如何使用 loguru 接管程序的所有日志输出


universe_king
3.4k 声望680 粉丝