monitoring-setup
Expert guide for setting up monitoring dashboards, alerting, metrics collection, and observability. Use when implementing application monitoring, setting up alerts, or building dashboards.
What this skill does
# Monitoring Setup Skill
## Overview
This skill helps you implement comprehensive monitoring for applications. Covers metrics collection, dashboard creation, alerting strategies, health checks, and observability best practices.
## Monitoring Philosophy
### Four Golden Signals
1. **Latency**: Time to serve a request
2. **Traffic**: Request volume
3. **Errors**: Failed request rate
4. **Saturation**: Resource utilization
### Observability Pillars
- **Metrics**: Numeric measurements over time
- **Logs**: Discrete events with context
- **Traces**: Request flow across services
## Health Check Endpoints
### Comprehensive Health Check
```typescript
// src/app/api/health/route.ts
import { NextResponse } from 'next/server';
import { createClient } from '@supabase/supabase-js';
import Redis from 'ioredis';
interface HealthCheck {
status: 'healthy' | 'degraded' | 'unhealthy';
timestamp: string;
version: string;
uptime: number;
checks: {
database: CheckResult;
redis: CheckResult;
external: CheckResult;
};
}
interface CheckResult {
status: 'pass' | 'fail';
latency?: number;
message?: string;
}
async function checkDatabase(): Promise<CheckResult> {
const start = Date.now();
try {
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
);
await supabase.from('health_check').select('1').single();
return {
status: 'pass',
latency: Date.now() - start,
};
} catch (error) {
return {
status: 'fail',
message: error instanceof Error ? error.message : 'Unknown error',
};
}
}
async function checkRedis(): Promise<CheckResult> {
const start = Date.now();
try {
const redis = new Redis(process.env.REDIS_URL!);
await redis.ping();
redis.disconnect();
return {
status: 'pass',
latency: Date.now() - start,
};
} catch (error) {
return {
status: 'fail',
message: error instanceof Error ? error.message : 'Unknown error',
};
}
}
async function checkExternal(): Promise<CheckResult> {
const start = Date.now();
try {
const response = await fetch('https://api.stripe.com/v1/health', {
method: 'HEAD',
});
return {
status: response.ok ? 'pass' : 'fail',
latency: Date.now() - start,
};
} catch (error) {
return {
status: 'fail',
message: 'External service unavailable',
};
}
}
const startTime = Date.now();
export async function GET() {
const [database, redis, external] = await Promise.all([
checkDatabase(),
checkRedis(),
checkExternal(),
]);
const checks = { database, redis, external };
const allPassed = Object.values(checks).every((c) => c.status === 'pass');
const anyFailed = Object.values(checks).some((c) => c.status === 'fail');
const health: HealthCheck = {
status: allPassed ? 'healthy' : anyFailed ? 'unhealthy' : 'degraded',
timestamp: new Date().toISOString(),
version: process.env.VERCEL_GIT_COMMIT_SHA || 'local',
uptime: Math.floor((Date.now() - startTime) / 1000),
checks,
};
return NextResponse.json(health, {
status: health.status === 'healthy' ? 200 : 503,
headers: {
'Cache-Control': 'no-store',
},
});
}
```
### Kubernetes-Style Probes
```typescript
// src/app/api/health/live/route.ts
// Liveness probe - is the app running?
export async function GET() {
return new Response('OK', { status: 200 });
}
// src/app/api/health/ready/route.ts
// Readiness probe - can the app handle traffic?
export async function GET() {
try {
// Check critical dependencies
await checkDatabase();
return new Response('OK', { status: 200 });
} catch {
return new Response('Not Ready', { status: 503 });
}
}
```
## Metrics Collection
### Custom Metrics with Prometheus Client
```typescript
// src/lib/metrics.ts
import { Counter, Histogram, Gauge, Registry } from 'prom-client';
export const registry = new Registry();
// HTTP request metrics
export const httpRequestsTotal = new Counter({
name: 'http_requests_total',
help: 'Total HTTP requests',
labelNames: ['method', 'route', 'status'],
registers: [registry],
});
export const httpRequestDuration = new Histogram({
name: 'http_request_duration_seconds',
help: 'HTTP request duration in seconds',
labelNames: ['method', 'route'],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5],
registers: [registry],
});
// Business metrics
export const activeUsers = new Gauge({
name: 'active_users',
help: 'Number of currently active users',
registers: [registry],
});
export const ordersTotal = new Counter({
name: 'orders_total',
help: 'Total orders processed',
labelNames: ['status', 'payment_method'],
registers: [registry],
});
// Database metrics
export const dbQueryDuration = new Histogram({
name: 'db_query_duration_seconds',
help: 'Database query duration',
labelNames: ['operation', 'table'],
buckets: [0.001, 0.01, 0.05, 0.1, 0.5, 1],
registers: [registry],
});
```
### Metrics Endpoint
```typescript
// src/app/api/metrics/route.ts
import { NextResponse } from 'next/server';
import { registry } from '@/lib/metrics';
export async function GET(request: Request) {
// Optional: Basic auth protection
const authHeader = request.headers.get('authorization');
if (authHeader !== `Bearer ${process.env.METRICS_TOKEN}`) {
return new Response('Unauthorized', { status: 401 });
}
const metrics = await registry.metrics();
return new Response(metrics, {
headers: {
'Content-Type': registry.contentType,
},
});
}
```
### Middleware for Request Metrics
```typescript
// src/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { httpRequestsTotal, httpRequestDuration } from '@/lib/metrics';
export async function middleware(request: NextRequest) {
const start = Date.now();
const response = NextResponse.next();
// Record metrics after response
const route = request.nextUrl.pathname;
const method = request.method;
const status = response.status.toString();
httpRequestsTotal.inc({ method, route, status });
httpRequestDuration.observe(
{ method, route },
(Date.now() - start) / 1000
);
return response;
}
```
## Alerting Configuration
### Alert Rules (Prometheus/Grafana)
```yaml
# alerts.yml
groups:
- name: application
rules:
# High error rate
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
> 0.05
for: 5m
labels:
severity: critical
annotations:
summary: High error rate detected
description: Error rate is {{ $value | humanizePercentage }} over the last 5 minutes
# High latency
- alert: HighLatency
expr: |
histogram_quantile(0.95,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
) > 2
for: 5m
labels:
severity: warning
annotations:
summary: High latency detected
description: 95th percentile latency is {{ $value | humanizeDuration }}
# Service down
- alert: ServiceDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: Service is down
description: "{{ $labels.instance }} has been down for more than 1 minute"
# Database connection pool exhausted
- alert: DatabaseConnectionsHigh
expr: pg_stat_activity_count > 80
for: 5m
labels:
severity: warning
annotations:
summary: Database connection pool nearly exhausted
description: "{{ $value }} connections in use"
- name: infrastructure
rules:
# High CPU
- alert: HighCPU
expr: node_cpu_seconds_total{mode="idle"} < 20
for: 10m
Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.