☰
Elsa 结构化日志持久化落地指南:从存储抽象重构到 SQLite 持久化与关系型扩展
2026/10/5 6:44:33 网站建设 项目流程
  • 后端
  • 工作流自动化
  • 流程编排
  • 低代码

【免费下载链接】elsa-core

The Workflow Engine for .NET

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

导读

本文围绕 Elsa(The Workflow Engine for .NET)诊断能力中的“结构化日志持久化”(Structured Log Persistence)专项展开,完整梳理该特性从需求分解、存储抽象重构到 SQLite 持久化落地与关系型提供方扩展的全部实现路径。读者将掌握:结构化日志模块如何把“查询存储”“实时推送”“日志采集”三类职责拆分为可替换契约;如何在不回归既有行为的前提下新增 SQLite 持久化(FluentMigrator 建表、异步批量写队列、优雅停机冲刷、可选保留策略);以及如何按任务清单分阶段验收并在未来接入 SQL Server、PostgreSQL、MySQL 等更多关系型数据库。

该专项的完整设计文档位于specs/005-structured-log-persistence/目录,本文以其中的tasks.md任务清单为骨架,结合spec.md、plan.md、data-model.md、quickstart.md、contracts/persistence-contract.md以及src/modules/下的实际实现展开讲解。

一、特性背景与设计目标

1.1 输入需求

特性的原始需求一句话可以概括为(见 spec.md):

让结构化日志存储成为一个可插拔的关注点:保留内存支持,先提供简单易用的 SQLite 持久化存储,使用 FluentMigrator 管理 Schema,并推迟 OpenTelemetry/导出器范围。

即:存储可插拔、内存默认、SQLite 先行、FluentMigrator 管 Schema、导出器后置。

1.2 关键设计决策(Clarifications)

在规格澄清阶段(2026-05-12 与 2026-05-13 两次会话)敲定的核心决策如下:

决策项结论
是否现在就加 Logstash / Datadog / Splunk / Loki / Seq 等厂商 Sink?否,第一刀保持最小范围
是否现在就加 OTLP 日志导出器?否,推迟到诊断 OpenTelemetry 边界更清晰时
初始存储提供方有哪些?保留内存存储,新增 opt-in 的 SQLite 持久化
SQLite 是否做成一次性实现?否,作为第一个关系型提供方,便于后续扩展 SQL Server / PostgreSQL / MySQL
是否使用 EF Core?否,本特性避免 EF Core 与各提供方的 EF 迁移
Schema 创建与升级用谁?FluentMigrator 做版本化迁移;热路径用显式 SQL / Dapper 风格访问
SQLite 写入的持久性保证?异步批量写入 + 优雅停机冲刷;进程崩溃可能丢失排队未刷的事件
写队列满了怎么办?丢弃最新入队事件并上报丢弃计数/指标,绝不阻塞日志调用、不无限增长内存
SQLite 迁移何时运行?默认启动时自动迁移,提供 opt-out
时间戳如何存?以 UTC ISO-8601 文本存储
默认保留策略?默认不删除;仅当配置了最大年龄或最大行数时才执行清理

1.3 范围边界

  • 明确不做:OTLP 日志导出、厂商 Sink、原始控制台流式输出、Trace/Metric 探索(见 quickstart.md 的 Out of scope 与 spec.md 的 Assumptions)。
  • 必须保留:脱敏(redaction)必须先于任何存储与实时投递(FR-005);Studio 的 REST 与 SignalR 契约不变(FR-024、US3 验收场景 3)。

二、总体架构:存储 / 实时 / 采集三职责拆分

2.1 为什么必须重构

在持久化之前,IStructuredLogProvider同时承担了“查询最近日志”“订阅实时日志”“发布日志事件”等多个职责。若直接为 SQLite 写一个一次性的 Store,会造成不可扩展的死胡同。因此plan.md的 Phase 2 明确要求:拆分 Store 与 Live-Feed 关注点,同时保留现有IStructuredLogProvider门面(plan.md 的 Summary)。

2.2 核心契约(Phase 2,任务 T009–T011)

