From 52bde64e7f7935dce02b8810973e3946dbfa412a Mon Sep 17 00:00:00 2001 From: Lukas Davidovic Date: Tue, 19 Aug 2025 14:56:42 +0200 Subject: [PATCH] comprehensive monitoring and logging system --- lib/monitoring.ts | 99 +++++++++++++++++++++++++++++++++++++++++++++ pages/api/health.ts | 17 ++++++++ 2 files changed, 116 insertions(+) create mode 100644 lib/monitoring.ts create mode 100644 pages/api/health.ts diff --git a/lib/monitoring.ts b/lib/monitoring.ts new file mode 100644 index 0000000..d2e2f3d --- /dev/null +++ b/lib/monitoring.ts @@ -0,0 +1,99 @@ +interface MonitoringMetric { + name: string + value: number + timestamp: number + tags?: Record +} + +class MetricsCollector { + private metrics: MonitoringMetric[] = [] + private maxMetrics = 1000 + + record(name: string, value: number, tags?: Record): void { + const metric: MonitoringMetric = { + name, + value, + timestamp: Date.now(), + tags + } + + this.metrics.push(metric) + + // Keep only recent metrics + if (this.metrics.length > this.maxMetrics) { + this.metrics = this.metrics.slice(-this.maxMetrics) + } + } + + getMetrics(name?: string, since?: number): MonitoringMetric[] { + let filtered = this.metrics + + if (name) { + filtered = filtered.filter(m => m.name === name) + } + + if (since) { + filtered = filtered.filter(m => m.timestamp >= since) + } + + return filtered + } + + getAverageResponseTime(since?: number): number { + const responseMetrics = this.getMetrics('api.response_time', since) + if (responseMetrics.length === 0) return 0 + + const sum = responseMetrics.reduce((acc, m) => acc + m.value, 0) + return sum / responseMetrics.length + } + + getRequestCount(endpoint?: string, since?: number): number { + const requestMetrics = this.getMetrics('api.request', since) + + if (endpoint) { + return requestMetrics.filter(m => m.tags?.endpoint === endpoint).length + } + + return requestMetrics.length + } + + getErrorCount(since?: number): number { + return this.getMetrics('api.error', since).length + } + + clear(): void { + this.metrics = [] + } +} + +export const metrics = new MetricsCollector() + +export function trackApiCall(endpoint: string, method: string, statusCode: number, responseTime: number): void { + metrics.record('api.request', 1, { endpoint, method, status: statusCode.toString() }) + metrics.record('api.response_time', responseTime, { endpoint }) + + if (statusCode >= 400) { + metrics.record('api.error', 1, { endpoint, status: statusCode.toString() }) + } +} + +export function trackDatabaseQuery(operation: string, duration: number): void { + metrics.record('db.query', 1, { operation }) + metrics.record('db.duration', duration, { operation }) +} + +export function getHealthStatus(): any { + const now = Date.now() + const fiveMinutesAgo = now - (5 * 60 * 1000) + + return { + status: 'healthy', + timestamp: new Date().toISOString(), + metrics: { + requests_5min: metrics.getRequestCount(undefined, fiveMinutesAgo), + avg_response_time: Math.round(metrics.getAverageResponseTime(fiveMinutesAgo)), + errors_5min: metrics.getErrorCount(fiveMinutesAgo), + uptime: process.uptime() + } + } +} \ No newline at end of file diff --git a/pages/api/health.ts b/pages/api/health.ts new file mode 100644 index 0000000..b436269 --- /dev/null +++ b/pages/api/health.ts @@ -0,0 +1,17 @@ +import type { NextApiRequest, NextApiResponse } from "next" +import { getHealthStatus } from "../../lib/monitoring" + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method !== "GET") return res.status(405).json({ error: "Method not allowed" }) + + try { + const health = getHealthStatus() + res.status(200).json(health) + } catch (error) { + res.status(500).json({ + status: 'unhealthy', + error: 'Health check failed', + timestamp: new Date().toISOString() + }) + } +} \ No newline at end of file