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()则给出了完整的解析优先级:
BaseConfig.logs_root_directory(默认~/.cognee/logs,可被COGNEE_LOGS_DIR覆盖),并尝试创建目录、校验可写性;- 若上述目录不可写,则回退到
/tmp/cognee_logs(同样尽力创建并校验); - 两者都不可用时返回
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_FILE | true | 设为false/0/no可完全禁用文件日志,仅保留控制台输出 |
LOG_FILE_NAME | 无(自动生成) | 显式指定日志文件完整路径;多进程部署中可共享同一路径实现单文件合并写入 |
LOG_LEVEL | INFO | 全局日志级别(DEBUG/INFO/WARNING/ERROR/CRITICAL等),setup_logging()按此设置控制台与文件 handler 的级别 |
COGNEE_LOG_MAX_BYTES | 52428800(50 MB) | 单个日志文件的大小上限,触发轮转 |
COGNEE_LOG_BACKUP_COUNT | 5 | 轮转备份文件的最大数量 |
COGNEE_CLI_MODE | 无 | true时清理旧日志只输出汇总信息,避免逐文件刷屏 |
使用方式:自动生成,无需人工干预
README 明确指出:日志由应用自身的日志机制自动生成,使用该功能无需任何手动操作。
结合源码,日志文件的生命周期大致如下:
- 进程启动时,
cognee/__init__.py调用setup_logging(); setup_logging()解析日志目录(COGNEE_LOGS_DIR→~/.cognee/logs→/tmp/cognee_logs),以启动时刻生成YYYY-MM-DD_HH-MM-SS.log文件名并挂载PlainFileHandler;- 运行期间,各模块通过
get_logger()获取 structlog 日志器,写入结构化日志(控制台 + 文件双通道); - 单文件达到 50 MB 时触发大小轮转;
- 新一次启动后,
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),仅供参考