设计文档 contracts/persistence-contract.md 定义了四个核心契约,仓库中对应的实现位于 src/modules/Elsa.Diagnostics.StructuredLogs/Contracts/:

  • IStructuredLogSink:只追加的写入边界,接收单个事件或批量事件,不必支持查询。源码见 IStructuredLogSink.cs。
  • IStructuredLogStore : IStructuredLogSink:可查询的存储边界,负责追加脱敏事件、按StructuredLogFilter查询最近事件、列出StructuredLogSource。源码见 IStructuredLogStore.cs。
  • IStructuredLogLiveFeed:运行时实时投递边界,向 SignalR 订阅者发布新事件、应用订阅过滤器、在订阅队列溢出时报告丢弃摘要,不保证持久性。源码见 IStructuredLogLiveFeed.cs。

在契约层面,三个接口的形态为:

// IStructuredLogSink —— 只追加 public interface IStructuredLogSink { ValueTask WriteAsync(StructuredLogEvent logEvent, CancellationToken cancellationToken = default); ValueTask WriteManyAsync(IReadOnlyCollection<StructuredLogEvent> logEvents, CancellationToken cancellationToken = default); } // IStructuredLogStore —— 可查询存储 public interface IStructuredLogStore : IStructuredLogSink { ValueTask<RecentStructuredLogsResult> QueryAsync(StructuredLogFilter filter, CancellationToken cancellationToken = default); ValueTask<IReadOnlyCollection<StructuredLogSource>> ListSourcesAsync(CancellationToken cancellationToken = default); } // IStructuredLogLiveFeed —— 实时推送 public interface IStructuredLogLiveFeed { ValueTask PublishAsync(StructuredLogEvent logEvent, CancellationToken cancellationToken = default); IAsyncEnumerable<StructuredLogStreamItem> SubscribeAsync(StructuredLogFilter filter, CancellationToken cancellationToken = default); }

2.3 门面保持不变:IStructuredLogProvider

REST 端点与 SignalR 订阅管理器继续面向IStructuredLogProvider(T021–T023),它由新的组合型实现DefaultStructuredLogProvider承担(T015,DefaultStructuredLogProvider.cs):内部组合IStructuredLogStore与IStructuredLogLiveFeed,PublishAsync依次写 Store、推 LiveFeed,查询与来源列表透传 Store。默认注册路径在 ServiceCollectionExtensions.cs(T016)。

内存默认实现被拆分为可复用的两块(T012–T013):

  • InMemoryStructuredLogStore(InMemoryStructuredLogStore.cs):负责有界环形缓冲(RingBuffer)内的最近历史查询、来源列表、容量与过滤行为;
  • InMemoryStructuredLogLiveFeed(InMemoryStructuredLogLiveFeed.cs):负责订阅者分发与丢弃摘要;
  • InMemoryStructuredLogProvider(InMemoryStructuredLogProvider.cs)保留为兼容门面,将请求转发给上述 store 与 liveFeed。

这一拆分使“默认内存行为”与“未来任何持久化 Store”都能复用同一套 REST/SignalR 接入层。

2.4 共享行为约定(README 明确说明)

核心模块 README.md 记录了所有 Store 实现共享的语义:

  • MaxRecentLogQuerySize是所有IStructuredLogStore的默认Take与上限钳制(由StructuredLogsOptions.ClampRecentLogQueryTake实现,负值按 0 处理);
  • 查询排序固定为Timestamp, ReceivedAt, SourceId, Sequence, Id;
  • ListSources优先使用进程内源注册表提供身份元数据,SourceHeartbeatTimeout到期后标记为Stale;
  • QueryAsync.DroppedEvents表示内存环形缓冲溢出计数;关系型写队列丢弃通过存储诊断上报,不复用该字段;
  • 大小写不同的相等过滤并非跨实现可移植:内存为OrdinalIgnoreCase,SQL 取决于数据库排序规则。

三、分阶段任务地图(tasks.md 全文骨架)

tasks.md 将整个实现组织为 6 个阶段、3 个用户故事,共 73 项任务(T001–T073),且全部标记为已完成([X])。任务命名格式为:

[ID] [P?] [Story] Description
  • [P]:满足前置条件后可与其它标记任务并行;
  • [Story]:来自 spec.md 的用户故事标签;
  • 每项任务都包含要创建或修改的主文件路径。

3.1 Phase 1:搭建(Setup,T001–T008)

目的:为关系型与 SQLite 持久化建立包骨架、包引用与解决方案条目。

