You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
255 lines
7.4 KiB
255 lines
7.4 KiB
/**
|
|
* Enhanced logger with self-contained database logging
|
|
*
|
|
* Provides comprehensive logging with console and database output.
|
|
* Supports configurable log levels via VITE_LOG_LEVEL environment variable.
|
|
*
|
|
* @author Matthew Raymer
|
|
* @version 2.1.0
|
|
* @since 2025-01-25
|
|
*/
|
|
|
|
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
|
|
|
const _memoryLogs: string[] = [];
|
|
|
|
export function appendToMemoryLogs(message: string): void {
|
|
_memoryLogs.push(`${new Date().toISOString()}: ${message}`);
|
|
if (_memoryLogs.length > 1000) {
|
|
_memoryLogs.splice(0, _memoryLogs.length - 1000);
|
|
}
|
|
}
|
|
|
|
export function getMemoryLogs(): string[] {
|
|
return [..._memoryLogs];
|
|
}
|
|
|
|
export function safeStringify(obj: unknown) {
|
|
const seen = new WeakSet();
|
|
|
|
return JSON.stringify(obj, (_key, value) => {
|
|
if (typeof value === "object" && value !== null) {
|
|
if (seen.has(value)) {
|
|
return "[Circular]";
|
|
}
|
|
seen.add(value);
|
|
}
|
|
|
|
if (typeof value === "function") {
|
|
return `[Function: ${value.name || "anonymous"}]`;
|
|
}
|
|
|
|
return value;
|
|
});
|
|
}
|
|
|
|
// Determine if we should suppress verbose logging (for Electron)
|
|
const isElectron = process.env.VITE_PLATFORM === "electron";
|
|
const isProduction = process.env.NODE_ENV === "production";
|
|
|
|
// Log level configuration via environment variable
|
|
const LOG_LEVELS = {
|
|
error: 0,
|
|
warn: 1,
|
|
info: 2,
|
|
debug: 3,
|
|
} as const;
|
|
|
|
type LogLevel = keyof typeof LOG_LEVELS;
|
|
|
|
// Parse VITE_LOG_LEVEL environment variable
|
|
const getLogLevel = (): LogLevel => {
|
|
const envLogLevel = process.env.VITE_LOG_LEVEL?.toLowerCase();
|
|
|
|
if (envLogLevel && envLogLevel in LOG_LEVELS) {
|
|
return envLogLevel as LogLevel;
|
|
}
|
|
|
|
// Default log levels based on environment
|
|
if (isProduction && !isElectron) {
|
|
return "warn"; // Production web: warnings and errors only
|
|
} else if (isElectron) {
|
|
return "error"; // Electron: errors only
|
|
} else {
|
|
return "info"; // Development/Capacitor: info and above
|
|
}
|
|
};
|
|
|
|
const currentLogLevel = getLogLevel();
|
|
const currentLevelValue = LOG_LEVELS[currentLogLevel];
|
|
|
|
// Helper function to check if a log level should be displayed
|
|
const shouldLog = (level: LogLevel): boolean => {
|
|
return LOG_LEVELS[level] <= currentLevelValue;
|
|
};
|
|
|
|
// Track initialization state to prevent circular dependencies
|
|
let isInitializing = true;
|
|
|
|
// Mark initialization as complete after a delay to allow all services to start
|
|
setTimeout(() => {
|
|
isInitializing = false;
|
|
}, 2000); // 2 second delay to allow all services to initialize
|
|
|
|
// Function to check if we should skip database logging during initialization
|
|
function shouldSkipDatabaseLogging(message: string): boolean {
|
|
// Skip during initialization phase
|
|
if (isInitializing) {
|
|
return true;
|
|
}
|
|
|
|
// Skip specific initialization-related messages even after initialization
|
|
const initializationMessages = [
|
|
"[PlatformServiceFactory]",
|
|
"[SQLWorker]",
|
|
"[WebPlatformService]",
|
|
"[CapacitorPlatformService]",
|
|
"[CapacitorMigration]",
|
|
"[DB-Integrity]",
|
|
"[Migration]",
|
|
"[IndexedDBMigrationService]",
|
|
"Creating singleton instance",
|
|
"Worker loaded",
|
|
"Worker initialized",
|
|
"Platform service",
|
|
];
|
|
|
|
return initializationMessages.some((pattern) => message.includes(pattern));
|
|
}
|
|
|
|
// Self-contained database logging function
|
|
async function logToDatabase(
|
|
message: string,
|
|
level: string = "info",
|
|
): Promise<void> {
|
|
// Prevent infinite logging loops
|
|
if (isInitializing || shouldSkipDatabaseLogging(message)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const platform = PlatformServiceFactory.getInstance();
|
|
const todayKey = new Date().toDateString();
|
|
|
|
await platform.dbExec("INSERT INTO logs (date, message) VALUES (?, ?)", [
|
|
todayKey,
|
|
`[${level.toUpperCase()}] ${message}`,
|
|
]);
|
|
} catch (error) {
|
|
// Fallback to console if database logging fails
|
|
// eslint-disable-next-line no-console
|
|
console.error(`[Logger] Database logging failed: ${error}`);
|
|
}
|
|
}
|
|
|
|
// Enhanced logger with self-contained database methods and log level control
|
|
export const logger = {
|
|
debug: (message: string, ...args: unknown[]) => {
|
|
// Debug logs only show if VITE_LOG_LEVEL allows it
|
|
if (shouldLog("debug")) {
|
|
// eslint-disable-next-line no-console
|
|
console.debug(message, ...args);
|
|
}
|
|
// Don't log debug messages to database to reduce noise
|
|
},
|
|
|
|
log: (message: string, ...args: unknown[]) => {
|
|
// Regular logs - show if VITE_LOG_LEVEL allows info level
|
|
if (shouldLog("info")) {
|
|
// eslint-disable-next-line no-console
|
|
console.log(message, ...args);
|
|
}
|
|
|
|
// Database logging
|
|
const argsString = args.length > 0 ? " - " + safeStringify(args) : "";
|
|
logToDatabase(message + argsString, "info");
|
|
},
|
|
|
|
info: (message: string, ...args: unknown[]) => {
|
|
if (shouldLog("info")) {
|
|
// eslint-disable-next-line no-console
|
|
console.info(message, ...args);
|
|
}
|
|
|
|
// Database logging
|
|
const argsString = args.length > 0 ? " - " + safeStringify(args) : "";
|
|
logToDatabase(message + argsString, "info");
|
|
},
|
|
|
|
warn: (message: string, ...args: unknown[]) => {
|
|
if (shouldLog("warn")) {
|
|
// eslint-disable-next-line no-console
|
|
console.warn(message, ...args);
|
|
}
|
|
|
|
// Database logging
|
|
const argsString = args.length > 0 ? " - " + safeStringify(args) : "";
|
|
logToDatabase(message + argsString, "warn");
|
|
},
|
|
|
|
error: (message: string, ...args: unknown[]) => {
|
|
if (shouldLog("error")) {
|
|
// eslint-disable-next-line no-console
|
|
console.error(message, ...args);
|
|
}
|
|
|
|
// Database logging
|
|
const messageString = safeStringify(message);
|
|
const argsString = args.length > 0 ? safeStringify(args) : "";
|
|
logToDatabase(messageString + argsString, "error");
|
|
},
|
|
|
|
// New database-focused methods (self-contained)
|
|
toDb: async (message: string, level?: string): Promise<void> => {
|
|
await logToDatabase(message, level || "info");
|
|
},
|
|
|
|
toConsoleAndDb: async (message: string, isError = false): Promise<void> => {
|
|
// Console output based on log level
|
|
if (isError && shouldLog("error")) {
|
|
// eslint-disable-next-line no-console
|
|
console.error(message);
|
|
} else if (!isError && shouldLog("info")) {
|
|
// eslint-disable-next-line no-console
|
|
console.log(message);
|
|
}
|
|
// Database output
|
|
await logToDatabase(message, isError ? "error" : "info");
|
|
},
|
|
|
|
// Component context methods
|
|
withContext: (componentName?: string) => ({
|
|
log: (message: string, level?: string) =>
|
|
logToDatabase(`[${componentName}] ${message}`, level),
|
|
error: (message: string) =>
|
|
logToDatabase(`[${componentName}] ${message}`, "error"),
|
|
}),
|
|
|
|
// Log level information methods
|
|
getCurrentLevel: (): LogLevel => currentLogLevel,
|
|
getCurrentLevelValue: (): number => currentLevelValue,
|
|
isLevelEnabled: (level: LogLevel): boolean => shouldLog(level),
|
|
getAvailableLevels: (): LogLevel[] => Object.keys(LOG_LEVELS) as LogLevel[],
|
|
};
|
|
|
|
// Function to manually mark initialization as complete
|
|
export function markInitializationComplete(): void {
|
|
isInitializing = false;
|
|
}
|
|
|
|
// Function to check initialization status
|
|
export function isInitializationPhase(): boolean {
|
|
return isInitializing;
|
|
}
|
|
|
|
// Add CommonJS export for Electron
|
|
if (typeof module !== "undefined" && module.exports) {
|
|
module.exports = {
|
|
logger,
|
|
markInitializationComplete,
|
|
isInitializationPhase,
|
|
};
|
|
}
|
|
|
|
// Add default export for ESM
|
|
export default { logger, markInitializationComplete, isInitializationPhase };
|
|
|