feat: implement comprehensive logging infrastructure (Phases 1-3)
All checks were successful
Build and Push Docker Image to Gitea / build-and-push (push) Successful in 1m50s

Added production-ready logging using Pino with structured JSON output,
pretty printing in development, and automatic sensitive data redaction.

## Phase 1: Core Logger Setup
- Installed pino, pino-http, and pino-pretty dependencies
- Created logger utility (lib/utils/logger.ts):
  - Environment-based log levels (debug in dev, info in prod)
  - Pretty printing with colors in development
  - JSON structured logs in production
  - Sensitive data redaction (passwords, tokens, auth headers)
  - Custom serializers for errors and requests
  - Helper functions for child loggers and timing
- Added LOG_LEVEL environment variable to .env.example
- Configured Next.js for Turbopack with external pino packages

## Phase 2: API Request Logging
- Created API logger wrapper (lib/utils/api-logger.ts):
  - withLogging() HOF for wrapping API route handlers
  - Automatic request/response logging with timing
  - Correlation ID generation (X-Request-ID header)
  - Error catching and structured error responses
  - logPerformance() helper for timing operations
  - createApiLogger() for manual logging in routes

## Phase 3: Supervisor Client Logging
- Updated lib/supervisor/client.ts:
  - Added logger instance to SupervisorClient class
  - Comprehensive XML-RPC call logging (method, params, duration)
  - Error logging with full context and stack traces
  - Success logging with result size tracking
  - DEBUG level logs for all XML-RPC operations
  - Constructor logging for client initialization

## Configuration Changes
- Updated next.config.ts for Turbopack compatibility
- Added serverComponentsExternalPackages for pino
- Removed null-loader workaround (not needed)

## Features Implemented
 Request correlation IDs for tracing
 Performance timing for all operations
 Sensitive data redaction (passwords, auth)
 Environment-based log formatting
 Structured JSON logs for production
 Pretty colored logs for development
 Error serialization with stack traces
 Ready for log aggregation (stdout/JSON)

## Next Steps (Phases 4-7)
- Update ~30 API routes with logging
- Add React Query/hooks error logging
- Implement client-side error boundary
- Add documentation and testing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-23 20:44:46 +01:00
parent 3d5e9e36d6
commit b252a0b3bf
7 changed files with 1076 additions and 16 deletions

View File

@@ -1,4 +1,5 @@
import * as xmlrpc from 'xmlrpc';
import { createLogger, formatError } from '../utils/logger';
import {
ProcessInfo,
ProcessInfoSchema,
@@ -23,10 +24,18 @@ export interface SupervisorClientConfig {
export class SupervisorClient {
private client: xmlrpc.Client;
private config: SupervisorClientConfig;
private logger: ReturnType<typeof createLogger>;
constructor(config: SupervisorClientConfig) {
this.config = config;
// Create logger with supervisor context
this.logger = createLogger({
component: 'SupervisorClient',
host: config.host,
port: config.port,
});
const clientOptions: any = {
host: config.host,
port: config.port,
@@ -39,20 +48,43 @@ export class SupervisorClient {
user: config.username,
pass: config.password,
};
this.logger.debug('Basic auth configured');
}
this.client = xmlrpc.createClient(clientOptions);
this.logger.info({ config: { host: config.host, port: config.port } }, 'Supervisor client initialized');
}
/**
* Generic method call wrapper with error handling
* Generic method call wrapper with error handling and logging
*/
private async call<T>(method: string, params: any[] = []): Promise<T> {
const startTime = Date.now();
// Log the method call
this.logger.debug({ method, params }, `Calling XML-RPC method: ${method}`);
return new Promise((resolve, reject) => {
this.client.methodCall(method, params, (error: any, value: any) => {
const duration = Date.now() - startTime;
if (error) {
const errorInfo = formatError(error);
this.logger.error({
method,
params,
duration,
error: errorInfo,
}, `XML-RPC call failed: ${method} (${duration}ms) - ${errorInfo.message}`);
reject(new Error(`XML-RPC Error: ${error?.message || 'Unknown error'}`));
} else {
this.logger.debug({
method,
duration,
resultSize: JSON.stringify(value).length,
}, `XML-RPC call successful: ${method} (${duration}ms)`);
resolve(value);
}
});