# 输出与报告

> shutdown-check 会打印和写出哪些内容：文本时间线、所有时间线事件、--json 结果、JUnit 报告以及退出码。

Source: https://shutdown.jscrate.dev/zh/docs/output
Last updated: 2026-09-23

shutdown-check 每次运行都会产出一个结果，包含结论、诊断码、消息、时间线、耗时，以及捕获到的服务输出。给人看选文本，给脚本用选 JSON，给 CI 报告用选 JUnit XML。

## 选择输出格式

| 格式      | 命令选项       | 输出位置 | 适用场景                   |
| --------- | -------------- | -------- | -------------------------- |
| 文本      | 默认           | stdout   | 本地调试和 CI 日志         |
| JSON      | `--json`       | stdout   | 脚本和结构化日志           |
| JUnit XML | `--junit FILE` | 文件     | CI 的测试报告界面          |
| 退出码    | 始终           | 进程     | 让调用方判定通过或失败     |

打印文本或 JSON 的同时，都可以写出 JUnit：

```bash
npx shutdown-check test --json --junit shutdown-result.xml
```

## 阅读文本输出

通过时，结果包含结论和时间线：

```text
PASS SC000: Graceful shutdown verified

Timeline:
  +    5 ms  process launched — pid=67911
  +  113 ms  service ready — HTTP 200
  +  113 ms  work request sent — #1 GET /slow
  +  113 ms  work request sent — #2 GET /slow
  +  114 ms  work confirmed active — 2 response(s) sent headers; bodies still in progress
  +  114 ms  signal sent — SIGTERM
  +  115 ms  readiness withdrawn — HTTP 503
  +  115 ms  new request rejected — HTTP 503
  + 2117 ms  work request finished — #1 HTTP 200
  + 2117 ms  work request finished — #2 HTTP 200
  + 2120 ms  process exited — code=0, signal=none
  + 2121 ms  shutdown verified — work completed and service exited before deadline
```

`+` 后面的数字是从运行开始算起的毫秒数。按顺序往下读，就能看到 `SIGTERM` 前后发生了什么。

失败时，最后一行是 `check failed`：

```text
FAIL SC201: In-flight request was interrupted: aborted

Timeline:
  +    6 ms  process launched — pid=73692
  +  112 ms  service ready — HTTP 200
  +  113 ms  work request sent — #1 GET /slow
  +  113 ms  work confirmed active — 1 response(s) sent headers; bodies still in progress
  +  113 ms  signal sent — SIGTERM
  +  116 ms  process exited — code=null, signal=SIGTERM
  +  116 ms  work request finished — #1 aborted
  +  116 ms  check failed — SC201: In-flight request was interrupted: aborted
```

