import winston, { format, transports } from 'winston'; import path from 'path'; import mkdirp from 'mkdirp'; import { inspect } from 'util'; import { pick, pad, padStart, padEnd, isObject, defaults } from 'lodash'; import { cachedir, getUploadKeyLink } from './util'; // 获取当天的日志目录 function geLogPathByTime(date: Date): string { const month = date.getMonth() + 1; const monthNum = String(month).padStart(2, '0'); // TS MODIFY const day = padStart(String(date.getDate()), 2, '0'); return path.join(cachedir(), `log/${date.getFullYear()}/${monthNum}/${day}`); } /** * 按年月日期创建文件 */ function getFileNameByDate(level: string): string { return path.join(geLogPathByTime(new Date()), `${level}.log`); } // Define the format that mutates the info object. const cmdFormat = format((info) => { const prefix = info.prefix; if (prefix) { info.level = prefix; Reflect.deleteProperty(info, 'prefix'); } const result = pick(info, ['level', 'message', Symbol.for('level')]); return result as any; }); export function createLogger(catgory: string, print = true) { // TS MODIFY const list: (transports.ConsoleTransportInstance | transports.FileTransportInstance)[] = []; const filename = getFileNameByDate(catgory); // 提前创建文件夹 mkdirp.sync(path.dirname(filename)); // 在控制台打印日志 if (print) { list.push( new transports.Console({ format: format.combine( cmdFormat(), format.colorize({ all: true, }), format.errors({ stack: true }), format.simple() ), }) ); } list.push( new transports.File({ filename: filename, maxsize: 1024 * 1024, format: format.combine( format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss', }), format.errors({ stack: true }), format.json() ), }) ); // 提前创建文件夹 return winston.createLogger({ level: 'silly', transports: list, }); } const infoLogger = createLogger('info'); const errorLogger = createLogger('error'); const warnLogger = createLogger('warn'); /** * 中文是两个字符 */ function getLength(str: string) { let length = 0; const reg = /[\u4e00-\u9fa5]/; for (let i = 0; i < str.length; i++) { const num = reg.test(str.charAt(i)) ? 2 : 1; length += num; } return length; } function getPrefixByType(type, number) { if (type === 'asterisk') { return ' * '; } return ` ${number}. `; } /** * 根据错误码,展示更友好的提示 */ function errorTip(e): void { if (!e || !e.code) return; const { code, msg } = e; // 没有配置工具 if (code === 'NOT_CONFIG_TOOL_KEY') { logger.orderListLog([ '使用工具前,必须先配置密钥', // '参考以下文档进行初始化配置:', // 'https://www.npmjs.com/package/alipay-dev', ]); return; } //无相应的公钥信息 if (code === 'NO_VALID_TOOL_KEY') { logger.orderListLog([ 'toolId配置出错, 运行 alipaydev config list 查看当前配置', 'chrome打开以下地址配置:', getUploadKeyLink(), ]); return; } // 验签失败 if (code === 'SIGN_VERIFY_FAIL') { logger.dashedLog([ msg, '命令行运行 alipaydev config list 查看当前工具私钥', `浏览器打开 ${getUploadKeyLink()} 查看工具公钥`, ]); return; } // ip 白名单验证失败 if (code === 'IP_VERIFY_FAIL') { logger.dashedLog([ '当前ip不在白名单列表中', `浏览器打开 ${getUploadKeyLink()} 查看ip白名单列表`, ]); return; } // 没有开通阿里云 if (code === 'ACCOUNT_NOT_BIND') { logger.dashedLog([ msg, '开通小程序云参考文档:', 'https://opendocs.alipay.com/mini/cloudservice/aban9r', ]); return; } } const logger = { log(...args) { const result = args.map((item) => { if (isObject(item)) { return inspect(item, { depth: 4, colors: true, }); } return item; }); console.log(...result); }, info(message: any, options?: object) { infoLogger.info({ message, ...options, }); }, error(message: any, options?: object) { errorLogger.error({ message, ...options, }); errorTip(message); }, warn(message: any, options?: object) { warnLogger.warn({ message, ...options, }); }, /** * 在控制台输出日志,用虚线包起来,更明显 * @example * dashedLog('www.taobao.com') * dashedLog(['www.taobao.com', '']) */ dashedLog(list: string[], options?): void { let maxLength = 0; // TS MODIFY const lengths: number[] = []; const params = defaults(options, { type: 'asterisk', }); const msgBody = ['', ...list, '']; for (const item of msgBody) { const strLength = getLength(item); lengths.push(strLength); maxLength = Math.max(strLength, maxLength); } maxLength += 7; const paddingLeft = padStart('', 4); const dashedWrap = pad('', maxLength, '-'); console.log(paddingLeft + dashedWrap); msgBody.forEach((item, index) => { const padLength = maxLength + item.length - lengths[index] - 2; if (index === 0 || index === msgBody.length - 1) { console.log(paddingLeft + `|${padEnd(item, padLength, ' ')}|`); } else { const prefix = getPrefixByType(params.type, index); console.log(paddingLeft + `|${prefix}${padEnd(item, padLength - prefix.length, ' ')}|`); } }); console.log(paddingLeft + dashedWrap); }, orderListLog(list: string[]): void { console.log(pad('', 60, '-')); list.forEach((item, index) => { console.log(`${index + 1}. ${item}`); }); console.log(pad('', 60, '-')); }, geLogPathByTime, }; export default logger;