@bernierllc/logging
Version:
A comprehensive logging package with Winston integration for audit and system logging
385 lines (384 loc) • 14.4 kB
JavaScript
;
/*
Copyright (c) 2025 Bernier LLC
This file is licensed to the client under a limited-use license.
The client may use and modify this code *only within the scope of the project it was delivered for*.
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceLoggingConfigurationLoader = exports.DEFAULT_LOGGING_SERVICE_CONFIG = void 0;
exports.loadServiceLoggingConfiguration = loadServiceLoggingConfiguration;
exports.loadServiceLoggingConfigurationWithSources = loadServiceLoggingConfigurationWithSources;
const cosmiconfig_1 = require("cosmiconfig");
const coreLogger_1 = require("../mocks/coreLogger");
/**
* Default configuration values for the logging service
*/
exports.DEFAULT_LOGGING_SERVICE_CONFIG = {
level: coreLogger_1.LogLevel.INFO,
enableCorrelation: true,
sanitize: true,
sanitizeFields: [
"password", "token", "secret", "key", "authorization", "cookie",
"apiKey", "accessToken", "refreshToken", "creditCard", "ssn",
"socialSecurityNumber", "sessionId", "jwt", "bearer"
],
context: {
service: 'service-logging',
version: process.env.npm_package_version || '0.1.12',
environment: process.env.NODE_ENV || 'development',
hostname: process.env.HOSTNAME || process.env.HOST || 'localhost',
pid: process.pid,
thread: undefined
},
system: {
enabled: true,
level: coreLogger_1.LogLevel.INFO,
console: {
enabled: process.env.NODE_ENV !== 'production',
level: coreLogger_1.LogLevel.DEBUG,
formatter: 'text',
formatterOptions: {
prettyPrint: false,
colorize: true,
includeTimestamp: true,
includeHostname: false
}
},
file: {
enabled: process.env.NODE_ENV === 'production',
filename: 'logs/system.log',
level: coreLogger_1.LogLevel.INFO,
formatter: 'json',
maxSize: 10 * 1024 * 1024, // 10MB
maxFiles: 5,
createDir: true,
formatterOptions: {
prettyPrint: false,
includeTimestamp: true,
includeHostname: true
}
}
},
audit: {
enabled: true,
level: coreLogger_1.LogLevel.INFO,
console: {
enabled: false,
level: coreLogger_1.LogLevel.INFO,
formatter: 'json',
formatterOptions: {
prettyPrint: false,
colorize: false,
includeTimestamp: true,
includeHostname: true
}
},
file: {
enabled: true,
filename: 'logs/audit.log',
level: coreLogger_1.LogLevel.INFO,
formatter: 'json',
maxSize: 50 * 1024 * 1024, // 50MB
maxFiles: 10,
createDir: true,
formatterOptions: {
prettyPrint: false,
includeTimestamp: true,
includeHostname: true
}
}
},
errorHandling: {
enableCallback: false,
logFailures: true
}
};
/**
* Environment variable mappings for the logging service
*/
const ENV_MAPPINGS = {
// Global settings
level: 'LOGGING_SERVICE_LEVEL',
enableCorrelation: 'LOGGING_SERVICE_ENABLE_CORRELATION',
sanitize: 'LOGGING_SERVICE_SANITIZE',
// Context settings
'context.service': 'LOGGING_SERVICE_NAME',
'context.version': 'LOGGING_SERVICE_VERSION',
'context.environment': 'NODE_ENV',
'context.hostname': 'HOSTNAME',
'context.thread': 'LOGGING_SERVICE_THREAD',
// System logging settings
'system.enabled': 'LOGGING_SERVICE_SYSTEM_ENABLED',
'system.level': 'LOGGING_SERVICE_SYSTEM_LEVEL',
'system.console.enabled': 'LOGGING_SERVICE_SYSTEM_CONSOLE_ENABLED',
'system.console.level': 'LOGGING_SERVICE_SYSTEM_CONSOLE_LEVEL',
'system.console.formatter': 'LOGGING_SERVICE_SYSTEM_CONSOLE_FORMATTER',
'system.file.enabled': 'LOGGING_SERVICE_SYSTEM_FILE_ENABLED',
'system.file.filename': 'LOGGING_SERVICE_SYSTEM_FILE_FILENAME',
'system.file.level': 'LOGGING_SERVICE_SYSTEM_FILE_LEVEL',
'system.file.formatter': 'LOGGING_SERVICE_SYSTEM_FILE_FORMATTER',
'system.file.maxSize': 'LOGGING_SERVICE_SYSTEM_FILE_MAX_SIZE',
'system.file.maxFiles': 'LOGGING_SERVICE_SYSTEM_FILE_MAX_FILES',
'system.http.enabled': 'LOGGING_SERVICE_SYSTEM_HTTP_ENABLED',
'system.http.url': 'LOGGING_SERVICE_SYSTEM_HTTP_URL',
'system.http.level': 'LOGGING_SERVICE_SYSTEM_HTTP_LEVEL',
// Audit logging settings
'audit.enabled': 'LOGGING_SERVICE_AUDIT_ENABLED',
'audit.level': 'LOGGING_SERVICE_AUDIT_LEVEL',
'audit.console.enabled': 'LOGGING_SERVICE_AUDIT_CONSOLE_ENABLED',
'audit.console.level': 'LOGGING_SERVICE_AUDIT_CONSOLE_LEVEL',
'audit.console.formatter': 'LOGGING_SERVICE_AUDIT_CONSOLE_FORMATTER',
'audit.file.enabled': 'LOGGING_SERVICE_AUDIT_FILE_ENABLED',
'audit.file.filename': 'LOGGING_SERVICE_AUDIT_FILE_FILENAME',
'audit.file.level': 'LOGGING_SERVICE_AUDIT_FILE_LEVEL',
'audit.file.formatter': 'LOGGING_SERVICE_AUDIT_FILE_FORMATTER',
'audit.file.maxSize': 'LOGGING_SERVICE_AUDIT_FILE_MAX_SIZE',
'audit.file.maxFiles': 'LOGGING_SERVICE_AUDIT_FILE_MAX_FILES',
'audit.database.enabled': 'LOGGING_SERVICE_AUDIT_DATABASE_ENABLED',
'audit.database.connectionString': 'LOGGING_SERVICE_AUDIT_DATABASE_CONNECTION_STRING',
'audit.database.tableName': 'LOGGING_SERVICE_AUDIT_DATABASE_TABLE_NAME',
'audit.database.level': 'LOGGING_SERVICE_AUDIT_DATABASE_LEVEL',
// Error handling settings
'errorHandling.enableCallback': 'LOGGING_SERVICE_ERROR_HANDLING_ENABLE_CALLBACK',
'errorHandling.logFailures': 'LOGGING_SERVICE_ERROR_HANDLING_LOG_FAILURES'
};
/**
* Configuration loader with source tracking
*/
class ServiceLoggingConfigurationLoader {
sources = [];
coreLoggerConfig = null;
/**
* Load core logger configuration to be overridden by service config
*/
setCoreLoggerConfiguration(coreConfig) {
this.coreLoggerConfig = coreConfig;
if (coreConfig) {
this.sources.push({
key: 'core-dependency',
value: coreConfig,
source: 'core-dependency',
description: 'Configuration from @bernierllc/logger core package'
});
}
}
/**
* Parse environment variable value to appropriate type
*/
parseEnvironmentValue(value) {
// Boolean values
if (value === 'true')
return true;
if (value === 'false')
return false;
// Number values
if (/^\d+$/.test(value))
return parseInt(value);
if (/^\d+\.\d+$/.test(value))
return parseFloat(value);
// Size values (e.g., "10m", "100k")
if (/^\d+[kmg]$/i.test(value)) {
return this.parseSizeToBytes(value);
}
// Array values (comma-separated)
if (value.includes(',')) {
return value.split(',').map(item => item.trim());
}
return value;
}
/**
* Parse size string to bytes
*/
parseSizeToBytes(size) {
const units = {
'k': 1024,
'm': 1024 * 1024,
'g': 1024 * 1024 * 1024
};
const match = size.toLowerCase().match(/^(\d+)([kmg])$/);
if (!match)
return parseInt(size);
const value = parseInt(match[1]);
const unit = match[2];
return value * (units[unit] || 1);
}
/**
* Set nested object value using dot notation
*/
setNestedValue(obj, path, value) {
const keys = path.split('.');
let current = obj;
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];
if (!(key in current)) {
current[key] = {};
}
current = current[key];
}
current[keys[keys.length - 1]] = value;
}
/**
* Load and resolve configuration from all sources
*/
loadConfiguration() {
const resolvedConfig = JSON.parse(JSON.stringify(exports.DEFAULT_LOGGING_SERVICE_CONFIG));
// Track default configuration source
this.sources.push({
key: 'defaults',
value: exports.DEFAULT_LOGGING_SERVICE_CONFIG,
source: 'default',
description: 'Package default configuration'
});
// Load configuration file if it exists
const explorer = (0, cosmiconfig_1.cosmiconfigSync)('logging-service', {
searchPlaces: [
'logging-service.config.js',
'logging-service.config.json',
'.logging-servicerc',
'.logging-servicerc.js',
'.logging-servicerc.json'
]
});
try {
const result = explorer.search();
if (result && result.config) {
this.mergeConfiguration(resolvedConfig, result.config);
this.sources.push({
key: 'file',
value: result.config,
source: 'file',
description: `Configuration file: ${result.filepath}`
});
}
}
catch (error) {
console.warn('Failed to load logging service configuration file:', error);
}
// Apply core logger configuration overrides
if (this.coreLoggerConfig) {
this.applyCoreLoggerOverrides(resolvedConfig);
}
// Apply environment variable overrides
this.applyEnvironmentOverrides(resolvedConfig);
// Log configuration sources for transparency
if (this.sources.length > 1) {
console.log('⚙️ Logging Service: Configuration loaded from multiple sources');
this.sources.forEach(source => {
if (source.source !== 'default') {
console.log(` └── ${source.source}: ${source.description}`);
}
});
}
return resolvedConfig;
}
/**
* Apply core logger configuration as base overrides
*/
applyCoreLoggerOverrides(config) {
if (!this.coreLoggerConfig)
return;
const coreConfig = this.coreLoggerConfig;
// Override level if core logger has different level
if (coreConfig.level && coreConfig.level !== config.level) {
config.level = coreConfig.level;
}
// Override context from core logger
if (coreConfig.context) {
config.context = { ...config.context, ...coreConfig.context };
}
// Override transports configuration from core logger
if (coreConfig.transports) {
if (coreConfig.transports.console) {
config.system.console = { ...config.system.console, ...coreConfig.transports.console };
}
if (coreConfig.transports.file) {
config.system.file = { ...config.system.file, ...coreConfig.transports.file };
}
}
// Override sanitization settings
if (coreConfig.sanitize !== undefined) {
config.sanitize = coreConfig.sanitize;
}
if (coreConfig.sanitizeFields) {
config.sanitizeFields = [...new Set([...config.sanitizeFields, ...coreConfig.sanitizeFields])];
}
}
/**
* Apply environment variable overrides
*/
applyEnvironmentOverrides(config) {
const envOverrides = [];
Object.entries(ENV_MAPPINGS).forEach(([configPath, envVar]) => {
const envValue = process.env[envVar];
if (envValue !== undefined) {
const parsedValue = this.parseEnvironmentValue(envValue);
this.setNestedValue(config, configPath, parsedValue);
envOverrides.push({
key: configPath,
value: parsedValue,
source: 'environment',
description: `Environment variable: ${envVar}=${envValue}`
});
}
});
if (envOverrides.length > 0) {
this.sources.push(...envOverrides);
}
}
/**
* Merge configuration objects recursively
*/
mergeConfiguration(target, source) {
Object.keys(source).forEach(key => {
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
if (!target[key])
target[key] = {};
this.mergeConfiguration(target[key], source[key]);
}
else {
target[key] = source[key];
}
});
}
/**
* Get configuration sources for transparency
*/
getConfigurationSources() {
return [...this.sources];
}
/**
* Get environment variable documentation
*/
getEnvironmentVariableDocumentation() {
const docs = {};
Object.entries(ENV_MAPPINGS).forEach(([configPath, envVar]) => {
docs[envVar] = `Controls ${configPath} configuration`;
});
return docs;
}
}
exports.ServiceLoggingConfigurationLoader = ServiceLoggingConfigurationLoader;
/**
* Load service logging configuration with core logger inheritance
*/
function loadServiceLoggingConfiguration(coreLoggerConfig) {
const loader = new ServiceLoggingConfigurationLoader();
if (coreLoggerConfig) {
loader.setCoreLoggerConfiguration(coreLoggerConfig);
}
return loader.loadConfiguration();
}
/**
* Load configuration with source tracking
*/
function loadServiceLoggingConfigurationWithSources(coreLoggerConfig) {
const loader = new ServiceLoggingConfigurationLoader();
if (coreLoggerConfig) {
loader.setCoreLoggerConfiguration(coreLoggerConfig);
}
const config = loader.loadConfiguration();
const sources = loader.getConfigurationSources();
return { config, sources };
}
//# sourceMappingURL=loadConfiguration.js.map