端到端 SaaS 可观测性演示
该演示将一组确定性的合成 SaaS 工作流事件发送到 Telemetry,并使用 SQL 查询它们。它故意设计得足够小,可以一次性检查,同时仍然连接可靠性、产品结果、后台工作和人工智能成本。
该示例不使用生产流量或客户数据。每次运行都有一个唯一的 run_id,因此它的查询可以隔离它创建的行。
演示证明了什么
一份广泛的事件合同可以回答几个问题:
- 工作流程完成了吗?
- 哪一步失败或重试?
- 每个步骤花了多长时间?
- 该工作流程产生的 AI 预估成本是多少?
- 哪些帐户计划和版本受到影响?
该代码直接使用公共 HTTP 端点,因此事件、API 请求和 SQL 结果之间没有框架或 SDK 抽象。
先决条件
您需要 Node.js 20 或更高版本以及 Telemetry API 密钥。仅在将运行示例的 shell 中导出密钥:
export TELEMETRY_API_KEY="YOUR_API_KEY"
该存储库还将可运行源保留在 examples/saas-observability-demo 中。完整的程序如下所示,因此数据协定和查询在此页面上仍然可见。
完整的程序
将其另存为 demo.mjs:
import { randomUUID } from "node:crypto";
const apiKey = process.env.TELEMETRY_API_KEY;
if (!apiKey) {
throw new Error("TELEMETRY_API_KEY is required to run this demo");
}
const apiOrigin = process.env.TELEMETRY_API_ORIGIN || "https://api.telemetry.sh";
const runId = randomUUID();
const table = "saas_observability_demo";
const base = {
run_id: runId,
account_id: "synthetic_acme",
plan: "growth",
release: "demo-2026.07",
region: "us-west",
};
const events = [
{
...base,
event_name: "checkout_started",
workflow: "subscription_checkout",
step: "checkout",
outcome: "started",
duration_ms: 18,
retry_count: 0,
estimated_cost_usd: 0,
},
{
...base,
event_name: "payment_authorized",
workflow: "subscription_checkout",
step: "payment",
outcome: "success",
duration_ms: 284,
retry_count: 0,
estimated_cost_usd: 0,
},
{
...base,
event_name: "invoice_job_completed",
workflow: "subscription_checkout",
step: "invoice_job",
outcome: "success",
duration_ms: 618,
retry_count: 1,
estimated_cost_usd: 0,
},
{
...base,
event_name: "welcome_email_completed",
workflow: "subscription_checkout",
step: "welcome_email",
outcome: "failed",
duration_ms: 910,
retry_count: 2,
error_type: "provider_timeout",
estimated_cost_usd: 0,
},
{
...base,
event_name: "ai_summary_completed",
workflow: "subscription_checkout",
step: "ai_summary",
outcome: "success",
duration_ms: 742,
retry_count: 0,
model: "configured-demo-model",
input_tokens: 820,
output_tokens: 146,
estimated_cost_usd: 0.0042,
},
];
const ingestResponse = await fetch(`${apiOrigin}/log`, {
method: "POST",
headers: {
Authorization: apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify({ table, data: events }),
});
if (!ingestResponse.ok) {
throw new Error(
`Ingest failed: ${ingestResponse.status} ${await ingestResponse.text()}`
);
}
const sql = `
SELECT
workflow,
COUNT(*) AS event_count,
SUM(CASE WHEN outcome = 'failed' THEN 1 ELSE 0 END) AS failed_steps,
SUM(retry_count) AS retries,
SUM(estimated_cost_usd) AS estimated_cost_usd,
MAX(duration_ms) AS slowest_step_ms
FROM ${table}
WHERE run_id = '${runId}'
GROUP BY workflow
ORDER BY workflow
`;
const queryResponse = await fetch(`${apiOrigin}/query`, {
method: "POST",
headers: {
Authorization: apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify({ query: sql, realtime: true, json: true }),
});
if (!queryResponse.ok) {
throw new Error(
`Query failed: ${queryResponse.status} ${await queryResponse.text()}`
);
}
const result = await queryResponse.json();
console.log(JSON.stringify({ run_id: runId, rows: result.data }, null, 2));
运行它:
node demo.mjs
预期的形状是一个汇总行:
{
"run_id": "generated-for-this-run",
"rows": [
{
"workflow": "subscription_checkout",
"event_count": 5,
"failed_steps": 1,
"retries": 3,
"estimated_cost_usd": 0.0042,
"slowest_step_ms": 910
}
]
}
JSON 响应中的确切数字编码可能因查询结果序列化而异。以形义为契约。
检查原始时间线
聚合告诉您工作流程有一个失败的步骤。相关的时间线会告诉您哪个步骤失败了以及周围发生了什么:
SELECT
timestamp_utc,
event_name,
step,
outcome,
duration_ms,
retry_count,
error_type
FROM saas_observability_demo
WHERE run_id = 'PASTE_RUN_ID'
ORDER BY timestamp_utc ASC;
run_id 的作用类似于工作流关联标识符。在实际应用程序中,使用在工作流边界创建的稳定标识符,并将其传递到 API 处理程序、队列有效负载、作业、Webhook 和 AI 调用。
从同一个合约构建三个视图
可靠性
按 release 或 region 绘制失败步骤和最大持续时间的图表。仅在定义最小量和团队期望的响应后发出告警。
产品完成
计算到达预期终端事件的不同工作流标识符。当一个工作流程可以发出多个步骤时,请勿将行计为已完成的工作流程。
成本和价值
对 AI 步骤求和 estimated_cost_usd 并将其加入或关联到后续结果,例如激活、接受的输出或成功的工作流程完成。将模型定价保留在版本化配置中,并将估算值与提供商发票进行核对。
生产变更
该演示更注重可见性而不是抽象性。生产实施应该:
- 在实际操作边界而不是在演示数组中创建事件;
- 使用有界模式并标准化路线、错误、计划和发布值;
- 避免将不受信任的输入插入到 SQL 中;
- 将 API 密钥保存在服务器端秘密存储中;
- 批量或缓冲事件而不隐藏永久性故障;
- 定义保留和删除要求;
- 记录生产者独立演化时的模式版本;
- 衡量事件传递本身是否失败。
使用 结构化日志记录 进行检测,使用 SQL 用于可观测性 进行分析模型,使用 连接SQL实验室 进行更大的六表数据集的可视化结果。