diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5a0f5c0436..41766c34df 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,12 @@ This section accumulates work toward the **2.3.0** milestone
### Fixed
+- **dev:** `scripts/dev_multi_instance.sh logs all` now prints the nginx logs
+ as well as both Gateway logs; it printed only the Gateway logs, the same as
+ `logs` with no argument. `logs nginx` and `logs all` also include `nginx.log`,
+ the nginx start command's own output, which no option printed before.
+ ([#7723])
+
- **memory:** `GET /api/memory/config` and `/api/memory/status` now mask
credential values in `backend_config` (`api_key`, tokens, passwords,
`Authorization` headers, at any depth) as `***`. They returned the config as
@@ -15927,3 +15933,4 @@ with **180 merged pull requests** since the first 2.0 milestone tag.
[#7651]: https://github.com/bytedance/deer-flow/pull/7651
[#7659]: https://github.com/bytedance/deer-flow/pull/7659
[#7665]: https://github.com/bytedance/deer-flow/pull/7665
+[#7723]: https://github.com/bytedance/deer-flow/pull/7723
diff --git a/CHANGELOG_zh.md b/CHANGELOG_zh.md
index 7f6d1fc9b1..a87009dbee 100644
--- a/CHANGELOG_zh.md
+++ b/CHANGELOG_zh.md
@@ -14,6 +14,11 @@
### 修复
+- **开发:** `scripts/dev_multi_instance.sh logs all` 现在除两个 Gateway 日志外也会输出
+ nginx 日志;此前它只输出 Gateway 日志,与不带参数的 `logs` 相同。`logs nginx` 与
+ `logs all` 还会包含 `nginx.log`(nginx 启动命令自身的输出),此前没有任何选项会输出它。
+ ([#7723])
+
- **记忆:** `GET /api/memory/config` 与 `/api/memory/status` 现在会把 `backend_config`
中的凭据值(`api_key`、token、密码、`Authorization` 请求头,任意层级)遮蔽为 `***`。
此前它们按加载结果原样返回配置,而 `$VAR` 引用此时已被解析,因此 DeerMem 提取模型的密钥
@@ -12695,3 +12700,4 @@ DeerFlow 2.0 是围绕"超级智能体"框架的彻底重写,核心包含子
[#7651]: https://github.com/bytedance/deer-flow/pull/7651
[#7659]: https://github.com/bytedance/deer-flow/pull/7659
[#7665]: https://github.com/bytedance/deer-flow/pull/7665
+[#7723]: https://github.com/bytedance/deer-flow/pull/7723
diff --git a/backend/docs/CONFIGURATION.md b/backend/docs/CONFIGURATION.md
index ffd724b1a5..8b885b4857 100644
--- a/backend/docs/CONFIGURATION.md
+++ b/backend/docs/CONFIGURATION.md
@@ -813,7 +813,7 @@ Notes:
scripts/dev_multi_instance.sh up # containers, config, Gateway A, then B, then nginx
scripts/dev_multi_instance.sh check # automated cross-instance checks (below)
scripts/dev_multi_instance.sh stop b --kill # SIGKILL B to simulate a crash; `start b` brings it back
-scripts/dev_multi_instance.sh logs a -f
+scripts/dev_multi_instance.sh logs a -f # follow Gateway A; also b, nginx, or all (both Gateways and nginx)
scripts/dev_multi_instance.sh down
```
diff --git a/backend/tests/test_dev_multi_instance_script.py b/backend/tests/test_dev_multi_instance_script.py
index 654ea7937c..d05666ce2f 100644
--- a/backend/tests/test_dev_multi_instance_script.py
+++ b/backend/tests/test_dev_multi_instance_script.py
@@ -607,6 +607,47 @@ def test_shell_script_rejects_unknown_commands(tmp_path):
assert "Unknown command" in result.stderr
+_GATEWAY_LOGS = ("gateway-a.log", "gateway-b.log")
+_NGINX_LOGS = ("nginx.log", "nginx-error.log", "nginx-access.log")
+
+
+@pytest.mark.parametrize(
+ ("args", "expected"),
+ [((), _GATEWAY_LOGS), (("b",), ("gateway-b.log",)), (("nginx",), _NGINX_LOGS), (("all",), _GATEWAY_LOGS + _NGINX_LOGS)],
+ ids=["default", "b", "nginx", "all"],
+)
+def test_shell_script_logs_prints_the_selected_files(tmp_path, args, expected):
+ logs = tmp_path / "state" / "logs"
+ logs.mkdir(parents=True)
+ for name in _GATEWAY_LOGS + _NGINX_LOGS:
+ (logs / name).write_text(f"line from {name}\n", encoding="utf-8")
+ env = {**os.environ, "DEERFLOW_MI_STATE_DIR": str(tmp_path / "state")}
+
+ result = subprocess.run([require_script_bash(), str(SHELL_PATH), "logs", *args], capture_output=True, text=True, timeout=30, env=env)
+
+ assert result.returncode == 0, result.stderr
+ headers = [line.removeprefix("==> ").removesuffix(" <==") for line in result.stdout.splitlines() if line.startswith("==> ")]
+ assert [Path(header).name for header in headers] == list(expected)
+ for name in expected:
+ assert f"line from {name}" in result.stdout
+
+
+@pytest.mark.parametrize(("args", "expected"), [(("all",), _GATEWAY_LOGS), (("nginx",), ())], ids=["all", "nginx"])
+def test_shell_script_logs_skips_nginx_files_when_nginx_never_started(tmp_path, args, expected):
+ # start_nginx bails on a missing binary or busy port, leaving only Gateway logs.
+ logs = tmp_path / "state" / "logs"
+ logs.mkdir(parents=True)
+ for name in _GATEWAY_LOGS:
+ (logs / name).write_text(f"line from {name}\n", encoding="utf-8")
+ env = {**os.environ, "DEERFLOW_MI_STATE_DIR": str(tmp_path / "state")}
+
+ result = subprocess.run([require_script_bash(), str(SHELL_PATH), "logs", *args], capture_output=True, text=True, timeout=30, env=env)
+
+ assert result.returncode == 0, result.stderr
+ headers = [line.removeprefix("==> ").removesuffix(" <==") for line in result.stdout.splitlines() if line.startswith("==> ")]
+ assert [Path(header).name for header in headers] == list(expected)
+
+
def test_makefile_exposes_harness_targets():
makefile = (REPO_ROOT / "Makefile").read_text(encoding="utf-8")
diff --git a/scripts/dev_multi_instance.sh b/scripts/dev_multi_instance.sh
index 830679d8a6..91850f1cd6 100755
--- a/scripts/dev_multi_instance.sh
+++ b/scripts/dev_multi_instance.sh
@@ -59,7 +59,7 @@ Commands:
up [--skip-install] [--no-nginx] Start containers, render the config, start A, then B, then nginx
down Stop everything; delete the containers and harness data (logs are kept)
status Show containers, Gateways and nginx
- logs [a|b|nginx|all] [-f] Print the last 100 log lines (-f follows)
+ logs [a|b|nginx|all] [-f] Print the last 100 log lines (default a and b; all adds nginx; -f follows)
check [--with-llm] Run the cross-instance checks against the running pair
stop [--kill] Stop one Gateway (SIGTERM; --kill sends SIGKILL to simulate a crash)
start Start a stopped Gateway again
@@ -579,12 +579,13 @@ cmd_status() {
}
cmd_logs() {
- local follow=false arg files=""
+ # nginx.log holds what the nginx command printed while starting.
+ local follow=false arg files="" nginx_logs="nginx.log nginx-error.log nginx-access.log"
for arg in "$@"; do
case "$arg" in
a | b) files="$files gateway-$arg.log" ;;
- nginx) files="$files nginx-error.log nginx-access.log" ;;
- all) files="$files gateway-a.log gateway-b.log" ;;
+ nginx) files="$files $nginx_logs" ;;
+ all) files="$files gateway-a.log gateway-b.log $nginx_logs" ;;
-f | --follow) follow=true ;;
*) die "Unknown option for logs: $arg" ;;
esac