☰
Cognee 日志系统完全指南:日志目录结构、明文格式与文件保留策略
2026/9/26 2:57:23 网站建设 项目流程

Cognee 日志系统完全指南:日志目录结构、明文格式与文件保留策略

【免费下载链接】cogneeCognee is the open-source AI memory platform for agents. Give your AI agents persistent long-term memory across sessions with a self-hosted knowledge graph engine.项目地址: https://gitcode.com/GitHub_Trending/co/cognee

本篇技术指南围绕 Cognee 仓库中的 logs/README.md 展开,完整讲解日志文件的命名规则、单条日志的结构化字段、自动保留策略,并结合 cognee/shared/logging_utils.py 的源码实现,深入剖析日志文件从创建、写入、轮转到清理的完整生命周期。读完本文,你将能够定位 Cognee 运行时日志的实际落盘位置,读懂每一条日志的结构,并通过环境变量灵活控制日志行为,为长期部署的自检与排障提供依据。

日志目录定位:logs/在仓库中的角色

在 Cognee 仓库根目录下存在一个logs/目录,其中只保留了 logs/README.md 一个说明文件。根据仓库根目录的 .gitignore 配置:

# Cognee logs directory - keep directory, ignore contents logs/* !logs/.gitkeep !logs/README.md

可以确认:目录结构被保留在版本控制中,而实际产生的.log日志文件全部被 gitignore 忽略。也就是说,logs/只是应用日志的约定存放位置,运行期生成的日志不会进入 Git 历史,避免了仓库体积随运行时长无限膨胀。这与 README 中 "The logs directory structure is preserved in version control, but the log files themselves are gitignored" 的描述完全一致。

需要补充的是,Cognee 的日志实际写入路径并非写死的仓库logs/目录,而是由配置动态解析的。在 cognee/base_config.py 中:

logs_root_directory: str = os.getenv("COGNEE_LOGS_DIR", str(Path.home() / ".cognee" / "logs"))

即日志根目录的默认值是用户主目录下的~/.cognee/logs,可通过环境变量COGNEE_LOGS_DIR覆盖。而 cognee/shared/logging_utils.py 中的resolve_logs_dir()则给出了完整的解析优先级:

  1. BaseConfig.logs_root_directory(默认~/.cognee/logs,可被COGNEE_LOGS_DIR覆盖),并尝试创建目录、校验可写性;
  2. 若上述目录不可写,则回退到/tmp/cognee_logs(同样尽力创建并校验);
  3. 两者都不可用时返回None,文件日志将被跳过,仅在控制台输出。

因此在排查问题时,可依次检查COGNEE_LOGS_DIR指向的目录、~/.cognee/logs与/tmp/cognee_logs三处。

日志文件命名规则

根据 logs/README.md,日志文件按日期命名,格式为:

YYYY-MM-DD_HH-MM-SS.log

例如一次启动产生的日志文件可能是2025-03-27_13-05-27.log。

这一命名规则在源码中得到印证。在 cognee/shared/logging_utils.py 的setup_logging()中:

# NOTE: environment variable must be used here as it allows us to # log to a single file with a name based on a timestamp in a multiprocess setting. # Without it, we would have a separate log file for every process. log_file_path = os.environ.get("LOG_FILE_NAME") if not log_file_path and logs_dir is not None: # Create a new log file name with the cognee start time start_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") log_file_path = str((logs_dir / f"{start_time}.log").resolve()) os.environ["LOG_FILE_NAME"] = log_file_path

两个关键细节值得注意:

  • 文件名取的是进程启动时刻的时间戳(strftime("%Y-%m-%d_%H-%M-%S")),而不是每条日志的写入时间。因此一个进程整个生命周期内的日志都写入同一个文件,便于按"一次运行"为单位回溯;
  • 生成的文件路径会被写入环境变量LOG_FILE_NAME,供多进程场景复用。注释明确指出:如果每次都重新生成文件名,多进程部署中每个进程都会各建一个日志文件;而共享LOG_FILE_NAME后,同一部署中的所有进程可以把日志合并写入单一文件,方便统一采集与分析。

单条日志的结构与示例

README 给出的示例日志条目为:

2025-03-27T13:05:27.481446Z [INFO ] Structured log message user_id=user123 action=login status=success [TestLogger]

对照 README 的字段说明,一条日志包含五个组成部分:

