Finalize log stream before exiting process

Reviewed By: antonk52

Differential Revision: D51229230

fbshipit-source-id: 0e7f657a170eb8602ade9abf1db1976c5b51dc3f
This commit is contained in:
Andrey Goncharov
2023-11-11 08:21:12 -08:00
committed by Facebook GitHub Bot
parent 0cbd640e5c
commit a400eb2872
8 changed files with 87 additions and 13 deletions

View File

@@ -583,6 +583,7 @@ export class FlipperServerImpl implements FlipperServer {
return uploadRes; return uploadRes;
}, },
shutdown: async () => { shutdown: async () => {
// Do not use processExit helper. We want to server immediatelly quit when this call is triggerred
process.exit(0); process.exit(0);
}, },
'is-logged-in': async () => { 'is-logged-in': async () => {

View File

@@ -13,6 +13,7 @@ export * from './tracker';
export {loadLauncherSettings} from './utils/launcherSettings'; export {loadLauncherSettings} from './utils/launcherSettings';
export {loadProcessConfig} from './utils/processConfig'; export {loadProcessConfig} from './utils/processConfig';
export {getEnvironmentInfo} from './utils/environmentInfo'; export {getEnvironmentInfo} from './utils/environmentInfo';
export {processExit, setProcessExitRoutine} from './utils/processExit';
export {getGatekeepers} from './gk'; export {getGatekeepers} from './gk';
export {setupPrefetcher} from './fb-stubs/Prefetcher'; export {setupPrefetcher} from './fb-stubs/Prefetcher';
export * from './server/attachSocketServer'; export * from './server/attachSocketServer';

View File

@@ -29,6 +29,7 @@ import {URLSearchParams} from 'url';
import {tracker} from '../tracker'; import {tracker} from '../tracker';
import {getFlipperServerConfig} from '../FlipperServerConfig'; import {getFlipperServerConfig} from '../FlipperServerConfig';
import {performance} from 'perf_hooks'; import {performance} from 'perf_hooks';
import {processExit} from '../utils/processExit';
const safe = (f: () => void) => { const safe = (f: () => void) => {
try { try {
@@ -266,7 +267,7 @@ export function attachSocketServer(
console.info( console.info(
'[flipper-server] Shutdown as no clients are currently connected', '[flipper-server] Shutdown as no clients are currently connected',
); );
process.exit(0); processExit(0);
} }
}, FIVE_HOURS); }, FIVE_HOURS);
} }

View File

@@ -27,6 +27,7 @@ import {EnvironmentInfo, isProduction} from 'flipper-common';
import {GRAPH_SECRET} from '../fb-stubs/constants'; import {GRAPH_SECRET} from '../fb-stubs/constants';
import {sessionId} from '../sessionId'; import {sessionId} from '../sessionId';
import {UIPreference, openUI} from '../utils/openUI'; import {UIPreference, openUI} from '../utils/openUI';
import {processExit} from '../utils/processExit';
type Config = { type Config = {
port: number; port: number;
@@ -123,7 +124,7 @@ export async function startServer(
console.error( console.error(
`[flipper-server] Unable to become ready within ${timeoutSeconds} seconds, exit`, `[flipper-server] Unable to become ready within ${timeoutSeconds} seconds, exit`,
); );
process.exit(1); processExit(1);
} }
}, timeoutSeconds * 1000); }, timeoutSeconds * 1000);
@@ -192,6 +193,7 @@ async function startHTTPServer(
res.json({success: true}); res.json({success: true});
// Just exit the process, this will trigger the shutdown hooks. // Just exit the process, this will trigger the shutdown hooks.
// Do not use prcoessExit util as we want the serve to shutdown immediately
process.exit(0); process.exit(0);
}); });
@@ -226,7 +228,7 @@ async function startHTTPServer(
`[flipper-server] Unable to listen at port: ${config.port}, is already in use`, `[flipper-server] Unable to listen at port: ${config.port}, is already in use`,
); );
tracker.track('server-socket-already-in-use', {}); tracker.track('server-socket-already-in-use', {});
process.exit(1); processExit(1);
} }
}); });

View File

