命令行执行Jupyter Notebook无法将完整运行输出写入日志文件
你当前命令重定向的是jupyter nbconvert主进程自身的stdout/stderr流,默认配置下nbconvert执行notebook时,所有代码单元格的print输出、logging日志、sys.stdout.write写入内容,都会被捕获后存入执行后.ipynb文件的单元格输出元数据中,不会直接透传到nbconvert主进程的输出流,因此日志文件里只能看到NbConvertApp自身的启动、调度日志,看不到代码内部的运行输出。
你日志里看到的Config option 'kernel_spec_manager_class' not recognized是SageMaker预装Jupyter配置与当前nbconvert版本的兼容警告,不影响实际执行逻辑,无需特殊处理。
- 先验证notebook执行状态:命令运行完成后打开对应
.ipynb文件,检查各代码单元格是否存在预期输出,排除代码未执行到输出逻辑、内核意外挂掉导致执行中断的问题 - 检查文件权限:确认执行命令的用户对
$LOG_FILE的父目录有读写权限,避免因权限不足导致输出无法写入 - 排除缓冲干扰:去掉
nohup和末尾的后台运行符&,前台执行nbconvert命令,观察终端是否能输出后续执行日志,确认是否为后台运行的块缓冲机制导致输出未落盘
方案1:开启nbconvert输出透传(最简便)
nbconvert自带参数可以直接将单元格输出透传到主进程标准流,修改执行命令如下,同时关闭输出缓冲、合并错误流:
NOTEBOOK_FILE="/home/ec2-user/SageMaker/mynb.ipynb" LOG_FILE="/home/ec2-user/SageMaker/logs.txt" nohup stdbuf -oL -eL jupyter nbconvert \ --to notebook \ --inplace \ --ExecutePreprocessor.timeout=-1 \ --ExecutePreprocessor.kernel_name=python3 \ --execute \ --output-debug \ "$NOTEBOOK_FILE" > "$LOG_FILE" 2>&1 &
参数说明:
--output-debug:开启单元格输出透传,所有cell的stdout/stderr内容都会直接输出到nbconvert主进程流stdbuf -oL -eL:将系统层面的stdout/stderr从默认块缓冲改为行缓冲,避免输出攒在内存中不写入日志文件2>&1:将stderr流合并重定向到日志文件,避免报错、警告信息丢失
方案2:Notebook内部直接配置日志写文件
如果不想依赖nbconvert的参数,可以在notebook第一个代码单元格添加如下配置,直接将所有输出重定向到指定日志文件,不受nbconvert执行逻辑影响:
import sys import logging from pathlib import Path LOG_FILE = "/home/ec2-user/SageMaker/logs.txt" Path(LOG_FILE).parent.mkdir(parents=True, exist_ok=True) # 自定义日志写入类,每次写入立即刷盘 class _LogWriter: def __init__(self, log_path): self.fp = open(log_path, "a", encoding="utf-8") def write(self, content): self.fp.write(content) self.fp.flush() def flush(self): self.fp.flush() # 重定向标准输出、错误流到日志文件 sys.stdout = _LogWriter(LOG_FILE) sys.stderr = _LogWriter(LOG_FILE) # 配置logging模块输出到同个日志文件 logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s", handlers=[logging.StreamHandler(sys.stdout)] )
配置完成后,后续所有单元格的print输出、logging日志、异常栈信息都会直接写入指定日志文件,不会存在缓冲丢失问题。
方案3:执行完成后从notebook文件提取输出
如果已经跑完notebook没抓到日志,可以直接用nbconvert将执行后的notebook导出为无代码的Markdown文件,提取所有单元格输出:
jupyter nbconvert --to markdown --no-input /home/ec2-user/SageMaker/mynb.ipynb --output /home/ec2-user/SageMaker/run_log
执行完成后会生成run_log.md文件,里面包含所有代码单元格的运行输出内容。
内容的提问来源于stack exchange,提问作者Clock Slave

