☰
Elsa Diagnostics Console Logs REST API 契约深度解析:Recent 查询与 Sources 枚举接口实战
2026/10/3 1:57:45 网站建设 项目流程
  • 后端
  • 工作流自动化
  • 流程编排
  • 低代码

【免费下载链接】elsa-core

The Workflow Engine for .NET

项目地址:https://gitcode.com/gh_mirrors/el/elsa-core
点击查看免费下载

本篇技术指南围绕 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/nodeNameKubernetes 场景下的可空标识,来自HOSTNAME、CONTAINER_NAME、POD_NAMESPACE、NODE_NAME环境变量
lastSeen最后一次收到行或心跳的时间
healthconnected/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 来源枚举的契约规则

契约文档给出三条规则:

  1. 权限一致:来源列表与行级访问使用同一个read:diagnostics:console-logs权限,不存在"列出来源更宽松"的旁路;
  2. 元数据脱敏:敏感来源元数据在 Provider 存储或返回之前即被脱敏(FR-027),Kubernetes 命名空间、容器名等可能包含敏感信息的字段受到同等保护;
  3. 失效来源可继续列出:处于 stale 或 disconnected 状态的来源在近期历史仍被保留期间继续可列出(对应 FR-029 与用户故事 P3 场景 3:来源停止心跳或行后,被标记为失效但不会立刻丢失近期历史)。

四、配置项与默认值速查

结合 README.md 与 ElsaConsoleLogOptions.cs,当前仓库支持的宿主配置项及默认值如下:

配置项默认值说明
SourceId机器名-进程ID默认来源标识
SourceDisplayNamePod 名或来源 ID来源展示名
ServiceNameOTEL_SERVICE_NAME或应用域友好名来源服务名
PreserveAnsitrue是否保留 ANSI 转义序列(false时服务端剥离)
RecentCapacity2000近期历史环形缓冲容量
MaxRecentQuerySize250单次 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

项目地址:https://gitcode.com/gh_mirrors/el/elsa-core
点击查看免费下载

相关推荐

上一篇:Task 命令行接口(CLI)完全参考:命令、Flags、退出码与配置优先级实战指南
下一篇:7个终极Claude-Flow工作流模式:从单功能脚本到企业级多项目管理

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询