Pr enhance (#1176)

* fix

* Refactor PacketApi status checks and fix typos

Replaced all usages of PacketApi.available with PacketApi.packetStatus for more accurate status checking. Fixed a typo in getFullQQVesion to getFullQQVersion. Updated plugin_onmessage to fetch and parse messages for a user. These changes improve reliability and consistency in API status handling.

* Fix typo in getFullQQVersion method name

Corrected the method name from getFullQQVesion to getFullQQVersion in multiple locations to ensure consistency and prevent potential runtime errors.

* Remove performance CLI and demo, fix typos, update proto

Deleted the performance CLI and demo files. Fixed a typo in getFullQQVesion to getFullQQVersion across multiple files. Changed the 'time' field type from UINT32 to UINT64 in Oidb.0x9067_202 proto. Commented out performanceMonitorPlugin in vite.config.ts. Removed an unimplemented log statement in NodeIKernelBuddyListener.

* Comment out default plugin adapter registration

The default registration of OB11PluginAdapter in NapCatOneBot11Adapter was commented out, likely to prevent automatic plugin loading or to allow for more flexible plugin management. Also, removed an unnecessary blank line in the plugin_onmessage function.

* fix

* Add shell-analysis mode with performance monitoring

Introduces a new .env.shell-analysis file and a dev:shell-analysis npm script for building in shell-analysis mode. Updates vite.config.ts to support the new mode, enabling the performance monitor plugin with an updated exclude list. Also extends the plugin's exclude patterns to filter out 'packet' files.

* Delete performance-api.ts

* Add commented export for performance-monitor

Added a commented-out export statement for '@/common/performance-monitor' in napcat.ts, possibly for future use or reference. No functional changes to the file.
This commit is contained in:
手瓜一十雪 2025-08-06 20:37:45 +08:00 committed by GitHub
parent 9f0ba6d385
commit e8ef08cae2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 769 additions and 120 deletions

2
.env.shell-analysis Normal file
View File

@ -0,0 +1,2 @@
VITE_BUILD_TYPE = DEBUG
VITE_BUILD_PLATFORM = Shell

145
package-lock.json generated
View File

@ -1,19 +1,24 @@
{
"name": "napcat",
"version": "4.8.93",
"version": "4.8.95",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "napcat",
"version": "4.8.93",
"version": "4.8.95",
"dependencies": {
"express": "^5.0.0",
"silk-wasm": "^3.6.1",
"ws": "^8.18.0"
},
"devDependencies": {
"@babel/core": "^7.28.0",
"@babel/generator": "^7.28.0",
"@babel/parser": "^7.28.0",
"@babel/preset-typescript": "^7.24.7",
"@babel/traverse": "^7.28.0",
"@babel/types": "^7.28.2",
"@eslint/compat": "^1.3.1",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.31.0",
@ -41,9 +46,8 @@
"cors": "^2.8.5",
"esbuild": "0.25.5",
"eslint": "^9.14.0",
"eslint-import-resolver-typescript": "^4.0.0",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-import": "^2.32.0",
"express-rate-limit": "^7.5.0",
"fast-xml-parser": "^4.3.6",
"file-type": "^21.0.0",
@ -65,7 +69,6 @@
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.24"
@ -95,29 +98,27 @@
"integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/core": {
"version": "7.27.4",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz",
"integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==",
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz",
"integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.27.3",
"@babel/generator": "^7.28.0",
"@babel/helper-compilation-targets": "^7.27.2",
"@babel/helper-module-transforms": "^7.27.3",
"@babel/helpers": "^7.27.4",
"@babel/parser": "^7.27.4",
"@babel/helpers": "^7.27.6",
"@babel/parser": "^7.28.0",
"@babel/template": "^7.27.2",
"@babel/traverse": "^7.27.4",
"@babel/types": "^7.27.3",
"@babel/traverse": "^7.28.0",
"@babel/types": "^7.28.0",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@ -133,16 +134,16 @@
}
},
"node_modules/@babel/generator": {
"version": "7.27.5",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz",
"integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==",
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz",
"integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.27.5",
"@babel/types": "^7.27.3",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
"@babel/parser": "^7.28.0",
"@babel/types": "^7.28.0",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
},
"engines": {
@ -168,7 +169,6 @@
"integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/compat-data": "^7.27.2",
"@babel/helper-validator-option": "^7.27.1",
@ -202,6 +202,16 @@
"@babel/core": "^7.0.0"
}
},
"node_modules/@babel/helper-globals": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-member-expression-to-functions": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz",
@ -339,7 +349,6 @@
"integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/template": "^7.27.2",
"@babel/types": "^7.27.6"
@ -349,13 +358,13 @@
}
},
"node_modules/@babel/parser": {
"version": "7.27.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz",
"integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==",
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
"integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.27.3"
"@babel/types": "^7.28.0"
},
"bin": {
"parser": "bin/babel-parser.js"
@ -469,38 +478,28 @@
}
},
"node_modules/@babel/traverse": {
"version": "7.27.4",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz",
"integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==",
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz",
"integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.27.3",
"@babel/parser": "^7.27.4",
"@babel/generator": "^7.28.0",
"@babel/helper-globals": "^7.28.0",
"@babel/parser": "^7.28.0",
"@babel/template": "^7.27.2",
"@babel/types": "^7.27.3",
"debug": "^4.3.1",
"globals": "^11.1.0"
"@babel/types": "^7.28.0",
"debug": "^4.3.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse/node_modules/globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/types": {
"version": "7.27.6",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz",
"integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==",
"version": "7.28.2",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
"integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1277,18 +1276,14 @@
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
"version": "0.3.12",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz",
"integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/set-array": "^1.2.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/resolve-uri": {
@ -1301,16 +1296,6 @@
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/set-array": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
@ -1319,9 +1304,9 @@
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"version": "0.3.29",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz",
"integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -3223,7 +3208,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"caniuse-lite": "^1.0.30001718",
"electron-to-chromium": "^1.5.160",
@ -3402,8 +3386,7 @@
"url": "https://github.com/sponsors/ai"
}
],
"license": "CC-BY-4.0",
"peer": true
"license": "CC-BY-4.0"
},
"node_modules/chalk": {
"version": "4.1.2",
@ -3579,8 +3562,7 @@
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/cookie": {
"version": "0.7.2",
@ -3858,8 +3840,7 @@
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.167.tgz",
"integrity": "sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==",
"dev": true,
"license": "ISC",
"peer": true
"license": "ISC"
},
"node_modules/enabled": {
"version": "2.0.0",
@ -4080,7 +4061,6 @@
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6"
}
@ -4945,7 +4925,6 @@
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6.9.0"
}
@ -5958,7 +5937,6 @@
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"license": "ISC",
"peer": true,
"dependencies": {
"yallist": "^3.0.2"
}
@ -6281,8 +6259,7 @@
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/object-assign": {
"version": "4.1.1",
@ -8115,7 +8092,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"escalade": "^3.2.0",
"picocolors": "^1.1.1"
@ -8479,8 +8455,7 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true,
"license": "ISC",
"peer": true
"license": "ISC"
},
"node_modules/yazl": {
"version": "2.5.1",

View File

@ -11,13 +11,19 @@
"dev:universal": "vite build --mode universal",
"dev:framework": "vite build --mode framework",
"dev:shell": "vite build --mode shell",
"dev:shell-analysis": "vite build --mode shell-analysis",
"dev:webui": "cd napcat.webui && npm run dev",
"lint": "eslint --fix src/**/*.{js,ts,vue}",
"depend": "cd dist && npm install --omit=dev",
"dev:depend": "npm i && cd napcat.webui && npm i"
},
"devDependencies": {
"@babel/core": "^7.28.0",
"@babel/generator": "^7.28.0",
"@babel/parser": "^7.28.0",
"@babel/preset-typescript": "^7.24.7",
"@babel/traverse": "^7.28.0",
"@babel/types": "^7.28.2",
"@eslint/compat": "^1.3.1",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.31.0",
@ -41,6 +47,7 @@
"ajv": "^8.13.0",
"async-mutex": "^0.5.0",
"commander": "^13.0.0",
"compressing": "^1.10.1",
"cors": "^2.8.5",
"esbuild": "0.25.5",
"eslint": "^9.14.0",
@ -52,14 +59,13 @@
"globals": "^16.0.0",
"json5": "^2.2.3",
"multer": "^2.0.1",
"napcat.protobuf": "^1.1.4",
"typescript": "^5.3.3",
"typescript-eslint": "^8.35.1",
"vite": "^6.0.1",
"vite-plugin-cp": "^6.0.0",
"vite-tsconfig-paths": "^5.1.0",
"napcat.protobuf": "^1.1.4",
"winston": "^3.17.0",
"compressing": "^1.10.1"
"winston": "^3.17.0"
},
"dependencies": {
"express": "^5.0.0",

View File

@ -0,0 +1,316 @@
/**
* -
*/
import * as fs from 'fs';
import * as path from 'path';
export interface FunctionStats {
name: string;
callCount: number;
totalTime: number;
averageTime: number;
minTime: number;
maxTime: number;
fileName?: string;
lineNumber?: number;
}
export class PerformanceMonitor {
private static instance: PerformanceMonitor;
private stats = new Map<string, FunctionStats>();
private startTimes = new Map<string, number>();
private reportInterval: NodeJS.Timeout | null = null;
static getInstance(): PerformanceMonitor {
if (!PerformanceMonitor.instance) {
PerformanceMonitor.instance = new PerformanceMonitor();
// 启动定时统计报告
PerformanceMonitor.instance.startPeriodicReport();
}
return PerformanceMonitor.instance;
}
/**
* (60)
*/
private startPeriodicReport(): void {
if (this.reportInterval) {
clearInterval(this.reportInterval);
}
this.reportInterval = setInterval(() => {
if (this.stats.size > 0) {
this.printPeriodicReport();
this.writeDetailedLogToFile();
}
}, 60000); // 60秒
}
/**
*
*/
stopPeriodicReport(): void {
if (this.reportInterval) {
clearInterval(this.reportInterval);
this.reportInterval = null;
}
}
/**
* ()
*/
private printPeriodicReport(): void {
const now = new Date().toLocaleString();
console.log(`\n=== 性能监控定时报告 [${now}] ===`);
const totalFunctions = this.stats.size;
const totalCalls = Array.from(this.stats.values()).reduce((sum, stat) => sum + stat.callCount, 0);
const totalTime = Array.from(this.stats.values()).reduce((sum, stat) => sum + stat.totalTime, 0);
console.log(`📊 总览: ${totalFunctions} 个函数, ${totalCalls} 次调用, 总耗时: ${totalTime.toFixed(2)}ms`);
// 显示Top 5最活跃的函数
console.log('\n🔥 最活跃函数 (Top 5):');
this.getTopByCallCount(5).forEach((stat, index) => {
console.log(`${index + 1}. ${stat.name} - 调用: ${stat.callCount}次, 总耗时: ${stat.totalTime.toFixed(2)}ms`);
});
// 显示Top 5最耗时的函数
console.log('\n⏱ 最耗时函数 (Top 5):');
this.getTopByTotalTime(5).forEach((stat, index) => {
console.log(`${index + 1}. ${stat.name} - 总耗时: ${stat.totalTime.toFixed(2)}ms, 平均: ${stat.averageTime.toFixed(2)}ms`);
});
console.log('===============================\n');
}
/**
*
*/
private writeDetailedLogToFile(): void {
try {
const now = new Date();
const dateStr = now.toISOString().replace(/[:.]/g, '-').split('T')[0];
const timeStr = now.toTimeString().split(' ')[0]?.replace(/:/g, '-') || 'unknown-time';
const timestamp = `${dateStr}_${timeStr}`;
const fileName = `${timestamp}.log.txt`;
const logPath = path.join(process.cwd(), 'logs', fileName);
// 确保logs目录存在
const logsDir = path.dirname(logPath);
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir, { recursive: true });
}
const totalFunctions = this.stats.size;
const totalCalls = Array.from(this.stats.values()).reduce((sum, stat) => sum + stat.callCount, 0);
const totalTime = Array.from(this.stats.values()).reduce((sum, stat) => sum + stat.totalTime, 0);
let logContent = '';
logContent += `=== 性能监控详细报告 ===\n`;
logContent += `生成时间: ${now.toLocaleString()}\n`;
logContent += `统计周期: 60秒\n`;
logContent += `总览: ${totalFunctions} 个函数, ${totalCalls} 次调用, 总耗时: ${totalTime.toFixed(2)}ms\n\n`;
// 详细函数统计
logContent += `=== 所有函数详细统计 ===\n`;
const allStats = this.getStats().sort((a, b) => b.totalTime - a.totalTime);
allStats.forEach((stat, index) => {
logContent += `${index + 1}. 函数: ${stat.name}\n`;
logContent += ` 文件: ${stat.fileName || 'N/A'}\n`;
logContent += ` 行号: ${stat.lineNumber || 'N/A'}\n`;
logContent += ` 调用次数: ${stat.callCount}\n`;
logContent += ` 总耗时: ${stat.totalTime.toFixed(4)}ms\n`;
logContent += ` 平均耗时: ${stat.averageTime.toFixed(4)}ms\n`;
logContent += ` 最小耗时: ${stat.minTime === Infinity ? 'N/A' : stat.minTime.toFixed(4)}ms\n`;
logContent += ` 最大耗时: ${stat.maxTime.toFixed(4)}ms\n`;
logContent += ` 性能占比: ${((stat.totalTime / totalTime) * 100).toFixed(2)}%\n`;
logContent += `\n`;
});
// 排行榜统计
logContent += `=== 总耗时排行榜 (Top 20) ===\n`;
this.getTopByTotalTime(20).forEach((stat, index) => {
logContent += `${index + 1}. ${stat.name} - 总耗时: ${stat.totalTime.toFixed(2)}ms, 调用: ${stat.callCount}次, 平均: ${stat.averageTime.toFixed(2)}ms\n`;
});
logContent += `\n=== 调用次数排行榜 (Top 20) ===\n`;
this.getTopByCallCount(20).forEach((stat, index) => {
logContent += `${index + 1}. ${stat.name} - 调用: ${stat.callCount}次, 总耗时: ${stat.totalTime.toFixed(2)}ms, 平均: ${stat.averageTime.toFixed(2)}ms\n`;
});
logContent += `\n=== 平均耗时排行榜 (Top 20) ===\n`;
this.getTopByAverageTime(20).forEach((stat, index) => {
logContent += `${index + 1}. ${stat.name} - 平均: ${stat.averageTime.toFixed(2)}ms, 调用: ${stat.callCount}次, 总耗时: ${stat.totalTime.toFixed(2)}ms\n`;
});
logContent += `\n=== 性能热点分析 ===\n`;
// 找出最耗时的前10个函数
const hotSpots = this.getTopByTotalTime(10);
hotSpots.forEach((stat, index) => {
const efficiency = stat.callCount / stat.totalTime; // 每毫秒的调用次数
logContent += `${index + 1}. ${stat.name}\n`;
logContent += ` 性能影响: ${((stat.totalTime / totalTime) * 100).toFixed(2)}%\n`;
logContent += ` 调用效率: ${efficiency.toFixed(4)} 调用/ms\n`;
logContent += ` 优化建议: ${stat.averageTime > 10 ? '考虑优化此函数的执行效率' :
stat.callCount > 1000 ? '考虑减少此函数的调用频率' :
'性能表现良好'}\n\n`;
});
logContent += `=== 报告结束 ===\n`;
// 写入文件
fs.writeFileSync(logPath, logContent, 'utf8');
console.log(`📄 详细性能报告已保存到: ${logPath}`);
} catch (error) {
console.error('写入性能日志文件时出错:', error);
}
}
/**
*
*/
startFunction(functionName: string, fileName?: string, lineNumber?: number): string {
const callId = `${functionName}_${Date.now()}_${Math.random()}`;
this.startTimes.set(callId, performance.now());
// 初始化或更新统计信息
if (!this.stats.has(functionName)) {
this.stats.set(functionName, {
name: functionName,
callCount: 0,
totalTime: 0,
averageTime: 0,
minTime: Infinity,
maxTime: 0,
fileName,
lineNumber
});
}
const stat = this.stats.get(functionName)!;
stat.callCount++;
return callId;
}
/**
*
*/
endFunction(callId: string, functionName: string): void {
const startTime = this.startTimes.get(callId);
if (!startTime) return;
const endTime = performance.now();
const duration = endTime - startTime;
this.startTimes.delete(callId);
const stat = this.stats.get(functionName);
if (!stat) return;
stat.totalTime += duration;
stat.averageTime = stat.totalTime / stat.callCount;
stat.minTime = Math.min(stat.minTime, duration);
stat.maxTime = Math.max(stat.maxTime, duration);
}
/**
*
*/
getStats(): FunctionStats[] {
return Array.from(this.stats.values());
}
/**
* -
*/
getTopByTotalTime(limit = 20): FunctionStats[] {
return this.getStats()
.sort((a, b) => b.totalTime - a.totalTime)
.slice(0, limit);
}
/**
* -
*/
getTopByCallCount(limit = 20): FunctionStats[] {
return this.getStats()
.sort((a, b) => b.callCount - a.callCount)
.slice(0, limit);
}
/**
* -
*/
getTopByAverageTime(limit = 20): FunctionStats[] {
return this.getStats()
.sort((a, b) => b.averageTime - a.averageTime)
.slice(0, limit);
}
/**
*
*/
clear(): void {
this.stats.clear();
this.startTimes.clear();
}
/**
*
*/
printReport(): void {
console.log('\n=== 函数性能监控报告 ===');
console.log('\n🔥 总耗时排行榜 (Top 10):');
this.getTopByTotalTime(10).forEach((stat, index) => {
console.log(`${index + 1}. ${stat.name} - 总耗时: ${stat.totalTime.toFixed(2)}ms, 调用次数: ${stat.callCount}, 平均耗时: ${stat.averageTime.toFixed(2)}ms`);
});
console.log('\n📈 调用次数排行榜 (Top 10):');
this.getTopByCallCount(10).forEach((stat, index) => {
console.log(`${index + 1}. ${stat.name} - 调用次数: ${stat.callCount}, 总耗时: ${stat.totalTime.toFixed(2)}ms, 平均耗时: ${stat.averageTime.toFixed(2)}ms`);
});
console.log('\n⏱ 平均耗时排行榜 (Top 10):');
this.getTopByAverageTime(10).forEach((stat, index) => {
console.log(`${index + 1}. ${stat.name} - 平均耗时: ${stat.averageTime.toFixed(2)}ms, 调用次数: ${stat.callCount}, 总耗时: ${stat.totalTime.toFixed(2)}ms`);
});
console.log('\n========================\n');
}
/**
* JSON格式的统计数据
*/
toJSON(): FunctionStats[] {
return this.getStats();
}
}
// 全局性能监控器实例
export const performanceMonitor = PerformanceMonitor.getInstance();
// 在进程退出时打印报告并停止定时器
if (typeof process !== 'undefined') {
process.on('exit', () => {
performanceMonitor.stopPeriodicReport();
performanceMonitor.printReport();
});
process.on('SIGINT', () => {
performanceMonitor.stopPeriodicReport();
performanceMonitor.printReport();
process.exit(0);
});
process.on('SIGTERM', () => {
performanceMonitor.stopPeriodicReport();
performanceMonitor.printReport();
process.exit(0);
});
}

