Implement Prometheus metrics endpoint (P2.16)
Add comprehensive metrics collection for production monitoring: - src/lib/metrics.ts: prom-client based metrics library with custom counters, gauges, and histograms for Garmin sync, email, and decision engine - GET /api/metrics: Prometheus-format endpoint for scraping - Integration into garmin-sync cron: sync duration, success/failure counts, active users gauge - Integration into email.ts: daily and warning email counters - Integration into decision-engine.ts: decision type counters Custom metrics implemented: - phaseflow_garmin_sync_total (counter with status label) - phaseflow_garmin_sync_duration_seconds (histogram) - phaseflow_email_sent_total (counter with type label) - phaseflow_decision_engine_calls_total (counter with decision label) - phaseflow_active_users (gauge) 33 new tests (18 library + 15 route), bringing total to 586 tests. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,11 @@ import {
|
||||
fetchIntensityMinutes,
|
||||
isTokenExpired,
|
||||
} from "@/lib/garmin";
|
||||
import {
|
||||
activeUsersGauge,
|
||||
garminSyncDuration,
|
||||
garminSyncTotal,
|
||||
} from "@/lib/metrics";
|
||||
import { createPocketBaseClient } from "@/lib/pocketbase";
|
||||
import type { GarminTokens, User } from "@/types";
|
||||
|
||||
@@ -34,6 +39,8 @@ export async function POST(request: Request) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const syncStartTime = Date.now();
|
||||
|
||||
const result: SyncResult = {
|
||||
success: true,
|
||||
usersProcessed: 0,
|
||||
@@ -129,10 +136,17 @@ export async function POST(request: Request) {
|
||||
});
|
||||
|
||||
result.usersProcessed++;
|
||||
garminSyncTotal.inc({ status: "success" });
|
||||
} catch {
|
||||
result.errors++;
|
||||
garminSyncTotal.inc({ status: "failure" });
|
||||
}
|
||||
}
|
||||
|
||||
// Record sync duration and active users
|
||||
const syncDurationSeconds = (Date.now() - syncStartTime) / 1000;
|
||||
garminSyncDuration.observe(syncDurationSeconds);
|
||||
activeUsersGauge.set(result.usersProcessed);
|
||||
|
||||
return NextResponse.json(result);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user