# dsh-plugin-log-forwarder 通道使用指南 > 版本:V1.0 · 更新:2026-09-09 · 适用:三个输出通道(WebSocket / File / Loki)的启用、验证与日常使用 > > 面向使用者。配置项全集与事件格式见 [api.md](api.md),完整接口说明见 [README](../README.md)。 ## 1. 插件在做什么 插件订阅 DeepSeek Harness 的会话事件流,把原始事件标准化为统一 JSON 格式,再**实时**转发到三个输出通道。三个通道相互独立、**可同时启用**。 | 通道 | 用途 | 特点 | |---|---|---| | `websocket` | 实时流式推送 / 调试观察 | 本地 WS Server,占用端口自动 +1,带状态页 | | `file` | 落盘归档 | 按会话写 JSONL,路径支持 `{sessionId}` 占位符 | | `loki` | 集中日志检索 / 可视化 | 批量推送到 Loki(常配 Grafana 查询) | ## 2. 配置文件在哪改 实际生效的是 DSH profile 的补丁层,例如 Desktop: ``` C:\Users\Lenovo\.dsh\profiles\desktop\cordis.patch.yml ``` 结构(节选): ```yaml - insert: - id: log-forwarder name: dsh-plugin-log-forwarder config: channels: websocket: enable: true port: 18765 file: enable: false path: 'D:/dsh-logs/{sessionId}.jsonl' loki: enable: true url: 'http://127.0.0.1:3100' ``` > ⚠️ **修改配置必须重载插件才生效**:通道只在插件加载时装配(`apply()` 一次性读取),无热加载。改完请重启 DSH Desktop 或重新加载插件,再验证。 ## 3. 通道一:WebSocket ### 配置 ```yaml channels: websocket: enable: true port: 18765 # 绑定 127.0.0.1;被占用时自动 +1(最多 5 次) ``` ### 怎么看 - 浏览器打开状态页:`http://127.0.0.1:18765/`(可看到各通道实时统计) - 命令行实时观察:`npx wscat -c ws://127.0.0.1:18765` - 用模型工具确认:`log_forwarder_channel_status`(channel=websocket,返回 `clientCount`/`port`) ### 输出样例 ```json {"id":"evt_...","timestamp":"2026-09-09T08:46:37.372Z","sessionId":"session-xxx","turnIndex":4,"type":"tool_call","payload":{...}} ``` ## 4. 通道二:File ### 配置 ```yaml channels: file: enable: true path: 'D:/dsh-logs/{sessionId}.jsonl' # 支持 {sessionId} 占位符;留空 → ~/.deepseek-harness/logs/session-.jsonl maxFileSizeBytes: 52428800 # 单文件上限,超出停止写入 ``` ### 验证 ```powershell Get-ChildItem 'D:\dsh-logs' -Filter '*.jsonl' | Sort-Object LastWriteTime -Descending | Select-Object Name, Length, LastWriteTime Get-Content 'D:\dsh-logs\session-*.jsonl' -Tail 5 # 每行一个标准事件 JSON ``` ### 经验提示 - 文件名**不要写成 `session-{sessionId}.jsonl`**:sessionId 本身已带 `session-` 前缀,会得到 `session-session-xxx.jsonl` 的双前缀文件,应直接用 `{sessionId}.jsonl`。 - 暂停转发期间文件**零增长**,恢复后立即续写(见 §7)。 ## 5. 通道三:Loki ### 配置 ```yaml channels: loki: enable: true url: 'http://127.0.0.1:3100' # Loki 地址,本地/容器内均可 tenantId: '' # 多租户时填(X-Scope-OrgID) token: '' # 认证(Authorization: Bearer) bufferSize: 1000 # 内存缓冲上限,超出丢最旧 maxConsecutiveFailures: 10 # 连续失败达阈值 → disconnected,需手动恢复 ``` ### 推送机制(了解即可) - 批量推送:攒满 **100 条**或 **200ms** 触发一次,POST `{url}/loki/api/v1/push` - 按流分组,标签固定为:`source=dsh-log-forwarder`、`session_id`、`event_type`、`turn_index` - 每条日志 = 纳秒时间戳 + 事件 JSON 行 - 失败处理:指数退避重试;连续失败 ≥ `maxConsecutiveFailures` 后标记 `disconnected` 停止重试,需手动恢复(`log_forwarder_resume`,或 pause→resume) ### 验证链路是否通 ```powershell # 1) Loki 本身活着 Invoke-WebRequest 'http://127.0.0.1:3100/loki/api/v1/labels' -UseBasicParsing # 200, status=success # 2) 插件通道状态(forwarded 增长、failed=0) # 用模型工具:log_forwarder_status / log_forwarder_channel_status(channel=loki) # 3) Loki 里能查到数据(range query;该版本 instant log query 不支持) $q=[uri]::EscapeDataString('{source="dsh-log-forwarder"}') $start=[DateTimeOffset]::UtcNow.AddMinutes(-10).ToUnixTimeSeconds() $end=[DateTimeOffset]::UtcNow.ToUnixTimeSeconds() Invoke-RestMethod 'http://127.0.0.1:3100/loki/api/v1/query_range' -Method Post -Body "query=$q&start=$start&end=$end&limit=5&direction=backward" -ContentType 'application/x-www-form-urlencoded' # 4) 总量核对(metric instant query) Invoke-RestMethod 'http://127.0.0.1:3100/loki/api/v1/query' -Method Post -Body "query=$([uri]::EscapeDataString('sum(count_over_time({source=\"dsh-log-forwarder\"}[5m]))'))" -ContentType 'application/x-www-form-urlencoded' ``` ## 6. 模型工具速查 | 工具 | 作用 | |---|---| | `log_forwarder_status` | 全局状态:是否运行、事件总数、各通道 forwarded/failed/dropped、uptime | | `log_forwarder_channel_status` | 单通道详情(channel=websocket/file/loki),含 target 与专属字段 | | `log_forwarder_pause` | 暂停:上游停采,暂停期事件不落盘**也不计丢弃** | | `log_forwarder_resume` | 恢复;同时自动重连断开的 Loki、重试失败的 WebSocket | ## 7. 暂停 / 恢复行为(已实测) - **pause**:立即停采,两个通道状态变 `paused`;暂停窗口内文件零增长、无事件入库。 - **resume**:立即恢复采集,文件续写 / Loki 继续推送,事件序号与时间戳连续。 - pause 期间发生的事件**不会补发**(丢是设计上的「不采集」,dropped 计数保持 0)。 ## 8. 用 Grafana 查看 Loki 日志 1. 登录 Grafana(如 `http://localhost:3000`),确认已添加 Loki 数据源。 - 数据源 URL 若为 `http://loki:3100` 属正常(compose 内部主机名),不必改成 localhost。 2. 左侧 **Explore**(罗盘图标)→ 数据源选 **Loki**。 3. 查询框输入 LogQL 回车 / 点 Run query: ```logql {source="dsh-log-forwarder"} -- 全部事件 {source="dsh-log-forwarder", session_id="session-xxx"} -- 某个会话 {source="dsh-log-forwarder", event_type="tool_call"} -- 某种事件 {source="dsh-log-forwarder", turn_index="4"} -- 某个轮次 {source="dsh-log-forwarder"} |~ "LIVE-TEST" -- 行内容模糊过滤 ``` 4. 实时直播:点查询框上方 **Live** 开关,事件会实时滚动。 5. 图形:切 Metrics 模式输入 `rate({source="dsh-log-forwarder"}[5m])` 看速率曲线。 ## 9. 本次实测结论(2026-09-09) - WebSocket / File / Loki 三通道全链路验证通过:转发持续增长,**failed / dropped 全程为 0**。 - pause → 停写(窗口内零写入)→ resume → 续写:行为符合设计。 - Loki 端到端:插件 → `/push` → 标签可查(`source`/`session_id`/`event_type`/`turn_index`)→ Grafana 经数据源代理查询成功,近 5 分钟事件 4959 条(`assistant/chunk` 为主,其余含 `tool_call`/`tool_result`/`model_output`/`session_start`/`turn_start` 等)。 ## 10. 常见问题 | 现象 | 处理 | |---|---| | 改了配置但通道没变 | 通道在加载时装配,需重启 DSH Desktop / 重新加载插件 | | Loki 通道 `disconnected` | 手动 `log_forwarder_resume`(会 recover 并重试缓冲)或 pause→resume | | 文件名出现 `session-session-xxx` | 路径模板用了 `session-{sessionId}.jsonl`,改回 `{sessionId}.jsonl` | | Grafana 代理查询 403 | 多为匿名/API 权限;浏览器登录后即可用 | | 想只转发部分事件 | 配置 `filter.includeEventTypes` / `excludeEventTypes`(api.md) | | 敏感字段想脱敏 | 配置 `redaction.enable` 与 `sensitiveFields`(api.md) |