ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Mastra ClickHouse vNext 日志事件(log_events)设计解析:逻辑模型、物理表结构与查询契约

Mastra ClickHouse vNext 日志事件(log_events)设计解析:逻辑模型、物理表结构与查询契约 Mastra ClickHouse vNext 日志事件log_events设计解析逻辑模型、物理表结构与查询契约【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra本文围绕 Mastra 可观测性v-next设计中log_events表的设计文档展开系统梳理日志事件Log Events的逻辑形状、物理形状与查询契约并结合仓库中已落地的 DDL、查询实现与过滤 Schema 进行源码级佐证。读完本文你将掌握 Mastra 如何在 ClickHouse 上建模 AI 应用日志实体层级、Trace 关联、上下文维度、Tag 与 JSON 载荷的取舍理解listLogs查询面与 v0 有意裁剪的能力边界并能将设计与实现一一对应起来用于二次开发或运维排查。设计文档定位一份面向实现的 v0 规格observability/clickhouse-design/log-events.md是 ClickHousev-next可观测性设计文档集的一部分。该文档集在 observability/clickhouse-design/README.md 中明确了定位文档是初始v-next实现的指南用来指导实现而不是长期存在的第二真相源一旦v-next落地代码与测试成为权威行为来源跨表的共性决策集中在 shared.md各表特有的行为物理形状、查询行为落在各自的表级文档中。log-events.md恰好属于后者它只聚焦log_events一张表回答三个问题——逻辑上一条日志长什么样、物理上 ClickHouse 里怎么存、查询上对外提供什么契约。逻辑形状一条日志记录承载的全部字段log-events.md将log_events的逻辑字段划分为五组这五组在代码中也直接体现为 helpers.ts 中logRecordToRow/rowToLogRecord的映射字段。逐组说明分组字段说明事件元数据timestamp、level日志发生时间与级别debug/info/warn/error/fatal关联与实验 IDtraceId、spanId、experimentId关联到 Trace/Span 与实验是日志与追踪信号打通的关键实体层级entityType、entityId、entityName、parentEntityType、parentEntityId、parentEntityName、rootEntityType、rootEntityId、rootEntityName三级实体层级当前/父级/根级用于把日志归位到具体的 Agent、Workflow、Tool 等实体上下文userId、organizationId、resourceId、runId、sessionId、threadId、requestId、environment、executionSource、serviceName跨信号的共享执行上下文日志标量message日志正文灵活与 JSON 载荷tags、data、metadata、scope高灵活度字段其中tags参与查询其余作为信息型载荷保留这套字段结构与 shared.md 中「五种信号共用加宽的上下文模型」一致日志、指标、评分、反馈共享executionSource作为执行来源列实体层级与关联 ID 全部以类型化列存在而不是塞进 JSON。物理形状MergeTree 与面向「最近优先」的排序键log-events.md给出的 v0 物理形状是ENGINE MergeTreePARTITION BY toDate(timestamp)ORDER BY (timestamp, traceId)文档同时给出了三条关键设计理由level、实体类型字段、environment、executionSource、serviceName是强LowCardinality候选——这些维度取值有限、重复度高用低基数字典编码可以显著压缩存储并加速过滤tags使用Array(LowCardinality(String))——配合「包含全部 tag」contains-all语义做数组过滤PARTITION BY toDate(timestamp)支撑按天粒度的日志 TTL 管理——按天分区后过期分区可以直接 drop天然契合 shared.md 中「以天为增量配置各信号 TTL」的保留策略ORDER BY (timestamp, traceId)是 v0 的有意选择——日志的首要读取模式是「最近优先」recency-first而不是按 Trace 聚合读取虽然traceId关联读取也被支持但它不是log_events物理设计的主要驱动。实现层面的演进从设计到落地 DDL设计文档描述的是 v0 初始意图。当前仓库中已经落地的 DDL 位于 ddl.ts 的LOG_EVENTS_DDL可以看到实现相对设计文档的演进CREATE TABLE IF NOT EXISTS mastra_log_events ( -- 时间戳 timestamp DateTime64(3, UTC), -- ID logId String, traceId Nullable(String), spanId Nullable(String), experimentId Nullable(String), -- 实体层级含 entity/parent/root 的 VersionId entityType LowCardinality(Nullable(String)), ... -- 上下文 userId Nullable(String), ... environment LowCardinality(Nullable(String)), executionSource LowCardinality(Nullable(String)), serviceName LowCardinality(Nullable(String)), -- 日志标量 level LowCardinality(String), message String, -- 查询相关灵活字段 tags Array(LowCardinality(String)) DEFAULT [], -- 仅信息型 JSON 载荷 data Nullable(String), metadata Nullable(String), scope Nullable(String) ) ENGINE ReplacingMergeTree PARTITION BY toDate(timestamp) ORDER BY (timestamp, logId)与设计文档相比实现上有三处可见演进引擎从MergeTree演进为ReplacingMergeTree并引入logId作为行标识参与排序键用于在重试写入场景下按logId去重这超出了设计文档「v0 不为日志加 dedupe 键」的初始约定属于落地阶段的能力增强排序键从(timestamp, traceId)调整为(timestamp, logId)仍然保持timestamp前置的「最近优先」读路径同时用logId保证同一时刻的行唯一性补齐了entityVersionId、parentEntityVersionId、rootEntityVersionId三个版本 ID 列用于精确锁定某个实体的某个部署版本。所有时间戳统一为DateTime64(3, UTC)JSON 载荷统一存为Nullable(String)JSON 编码写入、解码读出这些约定与 physical-types.md 中log_events一节完全一致。查询契约listLogs 与公开日志过滤面log-events.md的查询契约部分明确要求listLogs应支持当前公开的日志过滤面public log filter surfacetags在共享 tag 语义下保持可过滤contains-all即「必须包含全部指定 tag」data、metadata、scope保留在行上但v0 不参与发现discovery与分组grouping日志metadata出现在记录上但不属于当前公开日志过滤 Schema。公开过滤 Schema源码中的真实定义公开过滤面定义在 packages/_internal-core/src/storage/domains/observability/logs.ts由 packages/core/src/storage/domains/observability/logs.ts 对外 re-exportlogLevelSchema z.enum([debug, info, warn, error, fatal])logsFilterSchema 共享过滤字段 level单个或数组 已废弃的sourcelogsOrderByFieldSchema z.enum([timestamp])——v0 只允许按timestamp排序默认DESClistLogsArgsSchemamode、filters、pagination、orderBy、afterdelta 游标、limit。共享过滤字段commonFilterFields定义在 packages/_internal-core/src/storage/domains/shared.ts即设计文档「逻辑形状」中上下文与实体层级的可过滤版本timestamp区间、traceId、spanId、三级实体类型/名称/版本 ID、userId、organizationId、experimentId、serviceName、environment、resourceId、runId、sessionId、threadId、requestId、executionSource、tags。查询实现过滤条件如何变成 SQLlistLogs的实现位于 stores/clickhouse/src/storage/domains/observability/v-next/logs.ts其核心调用链为listLogsArgsSchema.parse(args)校验入参buildLogsFilterConditions(parsed.filters, l)把过滤对象编译成{ conditions, params }见 filters.ts 的buildLogsFilterConditions共享字段走addCommonFilterFields时间区间用timestamp {start:DateTime64(3)}这类参数化谓词ID/上下文字段用等值比较level是字符串时用等值level {level:String}是数组时用level IN {levels:Array(String)}tags采用 contains-all 语义每个 tag 生成一个has(tags, {tag_i:String})条件再 AND 连接明确拒绝已废弃的source过滤assertNoDeprecatedSourceFilter会直接抛错提示改用executionSourcebuildPaginationClause计算page/perPage/limit/offset默认page0, perPage10组装查询SELECT * FROM mastra_log_events AS l [WHERE ...] ORDER BY timestamp DESC LIMIT {limit} OFFSET {offset}并先执行SELECT count() AS total计算总数返回pagination与logs。Delta 增量轮询模式除了传统分页listLogs还支持mode: delta的增量游标轮询配合mastra_log_events_delta辅助表ddl.ts 中buildLogEventsDeltaDDL/buildLogEventsDeltaMvDDL增量表每行持有cursorIdUInt64、ingestedAt、timestamp、logId按ORDER BY (cursorId)存储并带TTL ingestedAt toIntervalDay(2)的两天短保留物化视图mastra_mv_log_events_delta在写入时用generateSerialID或基于指纹的回退游标表达式farmFingerprint64(logId)参与位运算为每一行铸造单调cursorId查询侧通过WHERE d.cursorId {afterCursor}拉取增量并以LIMIT fetchLimit limit 1判断hasMore返回delta与deltaCursor供下一轮使用这是前向增量索引早于该 delta Schema 的历史数据不会回填。写入路径从运行时到 batchCreateLogs结合 shared.md 的写入路径设计日志从产生到落库的链路是运行时发出可观测性信号DefaultExporter批量聚合事件导出器调用可观测性存储域的batchCreateLogsClickHousev-next通过标准存储接口持久化。batchCreateLogs的实现同样在 logs.ts空数组直接返回否则以JSONEachRow格式、配合CH_INSERT_SETTINGSdate_time_input_format: best_effort、use_client_time_zone: 1等批量插入mastra_log_events。写入前的字段归一化由 helpers.ts 的logRecordToRow完成tags经过normalizeTags只保留字符串、trim 空白、丢弃空值并去重data、metadata、scope用jsonEncode序列化为 JSON 字符串存入Nullable(String)executionSource列优先取log.executionSource缺失时回退到log.source读取侧rowToLogRecord反向用parseJson解码并把Nullable(String)归一为可空字符串还原出完整的LogRecord。v0 有意为之的限制log-events.md的最后一节明确了三条 v0 限制它们共同定义了日志信号的「不做清单」没有可搜索的日志 metadata map——metadata仅作为信息型载荷保留在行上不建Map索引不参与过滤/搜索对比tracing 信号的span_events.metadataSearch是 trace 专属的可搜索 map不支持对data过滤或分组——data是任意 JSON 载荷v0 不提供对其内部字段的谓词能力不支持对scope过滤或分组——scope同样只读不回查。与之呼应shared.md 中「Information-only JSON payloads」一节把日志data、metadata、scope一并归入不参与发现与分组的信息型载荷并规定「非 trace 信号把 metadata 视为信息型 payload而不是类型化身份字段的回退来源」——即身份与上下文信息必须显式写入类型化列而不是藏在 metadata 里。小结log_events的设计体现了一条清晰的取舍主线类型化列承载一切需要过滤/分组/发现的稳定维度实体层级、关联 ID、上下文、leveltags以数组形态提供轻量灵活的标签过滤而data/metadata/scope则作为信息型 JSON 载荷保留完整保真度但不进入热查询路径。物理上以按天分区支撑 TTL、以timestamp前置排序键服务「最近优先」读取并在落地实现中演进为ReplacingMergeTree logId以增强重试写幂等。后续如需深入了解其他信号Trace、Metric、Score、Feedback或跨表共性决策可继续阅读 observability/clickhouse-design 下的 span-events.md、metric-events.md、shared.md 与 physical-types.md实现的权威行为以 stores/clickhouse/src/storage/domains/observability/v-next 下的源码与测试为准。【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表