任务产出
T001–T002创建Elsa.Diagnostics.StructuredLogs.Persistence.Relational与.Persistence.Sqlite两个模块项目
T003–T004创建关系型单元测试项目与 SQLite 集成测试项目
T005–T006创建两个模块的 Shell 特性骨架(StructuredLogRelationalPersistenceFeature、SqliteStructuredLogPersistenceFeature)
T007将新项目加入Elsa.sln
T008在 Directory.Packages.props 中集中管理 FluentMigrator 与 SQLite 依赖版本

注意 T008 体现的是“版本集中管理”约定:新增的 FluentMigrator、Dapper 等依赖版本统一收口到中央Directory.Packages.props(见 spec.md 的 Assumptions)。

3.2 Phase 2:基础存储重构(Foundational,T009–T016)

目的:拆分 Store 与 Live-Feed 关注点,同时保持IStructuredLogProvider门面不变。

关键约束(Critical):在内存默认实现仍能通过既有结构化日志测试之前,不得开始任何 SQLite 故事工作(tasks.md 第 35 行)。这一“先保持不回归、再谈新功能”的顺序是 MVP 策略的基石。

本阶段即 2.2/2.3 节所述的内容:新增三个契约(T009–T011),将内存行为重构进 Store/LiveFeed(T012–T013),保留兼容门面(T014),新增组合门面(T015),更新 DI 注册(T016)。Checkpoint:既有结构化日志行为在新抽象背后被完整保留。

3.3 Phase 3:US1 —— 保留既有内存行为(P1,MVP)

  • 目标:只配置UseStructuredLogs的主机获得与之前完全相同的有界内存最近查询、来源列表、实时流、脱敏与丢弃事件行为(T021–T023 确保 REST 最近日志端点、来源端点与 SignalR 订阅都继续走IStructuredLogProvider)。
  • 独立测试:不配置持久化、启用结构化日志、发射若干ILogger记录,验证最近查询、过滤器、来源列表、实时订阅、脱敏与丢弃摘要。
  • 对应测试任务:T017(默认注册兼容性)、T018–T020(内存最近查询 / 来源列表 / 实时丢弃事件)。
  • Checkpoint:US1 完全可用且可独立测试。验证命令见 任务 T024:dotnet test test/unit/Elsa.Diagnostics.StructuredLogs.UnitTests/Elsa.Diagnostics.StructuredLogs.UnitTests.csproj。

3.4 Phase 4:US2 —— SQLite 持久化存储(P2)

  • 目标:主机可选择 SQLite 存储;默认启动迁移;脱敏日志跨重启持久化;可查询持久化记录;优雅停机冲刷排队写入;可显式配置保留策略。
  • 独立测试:配置 SQLite 存储、发射事件、用同一数据库文件刷新或重建服务,验证持久化最近查询、过滤、迁移行为、队列溢出行为、时间戳存储与保留行为。
  • 对应测试任务(T025–T032):空库迁移、跨提供方重建持久化、持久化脱敏、过滤覆盖(level/category/source/workflow/correlation/trace/time/limit)、写队列冲刷与DroppedWriteCount溢出、启动迁移 opt-out、ISO-8601 时间戳存储、保留(opt-in 清理与默认不删除)。
  • 对应实现任务(T033–T054):关系型记录模型、关系型选项、连接工厂与方言契约、Schema 迁移器契约、SQL 构建器、JSON/时间戳映射器、FluentMigrator 迁移、关系型 Store、有界写缓冲、保留清理服务、服务注册扩展、关系型与 SQLite 的 Feature/ShellFeature 注册、SQLite 连接工厂/方言/迁移器/启动服务/流式配置扩展。

3.5 Phase 5:US3 —— 关系型持久化可扩展(P3)

  • 目标:未来的关系型提供方可以复用共享的关系型 Store,只需提供连接工厂、方言与 FluentMigrator Runner 配置,无需改动核心结构化日志模块或 Studio 契约。
  • 独立测试:审查代码边界并运行使用假方言/连接服务的关系型测试(T056–T059):SQL 构建器(假方言)、映射器(JSON 与 UTC ISO-8601 时间戳)、迁移元数据/版本、以及证明Elsa.Diagnostics.StructuredLogs无 SQLite 依赖的核心边界测试。
  • 对应实现任务(T060–T062):将提供方专属 SQL 留在核心模块之外、SQLite 专属服务留在 SQLite 包内、补充 关系型提供方 README。
  • Checkpoint:SQLite 是第一个关系型提供方,而不是一条一次性的持久化路径。

