Overview
Monitor application health with health checks, track errors with structured logging, and use Vercel Analytics for performance insights.
What it is
Health check endpoints, Vercel Analytics integration, error tracking via logging, and alerting patterns.
Why we use it
Detect issues before users report them, understand performance bottlenecks, and respond quickly to incidents.
When to use
Always. Every production application needs monitoring. Set up health checks and alerting from day one.
Key Features
- Health check endpoints for uptime monitoring
- Vercel Analytics and Speed Insights
- Error tracking via structured logging
- Alerting patterns for critical issues
Quick Start
Health Check Endpoint
Create an endpoint that verifies all dependencies are healthy.
// Health check endpoint
// src/app/api/health/route.ts
import { NextResponse } from 'next/server';
import { prisma } from '@/lib/db';
import { logger } from '@/lib/logger';
export async function GET() {
try {
// Check database connection
await prisma.$queryRaw`SELECT 1`;
return NextResponse.json({
ok: true,
status: 'healthy',
timestamp: new Date().toISOString(),
});
} catch (error) {
logger.error('Health check failed', { error });
return NextResponse.json(
{ ok: false, status: 'unhealthy' },
{ status: 503 }
);
}
}Patterns
Vercel Analytics
Add Analytics and Speed Insights to your app.
// Vercel Analytics setup
// src/app/layout.tsx
import { Analytics } from '@vercel/analytics/react';
import { SpeedInsights } from '@vercel/speed-insights/next';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
<SpeedInsights />
</body>
</html>
);
}Error Tracking
Capture and log errors with context.
// Error tracking with logging
// src/lib/errors/handle-error.ts
import { logger } from '@/lib/logger';
import { captureException } from '@/lib/monitoring';
export function handleError(error: Error) {
// Log locally
logger.error('API error', {
message: error.message,
stack: error.stack,
name: error.name,
});
// Send to external service (if configured)
captureException(error);
return NextResponse.json(
{ ok: false, error: formatError(error) },
{ status: getStatusCode(error) }
);
}Performance Monitoring
Track operation duration and alert on slowness.
// Performance tracking
import { logger } from '@/lib/logger';
export async function GET(request: Request) {
const start = performance.now();
try {
const result = await expensiveOperation();
const duration = performance.now() - start;
logger.info('Operation completed', {
duration,
operation: 'expensiveOperation',
});
// Alert if slow
if (duration > 5000) {
logger.warn('Slow operation detected', { duration });
}
return NextResponse.json({ ok: true, data: result });
} catch (error) {
const duration = performance.now() - start;
logger.error('Operation failed', { duration, error });
throw error;
}
}Alerting Patterns
Set severity levels for automated alerting.
// Alerting patterns
// Use structured logging for alerting rules
// Critical: Immediate attention needed
logger.error('Database connection failed', {
severity: 'critical',
alert: true,
});
// Warning: Investigate soon
logger.warn('Rate limit threshold approaching', {
current: 85,
threshold: 100,
alert: true,
});
// Info: For dashboards
logger.info('Daily report generated', {
recordCount: 1500,
duration: 3200,
});Watch Out
Swallowing errors without logging
Don't
// Silent failure
try {
await riskyOperation();
} catch (error) {
// Error silently swallowed!
return { data: null };
}Do
// Logged failure with context
try {
await riskyOperation();
} catch (error) {
logger.error('Risky operation failed', {
error,
context: { userId, action },
});
throw new InternalError('Operation failed');
}No health check endpoint for uptime monitoring
Don't
// No health check // Vercel can't tell if your app is healthy // Unhealthy pods stay in rotation
Do
// Comprehensive health check
export async function GET() {
const checks = {
database: await checkDatabase(),
redis: await checkRedis(),
external: await checkExternalAPI(),
};
const healthy = Object.values(checks).every(c => c.ok);
return NextResponse.json(
{ ok: healthy, checks },
{ status: healthy ? 200 : 503 }
);
}- Too many alerts causing important ones to be ignored
- No visibility into system health