打开对应诊断码的页面（这里是 [SC201](https://shutdown.jscrate.dev/zh/docs/codes/sc201)），查看原因和修复方法。

## 服务的 stdout 和 stderr

检查失败时，文本输出会附上服务每个非空输出流的最后 8 KiB：

```text
Service stderr (last 8 KiB):
Error: database close timed out

Service stdout (last 8 KiB):
SIGTERM received
closing HTTP server
```

输出流超过 8 KiB 时，较早的内容会丢弃。所以关闭日志要简洁，重要的状态要在靠近失败的地方打印。

服务输出是先捕获、后显示的，不会实时输出。它显示在结果之后，这样时间线依然清晰易读。

## 时间线事件

| 事件                    | 含义                                               |
| ----------------------- | -------------------------------------------------- |
| `process launched`      | 配置的启动命令已启动                               |
| `service ready`         | 就绪检查路由返回了预期状态码                       |
| `work request sent`     | 发出了一个测试请求                                 |
| `work confirmed active` | 所选的启动确认已通过                               |
| `signal sent`           | 已发送第一次 `SIGTERM`                             |
| `signal repeated`       | 已发送可选的第二次 `SIGTERM`                       |
| `readiness withdrawn`   | 就绪检查路由不再报告就绪                           |
| `new request rejected`  | 新请求返回了允许的状态码，或连接被拒绝             |
| `work request finished` | 一个测试请求的响应已完成或失败                     |
| `process exited`        | 启动的进程已停止                                   |
| `shutdown verified`     | 所有配置的检查都已通过                             |
| `check failed`          | 运行以显示的诊断码结束                             |

只有配置中对应的部分运行了，相应的事件才会出现。例如，只有启用了 `shutdown.readinessWithdrawal`，才会出现 `readiness withdrawn`。

## JSON 输出

运行：

```bash
npx shutdown-check test --json
```

命令会打印一个 JSON 对象。下面是一个精简过的通过示例：

```json
{
  "pass": true,
  "code": "SC000",
  "message": "Graceful shutdown verified",
  "startedAt": "2026-09-23T10:00:00.000Z",
  "durationMs": 2121,
  "timeline": [
    { "ms": 5, "event": "process launched", "detail": "pid=67911" },
    { "ms": 114, "event": "signal sent", "detail": "SIGTERM" },
    {
      "ms": 2121,
      "event": "shutdown verified",
      "detail": "work completed and service exited before deadline"
    }
  ],
  "stdout": "",
  "stderr": ""
}
```

| 选项 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `pass` (必填) | `boolean` | — | 仅当结果为 `SC000` 时为 `true`。 |
| `code` (必填) | `string` | — | [诊断码](https://shutdown.jscrate.dev/zh/docs/codes)：通过时为 `SC000`，否则为第一个失败项的诊断码。 |
| `message` (必填) | `string` | — | 用一句话说明发生了什么，并附上观察到的值。 |
| `timeline` (必填) | `TimelineEvent[]` | — | 按顺序列出检查的每一步，并标出从检查开始算起的毫秒数。 |
| `stdout` (必填) | `string` | — | 服务写入 stdout 的最后 8 KiB 内容。 |
| `stderr` (必填) | `string` | — | 服务写入 stderr 的最后 8 KiB 内容。 |

`timeline` 数组中的每一项：

| 选项 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `ms` (必填) | `number` | — | 从检查开始算起的毫秒数，已取整。 |
| `event` (必填) | `string` | — | 发生了什么：`"process launched"`、`"service ready"`、`"signal sent"`、`"work request finished"`、`"process exited"` 等。 |
| `detail` | `string` | — | 事件对应的具体值，例如 `"HTTP 503"` 或 `"code=0, signal=none"`。 |

JSON 模式的退出码与文本模式相同。失败时仍然以 `1` 退出，所以 shell 步骤可能需要先保存 stdout，再根据退出码做处理。

## JUnit 报告

运行：

```bash
npx shutdown-check test --junit reports/shutdown.xml
```

父目录必须已经存在。shutdown-check 会写出一个测试套件，其中只有一个测试用例：

- 通过时没有 failure 元素。
- 关闭失败时，诊断码和消息作为 failure 写入。
- 时间线写入 system output。
- 如果捕获到了服务的 stdout 和 stderr，也会一并写入。
- 套件耗时取本次运行的耗时。

默认的套件名是 `shutdown-check`。使用 Node API 时，给 `junitXml(result, name)` 传第二个参数即可换一个名字。

CLI 会先写报告，再打印文本或 JSON，因此普通的检查失败仍会生成报告文件。出现环境或配置错误时不会生成报告，因为没有产生 `CheckResult`。

## 退出码

| 退出码 | 含义 |
| --- | --- |
| `0` | 通过 |
| `1` | 关闭检查失败 |
| `2` | 环境或配置错误 |

| 退出码 | 含义                                   | 可用的输出                     |
| ------ | -------------------------------------- | ------------------------------ |
| `0`    | 配置的关闭约定已通过                   | 文本/JSON，以及可选的 JUnit    |
| `1`    | 测试已运行，返回了表示失败的 SC 诊断码 | 文本/JSON，以及可选的 JUnit    |
| `2`    | 参数、环境或配置问题中止了运行         | stderr 上的错误；没有 JUnit 结果 |

只要退出码不为 0，CI 就应该判定失败。`2` 说明测试环境本身有问题；`1` 说明服务的行为需要排查。

## 在代码中使用结果

`checkShutdown()` 和 `runCheck()` 返回的 `CheckResult` 对象与 JSON 输出的相同。`junitXml()` 生成的 XML 也与 CLI 的一致。

```ts
import { checkShutdown, junitXml } from "shutdown-check";

const result = await checkShutdown(config);
const xml = junitXml(result, "orders-api shutdown");
```

完整的自定义运行器见 [Node API](https://shutdown.jscrate.dev/zh/docs/node-api)。

## 相关内容

- [CLI 参考](https://shutdown.jscrate.dev/zh/docs/cli)
- [诊断码](https://shutdown.jscrate.dev/zh/docs/codes)
- [在 CI 中运行](https://shutdown.jscrate.dev/zh/docs/ci)
- [Node API](https://shutdown.jscrate.dev/zh/docs/node-api)
- [故障排查](https://shutdown.jscrate.dev/zh/docs/troubleshooting)
