diff --git a/package.json b/package.json index d93af3d1..b19ac5bb 100644 --- a/package.json +++ b/package.json @@ -89,17 +89,17 @@ "@tigerconnect/ffi-napi": "4.0.3-tc3", "@tigerconnect/ref-napi": "4.0.0-tc8", "axios": "^1.4.0", + "chalk": "4", "crypto-js": "^4.1.1", "electron-squirrel-startup": "^1.0.0", "electron-store": "^8.1.0", - "execa": "^5.1.1", + "execa": "^7.1.1", "iconv-lite": "^0.6.3", "ps-list": "^8.1.1", "rotating-file-stream": "^3.1.0", "semver": "^7.5.1", "systeminformation": "^5.17.17", "tar": "^6.1.15", - "tslog": "^3.3.4", "unzipper": "^0.10.14" } } diff --git a/packages/main/index.ts b/packages/main/index.ts index a6286eb2..0373f557 100644 --- a/packages/main/index.ts +++ b/packages/main/index.ts @@ -18,6 +18,8 @@ import DownloadManager from './downloadManager' import { getAppBaseDir } from './utils/path' import { setupHookProxy } from './utils/ipc-main' +require('source-map-support').install() + // Disable GPU Acceleration for Windows 7 if (release().startsWith('6.1')) app.disableHardwareAcceleration() diff --git a/packages/main/utils/log/index.ts b/packages/main/utils/log/index.ts new file mode 100644 index 00000000..1923d050 --- /dev/null +++ b/packages/main/utils/log/index.ts @@ -0,0 +1,110 @@ +import { format, formatWithOptions } from 'util' +import type { TLogLevel, TLogger, TLoggerController, TLoggerEnv } from './types' +import chalk from 'chalk' + +let pathAlias = (p: string) => p + +export type { TLogger, TLoggerEnv } + +const LogLevel: TLogLevel = ['SILLY', 'DEBUG', 'TRACE', 'INFO', 'WARN', 'ERROR', 'FATAL'] + +export function initLogger(proj = process.cwd()) { + chalk.level = 3 + pathAlias = p => { + return p.replace(proj, '') + } +} + +export function createLogger(name: string, output: (env: TLoggerEnv) => void) { + const ctrl: TLoggerController = { + level: 0, + inspect: {}, + } + + function log(level: number, ...args: any[]) { + if (level < ctrl.level) { + return + } + + const now = new Date() + const stack = new Error().stack?.split('\n')[3] ?? '' + + const match = /^\s*at\s+([\s\S]+?)(?:\s+\(([\s\S]+?)\))?\s*$/.exec(stack) + + const file = pathAlias(match?.[2] ?? match?.[1] ?? 'unknown') + const func = (match?.[2] ? match?.[1] : null) ?? '' + + const env: TLoggerEnv = { + name, + source: { + file, + func, + stack, + }, + date: { + year: now.getFullYear(), + month: now.getMonth() + 1, + date: now.getDate(), + hour: now.getHours(), + minute: now.getMinutes(), + second: now.getSeconds(), + msec: now.getMilliseconds(), + }, + level: LogLevel[level], + content: { + pretty: formatWithOptions( + { + ...ctrl.inspect, + colors: true, + }, + '', + ...args + ).slice(1), + mono: formatWithOptions( + { + ...ctrl.inspect, + colors: false, + }, + '', + ...args + ).slice(1), + }, + } + output(env) + } + + const logger = {} as TLogger + for (const [l, s] of LogLevel.entries()) { + logger[s.toLowerCase() as Lowercase] = (...args) => { + log(l, ...args) + } + } + return [logger, ctrl] as const +} + +export function createPresetFormatter(output: (out: { pretty: string; mono: string }) => void) { + return (env: TLoggerEnv) => { + const time = `${env.date.year.toString().padStart(4, '0')}-${env.date.month + .toString() + .padStart(2, '0')}-${env.date.date.toString().padStart(2, '0')} ${env.date.hour + .toString() + .padStart(2, '0')}-${env.date.minute.toString().padStart(2, '0')}:${env.date.second + .toString() + .padStart(2, '0')}.${env.date.msec.toString().padStart(3, '0')}` + + output({ + pretty: [ + time, + chalk.bold(env.level), + `[${chalk.bold(env.name)} ${env.source.file} ${env.source.func}]`, + env.content.pretty, + ].join('\t'), + mono: [ + time, + env.level, + `[${env.name} ${env.source.file} ${env.source.func}]`, + env.content.mono, + ].join('\t'), + }) + } +} diff --git a/packages/main/utils/log/types.ts b/packages/main/utils/log/types.ts new file mode 100644 index 00000000..681db060 --- /dev/null +++ b/packages/main/utils/log/types.ts @@ -0,0 +1,46 @@ +import type { InspectOptions } from 'node:util' + +type LowerAll = Ks extends [infer X, ...infer YS] + ? X extends string + ? YS extends string[] + ? [Lowercase, ...LowerAll] + : [] + : [] + : [] + +export type TLogLevel = ['SILLY', 'DEBUG', 'TRACE', 'INFO', 'WARN', 'ERROR', 'FATAL'] +export type TLogLevelLo = LowerAll +export type TLogFunction = (...args: any[]) => void + +export type TLogger = { + [key in TLogLevelLo[number]]: TLogFunction +} + +export interface TLoggerEnv { + name: string + source: { + file: string + func: string + stack: string + } + date: { + year: number + month: number + date: number + + hour: number + minute: number + second: number + msec: number // XXX + } + level: string + content: { + pretty: string + mono: string + } +} + +export interface TLoggerController { + level: number // greater equal + inspect: InspectOptions +} diff --git a/packages/main/utils/logger.ts b/packages/main/utils/logger.ts index 5e652b88..10c26fc4 100644 --- a/packages/main/utils/logger.ts +++ b/packages/main/utils/logger.ts @@ -1,19 +1,12 @@ import path from 'path' import { format } from 'date-fns' -import tslog, { type ILogObject } from 'tslog' -import { createWriteStream, mkdirSync, existsSync, WriteStream } from 'fs' +import { createWriteStream, mkdirSync, existsSync, WriteStream, appendFileSync } from 'fs' import { getAppBaseDir } from './path' import { setupHookProxy } from './ipc-main' +import { createLogger, createPresetFormatter, initLogger, type TLogger } from './log' class Logger { public constructor() { - this.main_ = new tslog.Logger({ - name: 'main', - }) - this.renderer_ = new tslog.Logger({ - name: 'renderer', - }) - if (!existsSync(this.log_file_dir_)) { mkdirSync(this.log_file_dir_) } @@ -23,72 +16,53 @@ class Logger { this.log_file_ = createWriteStream(this.log_file_path_, { flags: 'a' }) - this.main_.attachTransport( - { - silly: this.logToTransport, - debug: this.logToTransport, - trace: this.logToTransport, - info: this.logToTransport, - warn: this.logToTransport, - error: this.logToTransport, - fatal: this.logToTransport, - }, - 'debug' - ) - - this.renderer_.attachTransport( - { - silly: this.logToTransport, - debug: this.logToTransport, - trace: this.logToTransport, - info: this.logToTransport, - warn: this.logToTransport, - error: this.logToTransport, - fatal: this.logToTransport, - }, - 'debug' - ) + const formatter = createPresetFormatter(out => { + console.log(out.pretty) + this.log_file_.write(out.mono + '\n') + }) + + initLogger() + + const [ml, mc] = createLogger('main', formatter) + this.main_ = ml + + const [rl, rc] = createLogger('renderer', formatter) + this.renderer_ = rl // 提前初始化 setupHookProxy() globalThis.main.Util = { LogSilly: (...params) => { + params.pop() this.renderer_.silly(...params) }, LogDebug: (...params) => { + params.pop() this.renderer_.debug(...params) }, LogTrace: (...params) => { + params.pop() this.renderer_.trace(...params) }, LogInfo: (...params) => { + params.pop() this.renderer_.info(...params) }, LogWarn: (...params) => { + params.pop() this.renderer_.warn(...params) }, LogError: (...params) => { + params.pop() this.renderer_.error(...params) }, LogFatal: (...params) => { + params.pop() this.renderer_.fatal(...params) }, } } - private readonly logToTransport = (logObject: ILogObject): void => { - this.main_ - .getChildLogger({ - colorizePrettyLogs: false, - dateTimeTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone, - prettyInspectOptions: { - colors: false, - depth: 20, - }, - }) - .printPrettyLog(this.log_file_, logObject) - } - public get logFilePath(): string { return this.log_file_path_ } @@ -97,14 +71,14 @@ class Logger { return this.log_file_dir_ } - public get main(): tslog.Logger { + public get main() { return this.main_ } private readonly log_file_path_: string - private readonly main_: tslog.Logger - private readonly renderer_: tslog.Logger + private readonly main_: TLogger + private readonly renderer_: TLogger private readonly log_file_: WriteStream diff --git a/packages/main/utils/shell.ts b/packages/main/utils/shell.ts index 54b5c297..0cf2890b 100644 --- a/packages/main/utils/shell.ts +++ b/packages/main/utils/shell.ts @@ -1,8 +1,14 @@ -import execa, { type ExecaError } from 'execa' +import { execa as EXECA, type ExecaError } from 'execa' import iconv from 'iconv-lite' import logger from '@main/utils/logger' import { getPlatform } from '@main/utils/os' +let execa: typeof EXECA = () => void 0 as any + +import('execa').then(({ execa: e }) => { + execa = e +}) + interface ProcessOutput { stdout: string stderr: string diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 60dd80ce..f4da150d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ dependencies: axios: specifier: ^1.4.0 version: 1.4.0 + chalk: + specifier: '4' + version: 4.1.2 crypto-js: specifier: ^4.1.1 version: 4.1.1 @@ -24,8 +27,8 @@ dependencies: specifier: ^8.1.0 version: 8.1.0 execa: - specifier: ^5.1.1 - version: 5.1.1 + specifier: ^7.1.1 + version: 7.1.1 iconv-lite: specifier: ^0.6.3 version: 0.6.3 @@ -44,9 +47,6 @@ dependencies: tar: specifier: ^6.1.15 version: 6.1.15 - tslog: - specifier: ^3.3.4 - version: 3.3.4 unzipper: specifier: ^0.10.14 version: 0.10.14 @@ -2645,7 +2645,6 @@ packages: engines: {node: '>=8'} dependencies: color-convert: 2.0.1 - dev: true /anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} @@ -2845,6 +2844,7 @@ packages: /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true /buffer-indexof-polyfill@1.0.2: resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==} @@ -2946,7 +2946,6 @@ packages: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - dev: true /chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} @@ -3050,11 +3049,9 @@ packages: engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 - dev: true /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: true /color-support@1.1.3: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} @@ -3752,19 +3749,19 @@ packages: strip-eof: 1.0.0 dev: true - /execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} + /execa@7.1.1: + resolution: {integrity: sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} dependencies: cross-spawn: 7.0.3 get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 + npm-run-path: 5.1.0 + onetime: 6.0.0 signal-exit: 3.0.7 - strip-final-newline: 2.0.0 + strip-final-newline: 3.0.0 dev: false /expand-tilde@2.0.2: @@ -4266,7 +4263,6 @@ packages: /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - dev: true /has-property-descriptors@1.0.0: resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} @@ -4361,9 +4357,9 @@ packages: - supports-color dev: true - /human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} + /human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} dev: false /humanize-ms@1.2.1: @@ -4513,9 +4509,9 @@ packages: engines: {node: '>=0.10.0'} dev: true - /is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: false /is-unicode-supported@0.1.0: @@ -4928,6 +4924,11 @@ packages: engines: {node: '>=8'} dev: false + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: false + /mimic-response@1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} @@ -5205,11 +5206,11 @@ packages: path-key: 2.0.1 dev: true - /npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} + /npm-run-path@5.1.0: + resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: - path-key: 3.1.1 + path-key: 4.0.0 dev: false /npmlog@6.0.2: @@ -5256,6 +5257,13 @@ packages: dependencies: mimic-fn: 2.1.0 + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: false + /optionator@0.8.3: resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} engines: {node: '>= 0.8.0'} @@ -5426,6 +5434,11 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: false + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true @@ -6017,10 +6030,12 @@ packages: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 + dev: true /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + dev: true /spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} @@ -6106,9 +6121,9 @@ packages: engines: {node: '>=0.10.0'} dev: true - /strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} dev: false /strip-outer@1.0.1: @@ -6140,7 +6155,6 @@ packages: engines: {node: '>=8'} dependencies: has-flag: 4.0.0 - dev: true /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} @@ -6262,13 +6276,6 @@ packages: resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==} dev: true - /tslog@3.3.4: - resolution: {integrity: sha512-N0HHuHE0e/o75ALfkioFObknHR5dVchUad4F0XyFf3gXJYB++DewEzwGI/uIOM216E5a43ovnRNEeQIq9qgm4Q==} - engines: {node: '>=10'} - dependencies: - source-map-support: 0.5.21 - dev: false - /type-check@0.3.2: resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} engines: {node: '>= 0.8.0'}