# OpenObserve 本地部署（接口 QPS / 耗时可观测）

app-server 通过中间件采集每个接口的 QPS + 耗时 + 错误率，批量上报到这里。
设计见 [../../docs/superpowers/specs/2026-07-06-openobserve-api-metrics-design.md](../../docs/superpowers/specs/2026-07-06-openobserve-api-metrics-design.md)。

## 启动

```bash
cd deploy/openobserve
docker compose up -d
```

- Web UI：http://localhost:5080
- 登录：`admin@shaguabijia.local` / `Complexpass#123`（见 `docker-compose.yml`）
- 数据落 `deploy/openobserve/data/`（已挂卷持久化；该目录已 gitignore）

## 让 app-server 上报

在项目根的 `.env` 打开观测（`OBSERVE_*`，账号密码与 compose 里 root 一致）：

```dotenv
OBSERVE_ENABLED=true
OBSERVE_ENDPOINT=http://localhost:5080
OBSERVE_ORG=default
OBSERVE_STREAM=app_requests
OBSERVE_USER=admin@shaguabijia.local
OBSERVE_PASSWORD=Complexpass#123
```

重启 app-server，随便打几个接口。stream `app_requests` **首次上报自动创建**，
在 UI 的 Logs → 选 `app_requests` 就能看到逐条请求事件（字段：`method` / `route` /
`status` / `duration_ms` / `service` / `env`）。

> 未开 `OBSERVE_ENABLED` 或缺账号密码时，中间件透传、worker 不启动，整套 no-op，不影响业务。

## 查询（Logs 页 SQL，或建 Dashboard 面板）

各接口 QPS（1 分钟分桶，面板里再除 60 得每秒）：

```sql
SELECT route, histogram(_timestamp, '1 minute') AS ts, count(*) AS cnt
FROM app_requests GROUP BY route, ts ORDER BY ts
```

各接口 P95 耗时（毫秒）：

```sql
SELECT route, approx_percentile_cont(duration_ms, 0.95) AS p95_ms
FROM app_requests GROUP BY route ORDER BY p95_ms DESC
```

各接口错误率（5xx 占比）：

```sql
SELECT route,
       count(*) FILTER (WHERE status >= 500) * 100.0 / count(*) AS err_pct
FROM app_requests GROUP BY route ORDER BY err_pct DESC
```

## 一键导入现成仪表盘（QPS / P95 / 分位 / 错误率）

备好了 [dashboard-api-metrics.json](dashboard-api-metrics.json)，4 个面板：各接口每分钟请求数(QPS 源)、
P95 耗时折线、P50/P95/P99 分位表、5xx 错误率表。

- **UI 导入**：Dashboards → 右上 **Import** → 选该 JSON 文件 → Import（每次导入新建，不覆盖）。
- **或 API 导入**：
  ```bash
  curl -u admin@shaguabijia.local:Complexpass#123 -H 'Content-Type: application/json' \
    -X POST 'http://localhost:5080/api/default/dashboards?folder=default' \
    --data-binary @deploy/openobserve/dashboard-api-metrics.json
  ```

导入后进仪表盘，右上角时间调到「最近 15 分钟 / 1 小时」、开自动刷新即可。低流量下 QPS 面板看「每分钟请求数」比「每秒」直观。

## 停止 / 清数据

```bash
docker compose down            # 停止(保留数据)
docker compose down -v && rm -rf data   # 停止并清空数据
```

## 生产部署（单机）+ UI 访问

前提:app-server 与 OpenObserve **同机**,app→OO 走 localhost(`127.0.0.1:5080`)、不出网、无需 TLS。
唯一要防的是**别把 :5080 裸暴露公网**。硬化版编排见 [docker-compose.prod.yml](docker-compose.prod.yml)。

### 部署步骤

```bash
# 1) 密码文件(本目录,已 gitignore)
echo "OO_ROOT_PASSWORD=$(python -c 'import secrets;print(secrets.token_urlsafe(24))')" > deploy/openobserve/.env

# 2) 起 OpenObserve(只绑 127.0.0.1、命名卷持久化、mem 1g)
cd deploy/openobserve && docker compose -f docker-compose.prod.yml up -d
sudo systemctl enable docker            # 开机自起
```

3) app-server 的 `.env` 打开观测并**重启**(用非 root 的专用 ingest 账号):
```dotenv
OBSERVE_ENABLED=true
OBSERVE_ENDPOINT=http://127.0.0.1:5080
OBSERVE_ORG=default
OBSERVE_STREAM=app_requests
OBSERVE_USER=ingest@shaguabijia.com     # UI → Users 建的非 root 账号
OBSERVE_PASSWORD=<该账号密码>
```
```bash
sudo systemctl restart shaguabijia-app-server   # 日志出现 "observe worker started" 即生效
```

4) 两个必做收口(磁盘/安全):
- **保留期**:UI → Streams → `app_requests` → Data Retention 设 14/30 天(一请求一行,不封顶迟早撑爆盘)。
- **专用账号**:UI → Users 建非 root 账号给 app 上报,root 只留人工登 UI。

### UI 访问(二选一)

**A. SSH 隧道(推荐,零暴露、不用域名/证书):**
```bash
ssh -L 5080:127.0.0.1:5080  用户@服务器IP
# 然后本机浏览器开 http://localhost:5080
```

**B. nginx 子域名反代(要固定 URL / 团队常看):** 见 [../nginx/observe.shaguabijia.com.conf](../nginx/observe.shaguabijia.com.conf)。
需 DNS `observe.shaguabijia.com` → 本机 + 证书放 `/etc/nginx/ssl/`;含 IP 白名单 + TLS + WebSocket 透传。

> ⚠️ prod compose 必须保持 `127.0.0.1:5080:5080`;写成 `5080:5080`(绑 0.0.0.0)= 裸暴露公网,这是唯一真正的坑。
