setup-monitoring
Access: /template setup-monitoring or /t setup-monitoring
Configures comprehensive monitoring, error tracking, and performance monitoring for your MakerKit application using industry-standard tools.
What It Does
The setup-monitoring command helps you:
- Set up error tracking with Sentry
- Configure performance monitoring
- Implement custom metrics and logging
- Add health check endpoints
- Create monitoring dashboards
- Set up alerting rules
Usage
bash
/template setup-monitoring "Description"
# or
/t setup-monitoring "Description"When prompted, specify:
- Monitoring services to configure (Sentry, LogRocket, etc.)
- Performance monitoring requirements
- Custom metrics to track
- Alert thresholds
Prerequisites
- A MakerKit project deployed or ready for deployment
- Monitoring service accounts (Sentry, etc.)
- Commercial MakerKit license from MakerKit
What Gets Created
1. Sentry Configuration
Error tracking setup:
typescript
// lib/monitoring/sentry.ts
import * as Sentry from '@sentry/nextjs';
export function initSentry() {
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
environment: process.env.NODE_ENV,
// Performance Monitoring
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
// Session Replay
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
// Release tracking
release: process.env.NEXT_PUBLIC_APP_VERSION,
// Integrations
integrations: [
new Sentry.BrowserTracing({
tracingOrigins: ['localhost', /^https:\/\/yourapp\.com/],
routingInstrumentation: Sentry.nextRouterInstrumentation,
}),
new Sentry.Replay({
maskAllText: false,
blockAllMedia: false,
}),
],
// Filtering
beforeSend(event, hint) {
// Filter out non-critical errors
if (event.exception) {
const error = hint.originalException;
// Ignore certain errors
if (error?.message?.includes('ResizeObserver')) {
return null;
}
}
// Add user context
if (event.user) {
event.user = {
...event.user,
ip_address: '{{auto}}',
};
}
return event;
},
});
}
// Error boundary wrapper
export function withErrorBoundary<P extends object>(
Component: React.ComponentType<P>,
fallback?: React.ComponentType<any>
) {
return Sentry.withErrorBoundary(Component, {
fallback: fallback || ErrorFallback,
showDialog: process.env.NODE_ENV === 'production',
});
}2. Performance Monitoring
Track key metrics:
typescript
// lib/monitoring/performance.ts
import { metrics } from '@opentelemetry/api-metrics';
const meter = metrics.getMeter('makerkit-app');
// Custom metrics
export const apiLatency = meter.createHistogram('api_latency', {
description: 'API endpoint latency',
unit: 'ms',
});
export const dbQueryDuration = meter.createHistogram('db_query_duration', {
description: 'Database query duration',
unit: 'ms',
});
export const activeUsers = meter.createUpDownCounter('active_users', {
description: 'Currently active users',
});
// Measure API performance
export function measureApiPerformance(
endpoint: string,
method: string
) {
const startTime = performance.now();
return {
end: (statusCode: number) => {
const duration = performance.now() - startTime;
apiLatency.record(duration, {
endpoint,
method,
status_code: statusCode.toString(),
});
// Log slow requests
if (duration > 1000) {
console.warn(`Slow API request: ${method} ${endpoint} took ${duration}ms`);
}
},
};
}
// Database query tracking
export function trackDatabaseQuery(
query: string,
duration: number
) {
dbQueryDuration.record(duration, {
query_type: getQueryType(query),
});
}3. Custom Logger
Structured logging:
typescript
// lib/monitoring/logger.ts
import pino from 'pino';
import { logflarePinoVercel } from 'pino-logflare';
// Create logger based on environment
const createLogger = () => {
if (process.env.NODE_ENV === 'production') {
const { stream, send } = logflarePinoVercel({
apiKey: process.env.LOGFLARE_API_KEY!,
sourceToken: process.env.LOGFLARE_SOURCE_TOKEN!,
});
return pino(
{
level: 'info',
base: {
env: process.env.NODE_ENV,
revision: process.env.VERCEL_GITHUB_COMMIT_SHA,
},
},
stream
);
}
return pino({
level: 'debug',
transport: {
target: 'pino-pretty',
options: {
colorize: true,
},
},
});
};
export const logger = createLogger();
// Typed logger methods
export const log = {
info: (msg: string, data?: any) => logger.info(data, msg),
error: (msg: string, error?: any) => logger.error(error, msg),
warn: (msg: string, data?: any) => logger.warn(data, msg),
debug: (msg: string, data?: any) => logger.debug(data, msg),
};
// Request logging middleware
export function requestLogger(req: Request) {
const url = new URL(req.url);
log.info('Incoming request', {
method: req.method,
path: url.pathname,
query: Object.fromEntries(url.searchParams),
headers: Object.fromEntries(req.headers),
});
}4. Health Check Endpoints
Monitor service health:
typescript
// app/api/health/route.ts
import { NextResponse } from 'next/server';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { redis } from '@/lib/redis';
export async function GET() {
const checks = {
status: 'healthy',
timestamp: new Date().toISOString(),
version: process.env.NEXT_PUBLIC_APP_VERSION,
checks: {} as Record<string, any>,
};
// Database check
try {
const client = getSupabaseServerClient();
const { error } = await client.from('users').select('count').limit(1);
checks.checks.database = {
status: error ? 'unhealthy' : 'healthy',
error: error?.message,
};
} catch (error) {
checks.checks.database = {
status: 'unhealthy',
error: error.message,
};
}
// Redis check
try {
await redis.ping();
checks.checks.redis = { status: 'healthy' };
} catch (error) {
checks.checks.redis = {
status: 'unhealthy',
error: error.message,
};
}
// Stripe check
try {
const stripe = getStripeInstance();
await stripe.products.list({ limit: 1 });
checks.checks.stripe = { status: 'healthy' };
} catch (error) {
checks.checks.stripe = {
status: 'unhealthy',
error: error.message,
};
}
// Overall status
const hasUnhealthy = Object.values(checks.checks).some(
(check: any) => check.status === 'unhealthy'
);
if (hasUnhealthy) {
checks.status = 'degraded';
}
return NextResponse.json(checks, {
status: hasUnhealthy ? 503 : 200,
});
}5. Client-Side Monitoring
Track user interactions:
typescript
// lib/monitoring/client-monitoring.ts
import { useEffect } from 'react';
import { usePathname } from 'next/navigation';
import * as Sentry from '@sentry/nextjs';
// Page view tracking
export function usePageTracking() {
const pathname = usePathname();
useEffect(() => {
// Track page view
if (window.gtag) {
window.gtag('config', process.env.NEXT_PUBLIC_GA_ID, {
page_path: pathname,
});
}
// Track in Sentry
Sentry.addBreadcrumb({
type: 'navigation',
category: 'pageview',
message: `Viewed ${pathname}`,
level: 'info',
});
}, [pathname]);
}
// Performance observer
export function usePerformanceMonitoring() {
useEffect(() => {
// Core Web Vitals
if ('web-vital' in window) {
const { getCLS, getFID, getLCP, getFCP, getTTFB } = window['web-vital'];
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getLCP(sendToAnalytics);
getFCP(sendToAnalytics);
getTTFB(sendToAnalytics);
}
// Long tasks
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log tasks longer than 50ms
if (entry.duration > 50) {
Sentry.addBreadcrumb({
category: 'performance',
message: `Long task detected: ${entry.duration}ms`,
level: 'warning',
});
}
}
});
observer.observe({ entryTypes: ['longtask'] });
return () => observer.disconnect();
}
}, []);
}6. Monitoring Dashboard
Admin monitoring view:
typescript
// app/admin/monitoring/page.tsx
export default function MonitoringDashboard() {
const { data: metrics } = useSystemMetrics();
const { data: errors } = useRecentErrors();
return (
<div className="space-y-6">
<SystemHealthCard health={metrics?.health} />
<div className="grid gap-6 md:grid-cols-2">
<Card>
<CardHeader>
<CardTitle>Response Times</CardTitle>
</CardHeader>
<CardContent>
<ResponseTimeChart data={metrics?.responseTime} />
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Error Rate</CardTitle>
</CardHeader>
<CardContent>
<ErrorRateChart data={metrics?.errorRate} />
</CardContent>
</Card>
</div>
<Card>
<CardHeader>
<CardTitle>Recent Errors</CardTitle>
</CardHeader>
<CardContent>
<ErrorsList errors={errors} />
</CardContent>
</Card>
</div>
);
}7. Alert Configuration
Set up monitoring alerts:
typescript
// lib/monitoring/alerts.ts
interface AlertRule {
name: string;
condition: (metrics: SystemMetrics) => boolean;
message: string;
severity: 'info' | 'warning' | 'critical';
}
export const alertRules: AlertRule[] = [
{
name: 'high_error_rate',
condition: (metrics) => metrics.errorRate > 0.05, // 5%
message: 'Error rate exceeds 5%',
severity: 'critical',
},
{
name: 'slow_response_time',
condition: (metrics) => metrics.p95ResponseTime > 2000, // 2s
message: 'P95 response time exceeds 2 seconds',
severity: 'warning',
},
{
name: 'database_connection_issues',
condition: (metrics) => metrics.dbErrors > 10,
message: 'Multiple database connection errors',
severity: 'critical',
},
];
// Check alerts and notify
export async function checkAlerts() {
const metrics = await getSystemMetrics();
for (const rule of alertRules) {
if (rule.condition(metrics)) {
await sendAlert({
rule: rule.name,
message: rule.message,
severity: rule.severity,
metrics,
});
}
}
}8. Custom Metrics Dashboard
Business metrics tracking:
typescript
// components/monitoring/business-metrics.tsx
export function BusinessMetrics() {
const metrics = useBusinessMetrics();
return (
<div className="grid gap-4 md:grid-cols-4">
<MetricCard
title="Daily Active Users"
value={metrics.dau}
change={metrics.dauChange}
format="number"
/>
<MetricCard
title="Conversion Rate"
value={metrics.conversionRate}
change={metrics.conversionChange}
format="percentage"
/>
<MetricCard
title="Average Session Duration"
value={metrics.avgSessionDuration}
change={metrics.sessionChange}
format="duration"
/>
<MetricCard
title="Churn Rate"
value={metrics.churnRate}
change={metrics.churnChange}
format="percentage"
inverse // Lower is better
/>
</div>
);
}Environment Variables
Required monitoring configuration:
bash
# Sentry
NEXT_PUBLIC_SENTRY_DSN=
SENTRY_ORG=
SENTRY_PROJECT=
SENTRY_AUTH_TOKEN=
# Logging
LOGFLARE_API_KEY=
LOGFLARE_SOURCE_TOKEN=
# Analytics
NEXT_PUBLIC_GA_ID=
NEXT_PUBLIC_MIXPANEL_TOKEN=
# Monitoring
DATADOG_API_KEY=
NEW_RELIC_LICENSE_KEY=Testing Monitoring
typescript
describe('Monitoring', () => {
it('captures errors correctly', async () => {
const testError = new Error('Test error');
Sentry.captureException(testError);
// Verify error was sent
expect(Sentry.getCurrentHub().getClient()).toBeDefined();
});
it('tracks performance metrics', async () => {
const metric = measureApiPerformance('/api/test', 'GET');
await delay(100);
metric.end(200);
// Verify metric was recorded
expect(apiLatency.record).toHaveBeenCalled();
});
});Best Practices
- Sample appropriately: Don't track 100% in production
- Filter noise: Ignore non-actionable errors
- Set budgets: Define performance budgets
- Alert wisely: Avoid alert fatigue
- Document metrics: Explain what each metric means
License Requirement
Important: This command requires a commercial MakerKit license from https://makerkit.dev?atp=MqaGgc MakerKit is a premium SaaS starter kit and requires proper licensing for commercial use.