字段说明示例值
时间戳ISO 格式,UTC,含微秒2025-03-27T13:05:27.481446Z
日志级别填充到固定宽度(左对齐补齐)[INFO ]
消息日志主体内容Structured log message
附加上下文以key=value形式附加的键值对(可选)user_id=user123 action=login status=success
日志器名称方括号包裹[TestLogger]

错误级别的日志还会附带完整的异常 traceback。

这一行式结构由 cognee/shared/logging_utils.py 中的PlainFileHandler生成。该类继承自logging.handlers.RotatingFileHandler,其emit()方法从 structlog 的字典型日志记录中提取各字段,按固定模板格式化:

log_entry = f"{timestamp} [{record.levelname.ljust(8)}] {message}{context_str} [{logger_name}]\n"

可以逐项对应到 README 的结构描述:

  • 时间戳:datetime.now().strftime(get_timestamp_format()),默认格式为%Y-%m-%dT%H:%M:%S.%f(UTC),即2025-03-27T13:05:27.481446加Z后缀;get_timestamp_format() 对不支持微秒的平台做了兜底——检测到ValueError时退化为%Y-%m-%dT%H:%M:%S;
  • 日志级别:record.levelname.ljust(8)将级别名左对齐填充到 8 个字符宽度,例如INFO补齐为INFO,保证整列日志级别垂直对齐,便于肉眼扫描;
  • 消息:取自 structlog 记录中的event键;
  • 附加上下文:剔除event、logger、level、timestamp、exc_info等元字段后,其余键值对以k=v空格分隔拼接——这正是示例中user_id=user123 action=login status=success的来源,适用于注入用户 ID、操作名、状态码等结构化诊断信息;
  • 日志器名称:取自记录中的logger键,缺省时回退到 Python 日志器的record.name;
  • 异常 traceback:当记录携带exc_info(无论是标准元组形式还是直接传入的异常对象)时,通过traceback.format_exception()格式化后追加写入文件,这与 README "Exception tracebacks are included for error logs" 的说明一致。

日志系统的底层:structlog 集成

理解上述结构离不开 Cognee 的日志基础设施。cognee/shared/logging_utils.py 的setup_logging()是统一入口,cognee/__init__.py在模块加载时即调用logger = setup_logging(),保证整个框架共享同一套日志配置。

setup_logging()完成的关键工作包括:

  • structlog 配置:通过structlog.configure()挂载处理器链(filter_by_level→add_logger_name→add_log_level→TimeStamper→ 自定义exception_handler等),使每条日志都携带统一的时间戳、级别与日志器名;
  • 系统级异常钩子:将sys.excepthook替换为handle_exception,让未捕获异常也经由 structlog 记录后再交给默认钩子;KeyboardInterrupt直接透传不记录。对应测试位于 cognee/tests/unit/shared/test_logging_setup_excepthook.py,其中验证了钩子安装、异常渲染失败时回退到普通 traceback、以及KeyboardInterrupt透传三个契约;
  • 控制台输出:使用带颜色的ConsoleRenderer,并特意关闭了 Rich traceback 的 locals 渲染(源码注释说明:检索路径上的异常若渲染帧局部变量,可能因递归展开嵌入向量等大对象导致内存暴涨甚至 OOM);
  • 外部库日志治理:configure_external_library_logging()通过环境变量与日志器级setLevel(logging.CRITICAL)+disabled=True抑制 LiteLLM 等依赖库的噪声日志,还挂载了过滤loggingworker cancelled、cancellederror等关键字的自定义 Filter。

文件保留策略:仅保留最近 10 个日志文件

README 明确说明:系统自动仅保留最近 10 个日志文件,新文件创建时自动删除更旧的日志,以防止长时间运行部署中磁盘占用失控。

这一策略在源码中有两处实现:

1. 文件数量级清理(cleanup_old_logs)

cognee/shared/logging_utils.py 中的cleanup_old_logs()在每次setup_logging()完成后被调用:

# Maximum number of log files to keep MAX_LOG_FILES = 10 ... log_files = [f for f in logs_dir.glob("*.log") if f.is_file()] log_files.sort(key=lambda x: x.stat().st_mtime, reverse=True) if len(log_files) > max_files: for old_file in log_files[max_files:]: old_file.unlink()

其逻辑是:扫描日志目录下所有*.log文件,按修改时间从新到旧排序,删除超出MAX_LOG_FILES(即 10)之外的最旧文件。这与 README "keeps only the 10 most recent log files" 完全一致。此外它还做了 CLI 模式适配——在COGNEE_CLI_MODE=true时只输出一条汇总信息,避免刷屏。

