
PostHog Logs MCPlogs-sparkline-query工具实战日志量 Sparkline 的低成本查询参数详解与源码解析【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthog本篇围绕 PostHog 日志产品Logs的 MCP 提示词文档 logs-sparkline.md 展开讲清logs-sparkline-query工具的用途、全部查询参数、可复制的 JSON 示例以及它背后的 REST 端点、SparklineQueryRunner与 ClickHouse 聚合 SQL 的完整实现链路。读完你可以直接在 MCP/Agent 场景中按规范构造 sparkline 查询并理解其时间桶划分、Top 10 折叠、count/bytes排名等底层行为从而在“先概览、后细查”的日志排障流程中做出正确的工具选型。Sparkline 的定位比全量日志查询便宜得多的概览工具logs-sparkline.md 开篇即定义了该工具的定位Get a time-bucketed sparkline of log volume, broken down by severity or service. Use this to understand log volume patterns before querying individual log entries — it is much cheaper than a full log query.即在按时间桶time-bucketed粒度上查看日志量曲线可按 severity 或 service 拆分。官方建议把它作为排障的第一步先理解日志量的时间分布规律再决定是否需要用全量日志查询工具下钻到具体日志条目因为 sparkline 的成本远低于完整日志查询full log query。在 MCP 工具清单 tools.yaml 中该工具注册为logs-sparkline-query对应的 OpenAPI 操作是logs_sparkline_create启用状态、只读readOnly: true、幂等需要logs:read权限响应仅保留results字段logs-sparkline-query: operation: logs_sparkline_create enabled: true scopes: - logs:read annotations: readOnly: true destructive: false idempotent: true title: Query logs sparkline description_file: ./prompts/logs-sparkline.md response: include: - results值得注意的是同文件中logs-services-create的描述推荐将 sparkline 与它搭配使用——“high volume × non-zero error_rate is the natural alert candidate”而logs-patterns的响应里也显式排除了patterns.*.sparkline、sparkline_buckets等字段以节省 token。也就是说sparkline 是 PostHog 日志 MCP 工具族中一个被刻意设计为低 token 成本的体积概览原语与 count、count-ranges、facet-values、patterns 等工具共同构成“先概览、再下钻”的查询分层。请求结构所有参数必须放在query内文档强调了一个关键约束所有参数都要放在query对象里顶层字段会被拒绝top-level fields are rejected{ query: { serviceNames: [api], dateRange: { date_from: -1h } } }这个约束在后端有对应的强制校验。视图 api.py 中的sparkline动作会先取出request.data[query]再调用self._require_dict_query(query_data)校验query必须是字典——把参数散落在顶层的请求会被直接拒绝。请求体由_LogsSparklineRequestSerializer承载api.py其唯一字段就是query即_LogsSparklineBodySerializer。REST 端点本身为POST /api/projects/{team_id}/logs/sparkline这一点可以从测试 test_sparkline_query_runner.py 直接确认response self.client.post(f/api/projects/{self.team.id}/logs/sparkline, data{query: query_params})因此无论是走 MCP 工具logs-sparkline-query还是直接调用 REST API请求体结构完全一致外层包一个query内层才是具体过滤与拆分参数。参数详解文档参数与源码完整参数对照query.dateRange时间范围默认最近一小时date_from范围起点。接受 ISO 8601 时间戳或相对格式-1h、-6h、-1d、-7d。date_to范围终点格式相同省略或传 null 表示“当前时间”。缺省整个dateRange时后端使用最近一小时-1h。视图代码印证了默认值逻辑api.pydate_range_data query_data.get(dateRange) date_range self.get_model(date_range_data, DateRange) if date_range_data else DateRange(date_from-1h)序列化器的 help_text 也写明 “Date range for the sparkline. Defaults to last hour.”api.py。query.serviceNames按服务名过滤字符串列表例如[api-gateway]。对应 ClickHouse 中的service_name过滤条件由LogsQueryRunner.where()生成见 sparkline_query_runner.py 的{where}占位符。query.severityLevels按严重级别过滤取值限于trace、debug、info、warn、error、fatal省略则包含所有级别。后端枚举与此严格一致api.pyseverityLevels serializers.ListField( childserializers.ChoiceField(choices[trace, debug, info, warn, error, fatal]), requiredFalse, default[], help_textFilter by log severity levels., )query.searchTerm日志正文全文检索对日志 body 做全文检索的字符串。query.filterGroup属性过滤器用于收窄结果的属性过滤组格式与query-logs工具的 filters 相同参见 query-logs.md。测试中的典型空过滤组写法可供参考filterGroup: {type: AND, values: [{type: AND, values: []}]}query.sparklineBreakdownBy按 severity 或 service 拆分severity默认按严重级别拆分service按服务名拆分用于观察哪个服务产生的日志最多。query.sparklineRankBy按 count 或 bytes 排名文档未提但后端支持logs-sparkline.md 只列出了上述参数但从源码看后端还接受一个sparklineRankBy字段api.pysparklineRankBy serializers.ChoiceField( choices[count, bytes], requiredFalse, help_textRank breakdown values by count (default) or bytes before collapsing the tail into other., )它决定“哪些拆分值保留独立序列、哪些被折叠进 other”的排名依据按event_count还是bytes_uncompressed排名见后文 SQL 解析。此外personId/sessionId也出现在 sparkline 请求序列化器中api.py属于按人/按会话限定查询范围的能力。三个官方示例错误量、按服务、按级别文档提供了三个可直接复制的示例覆盖最常见的三类排查意图最近一天的错误量error volume over the last day{ query: { serviceNames: [api-gateway], severityLevels: [error, fatal], dateRange: { date_from: -1d } } }按服务查看日志量log volume by service{ query: { serviceNames: [api-gateway], sparklineBreakdownBy: service, dateRange: { date_from: -6h } } }按严重级别查看日志量log volume by severity{ query: { serviceNames: [api-gateway], sparklineBreakdownBy: severity, dateRange: { date_from: -1d } } }三个示例的共同点都用date_from相对时间控制窗口date_to缺省到“现在”拆分维度通过sparklineBreakdownBy显式声明不声明时默认按 severity。响应结构每个时间桶一条记录响应是一个桶数组MCP 层保留results。从 OpenAPI 响应序列化器 api.py 可以看到每个桶的完整字段字段类型说明timestring桶起始时间ISO 8601severitystring仅当sparklineBreakdownByseverity时出现servicestring仅当sparklineBreakdownByservice时出现countint该桶内的日志条数bytes_uncompressedint该桶内未压缩字节数之和即拆分维度字段是“二选一”出现severity 拆分的桶带severityservice 拆分的桶带service与_calculate中动态生成的result_key一致见下节。源码链路从 REST 视图到 ClickHouse SQL视图层参数装配与运行模式api.py 中sparkline动作的完整流程tag_queries(productProduct.LOGS, featureFeature.QUERY)打点校验query必须是字典解析dateRange缺省回退DateRange(date_from-1h)构造LogsQuery字段包括severityLevels、serviceNames、searchTerm、filterGroup、resourceFingerprint、personId、sessionId、sparklineBreakdownBy、sparklineRankBy交给SparklineQueryRunner以ExecutionMode.CALCULATE_BLOCKING_ALWAYS同步执行上报logs sparkline queried用户行为事件含是否有 searchTerm、过滤组、级别/服务数量、拆分维度等属性返回response.results。运行器层30 秒超时与默认值sparkline_query_runner.py 中的SparklineQueryRunner继承LogsQueryRunner关键设计有四处1收紧执行超时至 30 秒。常量SPARKLINE_PREVIEW_MAX_EXECUTION_SECONDS 30的注释解释了动机volume preview 必须“快速返回或快速失败”bytes 拆分需要求和_bytes_uncompressed而分钟级聚合投影minute-aggregate projection并不覆盖它高流量服务可能回退到全表扫描因此把执行上限压到 60 秒默认值以下让慢预览立刻暴露错误而不是像挂起一样等待。注意settings属性是复制父类 settings 后仅更新max_execution_time注释特别说明这是有意为之——新建一个HogQLGlobalSettings会悄悄重新打开父类刻意关闭的allow_experimental_object_type/allow_experimental_join_condition/transform_null_in等“bug 绕过”标志cached_property def settings(self) - HogQLGlobalSettings: return super().settings.model_copy(update{max_execution_time: SPARKLINE_PREVIEW_MAX_EXECUTION_SECONDS})2拆分维度到 ClickHouse 字段的映射BREAKDOWN_DB_FIELD: dict[LogsSparklineBreakdownBy, str] { LogsSparklineBreakdownBy.SEVERITY: severity_text, LogsSparklineBreakdownBy.SERVICE: service_name, } DEFAULT_BREAKDOWN LogsSparklineBreakdownBy.SEVERITY3排名依据的映射即sparklineRankBy的落地RANK_BY_FIELD: dict[LogsSparklineRankBy, str] { LogsSparklineRankBy.COUNT: event_count, LogsSparklineRankBy.BYTES: bytes_uncompressed, } DEFAULT_RANK_BY LogsSparklineRankBy.COUNT注释点出“rank by bytes matters when the caller charts bytes: the top talkers by volume are not necessarily the top talkers by size”——按条数最多的服务和按字节最多的服务不一定是同一批。4Top 10 折叠SPARKLINE_TOP_BREAKDOWN_VALUES 10超过 10 个的拆分值会被折叠成单一 “other” 行注释说明这与 sparkline UI 实际绘制的内容一致折叠不会丢失会被画出来的东西。SQL 层时间桶骨架 排名折叠的聚合查询to_query()生成的 HogQLsparkline_query_runner.py结构值得逐层理解外层am子查询是“时间脊柱”用numbers(...)从date_from对齐到 interval 起点开始按 interval 步进生成一串time_bucket保证即使某个时间桶没有日志响应里也有对应行count为 0 而不是缺行。LEFT JOIN聚合子查询ac最内层对logs表按toStartOfInterval({time_field}, {one_interval_period})与拆分字段GROUP BY聚合出count() AS event_count与sum(_bytes_uncompressed) AS bytes_uncompressedWHERE {where} AND time ... AND time ...承载 serviceNames、severityLevels、searchTerm、filterGroup 等全部过滤条件中间层用窗口函数sum({rank_field}) OVER (PARTITION BY breakdown_value)计算每个拆分值在整个窗口内的总量再dense_rank() OVER (ORDER BY breakdown_total DESC, breakdown_value ASC)排名最外层if(breakdown_rank {top_n}, breakdown_value, {other_label})把 Top 10 之外的值统一改写成 other 标签BREAKDOWN_OTHER_STRING_LABEL后再GROUP BY time, breakdown_value求和——折叠的是序列不是丢弃数据。时间字段的投影优化占位符time_field在 interval 不是秒级时使用toStartOfMinute(timestamp)而非timestamp本身。注释解释了原因sparkline 投影是聚合在toStartOfMinute(timestamp)之上的若直接用timestamp即使外层再套toStartOfInterval也命中不了该投影。行数上界注释说明行数“由构造决定”有界——rollup 后每个时间桶最多 11 个拆分值10 otherbucket 目标把桶数控制在约 50 个附近所以任意时间范围下界在约 550 行LIMIT 1000只是兜底。结果按time asc, breakdown_value asc排序。结果整形UTC 时间戳与动态维度键_calculate()把 SQL 结果整形为 API 行sparkline_query_runner.py拆分值缺失None或空串时归一为(no value)桶时间显式打上 UTC 时区注释说明是为了“与日志行时间戳和前端比对用的 live_logs_checkpoint 序列化格式保持一致”维度键名由sparklineBreakdownBy决定severity或service这正好解释了响应中severity/service二选一出现的机制。测试佐证折叠不丢量、bytes 排名选出不同的 Top 10后端测试 test_sparkline_query_runner.py 用 25 个服务 × 48 个 30 分钟桶的构造数据验证了上述行为尾部折叠test_service_breakdown_collapses_the_tail_into_one_other_bucket断言拆分值集合恰好是SPARKLINE_TOP_BREAKDOWN_VALUES 110 个自身 1 个 other且折叠不丢量——所有行的 count 总和等于25 × 48other 行的 count 恰好等于被折叠的 15 个服务的总和同时断言总行数 1000注释指出折叠前会是 49 桶 × 25 服务 1225 行超出 1000 行上限。count 与 bytes 排名差异test_rank_by_bytes_keeps_a_different_top_ten_than_rank_by_count中每个服务日志条数相同但service-024每条日志的字节数是其余服务的 100 万倍断言rank_bycount时该服务不在 Top 10会被埋进 other而rank_bybytes时它必须是独立序列且两种排名下bytes_uncompressed总和不变。基础正确性test_sparkline_single_log验证单条日志窗口返回 1 行、count 为 1test_sparkline_near_full验证完整窗口返回 49 个桶、count 总和为 900。这些测试与 MCP 工具的定位互为印证sparkline 被设计成“行数恒定有界、总量可核对”的概览查询无论时间范围多大响应规模都稳定在数百行以内。实战建议把它放进日志排障的第一步结合 tools.yaml 中工具族的分工一个典型的 Agent 排障路径是用logs-services-createTop 25 服务各带 log_count / error_count / error_rate 与 per-service sparkline确定值得关注的服务——它是官方推荐的“triaging which services are worth alerting on”的入口对目标服务用本文的logs-sparkline-query拉取按 severity默认或按 service 拆分的体积曲线确认异常窗口例如“最近一天 error/fatal 量”示例拿到异常时间窗后再用query-logs下钻具体日志条目或用logs-count-ranges/logs-facet-values做进一步切分若怀疑是周期性异常可结合logs-anomalies-scan基于最多 6 周历史学习基线验证。参数选择上记住三点即可复现文档全部行为参数一律包在query内不传dateRange时默认-1h不传sparklineBreakdownBy时默认按 severity 拆分、按 count 排名、Top 10 之外的值折叠进 other。这套约定与 REST 端点、序列化器默认值和 ClickHouse 聚合 SQL 的注释完全一致可以直接照此在 MCP 或 API 客户端中构造请求。【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthog创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考