- 后端
- 工作流自动化
- 流程编排
- 低代码
【免费下载链接】elsa-core
The Workflow Engine for .NET
本文基于 Elsa Core 仓库中
specs/003-live-server-logs/规范体系(需求清单、功能规范、数据模型、REST/SignalR/Provider 契约、实施计划与任务清单)编写,系统解读"实时服务器日志流"这一后端能力的完整需求边界与契约设计。读者读完后,将掌握该功能的授权模型、脱敏规则、缓冲区过载语义、集群源身份建模,以及如何在 Elsa Server 中启用与本地验证这一能力。
一、需求清单导读:八条约束如何定义整个功能边界
关联文档 requirements.md 以八条验收式条目界定了该功能的需求撰写质量,每条都对应 spec.md 中可执行、可测试的具体要求。它们共同回答了三个核心问题:功能面向什么用户行为、安全边界在哪里、MVP 范围止于何处。
| 需求清单条目 | 对应规范要点 | 详细展开章节 |
|---|---|---|
| 需求描述用户可见行为而非纯实现 | FR-001 ~ FR-011,三条用户故事及验收场景 | 第二节 |
| 授权需求显式化 | FR-012、FR-025,read:server-logs权限 | 第三节 |
| 脱敏需求显式化 | FR-013、FR-014、FR-015 | 第四节 |
| 缓冲区边界与过载行为显式化 | FR-003、FR-004、FR-005,SC-001/SC-007 | 第五节 |
| 集群源身份纳入 MVP 契约 | FR-016 ~ FR-020 | 第六节 |
| 单节点开发保持简单 | FR-017,InMemory 提供程序,quickstart | 第七节 |
| 持久化审计日志明确排除 | 澄清记录与假设章节 | 第八节 |
| Studio 依赖以配对规范形式提出 | FR-022,specs/studio/003-live-server-logs/ | 第九节 |
二、用户可见行为:从"看日志"到"筛选安全日志"的三条用户故事
spec.md 将该功能的用户可见行为拆成三条带优先级(P1/P2/P3)的用户故事,每条故事都附带独立测试描述与可判定验收场景,而非只描述内部实现。
用户故事 1 - 从 Studio 实时尾随服务器日志(P1):管理员或开发者打开 Studio 即可观察后端实时日志事件,无需 ssh 进入服务器进程。验收场景包括:已授权调用方请求最近日志时返回有界的有序切片;订阅实时流后,ILogger事件按带时间戳、级别、类别、消息、异常细节与源元数据的结构送达;当实时事件缓冲区达到配置容量时,最旧事件被丢弃且服务器向订阅方报告丢弃计数。
用户故事 2 - 过滤并保护运维日志(P2):操作员可将流收窄到 Warning/Error、特定 logger 类别、租户、工作流实例或源进程,同时后端强制执行授权与脱敏。验收场景强调:未认证/未授权调用方在 recent 端点与 SignalR Hub 处一律被拒绝;命中脱敏规则的日志消息、异常细节、scope 与属性值被替换为脱敏标记;按级别、类别、租户、工作流实例、关联 ID、源过滤时仅返回匹配事件。
用户故事 3 - 检查集群日志源(P3):运行在 Kubernetes 等集群环境中的操作员,可以查看跨后端副本的合并流,并在排查特定副本时切换到单个 Pod/容器/进程源。验收场景包括:多个 Elsa 进程通过配置的 Provider 发布事件时,无源过滤的订阅收到带源身份的合并有序视图;按源 ID 过滤时只流式传输所选源的事件;源停止发布心跳后,在源列表中标记为 stale/disconnected,且不立即删除其近期日志历史。
三、授权模型:一个专用权限read:server-logs
清单第二条"授权需求显式化"在 spec.md 的 FR-012 中落地为一条强约束:
FR-012:所有日志流端点与 Hub 必须使用专用权限
read:server-logs授权。
这意味着:
- REST 端点(
GET /server-logs/recent、GET /server-logs/sources)与SignalR Hub(/elsa/hubs/server-logs)使用同一个权限令牌,权限检查发生在连接/请求建立阶段——Hub 契约中明确"未授权调用方在 Hub 连接期间即被拒绝"; - 成功标准SC-003要求"未授权调用方在 100% 的 Hub、recent 与 source-list 访问尝试中被拒绝",授权不是"尽量"而是可度量、可测试的;
- FR-025 要求该功能与 Elsa 现有的认证与 CORS 模式协同工作。从仓库结构看,认证/授权基础设施位于 src/modules/Elsa.Identity,SignalR 连接复用 Studio 已有的认证钩子(规范假设章节明确"SignalR 是首选实时传输,因为 Studio 已有 SignalR 连接的认证钩子")。
四、脱敏:默认保守规则 + 先于缓冲执行
清单第三条"脱敏需求显式化"由 FR-013、FR-014、FR-015 三条功能需求共同支撑:
- FR-013:支持可配置的脱敏规则,作用于消息、异常文本、scope 与结构化属性,且在事件离开服务器之前执行;
- FR-014:对常见敏感名称提供保守默认脱敏——
authorization、token、password、secret、api-key、cookie、connection-string等属性名默认被掩码; - FR-015:脱敏必须先于缓冲执行——在使用任何可能将缓冲事件暴露给调用方的 Provider 时,事件进入缓冲区之前就必须完成脱敏。
这条"先于缓冲"的顺序约束是整个安全设计的核心:正因为 InMemory 提供程序 会把事件存入有界环形缓冲区用于 backfill,若脱敏滞后于缓冲,就可能出现"先入缓冲、后补脱敏"的竞态泄露窗口。因此规范要求脱敏发生在ILogger捕获路径(ServerLogLogger)与 Provider 发布(PublishAsync)之间。数据模型>services.AddElsa(elsa => { elsa.UseServerLogStreaming(options => { options.RecentLogCapacity = 5_000; options.MaxRecentLogQuerySize = 1_000; options.SourceHeartbeatTimeout = TimeSpan.FromSeconds(30); }); });
对应 FR-021 与 FR-024:功能通过 fluent 扩展注册,并提供缓冲区容量、通道容量、近期日志查询上限、源心跳超时、脱敏规则、Provider 选择等选项。RecentLogCapacity = 5_000控制环形缓冲区大小,MaxRecentLogQuerySize = 1_000是服务端查询上限(FR-011 要求该上限无论客户端传什么值都在服务端封顶),SourceHeartbeatTimeout = 30s控制源 stale 判定。
2. 映射 Hub 与端点:
app.UseServerLogStreaming();该调用映射/elsa/hubs/server-logs,并在配置的 Elsa API 前缀下映射 REST 端点(FR-023 明确要求文档化中间件需求,包括 SignalR Hub 映射)。
3. 授权用户:为运维用户授予read:server-logs权限。
本地验证五步走(quickstart 原文):启动启用功能的 Elsa Server → 打开安装配对 Studio 模块的 Elsa Studio → 从服务器发出ILogger消息 → 确认其出现在 Studio 的 Server Logs 页面 → 将级别过滤器切到Warning验证低级日志被隐藏。
quickstart 同时记录了规范的验证命令(针对规划中的测试项目):
dotnet test test/unit/Elsa.ServerLogs.UnitTests/Elsa.ServerLogs.UnitTests.csproj --no-restore(22 个 server logs 单元测试);dotnet build src/modules/Elsa.ServerLogs/Elsa.ServerLogs.csproj --no-restore;dotnet restore src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj与dotnet build;dotnet test test/integration/Elsa.ServerLogs.IntegrationTests/Elsa.ServerLogs.IntegrationTests.csproj --no-restore(4 个 server logs 冒烟测试)。
说明:上述模块与测试项目路径来自规范文档(quickstart.md、tasks.md)规划的目标位置;截至本文撰写时,该功能模块尚未出现在仓库
src/modules/与test/主干目录中,属于"按规范实施中"的状态,相关命令仅供参考。
集群部署说明:InMemory Provider 只显示当前进程的日志。要获得跨 Pod 合并视图,需要未来配置实现IServerLogProvider的共享 Provider;Studio 继续使用同一套 API 与 Hub 契约,无需改动(这正是 FR-018 的意义)。
八、边界明确:持久化审计日志不在范围内
清单第七条"持久化审计日志明确排除"源自规范澄清记录(Session 2026-05-06):"这是实时服务器日志流,带有限的近期历史;持久化保留属于既有可观测性系统。"对应假设章节的两条:
- "日志事件是运维服务器遥测,而非合规/审计记录";
- "现有外部可观测性产品仍是长期保留机制"。
因此在需求层面:功能只保证有界近期历史 + 实时流,不提供磁盘持久化、不承担审计责任、不实现任何外部存储后端(任务清单 Notes 明确"不要在此功能切片中实现 Redis、OpenTelemetry、Loki、Seq、Elasticsearch 或 Application Insights Provider")。Kubernetes API 访问同样被排除在 MVP 之外("共享集群聚合由 Provider 驱动,MVP 不需要 Kubernetes API 访问")。
九、Studio 依赖:以配对规范管理前后端契约
清单第八条"Studio 依赖作为配对规范提出"对应 FR-022(功能必须出现在已安装功能列表中,供 Studio 根据后端能力显隐 UI)与规范假设"Studio 将在此功能 ID 下实现单独的配对规范"。该配对规范确实存在于仓库中:specs/studio/003-live-server-logs/spec.md。这种"后端规范 + Studio 配对规范"的拆分让后端契约先行落地、UI 后续跟进,符合计划中"Trunk-Based Development"的分片策略:后端切片可先合并,Studio UI 工作再着陆,前后端以 REST/API 与 SignalR 契约对齐。
十、核心契约速览:REST、SignalR 与 Provider
10.1 REST API(contracts/rest-api.md)
所有端点使用 Elsa API 路由前缀,且需要read:server-logs。
GET /server-logs/recent(近期日志 backfill),查询参数:minimumLevel、level、categoryPrefix、text、tenantId、workflowDefinitionId、workflowInstanceId、traceId、correlationId、sourceId、from、to、take。响应示例:
{ "items": [ { "id": "evt-1", "timestamp": "2026-05-06T10:00:00Z", "level": "Information", "category": "Elsa.Workflows.Runtime", "message": "Workflow instance started", "sourceId": "elsa-server-1" } ], "droppedEvents": 0 }GET /server-logs/sources(源列表与健康状态),响应示例:
{ "items": [ { "id": "elsa-server-1", "displayName": "elsa-server-7fd9c8b9c4-a2k1", "serviceName": "elsa-server", "podName": "elsa-server-7fd9c8b9c4-a2k1", "containerName": "elsa-server", "namespace": "production", "status": "Connected", "lastSeen": "2026-05-06T10:00:02Z" } ] }校验规则:take超过配置上限时被一致地封顶或拒绝;未知级别返回校验错误;日期过滤器按 UTC 规范化。
10.2 SignalR Hub(contracts/signalr-hub.md)
- 端点:
/elsa/hubs/server-logs,需要read:server-logs; - 客户端→服务端方法:
SubscribeAsync(开始或替换当前订阅)、UpdateFilterAsync(不重连即更新过滤器,FR-010,载荷与 Subscribe 相同)、UnsubscribeAsync; - 服务端→客户端方法:
ReceiveLogEventAsync(载荷为ServerLogEvent)、ReceiveDroppedEventsAsync({ sourceId, droppedCount, reason })、ReceiveSourceChangedAsync(载荷为ServerLogSource); - 错误行为:非法过滤器产生带校验细节的 Hub 异常;未授权调用方在 Hub 连接期间被拒绝;Provider 故障发出终止性 Hub 错误并关闭订阅。
其中UpdateFilterAsync对应 FR-010 的"订阅方无需重开连接即可更新过滤器",订阅(ServerLogSubscription)被建模为"带可变过滤器与背压状态的 SignalR 连接"。
10.3 Provider 抽象(contracts/provider-contract.md)
public interface IServerLogProvider { ValueTask PublishAsync(ServerLogEvent logEvent, CancellationToken cancellationToken = default); ValueTask<RecentServerLogsResult> GetRecentAsync(ServerLogFilter filter, CancellationToken cancellationToken = default); IAsyncEnumerable<ServerLogEvent> SubscribeAsync(ServerLogFilter filter, CancellationToken cancellationToken = default); ValueTask<IReadOnlyCollection<ServerLogSource>> ListSourcesAsync(CancellationToken cancellationToken = default); }Provider 期望(Expectations):必须保留源身份;必须保证有界内存或依赖有界的外部存储/查询限制;应暴露丢弃事件元数据;应暴露源LastSeen或心跳数据;绝不能暴露未脱敏事件。
10.4 核心实体与数据模型
data-model.md 定义了三个核心实体:
ServerLogEvent:捕获自ILogger的结构化服务器日志事件,字段覆盖Id、Sequence、Timestamp(UTC)、ReceivedAt、Level(trace/debug/information/warning/error/critical)、Category、EventId(数值/名称对)、Message(已脱敏渲染消息)、MessageTemplate、Exception(已脱敏摘要/详情)、Scopes、Properties、TraceId/SpanId/CorrelationId、TenantId/WorkflowDefinitionId/WorkflowInstanceId(Elsa 上下文存在时)、SourceId(指向ServerLogSource的外键)。这与 FR-002 的完整捕获字段清单一一对应;ServerLogSource:进程/Pod/容器/外部 Provider 来源(字段见第六节表格);ServerLogFilter:MinimumLevel、Levels、CategoryPrefix、Text、TenantId、WorkflowDefinitionId、WorkflowInstanceId、TraceId、CorrelationId、SourceId、From、To、Take——覆盖 FR-009 的全部过滤维度。
数据模型不变式(Invariants)是全文的安全底线:暴露给调用方的事件始终已脱敏;缓冲区大小由配置限定;每个事件都有源 ID;近期查询Take由服务端封顶;源状态由 Provider 状态与LastSeen推导。
十一、需求 → 实现 → 测试的落地映射
plan.md 规划了一个独立模块src/modules/Elsa.ServerLogs(而非塞入Elsa.Workflows.Api),理由是该模块可被非工作流型 Elsa 宿主使用,并可在存在工作流/租户上下文时对事件做富化。模块结构规划为:Contracts/(IServerLogProvider、IServerLogRedactor、IServerLogSourceRegistry)、Logging/(ServerLogLoggerProvider、ServerLogLogger)、Providers/InMemory/(InMemoryServerLogProvider、RingBuffer)、RealTime/ServerLogsHub、Endpoints/ServerLogs/(Recent、Sources)、Models/、Options/、Features/ServerLogStreamingFeature、Extensions/(ModuleExtensions、ApplicationBuilderExtensions)。
tasks.md 按六个阶段组织:Setup(项目外壳)→ Foundational(契约/模型/选项/注册)→ US1 MVP(环形缓冲区、Provider、ILoggerProvider捕获、recent 端点、Hub 订阅)→ US2 安全(过滤器求值、脱敏、权限、UpdateFilterAsync、查询上限封顶)→ US3 集群(源注册表、K8s 环境探测、合并排序、source-list 端点、源变更广播)→ Polish(打包、README、示例宿主接线、验证)。每条用户故事都有对应单元/集成测试项,例如RingBufferTests(容量与排序)、ServerLogFilterTests(过滤器谓词)、ServerLogRedactorTests(脱敏)、InMemoryServerLogProviderSourceTests(多源)、ServerLogsHubAuthorizationTests(Hub 授权与过滤器更新)。实施策略强调MVP First:先完成 Phase 1/2,再只做 Phase 3,本地验证 backfill 与实时尾随后停下评审,再叠加过滤器、脱敏与集群源 UX。
十二、总结
需求清单的八条约束共同刻画了一个有界、安全、源感知、可渐进扩展的服务器日志流功能:以结构化ILogger捕获替代原始控制台重定向,以 SignalR 实时流 + REST backfill 双通道交付,以read:server-logs单一权限收紧访问,以"先脱敏后缓冲"杜绝泄露窗口,以 InMemory Provider 保持单节点简单、以 Provider 抽象预留 Redis/OTel/Loki/Seq/ES/App Insights 集群接入,并以配对 Studio 规范锁住前后端契约。对于需要在 Elsa 部署中"看到后端在干什么"的开发者与运维者而言,这套规范就是该能力从需求到契约的完整地图。
进一步阅读:
- 功能规范全文:spec.md
- 数据模型与不变式:data-model.md
- REST 契约:contracts/rest-api.md
- SignalR 契约:contracts/signalr-hub.md
- Provider 契约:contracts/provider-contract.md
- 快速上手与验证命令:quickstart.md
- 实施计划与模块结构:plan.md
- 任务清单与阶段划分:tasks.md
- Studio 配对规范:specs/studio/003-live-server-logs/spec.md
- 后端
- 工作流自动化
- 流程编排
- 低代码
【免费下载链接】elsa-core
The Workflow Engine for .NET
相关推荐
Elsa Server 实时日志流式传输(Live Server Log Streaming)快速上手与契约详解
Elsa Server 实时日志流式传输(Live Server Log Streaming)快速上手与契约详解 本篇技术指南围绕 Elsa(Workflow
后端工作流自动化流程编排低代码Elsa Server Logs SignalR Hub 契约详解:为 Elsa Studio 实现实时服务器日志流的 SignalR 接口规范
Elsa Server Logs SignalR Hub 契约详解:为 Elsa Studio 实现实时服务器日志流的 SignalR 接口规范 本文基于 El
后端工作流自动化流程编排低代码Elsa Server Logs REST API 实战指南:实时日志流的查询接口与安全契约
Elsa Server Logs REST API 实战指南:实时日志流的查询接口与安全契约 Elsa Server Logs( Elsa.Diagnostic
后端工作流自动化流程编排低代码
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考