feat(metrics): per-app observability dashboard over execution_logs (A2)
Aggregates the existing execution_logs table (no hot-path instrumentation):
ExecutionLogRepository::summarize_for_app returns counts, error rate, latency
avg/p50/p95 (percentile_cont), by-status/by-source breakdowns, and an hourly
series over a trailing window (clamped 1h..90d). New metrics_api router at
GET /api/v1/admin/apps/{id}/metrics?window= (AppLogRead), and a dashboard
Metrics tab (summary cards + an inline-SVG per-hour bar chart, no JS dep).
Pinned by a manager-core test asserting the exact rollup (percentiles included)
and the empty-app zero case.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,7 @@
|
||||
href: `${base}/apps/${slug}/workflows`,
|
||||
adminOnly: true
|
||||
},
|
||||
{ id: 'metrics', label: 'Metrics', href: `${base}/apps/${slug}/metrics`, adminOnly: true },
|
||||
{
|
||||
id: 'dead-letters',
|
||||
label: 'Dead letters',
|
||||
|
||||
@@ -218,6 +218,33 @@ export interface ExecutionLog {
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
/** A2: one `(key, count)` row of a metrics breakdown. */
|
||||
export interface MetricsCount {
|
||||
key: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
/** A2: one hour-bucket of the metrics time series. */
|
||||
export interface MetricsBucket {
|
||||
ts: string;
|
||||
total: number;
|
||||
errors: number;
|
||||
}
|
||||
|
||||
/** A2: the observability rollup for one app over a trailing window. */
|
||||
export interface MetricsSummary {
|
||||
window_hours: number;
|
||||
total: number;
|
||||
errors: number;
|
||||
error_rate: number;
|
||||
avg_duration_ms: number;
|
||||
p50_duration_ms: number;
|
||||
p95_duration_ms: number;
|
||||
by_status: MetricsCount[];
|
||||
by_source: MetricsCount[];
|
||||
series: MetricsBucket[];
|
||||
}
|
||||
|
||||
export interface CreateScriptInput {
|
||||
app_id: string;
|
||||
name: string;
|
||||
@@ -1169,6 +1196,14 @@ export const api = {
|
||||
)
|
||||
},
|
||||
|
||||
metrics: {
|
||||
/** A2: aggregate execution metrics for an app over the trailing window (hours). */
|
||||
summary: (idOrSlug: string, windowHours = 24) =>
|
||||
adminRequest<MetricsSummary>(
|
||||
`/api/v1/admin/apps/${encodeURIComponent(idOrSlug)}/metrics?window=${windowHours}`
|
||||
)
|
||||
},
|
||||
|
||||
deadLetters: {
|
||||
count: (idOrSlug: string) =>
|
||||
adminRequest<{ unresolved: number }>(
|
||||
|
||||
Reference in New Issue
Block a user