mirror of
https://github.com/Dvorinka/Primora.git
synced 2026-10-28 15:51:23 +00:00
feat: phase 3 observability — IMS telemetry merge
Port IMS telemetry into Primora conventions: - core.components/core.events (migration 00004 + sqlc) scoped to projects - POST /api/v1/ingest — single or <=500-event batches via project prm_ keys (X-Primora-Key alias), auto-registering components, permissive CORS - Issues: fingerprint-grouped errors with count/severity/first+last seen - Reads: events w/ filters + before-cursor, components, stats (1h/24h/7d/30d bucketed series + component health + totals + metric names), metric series (avg/p50/p95/max) - GET .../telemetry/stream — project-scoped SSE through an in-process hub (buffered subs, drop-on-slow, 25s keepalives); ?api_key=/?token= auth for EventSource - Telemetry page: health/issues/events/metrics/integrate tabs, live badge, 390px-clean; demo stubs - @primora/client SDK (createPrimoraClient: capture*/heartbeat/installAuto, queued keepalive flushes) - MCP: primora_list_issues/list_events/telemetry_stats/metric_series - IMS README deprecation note pointing here Also fixes workspace pins to 0.3.0 — npm ci in the frontend image was resolving the unpublished 0.2.0 spec from the registry. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
6507eee606
commit
8cfddf9184
60 files changed
+3694
-30
No files matched your search
@@ -0,0 +1,102 @@
|
||||
-- name: UpsertComponent :one
|
||||
INSERT INTO core.components (project_id, name, kind)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (project_id, name) DO UPDATE SET name = EXCLUDED.name
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetComponentByName :one
|
||||
SELECT * FROM core.components
|
||||
WHERE project_id = $1 AND name = $2;
|
||||
|
||||
-- name: ListComponents :many
|
||||
SELECT * FROM core.components
|
||||
WHERE project_id = $1
|
||||
ORDER BY name;
|
||||
|
||||
-- name: DeleteComponent :exec
|
||||
DELETE FROM core.components
|
||||
WHERE id = $1 AND project_id = $2;
|
||||
|
||||
-- name: InsertEvent :one
|
||||
INSERT INTO core.events (project_id, component_id, type, severity, message, payload, fingerprint, ts)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
RETURNING *;
|
||||
|
||||
-- name: ListEvents :many
|
||||
SELECT e.*, co.name AS component_name
|
||||
FROM core.events e
|
||||
LEFT JOIN core.components co ON co.id = e.component_id
|
||||
WHERE e.project_id = $1
|
||||
AND (sqlc.narg('type')::text IS NULL OR e.type = sqlc.narg('type'))
|
||||
AND (sqlc.narg('component')::text IS NULL OR co.name = sqlc.narg('component'))
|
||||
AND (sqlc.narg('fingerprint')::text IS NULL OR e.fingerprint = sqlc.narg('fingerprint'))
|
||||
AND (sqlc.narg('before')::bigint IS NULL OR e.id < sqlc.narg('before'))
|
||||
ORDER BY e.id DESC
|
||||
LIMIT sqlc.arg('limit');
|
||||
|
||||
-- name: ListErrorGroups :many
|
||||
SELECT e.fingerprint,
|
||||
(array_agg(e.message ORDER BY e.ts DESC))[1]::text AS message,
|
||||
coalesce((array_agg(e.component_id::text ORDER BY e.ts DESC))[1], '')::text AS component_id,
|
||||
coalesce((array_agg(co.name ORDER BY e.ts DESC))[1], '')::text AS component_name,
|
||||
count(*) AS count,
|
||||
min(e.ts)::timestamptz AS first_seen,
|
||||
max(e.ts)::timestamptz AS last_seen,
|
||||
(array_agg(e.severity ORDER BY e.ts DESC))[1]::text AS severity
|
||||
FROM core.events e
|
||||
LEFT JOIN core.components co ON co.id = e.component_id
|
||||
WHERE e.project_id = $1 AND e.type = 'error' AND e.fingerprint <> ''
|
||||
AND e.ts > now() - (sqlc.narg('days')::text || ' days')::interval
|
||||
GROUP BY e.fingerprint
|
||||
ORDER BY max(e.ts) DESC
|
||||
LIMIT 200;
|
||||
|
||||
-- name: EventTypeSeries :many
|
||||
SELECT to_timestamp(floor(extract(epoch FROM e.ts) / sqlc.arg('bucket_sec')::bigint) * sqlc.arg('bucket_sec')::bigint)::timestamptz AS bucket,
|
||||
e.type,
|
||||
count(*) AS count
|
||||
FROM core.events e
|
||||
WHERE e.project_id = $1 AND e.ts > now() - make_interval(secs => sqlc.arg('window_sec')::bigint)
|
||||
GROUP BY bucket, e.type
|
||||
ORDER BY bucket;
|
||||
|
||||
-- name: ComponentHealthRows :many
|
||||
SELECT co.id, co.name, co.kind, co.meta,
|
||||
max(e.ts)::timestamptz AS last_seen_at,
|
||||
coalesce(((array_agg(e.payload->>'status' ORDER BY e.ts DESC)
|
||||
FILTER (WHERE e.type = 'heartbeat'))[1]), '')::text AS last_status,
|
||||
count(*) FILTER (WHERE e.type = 'error' AND e.ts > now() - interval '24 hours') AS errors_24h,
|
||||
count(*) FILTER (WHERE e.ts > now() - interval '24 hours') AS events_24h
|
||||
FROM core.components co
|
||||
LEFT JOIN core.events e ON e.component_id = co.id
|
||||
WHERE co.project_id = $1
|
||||
GROUP BY co.id
|
||||
ORDER BY co.name;
|
||||
|
||||
-- name: EventTotals :one
|
||||
SELECT count(*) FILTER (WHERE type = 'error') AS errors,
|
||||
count(*) AS events,
|
||||
count(*) FILTER (WHERE type = 'metric') AS metrics
|
||||
FROM core.events
|
||||
WHERE project_id = $1 AND ts > now() - make_interval(secs => sqlc.arg('window_sec')::bigint);
|
||||
|
||||
-- name: MetricNames :many
|
||||
SELECT DISTINCT (payload->>'name')::text AS name
|
||||
FROM core.events
|
||||
WHERE project_id = $1 AND type = 'metric' AND payload ? 'name'
|
||||
ORDER BY 1
|
||||
LIMIT 50;
|
||||
|
||||
-- name: MetricSeries :many
|
||||
SELECT to_timestamp(floor(extract(epoch FROM ts) / sqlc.arg('bucket_sec')::bigint) * sqlc.arg('bucket_sec')::bigint)::timestamptz AS bucket,
|
||||
avg((payload->>'value')::float8)::float8 AS avg,
|
||||
percentile_cont(0.5) WITHIN GROUP (ORDER BY (payload->>'value')::float8)::float8 AS p50,
|
||||
percentile_cont(0.95) WITHIN GROUP (ORDER BY (payload->>'value')::float8)::float8 AS p95,
|
||||
max((payload->>'value')::float8)::float8 AS max,
|
||||
count(*) AS count
|
||||
FROM core.events
|
||||
WHERE project_id = $1 AND type = 'metric' AND payload->>'name' = sqlc.arg('name')::text
|
||||
AND ts > now() - make_interval(secs => sqlc.arg('window_sec')::bigint)
|
||||
AND jsonb_typeof(payload->'value') = 'number'
|
||||
GROUP BY bucket
|
||||
ORDER BY bucket;
|
||||
Reference in new issue
Block a user