217 lines
7.7 KiB
JavaScript
217 lines
7.7 KiB
JavaScript
|
|
/**
|
||
|
|
* Centralized logging utility for Brainy
|
||
|
|
* Provides configurable log levels and consistent logging across the codebase
|
||
|
|
* Automatically reduces logging in production environments to minimize costs
|
||
|
|
*/
|
||
|
|
import { isProductionEnvironment, getLogLevel } from './environment.js';
|
||
|
|
export var LogLevel;
|
||
|
|
(function (LogLevel) {
|
||
|
|
LogLevel[LogLevel["ERROR"] = 0] = "ERROR";
|
||
|
|
LogLevel[LogLevel["WARN"] = 1] = "WARN";
|
||
|
|
LogLevel[LogLevel["INFO"] = 2] = "INFO";
|
||
|
|
LogLevel[LogLevel["DEBUG"] = 3] = "DEBUG";
|
||
|
|
LogLevel[LogLevel["TRACE"] = 4] = "TRACE";
|
||
|
|
})(LogLevel || (LogLevel = {}));
|
||
|
|
class Logger {
|
||
|
|
constructor() {
|
||
|
|
this.config = {
|
||
|
|
level: LogLevel.ERROR, // Default to ERROR in production for cost optimization
|
||
|
|
timestamps: false, // Disable timestamps in production to reduce log size
|
||
|
|
includeModule: true
|
||
|
|
};
|
||
|
|
// Auto-detect production environment and set appropriate defaults
|
||
|
|
this.applyEnvironmentDefaults();
|
||
|
|
// Set log level from environment variable if available (overrides auto-detection)
|
||
|
|
const envLogLevel = process.env.BRAINY_LOG_LEVEL;
|
||
|
|
if (envLogLevel) {
|
||
|
|
const level = LogLevel[envLogLevel.toUpperCase()];
|
||
|
|
if (level !== undefined) {
|
||
|
|
this.config.level = level;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// Parse module-specific log levels
|
||
|
|
const moduleLogLevels = process.env.BRAINY_MODULE_LOG_LEVELS;
|
||
|
|
if (moduleLogLevels) {
|
||
|
|
try {
|
||
|
|
this.config.modules = JSON.parse(moduleLogLevels);
|
||
|
|
}
|
||
|
|
catch (e) {
|
||
|
|
// Ignore parsing errors
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
applyEnvironmentDefaults() {
|
||
|
|
const envLogLevel = getLogLevel();
|
||
|
|
// Convert environment log level to Logger LogLevel
|
||
|
|
switch (envLogLevel) {
|
||
|
|
case 'silent':
|
||
|
|
this.config.level = -1; // Below ERROR to silence all logs
|
||
|
|
break;
|
||
|
|
case 'error':
|
||
|
|
this.config.level = LogLevel.ERROR;
|
||
|
|
this.config.timestamps = false; // Minimize log size in production
|
||
|
|
break;
|
||
|
|
case 'warn':
|
||
|
|
this.config.level = LogLevel.WARN;
|
||
|
|
this.config.timestamps = false;
|
||
|
|
break;
|
||
|
|
case 'info':
|
||
|
|
this.config.level = LogLevel.INFO;
|
||
|
|
this.config.timestamps = true;
|
||
|
|
break;
|
||
|
|
case 'verbose':
|
||
|
|
this.config.level = LogLevel.DEBUG;
|
||
|
|
this.config.timestamps = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
// In production environments, be extra conservative to minimize costs
|
||
|
|
if (isProductionEnvironment()) {
|
||
|
|
this.config.level = Math.min(this.config.level, LogLevel.ERROR);
|
||
|
|
this.config.timestamps = false;
|
||
|
|
this.config.includeModule = false; // Reduce log size
|
||
|
|
}
|
||
|
|
}
|
||
|
|
static getInstance() {
|
||
|
|
if (!Logger.instance) {
|
||
|
|
Logger.instance = new Logger();
|
||
|
|
}
|
||
|
|
return Logger.instance;
|
||
|
|
}
|
||
|
|
configure(config) {
|
||
|
|
this.config = { ...this.config, ...config };
|
||
|
|
}
|
||
|
|
shouldLog(level, module) {
|
||
|
|
// Check module-specific level first
|
||
|
|
if (this.config.modules && this.config.modules[module] !== undefined) {
|
||
|
|
return level <= this.config.modules[module];
|
||
|
|
}
|
||
|
|
// Otherwise use global level
|
||
|
|
return level <= this.config.level;
|
||
|
|
}
|
||
|
|
formatMessage(level, module, message) {
|
||
|
|
const parts = [];
|
||
|
|
if (this.config.timestamps) {
|
||
|
|
parts.push(`[${new Date().toISOString()}]`);
|
||
|
|
}
|
||
|
|
parts.push(`[${LogLevel[level]}]`);
|
||
|
|
if (this.config.includeModule) {
|
||
|
|
parts.push(`[${module}]`);
|
||
|
|
}
|
||
|
|
parts.push(message);
|
||
|
|
return parts.join(' ');
|
||
|
|
}
|
||
|
|
log(level, module, message, ...args) {
|
||
|
|
if (!this.shouldLog(level, module)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (this.config.handler) {
|
||
|
|
this.config.handler(level, module, message, ...args);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const formattedMessage = this.formatMessage(level, module, message);
|
||
|
|
switch (level) {
|
||
|
|
case LogLevel.ERROR:
|
||
|
|
console.error(formattedMessage, ...args);
|
||
|
|
break;
|
||
|
|
case LogLevel.WARN:
|
||
|
|
console.warn(formattedMessage, ...args);
|
||
|
|
break;
|
||
|
|
case LogLevel.INFO:
|
||
|
|
console.info(formattedMessage, ...args);
|
||
|
|
break;
|
||
|
|
case LogLevel.DEBUG:
|
||
|
|
case LogLevel.TRACE:
|
||
|
|
console.log(formattedMessage, ...args);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
error(module, message, ...args) {
|
||
|
|
this.log(LogLevel.ERROR, module, message, ...args);
|
||
|
|
}
|
||
|
|
warn(module, message, ...args) {
|
||
|
|
this.log(LogLevel.WARN, module, message, ...args);
|
||
|
|
}
|
||
|
|
info(module, message, ...args) {
|
||
|
|
this.log(LogLevel.INFO, module, message, ...args);
|
||
|
|
}
|
||
|
|
debug(module, message, ...args) {
|
||
|
|
this.log(LogLevel.DEBUG, module, message, ...args);
|
||
|
|
}
|
||
|
|
trace(module, message, ...args) {
|
||
|
|
this.log(LogLevel.TRACE, module, message, ...args);
|
||
|
|
}
|
||
|
|
// Create a module-specific logger
|
||
|
|
createModuleLogger(module) {
|
||
|
|
return {
|
||
|
|
error: (message, ...args) => this.error(module, message, ...args),
|
||
|
|
warn: (message, ...args) => this.warn(module, message, ...args),
|
||
|
|
info: (message, ...args) => this.info(module, message, ...args),
|
||
|
|
debug: (message, ...args) => this.debug(module, message, ...args),
|
||
|
|
trace: (message, ...args) => this.trace(module, message, ...args)
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// Export singleton instance
|
||
|
|
export const logger = Logger.getInstance();
|
||
|
|
// Export convenience function for creating module loggers
|
||
|
|
export function createModuleLogger(module) {
|
||
|
|
return logger.createModuleLogger(module);
|
||
|
|
}
|
||
|
|
// Export function to configure logger
|
||
|
|
export function configureLogger(config) {
|
||
|
|
logger.configure(config);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Smart console replacement that automatically reduces logging in production
|
||
|
|
* Dramatically reduces Google Cloud Run logging costs
|
||
|
|
*
|
||
|
|
* Usage: Replace console.log with smartConsole.log, etc.
|
||
|
|
*/
|
||
|
|
export const smartConsole = {
|
||
|
|
log: (message, ...args) => {
|
||
|
|
if (logger['shouldLog'](LogLevel.INFO, 'console')) {
|
||
|
|
console.log(message, ...args);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
info: (message, ...args) => {
|
||
|
|
if (logger['shouldLog'](LogLevel.INFO, 'console')) {
|
||
|
|
console.info(message, ...args);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
warn: (message, ...args) => {
|
||
|
|
if (logger['shouldLog'](LogLevel.WARN, 'console')) {
|
||
|
|
console.warn(message, ...args);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
error: (message, ...args) => {
|
||
|
|
if (logger['shouldLog'](LogLevel.ERROR, 'console')) {
|
||
|
|
console.error(message, ...args);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
debug: (message, ...args) => {
|
||
|
|
if (logger['shouldLog'](LogLevel.DEBUG, 'console')) {
|
||
|
|
console.debug(message, ...args);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
trace: (message, ...args) => {
|
||
|
|
if (logger['shouldLog'](LogLevel.TRACE, 'console')) {
|
||
|
|
console.trace(message, ...args);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
/**
|
||
|
|
* Production-optimized logging functions
|
||
|
|
* These only log in non-production environments or when explicitly enabled
|
||
|
|
*/
|
||
|
|
export const prodLog = {
|
||
|
|
// Only log errors in production (always visible)
|
||
|
|
error: (message, ...args) => {
|
||
|
|
console.error(message, ...args);
|
||
|
|
},
|
||
|
|
// These are suppressed in production unless BRAINY_LOG_LEVEL is set
|
||
|
|
warn: (message, ...args) => smartConsole.warn(message, ...args),
|
||
|
|
info: (message, ...args) => smartConsole.info(message, ...args),
|
||
|
|
debug: (message, ...args) => smartConsole.debug(message, ...args),
|
||
|
|
log: (message, ...args) => smartConsole.log(message, ...args)
|
||
|
|
};
|
||
|
|
//# sourceMappingURL=logger.js.map
|