- 后端
- 工作流自动化
- 流程编排
- 低代码
【免费下载链接】elsa-core
The Workflow Engine for .NET
本篇技术指南围绕 Elsa Workflow Engine(elsa-core)中006-diagnostics-console-logs特性分支的 REST API 契约展开,系统讲解控制台日志诊断模块暴露的两个核心端点——近期日志回放(POST /diagnostics/console-logs/recent)与日志来源枚举(GET /diagnostics/console-logs/sources)。读完本文,你将掌握请求/响应数据契约、权限与脱敏边界、服务端限制与排序规则,并能结合源码理解底层缓冲、过滤与来源标识的实现原理。
一、契约总览:两个端点、一个权限、一条安全底线
控制台日志诊断(Diagnostics Console Logs)是 Elsa 一个**可选启用(opt-in)**的核心诊断模块,用于在不借助 Shell 访问的情况下查看后端进程的原始stdout/stderr输出。它独立于结构化日志(Elsa.Diagnostics.StructuredLogs),不解析 ILogger 记录、不提供持久化审计存储、不调用编排器日志 API,仅聚焦"原始控制台行的捕获、过滤、脱敏与转发"。
REST API 契约定义于 rest-api.md,其核心约定可概括为:
| 项目 | 约定 |
|---|---|
| 路由前缀 | 全部端点使用 Elsa API 路由前缀(通常为/elsa/api) |
| 统一权限 | 两个端点均要求read:diagnostics:console-logs权限 |
| 认证模型 | 复用 Elsa 既有的认证、授权与跨域(CORS)机制 |
| 安全底线 | 行文本与来源元数据在离开后端前必须完成脱敏(redaction) |
从源码看,权限常量定义在 ConsoleLogsResourcePermissions.cs:
public const string ConsoleLogs = "diagnostics/console-logs";并通过IPermissionDescriptorProvider向权限目录贡献View动词描述符。也就是说,契约文档中的read:diagnostics:console-logs实际由资源diagnostics/console-logs+ 动词View组合构成,端点通过RequirePermission(...)引用常量而非字符串字面量,避免权限名漂移。
二、端点一:查询近期控制台日志
2.1 请求契约
POST /diagnostics/console-logs/recent请求体为ConsoleLogFilter,用于描述查询条件。契约文档给出了如下示例:
{ "sourceId": "local", "stream": "stdout", "query": "workflow", "from": "2026-05-18T10:00:00Z", "to": "2026-05-18T10:05:00Z", "limit": 100 }在服务端实现中,该请求体反序列化为 ElsaConsoleLogFilter.cs 中定义的ElsaConsoleLogFilter记录类型。除契约文档示例中的五个字段外,实现还扩展了以下字段(供 Studio 侧按工作流上下文过滤):
WorkflowInstanceId、WorkflowDefinitionId、WorkflowDefinitionVersionId:按工作流实例/定义过滤;ActivityInstanceId、ActivityId、ActivityNodeId:按活动实例/节点过滤;Metadata(IReadOnlyDictionary<string, string>):按附加元数据键值过滤。
2.2 响应契约
响应体为RecentConsoleLogsResult,契约文档示例:
{ "items": [ { "id": "01j...", "timestamp": "2026-05-18T10:00:01Z", "receivedAt": "2026-05-18T10:00:01Z", "sequence": 42, "stream": "stdout", "text": "Workflow order-123 started", "source": { "id": "local", "displayName": "elsa-server", "serviceName": "Elsa.Server.Web", "processId": 12345, "machineName": "dev-machine", "podName": null, "containerName": null, "namespace": null, "nodeName": null, "lastSeen": "2026-05-18T10:00:01Z", "health": "connected" }, "truncated": false, "dropped": null } ], "dropped": [] }响应中每条日志行的关键语义:
id:日志行唯一标识(时间有序 ID,示例中缩写为01j...);timestamp:行产生的时间戳;receivedAt:后端接收时间戳(过滤与排序基于receivedAt);sequence:来源内的局部序号(source-local sequence),用于多来源合并时的排序辅助;stream:stdout或stderr;text:已脱敏的原始行文本;source:来源描述符(详见第四节);truncated:是否因超长而被截断;dropped:该行是否伴随丢弃信息(当缓冲或订阅队列溢出时非空);- 顶层
dropped数组:本次查询涉及的来源丢弃行汇总。
2.3 服务端强制规则
契约文档明确列出了四条服务端规则,结合源码可以逐一印证:
规则 1:limit 服务端钳制。服务器将limit钳制在ConsoleLogsOptions.MaxRecentQuerySize之内。在 ElsaConsoleLogRecentBuffer.cs 中:
var limit = filter.Limit is > 0 ? Math.Min(filter.Limit.Value, _maxQuerySize) : _maxQuerySize; return snapshot .Where(line => Matches(line, filter)) .TakeLast(limit) .ToArray();即:无论调用方传入多大的limit,实际返回数量都不会超过MaxRecentQuerySize(该选项的默认值为250,见下文第四节),且RecentCapacity默认值为 2000,共同构成"内存有界"的保证。
规则 2:返回的文本与来源元数据均已脱敏。脱敏发生在捕获/脱敏边界内,REST 端点、SignalR 推送、近期缓冲与 Provider 存储只能见到脱敏后的内容(对应功能需求 FR-024、FR-027a)。
规则 3:ANSI 转义序列默认剥离。契约文档表述为"默认剥离,除非宿主选择保留"。需要注意实现细节:在 ElsaConsoleLogOptions.cs 的ConfigureDefaults中,PreserveAnsi被设置为true(保留),而 README 中说明PreserveAnsi = false才在服务端剥离。这与契约文档"默认剥离"在表述上存在版本差异——以当前仓库源码为准,当前默认行为是保留 ANSI,交由消费端(如 Studio 的Raw ANSI开关)决定渲染或剥离。此外,颜色是否真正出现在捕获流中还取决于 .NET 控制台 Logger 的ColorBehavior:SimpleConsoleFormatter默认在输出被重定向(Docker、K8s、IDE 内运行)时抑制颜色,如需强制可在appsettings.json配置Logging:Console:FormatterOptions:ColorBehavior = "Enabled"。
规则 4:结果确定性排序。查询结果按接收顺序(received order)确定性排序,并带有基于来源的稳定 tiebreaker,以处理多来源时钟偏差或序号重叠场景(对应 FR-033)。
2.4 端点实现要点
Recent/Endpoint.cs 使用 FastEndpoints 实现:
Verbs(FastEndpoints.Http.POST); Routes("/diagnostics/console-logs/recent"); RequirePermission(ConsoleLogsResourcePermissions.ConsoleLogs, CoreVerbs.View);执行流程为:读取 JSON 请求体 → 反序列化为ElsaConsoleLogFilter(JSON 非法时返回 400 Bad Request)→ 经ConsoleLogFilterMapper.ToStreamingFilter转换为共享核心模型的ConsoleLogFilter→ 调用IConsoleLogProvider.GetRecentAsync获取结果。可见 REST 层只是薄薄的适配层,真正逻辑在 Provider 与缓冲中。
三、端点二:枚举控制台日志来源
3.1 请求契约
GET /diagnostics/console-logs/sources无请求参数,响应体为ConsoleLogSource的集合:
[ { "id": "local", "displayName": "elsa-server", "serviceName": "Elsa.Server.Web", "processId": 12345, "machineName": "dev-machine", "podName": null, "containerName": null, "namespace": null, "nodeName": null, "lastSeen": "2026-05-18T10:00:01Z", "health": "connected" } ]该端点实现于 Sources/Endpoint.cs,同样要求RequirePermission(..., CoreVerbs.View),内部直接调用IConsoleLogProvider.ListSourcesAsync。
3.2 来源描述符字段说明
| 字段 | 含义 |
|---|---|
id | 来源唯一标识,如local或机器名-进程ID |
displayName | 展示名,默认取自 Pod 名(HOSTNAME环境变量)或来源 ID |
serviceName | 服务名,取自OTEL_SERVICE_NAME或应用域友好名 |
processId/machineName | 进程 ID 与机器名 |
podName/containerName/namespace/nodeName | Kubernetes 场景下的可空标识,来自HOSTNAME、CONTAINER_NAME、POD_NAMESPACE、NODE_NAME环境变量 |
lastSeen | 最后一次收到行或心跳的时间 |
health | connected/stale/disconnected三种健康状态 |
来源元数据在 ElsaConsoleLogOptions.cs 中从环境变量自动装配:
var sourceId = $"{Environment.MachineName}-{Environment.ProcessId}"; var podName = Environment.GetEnvironmentVariable("HOSTNAME"); options.SourceId = sourceId; options.SourceDisplayName = !string.IsNullOrWhiteSpace(podName) ? podName : sourceId; options.ServiceName = Environment.GetEnvironmentVariable("OTEL_SERVICE_NAME") ?? AppDomain.CurrentDomain.FriendlyName; SetMetadata(options, "kubernetes.pod.name", podName); SetMetadata(options, "kubernetes.namespace.name", Environment.GetEnvironmentVariable("POD_NAMESPACE")); SetMetadata(options, "container.name", Environment.GetEnvironmentVariable("CONTAINER_NAME")); SetMetadata(options, "kubernetes.node.name", Environment.GetEnvironmentVariable("NODE_NAME"));这正是"单进程捕获优先、集群能力通过来源身份与 Provider 边界延后提供"设计思路的落地:本地开发、测试与单节点宿主使用进程内捕获;未来共享/外部 Provider 可在不改变 Studio 侧契约的前提下聚合多个 Core 实例的日志。
3.3 来源枚举的契约规则
契约文档给出三条规则:
- 权限一致:来源列表与行级访问使用同一个
read:diagnostics:console-logs权限,不存在"列出来源更宽松"的旁路; - 元数据脱敏:敏感来源元数据在 Provider 存储或返回之前即被脱敏(FR-027),Kubernetes 命名空间、容器名等可能包含敏感信息的字段受到同等保护;
- 失效来源可继续列出:处于 stale 或 disconnected 状态的来源在近期历史仍被保留期间继续可列出(对应 FR-029 与用户故事 P3 场景 3:来源停止心跳或行后,被标记为失效但不会立刻丢失近期历史)。
四、配置项与默认值速查
结合 README.md 与 ElsaConsoleLogOptions.cs,当前仓库支持的宿主配置项及默认值如下:
| 配置项 | 默认值 | 说明 |
|---|---|---|
SourceId | 机器名-进程ID | 默认来源标识 |
SourceDisplayName | Pod 名或来源 ID | 来源展示名 |
ServiceName | OTEL_SERVICE_NAME或应用域友好名 | 来源服务名 |
PreserveAnsi | true | 是否保留 ANSI 转义序列(false时服务端剥离) |
RecentCapacity | 2000 | 近期历史环形缓冲容量 |
MaxRecentQuerySize | 250 | 单次 recent 查询最大返回行数(服务端钳制上限) |
SubscriberCapacity | — | 实时订阅者队列容量(见 SignalR 契约) |
MaxLineLength | — | 单行最大长度,超长截断并标记truncated |
模块注册方式(Program.cs):
services.AddElsa(elsa => { elsa.UseConsoleLogs(options => { options.RecentCapacity = 5_000; options.SubscriberCapacity = 1_000; options.MaxRecentQuerySize = 1_000; options.MaxLineLength = 16_384; options.PreserveAnsi = true; // 将颜色原样传递给消费端 }); }); app.UseConsoleLogs(); // 映射实时 SignalR Hub五、与实时链路的关系与边界
REST 端点是控制台日志诊断的"回放层"(backfill),实时能力由 SignalR Hub/elsa/hubs/diagnostics/console-logs提供(见 signalr-hub.md)。二者共享同一套ConsoleLogFilter语义与read:diagnostics:console-logs权限。典型调用流程为:先调用POST /diagnostics/console-logs/recent获取有界的有序回填,再订阅实时流接收新行;实时订阅支持SubscribeAsync/UpdateFilterAsync(不重连即可更换过滤条件)/UnsubscribeAsync,并可能收到丢弃行摘要与来源状态变更事件。
需要特别强调的边界(对应 FR-002、FR-004、FR-032):
- 本 REST 契约仅覆盖原始 stdout/stderr 行,与结构化日志(
Elsa.Diagnostics.StructuredLogs)、结构化日志持久化、Trace 瀑布、Metrics 及 OpenTelemetry 探索完全分离; - 近期历史属于运维排障数据而非持久审计日志;需要长期保留的宿主应继续使用既有可观测性或平台日志系统;
- Kubernetes/Docker 编排器日志 API、厂商 sink、OpenTelemetry 集成不在本特性范围内。
六、质量保障与验证锚点
仓库中与本文契约对应的验证资产包括:
- ConsoleLogsAuthorizationTests.cs:验证未认证/未授权调用者对 recent、sources 与 hub 的访问一律被拒绝(对应 SC-003);
- ConsoleLogsModuleTests.cs:验证模块接线与端点行为;
- ConsoleLogsHubSourceStatusTests.cs:验证来源健康状态流转;
- ConsoleLogsNamingTests.cs 与 ConsoleLogsRegistrationTests.cs:验证命名稳定与模块注册。
这些测试与契约文档共同构成了"契约—实现—验证"的闭环:限制钳制、权限一致、脱敏先行、来源失效保留等规则均有对应测试与源码锚点,便于读者在 Elsa.Diagnostics.ConsoleLogs 模块内继续深入探索。
- 后端
- 工作流自动化
- 流程编排
- 低代码
【免费下载链接】elsa-core
The Workflow Engine for .NET
相关推荐
Elsa Diagnostics Structured Logs REST API 契约深度解析:结构化日志的查询、来源与兼容边界
Elsa Diagnostics Structured Logs REST API 契约深度解析:结构化日志的查询、来源与兼容边界 本指南以 Elsa core
后端工作流自动化流程编排低代码Elsa Server Logs REST API 实战指南:实时日志流的查询接口与安全契约
Elsa Server Logs REST API 实战指南:实时日志流的查询接口与安全契约 Elsa Server Logs( Elsa.Diagnostic
后端工作流自动化流程编排低代码三条命令跑通:skill-installer 一键安装 Codex 技能
三条命令跑通:skill installer 一键安装 Codex 技能 给 Codex 加个技能,过去要克隆仓库、翻目录、手动拷文件夹,慢还容易装重。awes
AI 技能AI 插件工作流自动化人工智能
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考