2. 单文件大小级轮转(PlainFileHandler继承自RotatingFileHandler)

除了文件数量上限,每个日志文件本身还有大小上限。PlainFileHandler在emit()中自行检查当前文件大小,超过阈值即调用doRollover()触发轮转:

# Log rotation defaults — override via COGNEE_LOG_MAX_BYTES / COGNEE_LOG_BACKUP_COUNT LOG_MAX_BYTES = int(os.getenv("COGNEE_LOG_MAX_BYTES", 50 * 1024 * 1024)) # 50 MB LOG_BACKUP_COUNT = int(os.getenv("COGNEE_LOG_BACKUP_COUNT", 5)) # 5 backups → 300 MB cap

默认每个文件 50 MB,最多保留 5 个轮转备份,单进程累计上限约 250–300 MB。这一大小轮转策略与"保留最近 10 个文件"的数量策略相互配合:大小轮转防止单文件无限增长,数量清理防止文件数量无限积累,两者共同构成长期运行部署的磁盘保护机制。

相关环境变量汇总

综合源码,与日志行为相关的环境变量如下:

环境变量默认值作用
COGNEE_LOGS_DIR~/.cognee/logs指定日志根目录
COGNEE_LOG_FILEtrue设为false/0/no可完全禁用文件日志,仅保留控制台输出
LOG_FILE_NAME无(自动生成)显式指定日志文件完整路径;多进程部署中可共享同一路径实现单文件合并写入
LOG_LEVELINFO全局日志级别(DEBUG/INFO/WARNING/ERROR/CRITICAL等),setup_logging()按此设置控制台与文件 handler 的级别
COGNEE_LOG_MAX_BYTES52428800(50 MB)单个日志文件的大小上限,触发轮转
COGNEE_LOG_BACKUP_COUNT5轮转备份文件的最大数量
COGNEE_CLI_MODE无true时清理旧日志只输出汇总信息,避免逐文件刷屏

使用方式:自动生成,无需人工干预

README 明确指出:日志由应用自身的日志机制自动生成,使用该功能无需任何手动操作。

结合源码,日志文件的生命周期大致如下:

  1. 进程启动时,cognee/__init__.py调用setup_logging();
  2. setup_logging()解析日志目录(COGNEE_LOGS_DIR→~/.cognee/logs→/tmp/cognee_logs),以启动时刻生成YYYY-MM-DD_HH-MM-SS.log文件名并挂载PlainFileHandler;
  3. 运行期间,各模块通过get_logger()获取 structlog 日志器,写入结构化日志(控制台 + 文件双通道);
  4. 单文件达到 50 MB 时触发大小轮转;
  5. 新一次启动后,cleanup_old_logs(logs_dir, MAX_LOG_FILES)删除超出 10 个的最旧文件。

对开发与运维人员而言,常见的排查手段是:

  • 通过 get_log_file_location()(遍历 root logger 的 handler,返回当前文件日志路径)在运行时确认实际落盘文件;启动日志中也会输出一行Log file created at: <path>(见 setup_logging());
  • 启动时通过LOG_LEVEL=DEBUG临时提升日志级别以获取更细粒度的诊断信息;
  • 长期部署中无需手动清理——数量上限(10 个文件)与大小上限(50 MB × 5 备份)已由系统自动兜底。

总结

logs/README.md用极简的篇幅定义了 Cognee 应用日志的约定,而其背后的实现远比表面丰富:YYYY-MM-DD_HH-MM-SS.log命名对应进程启动时间戳与多进程单文件合并机制;[INFO ]的定宽级别来自ljust(8)格式化;10 个文件上限来自cleanup_old_logs()的修改时间排序删除;而大小轮转与数量清理的双层策略,为长期运行的 AI 记忆服务提供了磁盘使用的双重保障。无论是阅读日志定位问题,还是通过环境变量调整日志行为,本文梳理的目录定位、字段语义与保留策略均可直接对照 cognee/shared/logging_utils.py 源码进一步验证与扩展。

【免费下载链接】cogneeCognee is the open-source AI memory platform for agents. Give your AI agents persistent long-term memory across sessions with a self-hosted knowledge graph engine.项目地址: https://gitcode.com/GitHub_Trending/co/cognee

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询