From 2f65d291aa4a324502b1e7aa61bc92fbaa416d55 Mon Sep 17 00:00:00 2001 From: killa Date: Wed, 6 May 2026 18:00:23 +0800 Subject: [PATCH 1/4] fix(egg): fallback runtime diagnostic config --- packages/egg/src/lib/application.ts | 5 +++- packages/egg/src/lib/egg.ts | 32 +++++++++++++++++++---- packages/egg/test/egg.test.ts | 39 +++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/packages/egg/src/lib/application.ts b/packages/egg/src/lib/application.ts index 54727e0f34..52ea92da4b 100644 --- a/packages/egg/src/lib/application.ts +++ b/packages/egg/src/lib/application.ts @@ -200,9 +200,12 @@ export class Application extends EggApplicationCore { super.dumpConfig(); // dump routers to router.json - const rundir = this.config.rundir; + const rundir = this.getRuntimeRundir(); const FULLPATH = this.loader.FileLoader.FULLPATH; try { + if (!fs.existsSync(rundir)) { + fs.mkdirSync(rundir, { recursive: true }); + } const dumpRouterFile = path.join(rundir, 'router.json'); const routers = []; for (const layer of this.router.stack) { diff --git a/packages/egg/src/lib/egg.ts b/packages/egg/src/lib/egg.ts index a27e479da7..53dda1e681 100644 --- a/packages/egg/src/lib/egg.ts +++ b/packages/egg/src/lib/egg.ts @@ -38,6 +38,8 @@ import { convertObject, createTransparentProxy } from './core/utils.ts'; import type { EggApplicationLoader } from './loader/index.ts'; import type { EggAppConfig } from './types.ts'; +const DEFAULT_WORKER_START_TIMEOUT = 10 * 60 * 1000; + export interface EggApplicationCoreOptions extends Omit { mode?: 'cluster' | 'single'; clusterPort?: number; @@ -565,10 +567,10 @@ export class EggApplicationCore extends EggCore { * @private */ dumpConfig(): void { - const rundir = this.config.rundir; + const rundir = this.getRuntimeRundir(); try { if (!fs.existsSync(rundir)) { - fs.mkdirSync(rundir); + fs.mkdirSync(rundir, { recursive: true }); } // get dumped object @@ -589,7 +591,10 @@ export class EggApplicationCore extends EggCore { dumpTiming(): void { try { const items = this.timing.toJSON(); - const rundir = this.config.rundir; + const rundir = this.getRuntimeRundir(); + if (!fs.existsSync(rundir)) { + fs.mkdirSync(rundir, { recursive: true }); + } const dumpFile = path.join(rundir, `${this.type}_timing_${process.pid}.json`); fs.writeFileSync(dumpFile, CircularJSON.stringify(items, null, 2)); this.coreLogger.info(this.timing.toString()); @@ -641,9 +646,10 @@ export class EggApplicationCore extends EggCore { } #setupTimeoutTimer(): void { + const workerStartTimeout = this.getWorkerStartTimeout(); const startTimeoutTimer = setTimeout(() => { this.coreLogger.error(this.timing.toString()); - this.coreLogger.error(`${this.type} still doesn't ready after ${this.config.workerStartTimeout} ms.`); + this.coreLogger.error(`${this.type} still doesn't ready after ${workerStartTimeout} ms.`); // log unfinished const items = this.timing.toJSON(); for (const item of items) { @@ -658,10 +664,26 @@ export class EggApplicationCore extends EggCore { this.emit('startTimeout'); this.dumpConfig(); this.dumpTiming(); - }, this.config.workerStartTimeout); + }, workerStartTimeout); this.ready(() => clearTimeout(startTimeoutTimer)); } + protected getRuntimeRundir(): string { + const rundir = this.config.rundir; + if (typeof rundir === 'string' && rundir.length > 0) { + return rundir; + } + return path.join(this.baseDir, 'run'); + } + + private getWorkerStartTimeout(): number { + const workerStartTimeout = this.config.workerStartTimeout; + if (typeof workerStartTimeout === 'number' && Number.isFinite(workerStartTimeout)) { + return workerStartTimeout; + } + return DEFAULT_WORKER_START_TIMEOUT; + } + get config() { return super.config as EggAppConfig; } diff --git a/packages/egg/test/egg.test.ts b/packages/egg/test/egg.test.ts index 9d4f7ecda2..285b637158 100644 --- a/packages/egg/test/egg.test.ts +++ b/packages/egg/test/egg.test.ts @@ -263,6 +263,45 @@ describe.sequential('test/egg.test.ts', () => { }); }); + describe('runtime diagnostics fallback config', () => { + const baseDir = getFilepath('apps/dumpconfig'); + const runDir = path.join(baseDir, 'run'); + let app: MockApplication; + + beforeAll(async () => { + app = createApp('apps/dumpconfig'); + await app.ready(); + }); + + afterAll(() => app.close()); + + it('should dump config and timing to baseDir/run when rundir is missing', () => { + fs.rmSync(runDir, { recursive: true, force: true }); + const originalRundir = app.config.rundir; + Reflect.set(app.config, 'rundir', undefined); + try { + app.dumpConfig(); + app.dumpTiming(); + } finally { + Reflect.set(app.config, 'rundir', originalRundir); + } + + assertFile(path.join(runDir, 'application_config.json')); + assertFile(path.join(runDir, `application_timing_${process.pid}.json`)); + assertFile(path.join(runDir, 'router.json')); + }); + + it('should use the default worker start timeout when config is missing', () => { + const originalWorkerStartTimeout = app.config.workerStartTimeout; + Reflect.set(app.config, 'workerStartTimeout', undefined); + try { + assert.equal((app as any).getWorkerStartTimeout(), 10 * 60 * 1000); + } finally { + Reflect.set(app.config, 'workerStartTimeout', originalWorkerStartTimeout); + } + }); + }); + describe('custom config from env', () => { let app: MockApplication; let baseDir: string; From a3d89c0996513f89a03ecaa19e7e735a74339fd6 Mon Sep 17 00:00:00 2001 From: killa Date: Wed, 6 May 2026 18:16:14 +0800 Subject: [PATCH 2/4] fix(egg): address runtime diagnostic review --- packages/egg/src/lib/application.ts | 4 +--- packages/egg/src/lib/egg.ts | 10 +++------- packages/egg/test/egg.test.ts | 12 ++++++++++-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/egg/src/lib/application.ts b/packages/egg/src/lib/application.ts index 52ea92da4b..0c80e7fa90 100644 --- a/packages/egg/src/lib/application.ts +++ b/packages/egg/src/lib/application.ts @@ -203,9 +203,7 @@ export class Application extends EggApplicationCore { const rundir = this.getRuntimeRundir(); const FULLPATH = this.loader.FileLoader.FULLPATH; try { - if (!fs.existsSync(rundir)) { - fs.mkdirSync(rundir, { recursive: true }); - } + fs.mkdirSync(rundir, { recursive: true }); const dumpRouterFile = path.join(rundir, 'router.json'); const routers = []; for (const layer of this.router.stack) { diff --git a/packages/egg/src/lib/egg.ts b/packages/egg/src/lib/egg.ts index 53dda1e681..2ef6461896 100644 --- a/packages/egg/src/lib/egg.ts +++ b/packages/egg/src/lib/egg.ts @@ -569,9 +569,7 @@ export class EggApplicationCore extends EggCore { dumpConfig(): void { const rundir = this.getRuntimeRundir(); try { - if (!fs.existsSync(rundir)) { - fs.mkdirSync(rundir, { recursive: true }); - } + fs.mkdirSync(rundir, { recursive: true }); // get dumped object const { config, meta } = this.dumpConfigToObject(); @@ -592,9 +590,7 @@ export class EggApplicationCore extends EggCore { try { const items = this.timing.toJSON(); const rundir = this.getRuntimeRundir(); - if (!fs.existsSync(rundir)) { - fs.mkdirSync(rundir, { recursive: true }); - } + fs.mkdirSync(rundir, { recursive: true }); const dumpFile = path.join(rundir, `${this.type}_timing_${process.pid}.json`); fs.writeFileSync(dumpFile, CircularJSON.stringify(items, null, 2)); this.coreLogger.info(this.timing.toString()); @@ -678,7 +674,7 @@ export class EggApplicationCore extends EggCore { private getWorkerStartTimeout(): number { const workerStartTimeout = this.config.workerStartTimeout; - if (typeof workerStartTimeout === 'number' && Number.isFinite(workerStartTimeout)) { + if (typeof workerStartTimeout === 'number' && Number.isFinite(workerStartTimeout) && workerStartTimeout > 0) { return workerStartTimeout; } return DEFAULT_WORKER_START_TIMEOUT; diff --git a/packages/egg/test/egg.test.ts b/packages/egg/test/egg.test.ts index 285b637158..e99d4c3876 100644 --- a/packages/egg/test/egg.test.ts +++ b/packages/egg/test/egg.test.ts @@ -291,11 +291,19 @@ describe.sequential('test/egg.test.ts', () => { assertFile(path.join(runDir, 'router.json')); }); - it('should use the default worker start timeout when config is missing', () => { + it('should use the default worker start timeout when config is missing or invalid', () => { const originalWorkerStartTimeout = app.config.workerStartTimeout; - Reflect.set(app.config, 'workerStartTimeout', undefined); try { + Reflect.set(app.config, 'workerStartTimeout', undefined); assert.equal((app as any).getWorkerStartTimeout(), 10 * 60 * 1000); + Reflect.set(app.config, 'workerStartTimeout', 0); + assert.equal((app as any).getWorkerStartTimeout(), 10 * 60 * 1000); + Reflect.set(app.config, 'workerStartTimeout', -1); + assert.equal((app as any).getWorkerStartTimeout(), 10 * 60 * 1000); + Reflect.set(app.config, 'workerStartTimeout', Number.POSITIVE_INFINITY); + assert.equal((app as any).getWorkerStartTimeout(), 10 * 60 * 1000); + Reflect.set(app.config, 'workerStartTimeout', 1); + assert.equal((app as any).getWorkerStartTimeout(), 1); } finally { Reflect.set(app.config, 'workerStartTimeout', originalWorkerStartTimeout); } From 6d74bddb6509a143bd54e1f6e23522a02148e521 Mon Sep 17 00:00:00 2001 From: killa Date: Wed, 6 May 2026 18:18:06 +0800 Subject: [PATCH 3/4] fix(egg): log resolved timing dump path --- packages/egg/src/lib/egg.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/egg/src/lib/egg.ts b/packages/egg/src/lib/egg.ts index 2ef6461896..832e0b8e8a 100644 --- a/packages/egg/src/lib/egg.ts +++ b/packages/egg/src/lib/egg.ts @@ -652,11 +652,8 @@ export class EggApplicationCore extends EggCore { if (item.end) continue; this.coreLogger.error(`unfinished timing item: ${CircularJSON.stringify(item)}`); } - this.coreLogger.error( - '[egg][setupTimeoutTimer] check run/%s_timing_%s.json for more details.', - this.type, - process.pid, - ); + const dumpTimingFile = path.join(this.getRuntimeRundir(), `${this.type}_timing_${process.pid}.json`); + this.coreLogger.error('[egg][setupTimeoutTimer] check %s for more details.', dumpTimingFile); this.emit('startTimeout'); this.dumpConfig(); this.dumpTiming(); From 45e50f6f539a9e9358e35199932a97be42c974aa Mon Sep 17 00:00:00 2001 From: killa Date: Wed, 6 May 2026 18:47:35 +0800 Subject: [PATCH 4/4] fix(egg): align diagnostic docs --- packages/egg/src/lib/application.ts | 2 +- packages/egg/src/lib/egg.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/egg/src/lib/application.ts b/packages/egg/src/lib/application.ts index 0c80e7fa90..71b83f7825 100644 --- a/packages/egg/src/lib/application.ts +++ b/packages/egg/src/lib/application.ts @@ -193,7 +193,7 @@ export class Application extends EggApplicationCore { } /** - * save routers to `run/router.json` + * save routers to `${rundir}/router.json` * @private */ dumpConfig(): void { diff --git a/packages/egg/src/lib/egg.ts b/packages/egg/src/lib/egg.ts index 832e0b8e8a..8ecce9af91 100644 --- a/packages/egg/src/lib/egg.ts +++ b/packages/egg/src/lib/egg.ts @@ -563,7 +563,7 @@ export class EggApplicationCore extends EggCore { } /** - * save app.config to `run/${type}_config.json` + * save app.config to `${rundir}/${type}_config.json` * @private */ dumpConfig(): void {