Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enhance performance tracking and output #34

Merged
merged 2 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 85 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const chokidar = require('chokidar');
const fs = require('fs');
const { startProxyServer } = require('./proxy');
const consoleOutput = require('./output');
const { performance } = require('perf_hooks');

function run() {
const { cmdPath, tscArgs, parentArgs, childArgs } = parseArgs(process.argv);
Expand All @@ -35,7 +36,7 @@ function run() {
tscArgs.push(['--preserveWatchOutput']);
}

// 添加 --listEmittedFiles 参数以便于获取编译后的文件列表
// 添加 --listEmittedFiles 参数以便于获取编译后文件列表
if (
tscArgs.includes('--watch') &&
!tscArgs.includes('--listEmittedFiles') &&
Expand Down Expand Up @@ -131,6 +132,13 @@ function run() {
const restart = debounce(async fileChangedList => {
debug(`fileChangedList: ${fileChangedList}`);
if (fileChangedList && fileChangedList.length === 0) return;

performance.clearMarks();
performance.clearMeasures();

// 1. 进程关闭阶段
performance.mark('kill-start');

async function aliasReplace() {
if (hasPaths) {
// 这里使用全量替换,tsc 增量编译会把老文件修改回去
Expand All @@ -148,11 +156,19 @@ function run() {
}

await Promise.all([runChild && runChild.kill(), aliasReplace()]);
performance.mark('kill-end');
// 根据是否有 alias replace 来决定性能统计的名称
const killMeasureName = hasPaths ? 'Kill & Alias Replace' : 'Process Kill';
performance.measure(killMeasureName, 'kill-start', 'kill-end');

// 清理代理文件缓存
if (proxyServer) {
proxyServer.clearCache();
}
debug('Fork new child process');

// 3. 应用启动阶段
performance.mark('app-start');
runChild && runChild.forkChild();
}, 1000);

Expand Down Expand Up @@ -217,32 +233,100 @@ function run() {
}

// 启动执行 tsc 命令
const perfStats = [];
let restartPerfStats = [];

// 检查是否需要输出性能统计
const isPerfInit = childArgs.includes('--perf-init');

// 1. TSC 编译阶段
performance.mark('tsc-start');
const child = forkTsc(tscArgs, {
cwd,
onFirstWatchCompileSuccess: () => {
performance.mark('tsc-end');
performance.measure('TSC Compilation', 'tsc-start', 'tsc-end');
const tscMeasure = performance.getEntriesByName('TSC Compilation')[0];
perfStats.push({
name: 'TSC Compilation',
duration: tscMeasure.duration
});

// 2. 后处理阶段(alias 替换和文件拷贝)
performance.mark('post-start');
runAfterTsc();
performance.mark('post-end');
performance.measure('Post Processing', 'post-start', 'post-end');
const postMeasure = performance.getEntriesByName('Post Processing')[0];
perfStats.push({
name: hasPaths ? 'Alias Replace & Copy' : 'File Copy',
duration: postMeasure.duration
});

watchDeleteFile();

if (cmdPath) {
// 3. 应用启动阶段
performance.mark('app-start');
runChild = forkRun(cmdPath, childArgs, {
cwd,
parentArgs,
});

// 设置一次性的 onServerReady 回调
runChild.onServerReady(
async (serverReportOption, isFirstCallback, during, debugUrl) => {
if (isFirstCallback) {
performance.mark('app-end');
performance.measure('App Startup', 'app-start', 'app-end');
const appMeasure = performance.getEntriesByName('App Startup')[0];
perfStats.push({
name: 'App Startup',
duration: appMeasure.duration
});

// 第一次启动把端口等信息打印出来
consoleOutput.renderServerFirstReady(
serverReportOption,
during,
hasPaths,
debugUrl
);

if (isPerfInit) {
consoleOutput.renderPerfStats(perfStats);
}

if (proxyServer) {
proxyServer.start();
}
triggerMessage('server-first-ready');
} else {
// 重启完成后的处理
performance.mark('app-end');
performance.measure('App Restart', 'app-start', 'app-end');

const measures = performance.getEntriesByType('measure');
const killMeasureName = hasPaths ? 'Kill & Alias Replace' : 'Process Kill';
const killMeasure = measures.find(m => m.name === killMeasureName);
const appMeasure = measures.find(m => m.name === 'App Restart');

restartPerfStats = [
{
name: killMeasureName,
duration: killMeasure.duration
},
{
name: 'App Restart',
duration: appMeasure.duration
}
];

consoleOutput.renderServerReady(during, debugUrl);
if (isPerfInit) {
consoleOutput.renderPerfStats(restartPerfStats);
}

triggerMessage('server-ready');
}
}
Expand Down
15 changes: 15 additions & 0 deletions lib/output.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,24 @@ class ConsoleOutput extends EventEmitter {
}
}

console.log('\nMidway Performance Statistics:');
console.log(table.toString());
}

renderPerfStats(items) {
const table = new Table({
head: ['Stage', 'Time(ms)'],
});

for (const item of items) {
table.push([item.name, item.duration.toFixed(2)]);
}

console.log('\nPerformance Statistics:');
console.log(table.toString());
console.log('');
}

renderKeepAlive() {
output(colors.red('*'.repeat(120)));
output(
Expand Down
2 changes: 1 addition & 1 deletion lib/process.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ const forkRun = (runCmdPath, runArgs = [], options = {}) => {
} else if (data.title === 'debug-url') {
currentDebugUrl = data.debugUrl;
} else if (data.title === 'perf-init') {
if (isPerfInit && isFirstFork) {
if (isPerfInit) {
// perf init
consoleOutput.renderPerfInit(data.data);
}
Expand Down