3.6 Phase 6:文档与打磨(T064–T073)

更新结构化日志 README 的存储模式指引(T064)、SQLite 持久化 README(T065)、Speckit quickstart 实现说明(T066)、仅当样例显式选择 SQLite 时才接线样例主机(T067,Program.cs),然后依次执行四个构建/测试命令(T068–T072)。T073 用rg "OpenTelemetry|OTLP|Datadog|Logstash|Splunk|Loki|Seq" src/modules/Elsa.Diagnostics.StructuredLogs* specs/005-structured-log-persistence确认只剩范围外的文档引用。

四、数据模型与表结构

4.1 领域实体(data-model.md)

data-model.md 定义了本特性的关键实体:

  • StructuredLogStore:可查询存储边界(追加、按StructuredLogFilter查询、列来源,不负责订阅者背压)。
  • StructuredLogLiveFeed:实时投递边界(发布新事件、应用订阅过滤器、报告溢出丢弃摘要,不保证持久性)。
  • StructuredLogSink:只追加边界,接受单条或批量事件,可被 Store 内部使用、也可被未来的导出器包使用。
  • RelationalStructuredLogRecord:StructuredLogEvent的关系型表示,包含Id、Sequence、Timestamp(UTC ISO-8601 文本)、ReceivedAt、Level、Category、EventId、EventName、Message、MessageTemplate、ExceptionJson、ScopesJson、PropertiesJson、TraceId、SpanId、CorrelationId、TenantId、WorkflowDefinitionId、WorkflowInstanceId、SourceId。
  • RelationalStructuredLogSourceRecord:可选的关系型来源记录(机器名、进程、Kubernetes/容器元数据、StartedAt/LastSeen、状态)。
  • StructuredLogRetentionOptions:MaxAge、MaxRows、CleanupInterval、RunCleanupOnStartup;默认不删除任何记录。
  • StructuredLogWriteQueueOptions:Capacity、BatchSize、FlushInterval、ShutdownFlushTimeout、DroppedWriteCount;溢出时丢弃新事件,绝不阻塞日志调用或无限分配内存。
  • RelationalStructuredLogDialect:标识符引用、参数前缀、Limit/Take 语法、时间戳存储与比较、自由文本谓词构造、供 FluentMigrator 分支用的提供方名。
  • StructuredLogMigrationRunner:运行 FluentMigrator 迁移、空库建表、按版本顺序升级、SQLite 默认启动执行且可关闭、文档化共享数据库的多实例启动锁约束。

4.2 关系型记录与建表迁移

关系型记录模型在 RelationalStructuredLogRecord.cs(T033)。SQLite 使用共享关系型迁移创建表(T040,M001_CreateStructuredLogTables.cs):

  • 表名StructuredLogEvents,迁移版本号[Migration(2026051301)];
  • 标量过滤字段建为可查询列:Id(String(64) 主键)、Sequence(Int64)、Timestamp/ReceivedAt(String(40) 非空,存 UTC ISO-8601 文本)、Level(Int32)、Category(String(512))、EventId、EventName;
  • 复杂负载存 JSON 文本:Message、MessageTemplate、ExceptionJson、ScopesJson、PropertiesJson(String(int.MaxValue));
  • Elsa 上下文列:TenantId、WorkflowDefinitionId、WorkflowInstanceId、CorrelationId、SourceId;
  • 建立 10 个索引:Timestamp、ReceivedAt(降序)、Level、Category、SourceId、TenantId、WorkflowDefinitionId、WorkflowInstanceId、CorrelationId、TraceId。

这与>// StructuredLogWriteQueueOptions Capacity = 10_000 // 队列容量 BatchSize = 100 // 每次刷写的最大事件数 FlushInterval = 1s // 后台刷写最大间隔 ShutdownFlushTimeout = 10s // 优雅停机冲刷超时 // StructuredLogRetentionOptions MaxAge = null // 未配置则不按时间删除 MaxRows = null // 未配置则不按行数删除 CleanupOnStartup = false

