UNPKG

modbus-connect

Version:

Modbus RTU over Web Serial and Node.js SerialPort

210 lines (176 loc) 5.42 kB
// logger.js const LEVELS = ['debug', 'info', 'warn', 'error']; let currentLevel = 'info'; let enabled = true; let useColors = true; let buffering = true; const COLORS = { debug: '\x1b[36m', // cyan info: '\x1b[32m', // green warn: '\x1b[33m', // yellow error: '\x1b[31m', // red reset: '\x1b[0m' }; let groupLevel = 0; let globalContext = {}; let buffer = []; let flushTimeout = null; const FLUSH_INTERVAL = 300; const categoryLevels = {}; function getIndent() { return ' '.repeat(groupLevel); } function getTimestamp() { const d = new Date(); return d.toISOString().replace('T', ' ').replace('Z', ''); } function format(level, args, context = {}) { const timestamp = getTimestamp(); const color = useColors ? COLORS[level] : ''; const reset = useColors ? COLORS.reset : ''; const indent = getIndent(); const transport = globalContext.transport?.toUpperCase?.() || 'UNKNOWN'; let responseTimeStr = ''; if (context.responseTime != null) { responseTimeStr = ` [responseTime: ${context.responseTime} ms]`; } return [`${color}[${timestamp}] [modbus] [${transport}] [${level.toUpperCase()}]${responseTimeStr}`, indent, ...args, reset]; } function shouldLog(level, context = {}) { if (!enabled) return false; if (context.logger && categoryLevels[context.logger]) { return LEVELS.indexOf(level) >= LEVELS.indexOf(categoryLevels[context.logger]); } return LEVELS.indexOf(level) >= LEVELS.indexOf(currentLevel); } function flushBuffer() { for (const item of buffer) { const formatted = format(item.level, item.args, item.context); if (useColors) { const [head, indent, ...rest] = formatted; console[item.level](head + indent, ...rest); } else { console[item.level](...formatted); } } buffer = []; flushTimeout = null; } async function output(level, args, context, immediate = false) { if (!shouldLog(level, context)) return; if (immediate || !buffering) { const formatted = format(level, args, context); if (useColors) { const [head, indent, ...rest] = formatted; console[level](head + indent, ...rest); } else { console[level](...formatted); } return; } buffer.push({ level, args, context }); if (!flushTimeout) { flushTimeout = setTimeout(flushBuffer, FLUSH_INTERVAL); } } function splitArgsAndContext(args) { if (args.length > 1 && typeof args[args.length - 1] === 'object' && !Array.isArray(args[args.length - 1])) { const context = args.pop(); return { args, context }; } return { args, context: {} }; } const logger = { debug: async (...args) => { const { args: newArgs, context } = splitArgsAndContext([...args]); await output('debug', newArgs, context); }, info: async (...args) => { const { args: newArgs, context } = splitArgsAndContext([...args]); await output('info', newArgs, context); }, warn: async (...args) => { const { args: newArgs, context } = splitArgsAndContext([...args]); await output('warn', newArgs, context); }, error: async (...args) => { const { args: newArgs, context } = splitArgsAndContext([...args]); await output('error', newArgs, context, true); }, group() { groupLevel++; }, groupCollapsed() { groupLevel++; }, groupEnd() { if (groupLevel > 0) groupLevel--; }, setLevel(level) { if (LEVELS.includes(level)) { currentLevel = level; } else { throw new Error(`Unknown log level: ${level}`); } }, setLevelFor(category, level) { if (!LEVELS.includes(level)) throw new Error(`Unknown log level: ${level}`); categoryLevels[category] = level; }, enable() { enabled = true; }, disable() { enabled = false; }, getLevel() { return currentLevel; }, isEnabled() { return enabled; }, disableColors() { useColors = false; }, setGlobalContext(ctx) { globalContext = { ...ctx }; }, addGlobalContext(ctx) { globalContext = { ...globalContext, ...ctx }; }, setTransportType(type) { globalContext.transport = type; }, setBuffering(value) { buffering = !!value; }, flush() { flushBuffer(); }, createLogger(name) { if (!name) throw new Error('Logger name required'); return { debug: (...args) => { const { args: newArgs, context } = splitArgsAndContext([...args]); output('debug', newArgs, { ...context, logger: name }); }, info: (...args) => { const { args: newArgs, context } = splitArgsAndContext([...args]); output('info', newArgs, { ...context, logger: name }); }, warn: (...args) => { const { args: newArgs, context } = splitArgsAndContext([...args]); output('warn', newArgs, { ...context, logger: name }); }, error: (...args) => { const { args: newArgs, context } = splitArgsAndContext([...args]); output('error', newArgs, { ...context, logger: name }, true); }, group: () => logger.group(), groupCollapsed: () => logger.groupCollapsed(), groupEnd: () => logger.groupEnd(), setLevel: (lvl) => logger.setLevelFor(name, lvl), }; } }; module.exports = logger