View File

@ -42,7 +42,7 @@ export class QQBasicInfoWrapper {
return this.isQuickUpdate ? this.QQVersionConfig?.buildId : this.QQPackageInfo?.buildVersion;
}
getFullQQVesion() {
getFullQQVersion() {
const version = this.isQuickUpdate ? this.QQVersionConfig?.curVersion : this.QQPackageInfo?.version;
if (!version) throw new Error('QQ版本获取失败');
return version;
@ -57,9 +57,9 @@ export class QQBasicInfoWrapper {
//此方法不要直接使用
getQUAFallback() {
const platformMapping: Partial<Record<NodeJS.Platform, string>> = {
win32: `V1_WIN_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`,
darwin: `V1_MAC_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`,
linux: `V1_LNX_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`,
win32: `V1_WIN_${this.getFullQQVersion()}_${this.getQQBuildStr()}_GW_B`,
darwin: `V1_MAC_${this.getFullQQVersion()}_${this.getQQBuildStr()}_GW_B`,
linux: `V1_LNX_${this.getFullQQVersion()}_${this.getQQBuildStr()}_GW_B`,
};
return platformMapping[systemPlatform] ?? (platformMapping.win32)!;
}
@ -76,7 +76,7 @@ export class QQBasicInfoWrapper {
getAppidV2(): { appid: string; qua: string } {
// 通过已有表 性能好
const appidTbale = AppidTable as unknown as QQAppidTableType;
const fullVersion = this.getFullQQVesion();
const fullVersion = this.getFullQQVersion();
if (fullVersion) {
const data = appidTbale[fullVersion];
if (data) {

View File

@ -65,7 +65,7 @@ export class NTQQFileApi {
}
async getFileUrl(chatType: ChatType, peer: string, fileUUID?: string, file10MMd5?: string | undefined) {
if (this.core.apis.PacketApi.available) {
if (this.core.apis.PacketApi.packetStatus) {
try {
if (chatType === ChatType.KCHATTYPEGROUP && fileUUID) {
return this.core.apis.PacketApi.pkt.operation.GetGroupFileUrl(+peer, fileUUID);
@ -80,7 +80,7 @@ export class NTQQFileApi {
}
async getPttUrl(peer: string, fileUUID?: string) {
if (this.core.apis.PacketApi.available && fileUUID) {
if (this.core.apis.PacketApi.packetStatus && fileUUID) {
let appid = new NapProtoMsg(FileId).decode(Buffer.from(fileUUID.replaceAll('-', '+').replaceAll('_', '/'), 'base64')).appid;
try {
if (appid && appid === 1403) {
@ -108,7 +108,7 @@ export class NTQQFileApi {
}
async getVideoUrlPacket(peer: string, fileUUID?: string) {
if (this.core.apis.PacketApi.available && fileUUID) {
if (this.core.apis.PacketApi.packetStatus && fileUUID) {
let appid = new NapProtoMsg(FileId).decode(Buffer.from(fileUUID.replaceAll('-', '+').replaceAll('_', '/'), 'base64')).appid;
try {
if (appid && appid === 1415) {
@ -502,7 +502,7 @@ export class NTQQFileApi {
};
try {
if (this.core.apis.PacketApi.available) {
if (this.core.apis.PacketApi.packetStatus) {
const rkey_expired_private = !this.packetRkey || (this.packetRkey[0] && this.packetRkey[0].time + Number(this.packetRkey[0].ttl) < Date.now() / 1000);
const rkey_expired_group = !this.packetRkey || (this.packetRkey[0] && this.packetRkey[0].time + Number(this.packetRkey[0].ttl) < Date.now() / 1000);
if (rkey_expired_private || rkey_expired_group) {

View File

@ -142,7 +142,6 @@ export class NTQQMsgApi {
}
async queryFirstMsgBySender(peer: Peer, SendersUid: string[]) {
console.log(peer, SendersUid);
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
chatInfo: peer,
filterMsgType: [],

View File

@ -21,20 +21,26 @@ export class NTQQPacketApi {
qqVersion: string | undefined;
pkt!: PacketClientSession;
errStack: string[] = [];
packetStatus: boolean = false;
constructor(context: InstanceContext, core: NapCatCore) {
this.context = context;
this.core = core;
this.logger = core.context.logger;
}
async initApi() {
await this.InitSendPacket(this.context.basicInfoWrapper.getFullQQVesion())
.then()
this.packetStatus = (await this.InitSendPacket(this.context.basicInfoWrapper.getFullQQVersion())
.then((result) => {
return result;
})
.catch((err) => {
this.logger.logError(err);
this.errStack.push(err);
});
return false;
})) && this.pkt?.available;
}
get available(): boolean {
return this.pkt?.available ?? false;
}
@ -61,6 +67,12 @@ export class NTQQPacketApi {
}
this.pkt = new PacketClientSession(this.core);
await this.pkt.init(process.pid, table.recv, table.send);
try {
await this.pkt.operation.FetchRkey();
} catch (error) {
this.logger.logError('测试Packet状态异常', error);
return false;
}
return true;
}
}

View File

@ -40,7 +40,6 @@ export class NodeIKernelBuddyListener {
}
onDelBatchBuddyInfos(_arg: unknown): any {
console.log('onDelBatchBuddyInfos not implemented', ...arguments);
}
onDoubtBuddyReqChange(_arg:

View File

@ -37,7 +37,7 @@ export async function NCoreInitFramework(
const pathWrapper = new NapCatPathWrapper();
const logger = new LogWrapper(pathWrapper.logsPath);
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion());
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVersion());
if (!process.env['NAPCAT_DISABLE_FFMPEG_DOWNLOAD']) {
downloadFFmpegIfNotExists(logger).then(({ path, reset }) => {
if (reset && path) {

View File

@ -130,7 +130,7 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
);
if (getSpecialMsgNum(payload, OB11MessageDataType.node)) {
const packetMode = this.core.apis.PacketApi.available;
const packetMode = this.core.apis.PacketApi.packetStatus;
let returnMsgAndResId: { message: RawMessage | null, res_id?: string } | null;
try {
returnMsgAndResId = packetMode

View File

@ -4,7 +4,7 @@ import { ActionName, BaseCheckResult } from '@/onebot/action/router';
export abstract class GetPacketStatusDepends<PT, RT> extends OneBotAction<PT, RT> {
protected override async check(payload: PT): Promise<BaseCheckResult>{
if (!this.core.apis.PacketApi.available) {
if (!this.core.apis.PacketApi.packetStatus) {
return {
valid: false,
message: 'packetBackend不可用请参照文档 https://napneko.github.io/config/advanced 和启动日志检查packetBackend状态或进行配置' +

View File

@ -147,7 +147,7 @@ export class OneBotMsgApi {
};
FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, element.fileUuid, element.fileUuid);
FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, element.fileUuid, element.fileName);
if (this.core.apis.PacketApi.available) {
if (this.core.apis.PacketApi.packetStatus) {
let url;
try {
url = await this.core.apis.FileApi.getFileUrl(msg.chatType, msg.peerUid, element.fileUuid, element.file10MMd5)
@ -391,7 +391,7 @@ export class OneBotMsgApi {
//开始兜底
if (!videoDownUrl) {
if (this.core.apis.PacketApi.available) {
if (this.core.apis.PacketApi.packetStatus) {
try {
videoDownUrl = await this.core.apis.FileApi.getVideoUrlPacket(msg.peerUid, element.fileUuid);
} catch (e) {
@ -422,7 +422,7 @@ export class OneBotMsgApi {
};
const fileCode = FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, '', element.fileName);
let pttUrl = '';
if (this.core.apis.PacketApi.available) {
if (this.core.apis.PacketApi.packetStatus) {
try {
pttUrl = await this.core.apis.FileApi.getPttUrl(msg.peerUid, element.fileUuid);
} catch (e) {

View File

@ -50,6 +50,7 @@ import {
import { OB11Message } from './types';
import { IOB11NetworkAdapter } from '@/onebot/network/adapter';
import { OB11HttpSSEServerAdapter } from './network/http-server-sse';
import { OB11PluginAdapter } from './network/plugin';
//OneBot实现类
export class NapCatOneBot11Adapter {
@ -169,7 +170,7 @@ export class NapCatOneBot11Adapter {
this.initBuddyListener();
this.initGroupListener();
WebUiDataRuntime.setQQVersion(this.core.context.basicInfoWrapper.getFullQQVesion());
WebUiDataRuntime.setQQVersion(this.core.context.basicInfoWrapper.getFullQQVersion());
WebUiDataRuntime.setQQLoginInfo(selfInfo);
WebUiDataRuntime.setQQLoginStatus(true);
WebUiDataRuntime.setOnOB11ConfigChanged(async (newConfig) => {

View File

@ -1,11 +1,16 @@
import { NapCatOneBot11Adapter, OB11Message } from '@/onebot';
import { NapCatCore } from '@/core';
import { ChatType, NapCatCore } from '@/core';
import { ActionMap } from '@/onebot/action';
import { OB11PluginAdapter } from '@/onebot/network/plugin';
export const plugin_onmessage = async (adapter: string, _core: NapCatCore, _obCtx: NapCatOneBot11Adapter, message: OB11Message, action: ActionMap, instance: OB11PluginAdapter) => {
if (message.raw_message === 'ping') {
const ret = await action.get('send_group_msg')?.handle({ group_id: String(message.group_id), message: 'pong' }, adapter, instance.config);
console.log(ret);
const uid = await _core.apis.UserApi.getUidByUinV2(message.sender.user_id.toString());
const msgs = (await _core.apis.MsgApi.queryFirstMsgBySender({
peerUid: message.group_id ? String(message.group_id) : String(uid),
chatType: ChatType.KCHATTYPEGROUP,
}, [uid])).msgList;
console.log('parse message ', message.sender.user_id, msgs.length);
for (const msg of msgs) {
await _obCtx.apis.MsgApi.parseMessageV2(msg)
}
};
}

View File

@ -80,7 +80,7 @@ async function initializeEngine(
base_path_prefix: '',
platform_type: systemPlatform,
app_type: 4,
app_version: basicInfoWrapper.getFullQQVesion(),
app_version: basicInfoWrapper.getFullQQVersion(),
os_version: systemVersion,
use_xlog: false,
qua: basicInfoWrapper.QQVersionQua ?? '',
@ -105,7 +105,7 @@ async function initializeLoginService(
appid: basicInfoWrapper.QQVersionAppid ?? '',
platVer: systemVersion,
commonPath: dataPathGlobal,
clientVer: basicInfoWrapper.getFullQQVesion(),
clientVer: basicInfoWrapper.getFullQQVersion(),
hostName: hostname,
});
}
@ -324,7 +324,7 @@ export async function NCoreInitShell() {
});
}
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion());
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVersion());
const o3Service = wrapper.NodeIO3MiscService.get();
o3Service.addO3MiscListener(new NodeIO3MiscListener());
@ -363,7 +363,7 @@ export async function NCoreInitShell() {
const sessionConfig = await genSessionConfig(
guid,
basicInfoWrapper.QQVersionAppid,
basicInfoWrapper.getFullQQVesion(),
basicInfoWrapper.getFullQQVersion(),
selfInfo.uin,
selfInfo.uid,
dataPath,

View File

@ -1,2 +1,4 @@
//export * from '@/common/performance-monitor';
import { NCoreInitShell } from './base';
NCoreInitShell();

View File

@ -0,0 +1,306 @@
import { Plugin } from 'vite';
import { parse } from '@babel/parser';
import traverseDefault from '@babel/traverse';
import generateDefault from '@babel/generator';
import * as t from '@babel/types';
import { resolve } from 'path';
// @ts-ignore
const traverse = traverseDefault.default || traverseDefault;
// @ts-ignore
const generate = generateDefault.default || generateDefault;
interface PerformancePluginOptions {
}
/**
* Vite插件
*/
export function performanceMonitorPlugin(options: PerformancePluginOptions): Plugin {
const exclude = [/node_modules/, /\.min\./, /performance-monitor\.ts$/, /packet/];
return {
name: 'performance-monitor',
transform(code: string, id: string) {
const fileName = id.replace(process.cwd(), '').replace(/\\/g, '/');
// 排除规则检查
if (exclude.some(pattern => pattern.test(id))) {
return null;
}
try {
// 解析AST
const ast = parse(code, {
sourceType: 'module',
plugins: [
'typescript',
'decorators-legacy',
'classProperties',
'asyncGenerators',
'bigInt',
'dynamicImport',
'exportDefaultFrom',
'exportNamespaceFrom',
'nullishCoalescingOperator',
'numericSeparator',
'optionalCatchBinding',
'optionalChaining',
'topLevelAwait'
]
});
let hasMonitorImport = false;
let hasMonitorExport = false;
let needsMonitor = false;
// 遍历AST
traverse(ast, {
// 检查是否已经导入了性能监控器
ImportDeclaration(path) {
if (path.node.source.value.includes('performance-monitor')) {
hasMonitorImport = true;
}
},
// 检查是否已经导出了性能监控器
ExportNamedDeclaration(path) {
if (path.node.declaration && t.isVariableDeclaration(path.node.declaration)) {
path.node.declaration.declarations.forEach(declarator => {
if (t.isIdentifier(declarator.id) && declarator.id.name === 'performanceMonitor') {
hasMonitorExport = true;
}
});
}
},
// 检查变量声明
VariableDeclaration(path) {
path.node.declarations.forEach(declarator => {
if (t.isIdentifier(declarator.id) && declarator.id.name === 'performanceMonitor') {
hasMonitorExport = true;
}
});
},
// 处理函数声明
FunctionDeclaration(path) {
const functionName = path.node.id?.name || 'anonymous';
const lineNumber = path.node.loc?.start.line || 0;
instrumentFunction(path, functionName, fileName, lineNumber);
needsMonitor = true;
},
// 处理箭头函数
ArrowFunctionExpression(path) {
const parent = path.parent;
let functionName = 'anonymous';
if (t.isVariableDeclarator(parent) && t.isIdentifier(parent.id)) {
functionName = parent.id.name;
} else if (t.isProperty(parent) && t.isIdentifier(parent.key)) {
functionName = parent.key.name;
} else if (t.isAssignmentExpression(parent) && t.isMemberExpression(parent.left)) {
const property = parent.left.property;
if (t.isIdentifier(property)) {
functionName = property.name;
}
}
const lineNumber = path.node.loc?.start.line || 0;
instrumentFunction(path, functionName, fileName, lineNumber);
needsMonitor = true;
},
// 处理函数表达式
FunctionExpression(path) {
const functionName = path.node.id?.name || 'anonymous';
const lineNumber = path.node.loc?.start.line || 0;
instrumentFunction(path, functionName, fileName, lineNumber);
needsMonitor = true;
},
// 处理类方法
ClassMethod(path) {
const methodName = t.isIdentifier(path.node.key) ? path.node.key.name : 'anonymous';
const className = getClassName(path);
const functionName = `${className}.${methodName}`;
const lineNumber = path.node.loc?.start.line || 0;
instrumentFunction(path, functionName, fileName, lineNumber);
needsMonitor = true;
},
// 处理对象方法
ObjectMethod(path) {
const methodName = t.isIdentifier(path.node.key) ? path.node.key.name : 'anonymous';
const lineNumber = path.node.loc?.start.line || 0;
instrumentFunction(path, methodName, fileName, lineNumber);
needsMonitor = true;
}
});
if (!needsMonitor) {
return null;
}
// 如果需要监控但还没有导入且没有导出,则添加导入语句
if (!hasMonitorImport && !hasMonitorExport) {
const importDeclaration = t.importDeclaration(
[t.importSpecifier(t.identifier('performanceMonitor'), t.identifier('performanceMonitor'))],
t.stringLiteral('@/common/performance-monitor')
);
ast.program.body.unshift(importDeclaration);
}
// 生成新代码
const result = generate(ast, {
retainLines: true,
compact: false
});
return {
code: result.code,
map: result.map
};
} catch (error) {
console.warn(`性能监控插件处理文件 ${id} 时出错:`, error);
return null;
}
}
};
}
/**
*
*/
function instrumentFunction(
path: any,
functionName: string,
fileName: string,
lineNumber: number
) {
// 跳过已经被监控的函数
if (functionName.includes('__perf_monitor__')) {
return;
}
const isAsync = path.node.async;
const body = path.node.body;
// 确保函数体是块语句
if (!t.isBlockStatement(body)) {
// 对于箭头函数的表达式体,转换为块语句
const returnStatement = t.returnStatement(body);
path.node.body = t.blockStatement([returnStatement]);
}
const blockBody = path.node.body as t.BlockStatement;
// 生成唯一的调用ID变量名
const callIdVar = `__perf_monitor_${functionName.replace(/[^a-zA-Z0-9]/g, '_')}_${lineNumber}__`;
// 创建开始监控的语句
const startMonitoring = t.variableDeclaration('const', [
t.variableDeclarator(
t.identifier(callIdVar),
t.callExpression(
t.memberExpression(
t.identifier('performanceMonitor'),
t.identifier('startFunction')
),
[
t.stringLiteral(functionName),
t.stringLiteral(fileName),
t.numericLiteral(lineNumber)
]
)
)
]);
// 创建结束监控的语句
const endMonitoring = t.expressionStatement(
t.callExpression(
t.memberExpression(
t.identifier('performanceMonitor'),
t.identifier('endFunction')
),
[
t.identifier(callIdVar),
t.stringLiteral(functionName)
]
)
);
if (isAsync) {
// 对于异步函数,需要在所有可能的返回点添加监控结束
instrumentAsyncFunction(blockBody, startMonitoring, endMonitoring, callIdVar, functionName);
} else {
// 对于同步函数使用try-finally确保监控结束
instrumentSyncFunction(blockBody, startMonitoring, endMonitoring);
}
}
/**
*
*/
function instrumentSyncFunction(
blockBody: t.BlockStatement,
startMonitoring: t.VariableDeclaration,
endMonitoring: t.ExpressionStatement
) {
const originalStatements = [...blockBody.body];
const tryStatement = t.tryStatement(
t.blockStatement(originalStatements),
null,
t.blockStatement([endMonitoring])
);
blockBody.body = [startMonitoring, tryStatement];
}
/**
*
*/
function instrumentAsyncFunction(
blockBody: t.BlockStatement,
startMonitoring: t.VariableDeclaration,
endMonitoring: t.ExpressionStatement,
callIdVar: string,
functionName: string
) {
const originalStatements = [...blockBody.body];
// 创建包装的异步执行体
const asyncTryStatement = t.tryStatement(
t.blockStatement(originalStatements),
null,
t.blockStatement([endMonitoring])
);
blockBody.body = [startMonitoring, asyncTryStatement];
}
/**
*
*/
function getClassName(path: any): string {
let current = path;
while (current) {
if (current.isClassDeclaration && current.isClassDeclaration()) {
return current.node.id?.name || 'AnonymousClass';
} else if (current.isClassExpression && current.isClassExpression()) {
return current.node.id?.name || 'AnonymousClass';
} else if (current.node && (t.isClassDeclaration(current.node) || t.isClassExpression(current.node))) {
return current.node.id?.name || 'AnonymousClass';
}
current = current.parent;
}
return 'UnknownClass';
}
export default performanceMonitorPlugin;

View File

@ -3,6 +3,7 @@ import { defineConfig, PluginOption, UserConfig } from 'vite';
import { resolve } from 'path';
import nodeResolve from '@rollup/plugin-node-resolve';
import { builtinModules } from 'module';
import { performanceMonitorPlugin } from './vite-plugin-performance-monitor';
//依赖排除
const external = [
'silk-wasm',
@ -21,6 +22,11 @@ if (process.env.NAPCAT_BUILDSYS == 'linux') {
}
const UniversalBaseConfigPlugin: PluginOption[] = [
// performanceMonitorPlugin({
// enabled: process.env.NODE_ENV !== 'production',
// exclude: [/node_modules/, /\.min\./, /performance-monitor/],
// include: [/\.ts$/, /\.js$/]
// }),
cp({
targets: [
{ src: './manifest.json', dest: 'dist' },
@ -44,6 +50,11 @@ const UniversalBaseConfigPlugin: PluginOption[] = [
];
const FrameworkBaseConfigPlugin: PluginOption[] = [
// performanceMonitorPlugin({
// enabled: process.env.NODE_ENV !== 'production',
// exclude: [/node_modules/, /\.min\./, /performance-monitor/],
// include: [/\.ts$/, /\.js$/]
// }),
cp({
targets: [
{ src: './manifest.json', dest: 'dist' },
@ -64,6 +75,11 @@ const FrameworkBaseConfigPlugin: PluginOption[] = [
];
const ShellBaseConfigPlugin: PluginOption[] = [
// performanceMonitorPlugin({
// enabled: process.env.NODE_ENV !== 'production',
// exclude: [/node_modules/, /\.min\./, /performance-monitor/],
// include: [/\.ts$/, /\.js$/]
// }),
cp({
targets: [
{ src: './src/native/packet', dest: 'dist/moehoo', flatten: false },
@ -177,10 +193,20 @@ export default defineConfig(({ mode }): UserConfig => {
...UniversalBaseConfig(),
plugins: [...UniversalBaseConfigPlugin],
};
} else {
} else if (mode == 'shell-analysis') {
return {
...ShellBaseConfig(),
plugins: [
performanceMonitorPlugin({
exclude: [/node_modules/, /\.min\./, /performance-monitor\.ts$/, /packet/],
include: [/\.ts$/, /\.js$/]
}),
...ShellBaseConfigPlugin
],
};
} else
return {
...FrameworkBaseConfig(),
plugins: [...FrameworkBaseConfigPlugin],
};
}
});