SQLite 选项默认值(SqliteStructuredLogOptions.cs):ConnectionString = "Data Source=elsa-structured-logs.db",RunMigrationsOnStartup = true。

5.3 保留清理服务(T043)

StructuredLogRetentionService.cs 实现IStructuredLogRetentionService.CleanupAsync:

  • 只有MaxAge或MaxRows被配置时才执行删除(FR-020,默认零删除);
  • 按MaxAge用ReceivedAt < Cutoff删除过期记录(BuildDeleteOlderThan);
  • 按MaxRows用“ReceivedAt DESC, Sequence DESC, Id DESC排序后取第 N 行之后的 Id”删除超量记录(BuildDeleteRowsBeyondMax);
  • 由 SQLite 启动服务决定是否在启动时立即执行(CleanupOnStartup)。

5.4 SQLite 启动服务(T051)

SqliteStructuredLogStartupService.cs 实现IHostedService与IStartupTask:

  • 用SemaphoreSlim保证幂等执行(_executed标志);
  • RunMigrationsOnStartup == true时调用IStructuredLogSchemaMigrator.MigrateAsync;
  • Relational.Retention.CleanupOnStartup == true时调用retentionService.CleanupAsync。

注册入口在 SqliteStructuredLogsModuleExtensions.cs(T052):AddSqliteStructuredLogPersistence注册连接工厂、方言、Schema 迁移器、启动服务(HostedService +IStartupTask),并把SqliteStructuredLogOptions.Relational拷贝到RelationalStructuredLogOptions(Copy方法逐项同步 WriteQueue 与 Retention 五个选项),最后调用AddRelationalStructuredLogPersistence()挂上共享关系型持久化注册(T044)。

六、配置实战:内存默认与 SQLite 持久化

6.1 默认内存存储(零配置)

不配置任何持久化提供方即可使用有界内存最近历史与实时 SignalR 流(来自 quickstart.md):

services.AddElsa(elsa => { elsa.UseStructuredLogs(options => { options.RecentLogCapacity = 5_000; options.MaxRecentLogQuerySize = 1_000; }); });

相关选项均定义在 StructuredLogsOptions.cs:RecentLogCapacity = 5_000、SubscriberChannelCapacity = 1_000、MaxRecentLogQuerySize = 1_000、SourceHeartbeatTimeout = 30s,以及一组默认脱敏名称/正则(authorization、token、password、secret、api-key、cookie、connection-string与 Bearer/Key 类正则)。

6.2 SQLite 持久化(opt-in)

安装Elsa.Diagnostics.StructuredLogs.Persistence.Sqlite包后,在UseStructuredLogs中通过UseSqliteStorage显式选择 SQLite(来自 quickstart.md 与 SQLite README):

services.AddElsa(elsa => { elsa.UseStructuredLogs(structuredLogs => { structuredLogs.UseSqliteStorage("Data Source=elsa-structured-logs.db", sqlite => { sqlite.RunMigrationsOnStartup = true; sqlite.Relational.WriteQueue.Capacity = 10_000; sqlite.Relational.WriteQueue.BatchSize = 100; }); }); });

随后映射既有端点与 Hub:

app.UseStructuredLogs();

Studio 无需任何 API 改动,继续使用以下既有契约:

  • /diagnostics/structured-logs/recent—— 最近日志查询
  • /diagnostics/structured-logs/sources—— 来源列表
  • /diagnostics/structured-logs/storage—— 存储诊断
  • /elsa/hubs/diagnostics/structured-logs—— SignalR 实时流

注意UseSqliteStorage还提供无连接字符串的重载(默认Data Source=elsa-structured-logs.db),源码见 SqliteStructuredLogsModuleExtensions.cs。

6.3 保留策略配置

SQLite 默认不删除任何持久化记录。需要限制存储增长时显式配置(quickstart.md):

sqlite.Relational.Retention.MaxAge = TimeSpan.FromDays(14); sqlite.Relational.Retention.MaxRows = 250_000; sqlite.Relational.Retention.CleanupOnStartup = true;

6.4 迁移策略说明

SQLite 存储默认在启动时运行 FluentMigrator 迁移(RunMigrationsOnStartup = true),这是 SQLite 的推荐默认。生产环境如选择在部署工具中一次性准备 Schema,可设false。对于未来的共享关系型数据库(SQL Server / PostgreSQL),spec 明确要求文档化“多实例启动锁”考虑(FR-030、quickstart 的 Migrations 节):生产部署可能希望只在部署阶段运行一次迁移,而不是由每个应用实例各自执行。

