---
title: 监控
description: PAC 的数据源滞后监控、每日健康日报、队列健康与关键 SQL 检查。
icon: Activity
---

PAC 的运行态监控分四块:数据源滞后、每日健康日报、队列健康、关键 SQL。前两块的告警与日报经 `AlertService` 推送(配 `ALERT_WEBHOOK_URL`,支持企微 / 钉钉 / slack;留空则仅落 log)。

---

## 一、数据源滞后监控

`DwLagMonitorService` 的 `@Cron`(`PAC_LAG_MONITOR_CRON`,如 `0 * * * *` 每小时):

```
读最近一次 incremental_bundle 的 cursor_after
   ↓
算 max(cursor) 距 now 的小时数
   ↓
🟢 < 24h    info       仅 log
🟡 24-48h   warning    log + 告警推送(数据源未刷新,提醒值班)
🔴 > 48h    critical   log + 告警推送(严重滞后,事件可能漏召)
```

阈值默认由 `PAC_LAG_WARN_HOURS` / `PAC_LAG_ERROR_HOURS` 控制,**每个 host 可在 `manifest.monitoring` 覆盖**(上游节奏不同,一刀切会误报/漏报):

```yaml
# data/<host>/manifest.yaml
monitoring:
  dw_lag_warn_hours:  24
  dw_lag_error_hours: 48
  push_lag_error_hours: 26   # push host:超此小时数无推送 → 告警(实时推设小,批量推设大)
```

未配 `ALERT_WEBHOOK_URL` 时仅落 log,值班查 `journalctl -u pac-service | grep dw-lag`。

**push 断流监控**:同一 `DwLagMonitorService` 对**曾推过**的 host 检查最近一次 push 距今时长,超 `push_lag_error_hours`(默认 26h)→ critical 告警;未上线 push 的 host 不检查。

---

## 二、每日健康日报

`DailyHealthReportService` 的 `@Cron`(`PAC_HEALTH_REPORT_CRON`,默认 `0 9 * * *`,北京时)把分散监控收口成**一条**健康快照:数据源延迟 / 画像覆盖 / 摄入 / 重算 / 召回 / AI 用量,每项带近 24h 变化;整体级别取最差项,经 `AlertService` → `ALERT_WEBHOOK_URL` 推送。手动预览端点 `internal/health-report` 由 `PAC_HEALTH_REPORT_DEMO=1` 门控。

---

## 三、队列健康(Bull Board)

`/admin/queues` 实时查看三条队列 —— `persona-recompute` / `plan-recompute` / `plan-asset-generate` 的处理速度、失败重试与死信。

<Callout type="warn">
**默认不挂载**,要用得启动时设 `PAC_BULL_BOARD=1`。

此前文档写的「需登录 admin」**不成立** —— `@bull-board/nestjs` 自己挂独立 Express handler,不经过 Nest 守卫,面板与其数据接口都是免鉴权的,且 `readOnlyMode:false`(能重试 / 清理任务)。2026-08-05 实测生产上经网关可从公网直接打开,故改为默认关闭。

关掉的只是**面板**;队列本身、定时任务、job 消费全部照常 —— 它只是个查看器。排障时临时开:`PAC_BULL_BOARD=1`,用完记得关。
</Callout>

---

## 四、关键 SQL 检查

```sql
-- 最近几次 sync 结果(应 success / failed=0)
SELECT to_char(started_at,'YYYY-MM-DD HH24:MI') AS t, resource, status, fetched, failed, error_message
FROM sync_logs ORDER BY started_at DESC LIMIT 5;

-- 数据规模
SELECT 'patients' AS k, count(*) FROM patients
UNION ALL SELECT 'facts', count(*) FROM patient_facts
UNION ALL SELECT 'active_plans', count(*) FROM followup_plans WHERE status IN ('active','assigned');

-- 数据滞后(与 lag monitor 同源)
SELECT to_char(started_at,'HH24:MI') AS sync_t, cursor_after
FROM sync_logs WHERE resource='incremental_bundle' AND status='success'
ORDER BY started_at DESC LIMIT 1;
```
