feat(logging): add professional logging with pino and headless E2E tests - Add ILogger port interface in application layer - Implement PinoLogAdapter with Electron-compatible structured logging - Add NoOpLogAdapter for testing - Wire logging into DI container and all adapters - Create 32 E2E tests for automation workflow (headless-only) - Add vitest.e2e.config.ts for E2E test configuration - All tests enforce HEADLESS mode (no headed browser allowed)

This commit is contained in:
2025-11-22 01:02:38 +01:00
parent 7eae6e3bd4
commit 3a60ffae91
13 changed files with 1287 additions and 14 deletions

View File

@@ -0,0 +1,82 @@
import type { LogLevel } from '../../application/ports/ILogger';
export type LogEnvironment = 'development' | 'production' | 'test';
export interface LoggingEnvironmentConfig {
level: LogLevel;
prettyPrint: boolean;
fileOutput: boolean;
filePath?: string;
maxFiles?: number;
maxFileSize?: string;
}
/**
* Load logging configuration from environment variables.
*
* Environment variables:
* - NODE_ENV: 'development' | 'production' | 'test' (default: 'development')
* - LOG_LEVEL: Override log level (optional)
* - LOG_FILE_PATH: Path for log files in production (default: './logs/gridpilot')
* - LOG_MAX_FILES: Max rotated files to keep (default: 7)
* - LOG_MAX_SIZE: Max file size before rotation (default: '10m')
*
* @returns LoggingEnvironmentConfig with parsed environment values
*/
export function loadLoggingConfig(): LoggingEnvironmentConfig {
const envValue = process.env.NODE_ENV;
const environment = isValidLogEnvironment(envValue) ? envValue : 'development';
const defaults = getDefaultsForEnvironment(environment);
const levelOverride = process.env.LOG_LEVEL;
const level = isValidLogLevel(levelOverride) ? levelOverride : defaults.level;
return {
level,
prettyPrint: defaults.prettyPrint,
fileOutput: defaults.fileOutput,
filePath: process.env.LOG_FILE_PATH || './logs/gridpilot',
maxFiles: parseIntSafe(process.env.LOG_MAX_FILES, 7),
maxFileSize: process.env.LOG_MAX_SIZE || '10m',
};
}
function getDefaultsForEnvironment(env: LogEnvironment): LoggingEnvironmentConfig {
switch (env) {
case 'development':
return {
level: 'debug',
prettyPrint: true,
fileOutput: false,
};
case 'production':
return {
level: 'info',
prettyPrint: false,
fileOutput: true,
};
case 'test':
return {
level: 'warn',
prettyPrint: false,
fileOutput: false,
};
}
}
function isValidLogEnvironment(value: string | undefined): value is LogEnvironment {
return value === 'development' || value === 'production' || value === 'test';
}
function isValidLogLevel(value: string | undefined): value is LogLevel {
return value === 'debug' || value === 'info' || value === 'warn' || value === 'error' || value === 'fatal';
}
function parseIntSafe(value: string | undefined, defaultValue: number): number {
if (value === undefined || value === '') {
return defaultValue;
}
const parsed = parseInt(value, 10);
return isNaN(parsed) ? defaultValue : parsed;
}