6.5 写入缓冲行为摘要

SQLite 写入通过有界后台队列批量落盘:

  • 优雅停机:ShutdownFlushTimeout(默认 10s)内尽力冲刷排队事件;
  • 进程崩溃:排队但未刷写的事件可能丢失(FR-016);
  • 队列满:丢弃最新事件,递增DroppedWriteCount,并输出警告摘要;日志调用不阻塞、内存有界。

七、测试策略与验收

7.1 测试矩阵

任务清单将测试与用户故事绑定,确保每个增量可独立验证(tasks.md 的 Tests 说明):

故事测试任务覆盖点测试文件(均已存在)
US1T017–T020默认注册兼容、内存最近查询、来源列表、实时丢弃事件Elsa.Diagnostics.StructuredLogs.UnitTests
US2T025–T032空库迁移、跨提供方重建持久化、持久化脱敏、过滤、队列冲刷与溢出、迁移 opt-out、ISO-8601 时间戳、保留Elsa.Diagnostics.StructuredLogs.Persistence.Sqlite.IntegrationTests 下的SqliteStructuredLogMigrationTests.cs、SqliteStructuredLogStoreTests.cs、SqliteStructuredLogFilterTests.cs、SqliteStructuredLogWriteQueueTests.cs、SqliteStructuredLogTimestampTests.cs、SqliteStructuredLogRetentionTests.cs、SqliteStructuredLogSourceTests.cs
US3T056–T059SQL 构建器(假方言)、映射器(JSON + UTC ISO-8601)、迁移元数据/版本、核心模块无 SQLite 依赖Elsa.Diagnostics.StructuredLogs.Persistence.Relational.UnitTests

7.2 与成功标准的对应

spec.md 的 Success Criteria(SC-001 至 SC-012)与上述测试一一对应:既有内存测试继续通过(SC-001)、SQLite 跨重启持久化(SC-002)、过滤覆盖八类字段(SC-003)、FluentMigrator 空库建表(SC-004)、保留仅按配置清理(SC-005)、优雅停机冲刷(SC-006)、队列满丢弃并上报计数(SC-007)、启动迁移默认/可关(SC-008)、UTC ISO-8601 文本一致存储与过滤(SC-009)、默认零删除(SC-010)、文档双模式说明(SC-011)、核心模块零 SQLite 引用(SC-012)。

7.3 验证命令

任务清单中的构建/测试命令可直接执行:

dotnet build src/modules/Elsa.Diagnostics.StructuredLogs/Elsa.Diagnostics.StructuredLogs.csproj dotnet build src/modules/Elsa.Diagnostics.StructuredLogs.Persistence.Sqlite/Elsa.Diagnostics.StructuredLogs.Persistence.Sqlite.csproj dotnet test test/unit/Elsa.Diagnostics.StructuredLogs.UnitTests/Elsa.Diagnostics.StructuredLogs.UnitTests.csproj dotnet test test/unit/Elsa.Diagnostics.StructuredLogs.Persistence.Relational.UnitTests/Elsa.Diagnostics.StructuredLogs.Persistence.Relational.UnitTests.csproj dotnet test test/integration/Elsa.Diagnostics.StructuredLogs.Persistence.Sqlite.IntegrationTests/Elsa.Diagnostics.StructuredLogs.Persistence.Sqlite.IntegrationTests.csproj

7.4 手动验收步骤(quickstart 提供)

  1. 启动开启 SQLite 结构化日志存储的 Elsa Server;
  2. 发射不同 level、category、workflow ID、correlation ID 的若干ILogger记录;
  3. 在 Studio 查询最近日志并验证各过滤器生效;
  4. 用同一 SQLite 数据库文件重启主机;
  5. 再次查询最近日志,确认重启前的事件仍可读;
  6. 确认时间戳以 UTC ISO-8601 值存储与过滤;
  7. 在测试环境调低保留设置,验证旧行/超量行被清理;
  8. 在测试环境灌满写队列,验证最新事件被丢弃且丢弃计数可见。

八、依赖关系、并行策略与实施顺序

8.1 阶段依赖

