Metadata-Version: 2.4
Name: jupyter-cell-logger
Version: 0.1.0
Summary: Jupyter server extension that logs the code of every executed cell to JSONL files under the user's home directory.
Author: Jupyter Cell Logger Contributors
License: BSD-3-Clause
Classifier: Framework :: Jupyter
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.6
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: jupyter_server>=1.0.0
Requires-Dist: traitlets
Requires-Dist: tornado
Dynamic: author
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: license
Dynamic: license-file
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# jupyter-cell-logger

一个 **Jupyter 后端（server extension）插件**：记录每一个运行的 cell 的代码内容，
并将日志以 JSON Lines 格式保存在用户 home 目录下（默认 `~/jupyter_cell_logs/`）。

- 目标环境：**Python 3.6.9 + JupyterLab 3.2.9**（jupyter_server 1.x）
- 同时兼容：jupyter_server 2.x（如 JupyterLab 3.6+/4.x 搭配的版本）
- **纯后端实现**，无需安装/构建任何前端插件

## 原理

JupyterLab 前端执行 cell 时，本质上是通过内核 websocket 向 kernel 发送一条
`execute_request` 消息（shell channel），消息里带着 cell 的完整代码。

本插件在服务端把内核 websocket handler（jupyter_server 1.x 的
`ZMQChannelsHandler`、2.x 的 `KernelWebsocketHandler`）的 `on_message`
包了一层：

1. 在原始消息继续转发给 kernel **之前**，先解析出 `execute_request` 中的代码；
2. 写入当天的日志文件（JSONL，每行一条记录）；
3. 任何解析/写入异常都只记 debug 日志，绝不影响 kernel 消息本身。

因此它能捕获 **所有来源** 的代码执行：JupyterLab notebook、Console、
nbclassic、以及通过 REST/编程方式发起的执行。

## 安装

在目标环境（Python 3.6.9）下：

```bash
pip install .
```

安装包自带 `etc/jupyter/jupyter_server_config.d/jupyter_cell_logger.json`，
会自动启用该扩展。安装后可用下面的命令确认：

```bash
jupyter server extension list
# 应看到: jupyter_cell_logger  enabled
# True
```

启动 `jupyter lab` 即可生效，服务端日志中会打印：

```
[I ...] jupyter_cell_logger: logging executed cell code to C:\Users\xxx\jupyter_cell_logs (patched: ZMQChannelsHandler)
```

> 如果没有自动启用（例如 pip `--user` 安装导致 data_files 落在非标准前缀），
> 可手动启用：
> ```bash
> jupyter server extension enable --py jupyter_cell_logger --sys-prefix
> ```
> 或在 `jupyter_server_config.py` 中加入：
> ```python
> c.ServerApp.jpserver_extensions = {"jupyter_cell_logger": True}
> ```

## 日志位置与格式

默认写到用户 home 目录：

```
~/jupyter_cell_logs/cell_log_YYYYMMDD.jsonl     # 每天一个文件
```

每行一条 JSON 记录（`ensure_ascii=False`，中文原样保存）：

```json
{"timestamp": "2026-10-13T15:01:23+08:00", "kernel_id": "3f2a1c9e-...", "session_id": "b17d...", "notebook_path": "demo/Untitled.ipynb", "code": "print('hello')"}
```

| 字段 | 说明 |
| --- | --- |
| `timestamp` | 执行时刻（本地时间，含时区偏移） |
| `kernel_id` | 内核 id |
| `session_id` | 前端会话 id |
| `notebook_path` | notebook 相对路径（首次执行时通过 session manager 异步解析，**第一条记录可能为 `null`**，从第二条起正常；Console 会话记录其会话路径） |
| `code` | 该次执行的完整代码 |

## 配置

在 `jupyter_server_config.py`（`~/.jupyter/` 或 `jupyter --config-dir`）中：

```python
c.CellLogger.enabled = True            # False 可关闭记录
c.CellLogger.log_dir = r"D:\cell_logs" # 自定义日志目录
```

也可用环境变量指定目录（优先级低于 `c.CellLogger.log_dir`）：

```
set JUPYTER_CELL_LOGGER_DIR=D:\cell_logs
```

## 测试

```bash
# 单元测试（校验两种 websocket 报文格式的解析 + JSONL 写入）
python -m pytest tests/ -v

# 端到端测试（本地启动 jupyter server，通过 websocket 发 execute_request，校验日志）
python tests/e2e_server_test.py
```

## 卸载

```bash
pip uninstall jupyter-cell-logger
```

## 项目结构

```
jupyter_cell_logger/
    __init__.py      # 扩展入口：load_jupyter_server_extension 等
    interceptor.py   # 拦截内核 websocket 的 on_message，解析 execute_request
    recorder.py      # CellLogger 配置类：kernel->notebook 路径解析 + JSONL 写入
jupyter-config/
    jupyter_server_config.d/jupyter_cell_logger.json   # 自动启用
tests/
    test_wire_formats.py   # 单元测试
    e2e_server_test.py     # 端到端测试
```