@@ -50,7 +50,9 @@ export async function checkServerRunning(
port: number, port: number,
): Promise<string | undefined> { ): Promise<string | undefined> {
try { try {
const response = await fetch(`http://localhost:${port}/info`); const response = await fetch(`http://localhost:${port}/info`, {
timeout: 1000,
});
if (response.status >= 200 && response.status < 300) { if (response.status >= 200 && response.status < 300) {
const environmentInfo: EnvironmentInfo = await response.json(); const environmentInfo: EnvironmentInfo = await response.json();
return environmentInfo.appVersion; return environmentInfo.appVersion;
@@ -74,7 +76,9 @@ export async function checkServerRunning(
*/ */
export async function shutdownRunningInstance(port: number): Promise<boolean> { export async function shutdownRunningInstance(port: number): Promise<boolean> {
try { try {
const response = await fetch(`http://localhost:${port}/shutdown`); const response = await fetch(`http://localhost:${port}/shutdown`, {
timeout: 1000,
});
if (response.status >= 200 && response.status < 300) { if (response.status >= 200 && response.status < 300) {
const json = await response.json(); const json = await response.json();
console.info( console.info(

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const onBeforeExitFns: (() => void | Promise<void>)[] = [];
export const setProcessExitRoutine = (
onBeforeExit: () => void | Promise<void>,
) => {
onBeforeExitFns.push(onBeforeExit);
};
const resIsPromise = (res: void | Promise<void>): res is Promise<void> =>
res instanceof Promise;
export const processExit = async (code: number) => {
console.debug('processExit', code);
// eslint-disable-next-line promise/catch-or-return
await Promise.all(
onBeforeExitFns.map(async (fn) => {
try {
const res = fn();
if (resIsPromise(res)) {
return res.catch((e) => {
console.error('Process exit routine failed', e);
});
}
} catch (e) {
console.error('Process exit routine failed', e);
}
}),
).finally(() => {
process.exit(code);
});
setTimeout(() => {
console.error('Process exit routines timed out');
process.exit(code);
}, 5000);
};

View File

@@ -30,6 +30,7 @@ import {
startFlipperServer, startFlipperServer,
startServer, startServer,
tracker, tracker,
processExit,
} from 'flipper-server-core'; } from 'flipper-server-core';
import {addLogTailer, isProduction, isTest, LoggerFormat} from 'flipper-common'; import {addLogTailer, isProduction, isTest, LoggerFormat} from 'flipper-common';
import exitHook from 'exit-hook'; import exitHook from 'exit-hook';
@@ -265,7 +266,7 @@ async function start() {
console.error( console.error(
'[flipper-server] state changed to error, process will exit.', '[flipper-server] state changed to error, process will exit.',
); );
process.exit(1); processExit(1);
} }
}); });
} }
@@ -335,7 +336,7 @@ process.on('uncaughtException', (error) => {
error, error,
); );
reportBrowserConnection(false); reportBrowserConnection(false);
process.exit(1); processExit(1);
}); });
process.on('unhandledRejection', (reason, promise) => { process.on('unhandledRejection', (reason, promise) => {
@@ -347,8 +348,15 @@ process.on('unhandledRejection', (reason, promise) => {
); );
}); });
start().catch((e) => { // Node.js process never waits for all promises to settle and exits as soon as there is not pending timers or open sockets or tasks in teh macroqueue
const runtimeTimeout = setTimeout(() => {}, Number.MAX_SAFE_INTEGER);
// eslint-disable-next-line promise/catch-or-return
start()
.catch((e) => {
console.error(chalk.red('Server startup error: '), e); console.error(chalk.red('Server startup error: '), e);
reportBrowserConnection(false); reportBrowserConnection(false);
process.exit(1); return processExit(1);
})
.finally(() => {
clearTimeout(runtimeTimeout);
}); });

View File

@@ -21,7 +21,10 @@ import fsRotator from 'file-stream-rotator';
import {ensureFile} from 'fs-extra'; import {ensureFile} from 'fs-extra';
import {access} from 'fs/promises'; import {access} from 'fs/promises';
import {constants} from 'fs'; import {constants} from 'fs';
import {initializeLogger as initLogger} from 'flipper-server-core'; import {
initializeLogger as initLogger,
setProcessExitRoutine,
} from 'flipper-server-core';
export const loggerOutputFile = 'flipper-server-log.out'; export const loggerOutputFile = 'flipper-server-log.out';
@@ -64,4 +67,14 @@ export async function initializeLogger(
logStream?.write(`${name}: \n${stack}\n`); logStream?.write(`${name}: \n${stack}\n`);
} }
}); });
const finalizeLogger = async () => {
const logStreamToEnd = logStream;
// Prevent future writes
logStream = undefined;
await new Promise<void>((resolve) => {
logStreamToEnd?.end(resolve);
});
};
setProcessExitRoutine(finalizeLogger);
} }