tasks.md 的 Dependencies 一节明确了依赖链:

  • Phase 1:无依赖;
  • Phase 2:依赖 Phase 1,阻塞所有用户故事;
  • Phase 3(US1):依赖 Phase 2,是 MVP;
  • Phase 4(US2):依赖 Phase 2,应在 US1 验证之后进行;
  • Phase 5(US3):依赖 US2 产出的关系型与 SQLite 代码;
  • Phase 6:依赖所选故事的实现。

8.2 用户故事依赖

  • US1(P1):第一个可执行切片,在存储重构后保持既有行为;
  • US2(P2):在新存储边界之上增加持久化 SQLite 存储;
  • US3(P3):为未来关系型存储固化提供方边界。

8.3 并行机会

任务清单明确标注的可并行任务组:

  • T001–T006(包骨架);
  • T009–T011(契约);
  • US1 测试 T017–T020(Phase 2 之后);
  • US2 测试 T025–T032(项目骨架存在之后);
  • US3 测试 T056–T059(关系型契约存在之后);
  • 文档任务 T064–T066(实现 API 稳定之后)。

8.4 实施策略:MVP 先行、增量交付

MVP First:先完成 Phase 1 + Phase 2 → 只完成 Phase 3 → 运行既有测试确认零回归 → 存储边界稳定后再进入 SQLite。

Incremental Delivery:US1(内存兼容)→ US2(SQLite 持久化:迁移、队列、时间戳、保留)→ US3(关系型扩展)→ Polish(文档、样例、构建、测试)。

九、边界与注意事项

9.1 边缘场景(spec.md Edge Cases)

设计文档明确列出了实现必须考虑的边缘情况:

  • 持久化写入不得阻塞ILogger调用方:SQLite 允许崩溃丢失排队未刷事件;
  • 写队列满:丢弃最新事件并上报丢失计数,而非阻塞或无限增长;
  • 主机可能在迁移完成前发射日志(启动顺序错误);
  • 多应用实例可能并发对共享关系型数据库执行迁移;
  • SQLite 文件路径可能缺失、相对或指向无写权限目录;
  • 进程可能在事件进入后台队列后、刷写前崩溃;
  • JSON 字段上的自由文本过滤可能是提供方相关、初始为近似匹配;
  • 保留清理可能与最近查询或实时订阅竞争;
  • Schema 变更必须尽量保留已持久化的事件。

9.2 实现约束(tasks.md Notes)

  • 本特性不实现 OTLP 或厂商 Sink;
  • 不为结构化日志持久化引入 EF Core;
  • 保持“先脱敏再存储”;
  • Studio 的 REST 与 SignalR 契约不变;
  • 优先使用小而显式的 SQL,而非精巧的通用查询抽象(除非实现压力证明必要)。

9.3 清单核对

checklists/requirements.md 的 8 项要求全部勾选通过:内存默认、SQLite 为唯一初始持久化提供方、存储/实时/门面职责分离、脱敏先行、FluentMigrator 而非 EF Core、未来关系型提供方不破坏 Studio 契约、含保留与优雅停机冲刷、显式推迟 OpenTelemetry 与厂商导出器。

十、总结

从 tasks.md 的 73 项任务可以看出,Elsa 的结构化日志持久化特性走的是“先抽象、再落地、后扩展”的工程路径:Phase 2 的存储/实时/采集三契约拆分让IStructuredLogProvider门面与 REST/SignalR 契约完全稳定;US2 以 SQLite 作为第一个关系型提供方验证了 FluentMigrator 迁移、有界异步写队列、UTC ISO-8601 时间戳与 opt-in 保留的完整闭环;US3 通过方言与连接工厂隔离保证了 SQL Server / PostgreSQL / MySQL 等后续提供方可以复用共享关系型 Store。对想要为 Elsa 主机接入持久化日志、或自行扩展更多关系型数据库的开发者,本文提供的配置示例、源码路径与测试命令可以直接复用。

  • 后端
  • 工作流自动化
  • 流程编排
  • 低代码

【免费下载链接】elsa-core

The Workflow Engine for .NET

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

相关推荐

上一篇:fastblock性能测试实战:使用block_bench与vhost实现百万级IOPS的完整指南
下一篇:secGear安全通信通道实现原理:保护数据在飞地内外传输的完整指南

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

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

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

立即咨询