Demostración de observabilidad de SaaS de extremo a extremo
Esta demostración envía un conjunto determinista de eventos de flujo de trabajo SaaS sintéticos a Telemetry y los consulta con SQL. Es intencionalmente lo suficientemente pequeño como para inspeccionarlo de una sola vez y al mismo tiempo conectar la confiabilidad, los resultados del producto, el trabajo en segundo plano y el costo de la IA.
La muestra no utiliza tráfico de producción ni datos de clientes. Cada ejecución tiene un run_id único, por lo que su consulta puede aislar las filas que creó.
Lo que prueba la demostración
Un contrato de evento amplio puede responder varias preguntas:
- ¿Se completó el flujo de trabajo?
- ¿Qué paso falló o se volvió a intentar?
- ¿Cuánto tiempo tomó cada paso?
- ¿Cuánto costo estimado de IA generó el flujo de trabajo?
- ¿Qué plan de cuenta y versión se vieron afectados?
El código utiliza los puntos finales públicos HTTP directamente, por lo que no hay ningún marco o abstracción SDK entre el evento, la solicitud API y el resultado SQL.
Requisitos previos
Necesita Node.js 20 o posterior y una clave Telemetry API. Exporte la clave solo en el shell que ejecutará el ejemplo:
export TELEMETRY_API_KEY="YOUR_API_KEY"
El repositorio también mantiene la fuente ejecutable en examples/saas-observability-demo. El programa completo se muestra a continuación por lo que el contrato de datos y la consulta permanecen visibles en esta página.
El programa completo
Guarde esto como 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));
Ejecútelo:
node demo.mjs
La forma esperada es una fila de resumen:
{
"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
}
]
}
La codificación numérica exacta en la respuesta JSON puede variar según la serialización del resultado de la consulta. Trate la forma y el significado como el contrato.
Inspeccionar la línea de tiempo sin procesar
Un agregado le indica que el flujo de trabajo tuvo un paso fallido. Una línea de tiempo correlacionada le indica qué paso falló y qué sucedió a su alrededor:
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;
El run_id actúa como un identificador de correlación de flujo de trabajo. En una aplicación real, utilice un identificador estable creado en el límite del flujo de trabajo y páselo a través del controlador API, la carga útil de la cola, el trabajo, el webhook y la llamada de IA.
Construya tres vistas desde el mismo contrato
Fiabilidad
Grafique los pasos fallidos y la duración máxima por release o region. Alertar sólo después de definir un volumen mínimo y la respuesta esperada del equipo.
Finalización del producto
Cuente los distintos identificadores de flujo de trabajo que alcanzaron el evento terminal esperado. No cuente las filas como flujos de trabajo completados cuando un flujo de trabajo puede emitir varios pasos.
Costo y valor
Sume estimated_cost_usd para los pasos de IA y únalos o correlacionelos con un resultado posterior, como activación, salida aceptada o finalización exitosa del flujo de trabajo. Mantenga los precios del modelo en la configuración versionada y concilie las estimaciones con la factura del proveedor.
Cambios de producción a realizar.
La demostración favorece la visibilidad sobre la abstracción. Una implementación de producción debería:
- crear eventos en límites de operación reales en lugar de en una matriz de demostración;
- utilizar un esquema acotado y normalizar los valores de ruta, error, plan y liberación;
- evite interpolar entradas que no sean de confianza en SQL;
- mantenga las claves API en el almacén secreto del lado del servidor;
- eventos por lotes o en búfer sin ocultar fallas permanentes;
- definir los requisitos de retención y eliminación;
- registrar una versión del esquema cuando los productores evolucionen de forma independiente;
- medir si la entrega del evento en sí está fallando.
Utilice Registro estructurado para instrumentación, SQL para observabilidad para el modelo de análisis y Laboratorio conectado SQL para un conjunto de datos más grande de seis tablas con resultados visuales.