Files
yunrui_asset/install/logger.js
T
zhang1106 9a7a975b57 refactor(install): 统一安装脚本日志风格与UI样式
- 新增日志图标、分隔符和格式化工具函数
- 统一所有安装脚本的输出格式与视觉风格
- 使用统一的图标和配色方案优化交互体验
- 重构命令行输入提示样式,添加视觉标识
2026-05-11 15:29:41 +08:00

159 lines
4.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const { INSTALL_STEPS } = require('./constants');
let currentStepIndex = 0;
let logFileStream = null;
const colors = {
reset: '\x1b[0m',
bright: '\x1b[1m',
dim: '\x1b[2m',
green: '\x1b[32m',
yellow: '\x1b[33m',
red: '\x1b[31m',
cyan: '\x1b[36m',
gray: '\x1b[90m',
magenta: '\x1b[35m',
blue: '\x1b[34m',
bgCyan: '\x1b[46m',
bgBlue: '\x1b[44m',
white: '\x1b[37m',
};
const ICONS = {
info: '',
success: '✔',
warning: '⚠',
error: '✖',
arrow: '▶',
bullet: '●',
pointer: '',
check: '✓',
cross: '✗',
dash: '─',
doubleDash: '═',
corner: '└',
pipe: '│',
diamond: '◆',
star: '★',
};
function padCenter(text, width) {
const stripped = text.replace(/\x1b\[[0-9;]*m/g, '');
const len = stripped.length;
const pad = Math.max(0, width - len);
const left = Math.floor(pad / 2);
const right = pad - left;
return ' '.repeat(left) + text + ' '.repeat(right);
}
function progressBar(current, total, width = 20) {
const filled = Math.round((current / total) * width);
const empty = width - filled;
const bar = `${colors.cyan}${'━'.repeat(filled)}${colors.dim}${'╌'.repeat(empty)}${colors.reset}`;
return bar;
}
const log = {
info: (msg) => console.log(` ${colors.cyan}${ICONS.info}${colors.reset} ${msg}`),
success: (msg) => console.log(` ${colors.green}${ICONS.success}${colors.reset} ${msg}`),
warning: (msg) => console.log(` ${colors.yellow}${ICONS.warning}${colors.reset} ${msg}`),
error: (msg) => console.log(` ${colors.red}${ICONS.error}${colors.reset} ${msg}`),
step: (msg) => {
const idx = INSTALL_STEPS.indexOf(msg);
if (idx >= 0) {
currentStepIndex = idx;
}
const current = currentStepIndex + 1;
const total = INSTALL_STEPS.length;
const bar = progressBar(current, total, 15);
const progress = `${colors.dim}[${current}/${total}]${colors.reset} ${bar}`;
console.log('');
console.log(`${progress}`);
console.log(` ${colors.bright}${colors.cyan}${ICONS.arrow}${colors.reset} ${colors.bright}${msg}${colors.reset}`);
},
divider: () => console.log(` ${colors.dim}${ICONS.dash.repeat(56)}${colors.reset}`),
thickDivider: () => console.log(` ${colors.dim}${ICONS.doubleDash.repeat(56)}${colors.reset}`),
subStep: (msg) => console.log(` ${colors.dim}${ICONS.corner}${colors.reset} ${msg}`),
keyValue: (key, value) => {
const keyStr = `${colors.dim}${key}${colors.reset}`;
const valueStr = `${colors.cyan}${value}${colors.reset}`;
console.log(` ${ICONS.pipe} ${keyStr} ${valueStr}`);
},
banner: (title, subtitle, version) => {
const width = 58;
const top = `${colors.bright}${colors.cyan}${ICONS.doubleDash.repeat(width + 2)}${colors.reset}`;
const bottom = top;
const titleLine = padCenter(`${colors.bright}${colors.white}${title}${colors.reset}`, width);
const subtitleLine = padCenter(`${colors.dim}${subtitle}${colors.reset}`, width);
const versionLine = version ? padCenter(`${colors.cyan}v${version}${colors.reset}`, width) : '';
console.log('');
console.log(` ${colors.cyan}${top.substring(2)}${colors.reset}`);
console.log(` ${colors.cyan}${titleLine}${colors.reset}`);
if (subtitle) {
console.log(` ${colors.cyan}${subtitleLine}${colors.reset}`);
}
if (version) {
console.log(` ${colors.cyan}${versionLine}${colors.reset}`);
}
console.log(` ${colors.cyan}${bottom.substring(2)}${colors.reset}`);
console.log('');
},
section: (title) => {
console.log('');
console.log(` ${colors.bright}${colors.magenta}${ICONS.diamond}${colors.reset} ${colors.bright}${title}${colors.reset}`);
log.divider();
},
tableRow: (items, widths) => {
const parts = items.map((item, i) => {
const stripped = item.replace(/\x1b\[[0-9;]*m/g, '');
const pad = Math.max(0, (widths[i] || 20) - stripped.length);
return item + ' '.repeat(pad);
});
console.log(` ${ICONS.pipe} ${parts.join(' ')}`);
},
};
function initLogFile(LOG_DIR) {
const fs = require('fs');
const path = require('path');
if (!fs.existsSync(LOG_DIR)) {
fs.mkdirSync(LOG_DIR, { recursive: true });
}
const logFileName = `install_${new Date().toISOString().replace(/[:.]/g, '-')}.log`;
const logFilePath = path.join(LOG_DIR, logFileName);
logFileStream = fs.createWriteStream(logFilePath, { flags: 'a' });
const originalConsoleLog = console.log;
console.log = (...args) => {
const timestamp = new Date().toISOString();
const message = args.map(arg =>
typeof arg === 'string' ? arg.replace(/\x1b\[[0-9;]*m/g, '') : String(arg)
).join(' ');
logFileStream.write(`[${timestamp}] ${message}\n`);
originalConsoleLog.apply(console, args);
};
}
function closeLogFile() {
if (logFileStream) {
logFileStream.end();
}
}
module.exports = {
colors,
ICONS,
log,
padCenter,
initLogFile,
closeLogFile,
};