← back to Wine Finder Next
app/api/performance/metrics/route.ts
126 lines
import { NextRequest, NextResponse } from 'next/server';
import { performanceMonitor } from '@/lib/performance';
import { getRedisClient } from '@/lib/redis';
export async function GET(request: NextRequest) {
try {
// Get performance metrics
const metrics = performanceMonitor.getAllStats();
// Get Redis stats
const redis = getRedisClient();
let redisInfo: any = {};
try {
const info = await redis.info();
const lines = info.split('\r\n');
lines.forEach(line => {
if (line && !line.startsWith('#')) {
const [key, value] = line.split(':');
if (key && value) {
redisInfo[key] = value;
}
}
});
} catch (error) {
console.error('Failed to get Redis info:', error);
}
// Calculate cache hit rate
const cacheHits = parseInt(redisInfo.keyspace_hits || '0');
const cacheMisses = parseInt(redisInfo.keyspace_misses || '0');
const cacheHitRate = cacheHits + cacheMisses > 0
? (cacheHits / (cacheHits + cacheMisses) * 100).toFixed(2)
: '0';
// Memory usage
const processMemory = process.memoryUsage();
// Response time buckets
const responseTimeBuckets = {
fast: 0, // < 100ms
medium: 0, // 100-500ms
slow: 0, // > 500ms
};
// Analyze API response times
Object.entries(metrics).forEach(([key, stats]: [string, any]) => {
if (key.startsWith('api.') && stats) {
if (stats.median < 100) responseTimeBuckets.fast++;
else if (stats.median < 500) responseTimeBuckets.medium++;
else responseTimeBuckets.slow++;
}
});
const performanceData = {
timestamp: new Date().toISOString(),
metrics: {
api: metrics,
cache: {
hitRate: `${cacheHitRate}%`,
hits: cacheHits,
misses: cacheMisses,
keys: parseInt(redisInfo.db0?.split(',')[0]?.split('=')[1] || '0'),
memoryUsed: redisInfo.used_memory_human,
evictedKeys: parseInt(redisInfo.evicted_keys || '0'),
},
memory: {
rss: `${(processMemory.rss / 1024 / 1024).toFixed(2)} MB`,
heapUsed: `${(processMemory.heapUsed / 1024 / 1024).toFixed(2)} MB`,
heapTotal: `${(processMemory.heapTotal / 1024 / 1024).toFixed(2)} MB`,
external: `${(processMemory.external / 1024 / 1024).toFixed(2)} MB`,
},
responseTimeBuckets,
recommendations: generateRecommendations(metrics, redisInfo, cacheHitRate),
},
};
return NextResponse.json(performanceData);
} catch (error) {
console.error('Error fetching performance metrics:', error);
return NextResponse.json(
{ error: 'Failed to fetch performance metrics' },
{ status: 500 }
);
}
}
function generateRecommendations(metrics: any, redisInfo: any, cacheHitRate: string): string[] {
const recommendations = [];
// Cache hit rate recommendations
if (parseFloat(cacheHitRate) < 80) {
recommendations.push('Cache hit rate is below 80%. Consider increasing cache TTL or warming cache on startup.');
}
// Memory recommendations
const usedMemory = parseInt(redisInfo.used_memory || '0');
const maxMemory = parseInt(redisInfo.maxmemory || '0');
if (maxMemory > 0 && usedMemory / maxMemory > 0.9) {
recommendations.push('Redis memory usage is above 90%. Consider increasing max memory or optimizing cache keys.');
}
// API response time recommendations
let slowApis: string[] = [];
Object.entries(metrics).forEach(([key, stats]: [string, any]) => {
if (key.startsWith('api.') && stats && stats.p95 > 500) {
slowApis.push(key.replace('api.', ''));
}
});
if (slowApis.length > 0) {
recommendations.push(`APIs with slow p95 response times (>500ms): ${slowApis.join(', ')}. Consider optimizing these endpoints.`);
}
// Eviction recommendations
if (parseInt(redisInfo.evicted_keys || '0') > 100) {
recommendations.push('High number of evicted keys detected. Consider increasing Redis memory limit.');
}
if (recommendations.length === 0) {
recommendations.push('Performance metrics look good! No immediate optimizations needed.');
}
return recommendations;
}