feat(logging): add comprehensive logging to all API routes (Phase 4)
Applied withLogging() wrapper to all 24 API routes for consistent logging: Process Control Routes: - Start/stop/restart individual processes - Start-all/stop-all/restart-all batch operations Signal Routes: - Signal individual processes - Signal all processes - Signal process groups Group Management Routes: - Start/stop/restart process groups - Signal operations for groups Configuration Routes: - Get all configs (GET) - Reload configuration (POST) - Add/remove process groups (POST/DELETE) Log Routes: - Read main supervisord log - Read process stdout/stderr logs - Clear process logs (individual and all) System Routes: - Get system info - Get all processes info - Get individual process info - Send stdin to process All routes now include: - Request/response logging with timing - Automatic error handling and correlation IDs - X-Request-ID header propagation - Consistent metadata in responses Also fixed Next.js 16 deprecation: - Moved experimental.serverComponentsExternalPackages to serverExternalPackages 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { NextRequest } from 'next/server';
|
||||
import { createSupervisorClient } from '@/lib/supervisor/client';
|
||||
import { createApiLogger, generateRequestId } from '@/lib/utils/api-logger';
|
||||
import { formatError } from '@/lib/utils/logger';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
@@ -8,9 +10,16 @@ export const dynamic = 'force-dynamic';
|
||||
* Polls supervisor every 2 seconds and sends state changes to clients
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
const requestId = generateRequestId();
|
||||
const logger = createApiLogger(request, 'SSE-Events');
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
let intervalId: NodeJS.Timeout | null = null;
|
||||
let previousState: string | null = null;
|
||||
let pollCount = 0;
|
||||
let stateChangeCount = 0;
|
||||
|
||||
logger.info({ requestId }, 'SSE connection initiated');
|
||||
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
@@ -22,9 +31,13 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
// Send initial connection message
|
||||
sendEvent('connected', { timestamp: Date.now() });
|
||||
logger.debug({ requestId }, 'SSE connected event sent');
|
||||
|
||||
// Poll supervisor for state changes
|
||||
const pollSupervisor = async () => {
|
||||
pollCount++;
|
||||
const pollStartTime = Date.now();
|
||||
|
||||
try {
|
||||
const client = createSupervisorClient();
|
||||
const processes = await client.getAllProcessInfo();
|
||||
@@ -41,17 +54,40 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
// Send update if state changed
|
||||
if (currentState !== previousState) {
|
||||
stateChangeCount++;
|
||||
sendEvent('process-update', {
|
||||
processes,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
logger.info({
|
||||
requestId,
|
||||
pollCount,
|
||||
stateChangeCount,
|
||||
processCount: processes.length,
|
||||
duration: Date.now() - pollStartTime,
|
||||
}, `Process state change detected (change #${stateChangeCount})`);
|
||||
previousState = currentState;
|
||||
}
|
||||
|
||||
// Send heartbeat every poll
|
||||
// Send heartbeat every poll (reduced logging - only log every 10th heartbeat)
|
||||
sendEvent('heartbeat', { timestamp: Date.now() });
|
||||
if (pollCount % 10 === 0) {
|
||||
logger.debug({
|
||||
requestId,
|
||||
pollCount,
|
||||
stateChangeCount,
|
||||
duration: Date.now() - pollStartTime,
|
||||
}, `SSE heartbeat #${pollCount}`);
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('SSE polling error:', error);
|
||||
const errorInfo = formatError(error);
|
||||
logger.error({
|
||||
requestId,
|
||||
pollCount,
|
||||
error: errorInfo,
|
||||
duration: Date.now() - pollStartTime,
|
||||
}, `SSE polling error: ${errorInfo.message}`);
|
||||
|
||||
sendEvent('error', {
|
||||
message: error.message || 'Failed to fetch process state',
|
||||
timestamp: Date.now(),
|
||||
@@ -70,6 +106,12 @@ export async function GET(request: NextRequest) {
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
logger.info({
|
||||
requestId,
|
||||
pollCount,
|
||||
stateChangeCount,
|
||||
duration: Date.now() - Date.now(),
|
||||
}, 'SSE connection closed');
|
||||
},
|
||||
});
|
||||
|
||||
@@ -79,6 +121,7 @@ export async function GET(request: NextRequest) {
|
||||
'Cache-Control': 'no-cache, no-transform',
|
||||
'Connection': 'keep-alive',
|
||||
'X-Accel-Buffering': 'no', // Disable nginx buffering
|
||||
'X-Request-ID': requestId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user