Performance Monitoring
Sentinel Guard provides comprehensive performance monitoring capabilities, including database latency tracking for popular databases and caching systems.
Overview
Performance monitoring in Sentinel Guard automatically measures and reports:
- Service response times
- Database query latency (Prisma)
- Cache operation latency (Redis)
- Performance trends over time
- Automatic alerts for performance degradation
All performance data is automatically included in heartbeats and displayed in your UptimeBeacon.cloud dashboard.
Database Latency Tracking
Prisma Integration
Monitor PostgreSQL, MySQL, SQLite, and other databases through Prisma ORM.
Setup
import { PrismaClient } from "@prisma/client";
import { SentinelGuard } from "@uptimebeacon/sentinel-guard";
const prisma = new PrismaClient();
const sentinel = new SentinelGuard({
apiKey: process.env.SENTINEL_API_KEY!,
baseUrl: process.env.SENTINEL_API_URL!,
monitorApiKey: process.env.SENTINEL_MONITOR_API_KEY!,
});
// Enable Prisma monitoring
sentinel.setPrismaClient(prisma);
// Start monitoring
sentinel.startMonitoring({
interval: 30000,
maxConsecutiveErrors: 5,
});
How It Works
When you configure a Prisma client, Sentinel Guard:
- Executes a lightweight
SELECT 1
query periodically - Measures the query execution time
- Includes latency data in heartbeats
- Alerts if database performance degrades
Supported Prisma Configurations
// Standard Prisma client
const prisma = new PrismaClient();
// Prisma with custom configuration
const prisma = new PrismaClient({
datasources: {
db: {
url: process.env.DATABASE_URL,
},
},
});
// Prisma with connection pooling
const prisma = new PrismaClient({
datasources: {
db: {
url: `${process.env.DATABASE_URL}?connection_limit=20&pool_timeout=20`,
},
},
});
// All configurations work with Sentinel Guard
sentinel.setPrismaClient(prisma);
Redis Integration
Monitor Redis cache performance and connection health.
Setup
import { createClient } from "redis";
import { SentinelGuard } from "@uptimebeacon/sentinel-guard";
const redis = createClient({
url: process.env.REDIS_URL,
});
const sentinel = new SentinelGuard({
apiKey: process.env.SENTINEL_API_KEY!,
baseUrl: process.env.SENTINEL_API_URL!,
monitorApiKey: process.env.SENTINEL_MONITOR_API_KEY!,
});
// Connect to Redis
await redis.connect();
// Enable Redis monitoring
sentinel.setRedisClient(redis);
// Start monitoring
sentinel.startMonitoring({
interval: 30000,
maxConsecutiveErrors: 5,
});
How It Works
With Redis monitoring enabled, Sentinel Guard:
- Executes
PING
commands to test connectivity - Measures response times
- Monitors connection health
- Includes Redis metrics in heartbeats
Supported Redis Configurations
// Standard Redis client
const redis = createClient();
// Redis with URL
const redis = createClient({
url: "redis://localhost:6379",
});
// Redis with authentication
const redis = createClient({
url: "redis://username:password@localhost:6379",
});
// Redis Cluster
const redis = createClient({
url: "redis://localhost:7000",
// cluster configuration
});
// All configurations work with Sentinel Guard
await redis.connect();
sentinel.setRedisClient(redis);
Performance Metrics Structure
PerformanceMetrics Interface
interface PerformanceMetrics {
serviceLatency: number;
prismaLatency?: number;
redisLatency?: number;
timestamp: string;
}
Metric Details
serviceLatency
- Type:
number
- Description: Overall service response time in milliseconds
- Calculated: Time from heartbeat initiation to completion
prismaLatency
- Type:
number
(optional) - Description: Database query latency in milliseconds
- Calculated: Time to execute
SELECT 1
query
redisLatency
- Type:
number
(optional) - Description: Redis operation latency in milliseconds
- Calculated: Time to execute
PING
command
timestamp
- Type:
string
- Description: ISO 8601 timestamp of measurement
- Example:
"2024-01-15T10:30:00.000Z"
Complete Integration Example
import { PrismaClient } from "@prisma/client";
import { createClient } from "redis";
import { SentinelGuard } from "@uptimebeacon/sentinel-guard";
class ApplicationMonitor {
private prisma: PrismaClient;
private redis: ReturnType<typeof createClient>;
private sentinel: SentinelGuard;
constructor() {
this.prisma = new PrismaClient();
this.redis = createClient({
url: process.env.REDIS_URL,
});
this.sentinel = new SentinelGuard({
apiKey: process.env.SENTINEL_API_KEY!,
baseUrl: process.env.SENTINEL_API_URL!,
monitorApiKey: process.env.SENTINEL_MONITOR_API_KEY!,
});
}
async initialize() {
// Connect to databases
await this.redis.connect();
console.log("Database connections established");
// Configure performance monitoring
this.sentinel.setPrismaClient(this.prisma);
this.sentinel.setRedisClient(this.redis);
// Start monitoring
this.sentinel.startMonitoring({
interval: 30000,
maxConsecutiveErrors: 5,
});
console.log("Performance monitoring started");
}
async getPerformanceReport() {
// Send a heartbeat to get current performance metrics
const response = await this.sentinel.sendHeartbeat({
status: "ONLINE",
metadata: {
operation: "performance_check",
timestamp: new Date().toISOString(),
},
});
return response;
}
async cleanup() {
await this.prisma.$disconnect();
await this.redis.disconnect();
this.sentinel.stopMonitoring();
}
}
// Usage
const monitor = new ApplicationMonitor();
await monitor.initialize();
// Get performance report
const report = await monitor.getPerformanceReport();
console.log("Performance report:", report);
Advanced Performance Monitoring
Custom Performance Tracking
import { SentinelGuard } from "@uptimebeacon/sentinel-guard";
class CustomPerformanceMonitor {
private sentinel: SentinelGuard;
private metrics: Map<string, number[]> = new Map();
constructor() {
this.sentinel = new SentinelGuard({
apiKey: process.env.SENTINEL_API_KEY!,
baseUrl: process.env.SENTINEL_API_URL!,
monitorApiKey: process.env.SENTINEL_MONITOR_API_KEY!,
});
}
async trackOperation<T>(
operationName: string,
operation: () => Promise<T>,
): Promise<T> {
const startTime = Date.now();
try {
const result = await operation();
const duration = Date.now() - startTime;
// Store metrics
this.addMetric(operationName, duration);
// Send heartbeat with performance data
await this.sentinel.sendHeartbeat({
status: "ONLINE",
metadata: {
operation: operationName,
duration,
averageLatency: this.getAverageLatency(operationName),
performanceGrade: this.getPerformanceGrade(duration),
},
});
return result;
} catch (error) {
const duration = Date.now() - startTime;
await this.sentinel.sendHeartbeat({
status: "ERROR",
metadata: {
operation: operationName,
duration,
error: error.message,
},
});
throw error;
}
}
private addMetric(operation: string, duration: number) {
if (!this.metrics.has(operation)) {
this.metrics.set(operation, []);
}
const metrics = this.metrics.get(operation)!;
metrics.push(duration);
// Keep only last 100 measurements
if (metrics.length > 100) {
metrics.shift();
}
}
private getAverageLatency(operation: string): number {
const metrics = this.metrics.get(operation) || [];
if (metrics.length === 0) return 0;
return metrics.reduce((sum, val) => sum + val, 0) / metrics.length;
}
private getPerformanceGrade(duration: number): string {
if (duration < 100) return "excellent";
if (duration < 500) return "good";
if (duration < 1000) return "fair";
if (duration < 2000) return "poor";
return "critical";
}
}
// Usage
const monitor = new CustomPerformanceMonitor();
// Track database operations
await monitor.trackOperation("user_query", async () => {
return await prisma.user.findMany({ take: 10 });
});
// Track API calls
await monitor.trackOperation("external_api", async () => {
return await fetch("https://api.example.com/data");
});
Performance Alerting
import { SentinelGuard } from "@uptimebeacon/sentinel-guard";
class PerformanceAlerting {
private sentinel: SentinelGuard;
private thresholds = {
database: 200, // 200ms
cache: 50, // 50ms
service: 1000, // 1s
};
constructor() {
this.sentinel = new SentinelGuard({
apiKey: process.env.SENTINEL_API_KEY!,
baseUrl: process.env.SENTINEL_API_URL!,
monitorApiKey: process.env.SENTINEL_MONITOR_API_KEY!,
});
}
async checkPerformance() {
const startTime = Date.now();
try {
// Check database performance
const dbStart = Date.now();
await this.checkDatabaseHealth();
const dbLatency = Date.now() - dbStart;
// Check cache performance
const cacheStart = Date.now();
await this.checkCacheHealth();
const cacheLatency = Date.now() - cacheStart;
const totalLatency = Date.now() - startTime;
// Determine status based on thresholds
const status = this.determineStatus(
dbLatency,
cacheLatency,
totalLatency,
);
await this.sentinel.sendHeartbeat({
status,
metadata: {
databaseLatency: dbLatency,
cacheLatency: cacheLatency,
totalLatency: totalLatency,
thresholds: this.thresholds,
performanceIssues: this.getPerformanceIssues(
dbLatency,
cacheLatency,
),
},
});
} catch (error) {
await this.sentinel.sendHeartbeat({
status: "ERROR",
metadata: {
error: error.message,
performanceCheck: "failed",
},
});
}
}
private determineStatus(
dbLatency: number,
cacheLatency: number,
serviceLatency: number,
): "ONLINE" | "HIGH_LATENCY" | "ERROR" {
if (
dbLatency > this.thresholds.database * 2 ||
cacheLatency > this.thresholds.cache * 2 ||
serviceLatency > this.thresholds.service * 2
) {
return "ERROR";
}
if (
dbLatency > this.thresholds.database ||
cacheLatency > this.thresholds.cache ||
serviceLatency > this.thresholds.service
) {
return "HIGH_LATENCY";
}
return "ONLINE";
}
private getPerformanceIssues(
dbLatency: number,
cacheLatency: number,
): string[] {
const issues: string[] = [];
if (dbLatency > this.thresholds.database) {
issues.push("database_slow");
}
if (cacheLatency > this.thresholds.cache) {
issues.push("cache_slow");
}
return issues;
}
private async checkDatabaseHealth() {
// Database health check logic
await new Promise((resolve) => setTimeout(resolve, 10));
}
private async checkCacheHealth() {
// Cache health check logic
await new Promise((resolve) => setTimeout(resolve, 5));
}
}
// Schedule performance checks
const alerting = new PerformanceAlerting();
setInterval(() => alerting.checkPerformance(), 60000); // Every minute
Best Practices
Performance Monitoring
-
Set Appropriate Thresholds
// Production thresholds const thresholds = { database: 200, // 200ms for database queries cache: 50, // 50ms for cache operations service: 1000, // 1s for service responses }; // Development thresholds (more lenient) const devThresholds = { database: 500, cache: 100, service: 2000, };
-
Monitor Database Connection Pool
const prisma = new PrismaClient({ datasources: { db: { url: `${process.env.DATABASE_URL}?connection_limit=20&pool_timeout=20`, }, }, }); // Monitor connection pool health setInterval(async () => { const poolStats = await prisma.$executeRaw` SELECT count(*) as active_connections FROM pg_stat_activity WHERE state = 'active' `; await sentinel.sendHeartbeat({ status: "ONLINE", metadata: { connectionPool: poolStats, maxConnections: 20, }, }); }, 300000); // Every 5 minutes
-
Track Performance Trends
class PerformanceTrends { private metrics: Array<{ timestamp: Date; latency: number }> = []; addMetric(latency: number) { this.metrics.push({ timestamp: new Date(), latency, }); // Keep only last 1000 measurements if (this.metrics.length > 1000) { this.metrics.shift(); } } getTrend(): "improving" | "stable" | "degrading" { if (this.metrics.length < 10) return "stable"; const recent = this.metrics.slice(-10); const older = this.metrics.slice(-20, -10); const recentAvg = recent.reduce((sum, m) => sum + m.latency, 0) / recent.length; const olderAvg = older.reduce((sum, m) => sum + m.latency, 0) / older.length; if (recentAvg < olderAvg * 0.9) return "improving"; if (recentAvg > olderAvg * 1.1) return "degrading"; return "stable"; } }
Resource Management
-
Efficient Database Queries
// Monitor query performance async function monitoredQuery<T>( operation: string, query: () => Promise<T>, ): Promise<T> { const start = Date.now(); try { const result = await query(); const duration = Date.now() - start; if (duration > 1000) { await sentinel.sendHeartbeat({ status: "HIGH_LATENCY", metadata: { operation, duration, warning: "slow_query", }, }); } return result; } catch (error) { await sentinel.sendHeartbeat({ status: "ERROR", metadata: { operation, error: error.message, }, }); throw error; } }
-
Connection Management
// Proper cleanup process.on("SIGINT", async () => { console.log("Shutting down gracefully..."); // Stop monitoring first sentinel.stopMonitoring(); // Close database connections await prisma.$disconnect(); await redis.disconnect(); // Final cleanup sentinel.destroy(); process.exit(0); });
Troubleshooting
Common Issues
High Database Latency
- Check database server load
- Optimize queries with indexes
- Review connection pool configuration
- Monitor database locks
Redis Connection Issues
- Verify Redis server is running
- Check network connectivity
- Review Redis configuration
- Monitor Redis memory usage
Missing Performance Data
- Ensure clients are properly connected
- Verify client compatibility
- Check for authentication issues
- Review error logs
Performance Debugging
// Debug performance issues
class PerformanceDebugger {
private sentinel: SentinelGuard;
constructor() {
this.sentinel = new SentinelGuard({
apiKey: process.env.SENTINEL_API_KEY!,
baseUrl: process.env.SENTINEL_API_URL!,
monitorApiKey: process.env.SENTINEL_MONITOR_API_KEY!,
});
}
async diagnosePerformance() {
const diagnostics = {
timestamp: new Date().toISOString(),
nodeVersion: process.version,
memoryUsage: process.memoryUsage(),
cpuUsage: process.cpuUsage(),
uptime: process.uptime(),
};
await this.sentinel.sendHeartbeat({
status: "ONLINE",
metadata: {
diagnostics,
performanceDebug: true,
},
});
}
}