Server now injects window.FRONTEND_TYPE in all base templates via get_context_for_frontend(). Both log-config.js and dev-toolbar.js read this instead of guessing from URL paths, fixing: - UNKNOWN prefix on merchant pages - Incorrect detection on custom domains/subdomains in prod Also adds frontend_type to login page contexts (admin, merchant, store). Renames all [SHOP] logger prefixes to [STOREFRONT] across 7 files (storefront-layout.js + 6 storefront templates). Adds 'merchant' and 'storefront' to log-config.js frontend detection, log levels, and logger selection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
520 lines
16 KiB
JavaScript
520 lines
16 KiB
JavaScript
// static/shared/js/log-config.js
|
||
// noqa: js-001 - This IS the centralized logger implementation
|
||
/**
|
||
* Centralized Logging Configuration for ALL Frontends
|
||
*
|
||
* This file provides a consistent logging system across:
|
||
* - Admin Frontend
|
||
* - Store Frontend
|
||
* - Shop Frontend
|
||
*
|
||
* Each frontend can customize log levels while sharing the same logging infrastructure.
|
||
*
|
||
* Usage in any frontend:
|
||
* ```javascript
|
||
* // Use the global logger
|
||
* log.info('Page loaded');
|
||
* log.error('Something went wrong', error);
|
||
*
|
||
* // Or use a pre-configured logger
|
||
* const storeLog = window.LogConfig.loggers.stores;
|
||
* storeLog.info('Stores loaded');
|
||
*
|
||
* // Or create a custom logger
|
||
* const pageLog = window.LogConfig.createLogger('MY-PAGE', 3);
|
||
* pageLog.info('Page initialized');
|
||
* ```
|
||
*/
|
||
|
||
// ============================================================================
|
||
// LOG LEVELS
|
||
// ============================================================================
|
||
|
||
const LOG_LEVELS = {
|
||
ERROR: 1, // Only errors
|
||
WARN: 2, // Errors and warnings
|
||
INFO: 3, // Errors, warnings, and info (default)
|
||
DEBUG: 4 // Everything including debug messages
|
||
};
|
||
|
||
// ============================================================================
|
||
// FRONTEND DETECTION
|
||
// ============================================================================
|
||
|
||
/**
|
||
* Detect which frontend we're in based on URL path
|
||
* @returns {string} 'admin' | 'store' | 'merchant' | 'storefront' | 'unknown'
|
||
*/
|
||
function detectFrontend() {
|
||
// Prefer server-injected value (set in base templates before this script loads)
|
||
if (window.FRONTEND_TYPE) return window.FRONTEND_TYPE;
|
||
|
||
return 'unknown';
|
||
}
|
||
|
||
// ============================================================================
|
||
// ENVIRONMENT DETECTION
|
||
// ============================================================================
|
||
|
||
/**
|
||
* Detect environment based on hostname
|
||
* @returns {string} 'development' | 'production'
|
||
*/
|
||
function detectEnvironment() {
|
||
const hostname = window.location.hostname;
|
||
|
||
// Development environments
|
||
if (hostname === 'localhost' ||
|
||
hostname === '127.0.0.1' ||
|
||
hostname.startsWith('192.168.') ||
|
||
hostname.endsWith('.local')) {
|
||
return 'development';
|
||
}
|
||
|
||
return 'production';
|
||
}
|
||
|
||
// ============================================================================
|
||
// LOG LEVEL CONFIGURATION
|
||
// ============================================================================
|
||
|
||
/**
|
||
* Default log levels per frontend
|
||
* Can be overridden by environment
|
||
*/
|
||
const DEFAULT_LOG_LEVELS = {
|
||
admin: {
|
||
development: LOG_LEVELS.DEBUG, // Show everything in development
|
||
production: LOG_LEVELS.WARN // Only warnings and errors in production
|
||
},
|
||
store: {
|
||
development: LOG_LEVELS.DEBUG,
|
||
production: LOG_LEVELS.INFO // Stores might need more logging
|
||
},
|
||
merchant: {
|
||
development: LOG_LEVELS.DEBUG,
|
||
production: LOG_LEVELS.INFO // Merchant portal: same as store
|
||
},
|
||
storefront: {
|
||
development: LOG_LEVELS.DEBUG,
|
||
production: LOG_LEVELS.ERROR // Storefront: minimal logging in production
|
||
},
|
||
unknown: {
|
||
development: LOG_LEVELS.DEBUG,
|
||
production: LOG_LEVELS.WARN
|
||
}
|
||
};
|
||
|
||
// ============================================================================
|
||
// ACTIVE LOG LEVEL DETERMINATION
|
||
// ============================================================================
|
||
|
||
const CURRENT_FRONTEND = detectFrontend();
|
||
const CURRENT_ENVIRONMENT = detectEnvironment();
|
||
const ACTIVE_LOG_LEVEL = DEFAULT_LOG_LEVELS[CURRENT_FRONTEND][CURRENT_ENVIRONMENT];
|
||
|
||
// ============================================================================
|
||
// LOGGING UTILITIES
|
||
// ============================================================================
|
||
|
||
/**
|
||
* Create a logger with a specific prefix and log level
|
||
*
|
||
* @param {string} prefix - Logger prefix (e.g., 'STORES', 'THEME', 'PRODUCTS')
|
||
* @param {number} level - Log level (1-4, defaults to ACTIVE_LOG_LEVEL)
|
||
* @param {string} context - Optional frontend context (defaults to CURRENT_FRONTEND)
|
||
* @returns {Object} Logger object with error, warn, info, debug methods
|
||
*/
|
||
function createLogger(prefix = 'APP', level = ACTIVE_LOG_LEVEL, context = CURRENT_FRONTEND) {
|
||
const frontendPrefix = context.toUpperCase();
|
||
const formatPrefix = (emoji, label) => `${emoji} [${frontendPrefix}:${prefix} ${label}]`;
|
||
|
||
return {
|
||
error: (...args) => {
|
||
if (level >= LOG_LEVELS.ERROR) {
|
||
console.error(formatPrefix('❌', 'ERROR'), ...args);
|
||
}
|
||
},
|
||
|
||
warn: (...args) => {
|
||
if (level >= LOG_LEVELS.WARN) {
|
||
console.warn(formatPrefix('⚠️', 'WARN'), ...args);
|
||
}
|
||
},
|
||
|
||
info: (...args) => {
|
||
if (level >= LOG_LEVELS.INFO) {
|
||
console.info(formatPrefix('ℹ️', 'INFO'), ...args);
|
||
}
|
||
},
|
||
|
||
debug: (...args) => {
|
||
if (level >= LOG_LEVELS.DEBUG) {
|
||
console.log(formatPrefix('🔍', 'DEBUG'), ...args);
|
||
}
|
||
},
|
||
|
||
// Additional utility methods
|
||
group: (label) => {
|
||
if (level >= LOG_LEVELS.INFO) {
|
||
console.group(formatPrefix('📂', 'GROUP') + ' ' + label);
|
||
}
|
||
},
|
||
|
||
groupEnd: () => {
|
||
if (level >= LOG_LEVELS.INFO) {
|
||
console.groupEnd();
|
||
}
|
||
},
|
||
|
||
table: (data) => {
|
||
if (level >= LOG_LEVELS.DEBUG) {
|
||
console.table(data);
|
||
}
|
||
},
|
||
|
||
time: (label) => {
|
||
if (level >= LOG_LEVELS.DEBUG) {
|
||
console.time(formatPrefix('⏱️', 'TIME') + ' ' + label);
|
||
}
|
||
},
|
||
|
||
timeEnd: (label) => {
|
||
if (level >= LOG_LEVELS.DEBUG) {
|
||
console.timeEnd(formatPrefix('⏱️', 'TIME') + ' ' + label);
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
// ============================================================================
|
||
// DEFAULT GLOBAL LOGGER
|
||
// ============================================================================
|
||
|
||
/**
|
||
* Default logger for general operations
|
||
* Automatically prefixed with current frontend
|
||
*/
|
||
const log = createLogger('APP', ACTIVE_LOG_LEVEL);
|
||
|
||
// ============================================================================
|
||
// PRE-CONFIGURED LOGGERS FOR ADMIN FRONTEND
|
||
// ============================================================================
|
||
|
||
const adminLoggers = {
|
||
// Store management
|
||
stores: createLogger('STORES', ACTIVE_LOG_LEVEL),
|
||
storeTheme: createLogger('THEME', ACTIVE_LOG_LEVEL),
|
||
storeUsers: createLogger('STORE-USERS', ACTIVE_LOG_LEVEL),
|
||
|
||
// Merchant management
|
||
merchants: createLogger('COMPANIES', ACTIVE_LOG_LEVEL),
|
||
|
||
// Product management
|
||
products: createLogger('PRODUCTS', ACTIVE_LOG_LEVEL),
|
||
inventory: createLogger('INVENTORY', ACTIVE_LOG_LEVEL),
|
||
|
||
// Order management
|
||
orders: createLogger('ORDERS', ACTIVE_LOG_LEVEL),
|
||
|
||
// User management
|
||
users: createLogger('USERS', ACTIVE_LOG_LEVEL),
|
||
|
||
// Admin operations
|
||
audit: createLogger('AUDIT', ACTIVE_LOG_LEVEL),
|
||
dashboard: createLogger('DASHBOARD', ACTIVE_LOG_LEVEL),
|
||
|
||
// Import operations
|
||
imports: createLogger('IMPORTS', ACTIVE_LOG_LEVEL),
|
||
marketplace: createLogger('MARKETPLACE', ACTIVE_LOG_LEVEL)
|
||
};
|
||
|
||
// ============================================================================
|
||
// PRE-CONFIGURED LOGGERS FOR STORE FRONTEND
|
||
// ============================================================================
|
||
|
||
const storeLoggers = {
|
||
// Dashboard
|
||
dashboard: createLogger('DASHBOARD', ACTIVE_LOG_LEVEL),
|
||
|
||
// Product management
|
||
storeProducts: createLogger('PRODUCTS', ACTIVE_LOG_LEVEL),
|
||
storeInventory: createLogger('INVENTORY', ACTIVE_LOG_LEVEL),
|
||
marketplace: createLogger('MARKETPLACE', ACTIVE_LOG_LEVEL),
|
||
|
||
// Order management
|
||
storeOrders: createLogger('ORDERS', ACTIVE_LOG_LEVEL),
|
||
orderDetail: createLogger('ORDER-DETAIL', ACTIVE_LOG_LEVEL),
|
||
|
||
// Theme customization
|
||
theme: createLogger('THEME', ACTIVE_LOG_LEVEL),
|
||
|
||
// Settings
|
||
storeSettings: createLogger('SETTINGS', ACTIVE_LOG_LEVEL),
|
||
|
||
// Analytics
|
||
storeAnalytics: createLogger('ANALYTICS', ACTIVE_LOG_LEVEL),
|
||
|
||
// Messaging
|
||
messages: createLogger('MESSAGES', ACTIVE_LOG_LEVEL),
|
||
|
||
// Team
|
||
storeTeam: createLogger('TEAM', ACTIVE_LOG_LEVEL),
|
||
|
||
// Notifications
|
||
storeNotifications: createLogger('NOTIFICATIONS', ACTIVE_LOG_LEVEL),
|
||
|
||
// Profile
|
||
storeProfile: createLogger('PROFILE', ACTIVE_LOG_LEVEL),
|
||
|
||
// Customers
|
||
storeCustomers: createLogger('CUSTOMERS', ACTIVE_LOG_LEVEL),
|
||
|
||
// Content pages
|
||
contentPages: createLogger('CONTENT-PAGES', ACTIVE_LOG_LEVEL),
|
||
contentPageEdit: createLogger('CONTENT-PAGE-EDIT', ACTIVE_LOG_LEVEL)
|
||
};
|
||
|
||
// ============================================================================
|
||
// PRE-CONFIGURED LOGGERS FOR STOREFRONT
|
||
// ============================================================================
|
||
|
||
const storefrontLoggers = {
|
||
// Product browsing
|
||
catalog: createLogger('CATALOG', ACTIVE_LOG_LEVEL),
|
||
product: createLogger('PRODUCT', ACTIVE_LOG_LEVEL),
|
||
search: createLogger('SEARCH', ACTIVE_LOG_LEVEL),
|
||
|
||
// Shopping cart
|
||
cart: createLogger('CART', ACTIVE_LOG_LEVEL),
|
||
checkout: createLogger('CHECKOUT', ACTIVE_LOG_LEVEL),
|
||
|
||
// User account
|
||
account: createLogger('ACCOUNT', ACTIVE_LOG_LEVEL),
|
||
orders: createLogger('ORDERS', ACTIVE_LOG_LEVEL),
|
||
|
||
// Wishlist
|
||
wishlist: createLogger('WISHLIST', ACTIVE_LOG_LEVEL)
|
||
};
|
||
|
||
// ============================================================================
|
||
// SMART LOGGER SELECTION
|
||
// ============================================================================
|
||
|
||
/**
|
||
* Get the appropriate logger set for current frontend
|
||
*/
|
||
function getLoggers() {
|
||
switch (CURRENT_FRONTEND) {
|
||
case 'admin':
|
||
return adminLoggers;
|
||
case 'store':
|
||
return storeLoggers;
|
||
case 'merchant':
|
||
return storeLoggers; // Merchant portal reuses store logger set
|
||
case 'storefront':
|
||
return storefrontLoggers;
|
||
default:
|
||
return {}; // Empty object, use createLogger instead
|
||
}
|
||
}
|
||
|
||
// Export frontend-specific loggers
|
||
const loggers = getLoggers();
|
||
|
||
// ============================================================================
|
||
// API CALL LOGGING
|
||
// ============================================================================
|
||
|
||
/**
|
||
* Log API calls with consistent formatting
|
||
*
|
||
* @param {string} method - HTTP method (GET, POST, PUT, DELETE)
|
||
* @param {string} url - API endpoint URL
|
||
* @param {*} data - Request/response data (optional)
|
||
* @param {string} status - 'request' or 'response'
|
||
*/
|
||
function logApiCall(method, url, data = null, status = 'request') {
|
||
const apiLogger = createLogger('API', ACTIVE_LOG_LEVEL);
|
||
|
||
const emoji = status === 'request' ? '📤' : '📥';
|
||
const message = `${emoji} ${method} ${url}`;
|
||
|
||
if (ACTIVE_LOG_LEVEL >= LOG_LEVELS.DEBUG) {
|
||
if (data) {
|
||
apiLogger.debug(message, data);
|
||
} else {
|
||
apiLogger.debug(message);
|
||
}
|
||
} else if (ACTIVE_LOG_LEVEL >= LOG_LEVELS.INFO) {
|
||
apiLogger.info(message);
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// ERROR LOGGING
|
||
// ============================================================================
|
||
|
||
/**
|
||
* Log errors with stack traces
|
||
*
|
||
* @param {Error} error - Error object
|
||
* @param {string} context - Context where error occurred
|
||
*/
|
||
function logError(error, context = 'Unknown') {
|
||
const errorLogger = createLogger('ERROR', ACTIVE_LOG_LEVEL);
|
||
|
||
errorLogger.error(`Error in ${context}:`, error.message);
|
||
|
||
if (ACTIVE_LOG_LEVEL >= LOG_LEVELS.DEBUG && error.stack) {
|
||
console.error('Stack trace:', error.stack);
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// PERFORMANCE LOGGING
|
||
// ============================================================================
|
||
|
||
/**
|
||
* Log performance metrics
|
||
*
|
||
* @param {string} operation - Operation name
|
||
* @param {number} duration - Duration in milliseconds
|
||
*/
|
||
function logPerformance(operation, duration) {
|
||
const perfLogger = createLogger('PERF', ACTIVE_LOG_LEVEL);
|
||
|
||
if (ACTIVE_LOG_LEVEL >= LOG_LEVELS.DEBUG) {
|
||
const emoji = duration < 100 ? '⚡' : duration < 500 ? '⏱️' : '🐌';
|
||
perfLogger.debug(`${emoji} ${operation} took ${duration}ms`);
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// EXPORTS (for modules)
|
||
// ============================================================================
|
||
|
||
// For pages that use module imports
|
||
if (typeof module !== 'undefined' && module.exports) {
|
||
module.exports = {
|
||
LOG_LEVELS,
|
||
log,
|
||
loggers,
|
||
createLogger,
|
||
logApiCall,
|
||
logError,
|
||
logPerformance,
|
||
detectFrontend,
|
||
detectEnvironment
|
||
};
|
||
}
|
||
|
||
// ============================================================================
|
||
// GLOBAL ACCESS (for inline scripts)
|
||
// ============================================================================
|
||
|
||
// Make available globally for inline scripts
|
||
window.LogConfig = {
|
||
LOG_LEVELS,
|
||
log,
|
||
loggers,
|
||
createLogger,
|
||
logApiCall,
|
||
logError,
|
||
logPerformance,
|
||
|
||
// Expose frontend/environment info
|
||
frontend: CURRENT_FRONTEND,
|
||
environment: CURRENT_ENVIRONMENT,
|
||
logLevel: ACTIVE_LOG_LEVEL
|
||
};
|
||
|
||
// ============================================================================
|
||
// INITIALIZATION
|
||
// ============================================================================
|
||
|
||
// Log that logging system is initialized
|
||
if (ACTIVE_LOG_LEVEL >= LOG_LEVELS.INFO) {
|
||
const frontendName = CURRENT_FRONTEND.charAt(0).toUpperCase() + CURRENT_FRONTEND.slice(1);
|
||
const envName = CURRENT_ENVIRONMENT.charAt(0).toUpperCase() + CURRENT_ENVIRONMENT.slice(1);
|
||
const levelName = Object.keys(LOG_LEVELS).find(k => LOG_LEVELS[k] === ACTIVE_LOG_LEVEL);
|
||
|
||
console.log(
|
||
`%c🎛️ ${frontendName} Frontend Logging System Initialized`,
|
||
'font-weight: bold; font-size: 14px; color: #6366f1;'
|
||
);
|
||
console.log(
|
||
`%c Environment: ${envName}`,
|
||
'color: #8b5cf6;'
|
||
);
|
||
console.log(
|
||
`%c Log Level: ${ACTIVE_LOG_LEVEL} (${levelName})`,
|
||
'color: #8b5cf6;'
|
||
);
|
||
}
|
||
|
||
// ============================================================================
|
||
// USAGE EXAMPLES (for developers)
|
||
// ============================================================================
|
||
|
||
/*
|
||
|
||
EXAMPLE 1: Use global logger
|
||
=============================
|
||
window.LogConfig.log.info('Application started');
|
||
window.LogConfig.log.error('Failed to load data', error);
|
||
|
||
|
||
EXAMPLE 2: Use pre-configured logger (RECOMMENDED)
|
||
===================================================
|
||
// In admin frontend
|
||
const themeLog = window.LogConfig.loggers.storeTheme;
|
||
themeLog.info('Theme editor loaded');
|
||
|
||
// In store frontend
|
||
const dashLog = window.LogConfig.loggers.dashboard;
|
||
dashLog.info('Dashboard initialized');
|
||
|
||
// In shop frontend
|
||
const cartLog = window.LogConfig.loggers.cart;
|
||
cartLog.info('Cart updated');
|
||
|
||
|
||
EXAMPLE 3: Create custom logger
|
||
================================
|
||
const myLog = window.LogConfig.createLogger('MY-FEATURE', 4);
|
||
myLog.info('Feature initialized');
|
||
|
||
|
||
EXAMPLE 4: Check current frontend
|
||
==================================
|
||
if (window.LogConfig.frontend === 'admin') {
|
||
// Admin-specific code
|
||
}
|
||
|
||
|
||
EXAMPLE 5: API call logging
|
||
============================
|
||
window.LogConfig.logApiCall('GET', '/api/stores', null, 'request');
|
||
const data = await apiClient.get('/api/stores');
|
||
window.LogConfig.logApiCall('GET', '/api/stores', data, 'response');
|
||
|
||
|
||
EXAMPLE 6: Performance logging
|
||
===============================
|
||
const start = performance.now();
|
||
await loadData();
|
||
const duration = performance.now() - start;
|
||
window.LogConfig.logPerformance('Load Data', duration);
|
||
|
||
|
||
EXAMPLE 7: Error logging
|
||
=========================
|
||
try {
|
||
await saveData();
|
||
} catch (error) {
|
||
window.LogConfig.logError(error, 'Save Operation');
|
||
}
|
||
|
||
*/
|