refactor: simplify gateway startup logs

This commit is contained in:
Peter Steinberger
2026-04-03 11:30:04 +01:00
parent 16ca1f4d74
commit 1c26e806ff
5 changed files with 40 additions and 21 deletions

View File

@@ -27,13 +27,14 @@ const gatewayLog = createSubsystemLogger("gateway");
type GatewayRunSignalAction = "stop" | "restart";
export async function runGatewayLoop(params: {
start: () => Promise<Awaited<ReturnType<typeof startGatewayServer>>>;
start: (params?: {
startupStartedAt?: number;
}) => Promise<Awaited<ReturnType<typeof startGatewayServer>>>;
runtime: RuntimeEnv;
lockPort?: number;
}) {
gatewayLog.info("acquiring gateway lock...");
let startupStartedAt = Date.now();
let lock = await acquireGatewayLock({ port: params.lockPort });
gatewayLog.info("gateway lock acquired");
let server: Awaited<ReturnType<typeof startGatewayServer>> | null = null;
let shuttingDown = false;
let restartResolver: (() => void) | null = null;
@@ -57,6 +58,7 @@ export async function runGatewayLoop(params: {
};
const reacquireLockForInProcessRestart = async (): Promise<boolean> => {
try {
startupStartedAt = Date.now();
lock = await acquireGatewayLock({ port: params.lockPort });
return true;
} catch (err) {
@@ -231,7 +233,7 @@ export async function runGatewayLoop(params: {
for (;;) {
onIteration();
try {
server = await params.start();
server = await params.start({ startupStartedAt });
isFirstStart = false;
} catch (err) {
// On initial startup, let the error propagate so the outer handler

View File

@@ -457,16 +457,17 @@ async function runGatewayCommand(opts: GatewayRunOpts) {
}
: undefined;
gatewayLog.info("starting gateway…");
gatewayLog.info("starting...");
const startLoop = async () =>
await runGatewayLoop({
runtime: defaultRuntime,
lockPort: port,
start: async () =>
start: async ({ startupStartedAt } = {}) =>
await startGatewayServer(port, {
bind,
auth: authOverride,
tailscale: tailscaleOverride,
startupStartedAt,
}),
});

View File

@@ -1,7 +1,11 @@
import { describe, expect, it, vi } from "vitest";
import { afterEach, describe, expect, it, vi } from "vitest";
import { logGatewayStartup } from "./server-startup-log.js";
describe("gateway startup log", () => {
afterEach(() => {
vi.useRealTimers();
});
it("warns when dangerous config flags are enabled", () => {
const info = vi.fn();
const warn = vi.fn();
@@ -15,6 +19,7 @@ describe("gateway startup log", () => {
},
},
bindHost: "127.0.0.1",
pluginCount: 0,
port: 18789,
log: { info, warn },
isNixMode: false,
@@ -35,6 +40,7 @@ describe("gateway startup log", () => {
logGatewayStartup({
cfg: {},
bindHost: "127.0.0.1",
pluginCount: 0,
port: 18789,
log: { info, warn },
isNixMode: false,
@@ -43,7 +49,10 @@ describe("gateway startup log", () => {
expect(warn).not.toHaveBeenCalled();
});
it("logs all listen endpoints on a single line", () => {
it("logs a compact ready line with plugin count and duration", () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-04-03T10:00:16.000Z"));
const info = vi.fn();
const warn = vi.fn();
@@ -51,16 +60,16 @@ describe("gateway startup log", () => {
cfg: {},
bindHost: "127.0.0.1",
bindHosts: ["127.0.0.1", "::1"],
pluginCount: 8,
port: 18789,
startupStartedAt: Date.parse("2026-04-03T10:00:00.000Z"),
log: { info, warn },
isNixMode: false,
});
const listenMessages = info.mock.calls
const readyMessages = info.mock.calls
.map((call) => call[0])
.filter((message) => message.startsWith("listening on "));
expect(listenMessages).toEqual([
`listening on ws://127.0.0.1:18789, ws://[::1]:18789 (PID ${process.pid})`,
]);
.filter((message) => message.startsWith("ready ("));
expect(readyMessages).toEqual(["ready (8 plugins, 16.0s)"]);
});
});

View File

@@ -10,6 +10,8 @@ export function logGatewayStartup(params: {
bindHost: string;
bindHosts?: string[];
port: number;
pluginCount: number;
startupStartedAt?: number;
tlsEnabled?: boolean;
log: { info: (msg: string, meta?: Record<string, unknown>) => void; warn: (msg: string) => void };
isNixMode: boolean;
@@ -23,12 +25,13 @@ export function logGatewayStartup(params: {
params.log.info(`agent model: ${modelRef}`, {
consoleMessage: `agent model: ${chalk.whiteBright(modelRef)}`,
});
const scheme = params.tlsEnabled ? "wss" : "ws";
const formatHost = (host: string) => (host.includes(":") ? `[${host}]` : host);
const hosts =
params.bindHosts && params.bindHosts.length > 0 ? params.bindHosts : [params.bindHost];
const listenEndpoints = hosts.map((host) => `${scheme}://${formatHost(host)}:${params.port}`);
params.log.info(`listening on ${listenEndpoints.join(", ")} (PID ${process.pid})`);
const startupDurationMs =
typeof params.startupStartedAt === "number" ? Date.now() - params.startupStartedAt : null;
const startupDurationLabel =
startupDurationMs == null ? null : `${(startupDurationMs / 1000).toFixed(1)}s`;
params.log.info(
`ready (${params.pluginCount} ${params.pluginCount === 1 ? "plugin" : "plugins"}${startupDurationLabel ? `, ${startupDurationLabel}` : ""})`,
);
params.log.info(`log file: ${getResolvedLoggerSettings().file}`);
if (params.isNixMode) {
params.log.info("gateway: running in Nix mode (config managed externally)");

View File

@@ -373,6 +373,10 @@ export type GatewayServerOptions = {
runtime: import("../runtime.js").RuntimeEnv,
prompter: import("../wizard/prompts.js").WizardPrompter,
) => Promise<void>;
/**
* Optional startup timestamp used for concise readiness logging.
*/
startupStartedAt?: number;
};
export async function startGatewayServer(
@@ -606,7 +610,6 @@ export async function startGatewayServer(
let pluginRegistry = emptyPluginRegistry;
let baseGatewayMethods = baseMethods;
if (!minimalTestGateway) {
log.info("loading plugins...");
({ pluginRegistry, gatewayMethods: baseGatewayMethods } = loadGatewayStartupPlugins({
cfg: gatewayPluginConfigAtStart,
workspaceDir: defaultWorkspaceDir,
@@ -616,7 +619,6 @@ export async function startGatewayServer(
pluginIds: startupPluginIds,
preferSetupRuntimeForChannelPlugins: deferredConfiguredChannelPluginIds.length > 0,
}));
log.info(`plugins loaded (${pluginRegistry.plugins.length} plugins)`);
} else {
setActivePluginRegistry(emptyPluginRegistry);
}
@@ -1340,8 +1342,10 @@ export async function startGatewayServer(
bindHosts: httpBindHosts,
port,
tlsEnabled: gatewayTls.enabled,
pluginCount: pluginRegistry.plugins.length,
log,
isNixMode,
startupStartedAt: opts.startupStartedAt,
});
stopGatewayUpdateCheck = minimalTestGateway
? () => {}