Gateway: add compact ws verbose logs
This commit is contained in:
@@ -1,6 +1,10 @@
|
|||||||
import type { Command } from "commander";
|
import type { Command } from "commander";
|
||||||
import { callGateway, randomIdempotencyKey } from "../gateway/call.js";
|
import { callGateway, randomIdempotencyKey } from "../gateway/call.js";
|
||||||
import { startGatewayServer } from "../gateway/server.js";
|
import { startGatewayServer } from "../gateway/server.js";
|
||||||
|
import {
|
||||||
|
type GatewayWsLogStyle,
|
||||||
|
setGatewayWsLogStyle,
|
||||||
|
} from "../gateway/ws-logging.js";
|
||||||
import { info, setVerbose } from "../globals.js";
|
import { info, setVerbose } from "../globals.js";
|
||||||
import { GatewayLockError } from "../infra/gateway-lock.js";
|
import { GatewayLockError } from "../infra/gateway-lock.js";
|
||||||
import { defaultRuntime } from "../runtime.js";
|
import { defaultRuntime } from "../runtime.js";
|
||||||
@@ -52,8 +56,29 @@ export function registerGatewayCli(program: Command) {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.option("--verbose", "Verbose logging to stdout/stderr", false)
|
.option("--verbose", "Verbose logging to stdout/stderr", false)
|
||||||
|
.option(
|
||||||
|
"--ws-log <style>",
|
||||||
|
'Verbose WebSocket log style ("full"|"compact")',
|
||||||
|
"full",
|
||||||
|
)
|
||||||
|
.option("--compact", 'Alias for "--ws-log compact"', false)
|
||||||
.action(async (opts) => {
|
.action(async (opts) => {
|
||||||
setVerbose(Boolean(opts.verbose));
|
setVerbose(Boolean(opts.verbose));
|
||||||
|
const wsLogRaw = (opts.compact ? "compact" : opts.wsLog) as
|
||||||
|
| string
|
||||||
|
| undefined;
|
||||||
|
const wsLogStyle: GatewayWsLogStyle =
|
||||||
|
wsLogRaw === "compact" ? "compact" : "full";
|
||||||
|
if (
|
||||||
|
wsLogRaw !== undefined &&
|
||||||
|
wsLogRaw !== "compact" &&
|
||||||
|
wsLogRaw !== "full"
|
||||||
|
) {
|
||||||
|
defaultRuntime.error('Invalid --ws-log (use "full" or "compact")');
|
||||||
|
defaultRuntime.exit(1);
|
||||||
|
}
|
||||||
|
setGatewayWsLogStyle(wsLogStyle);
|
||||||
|
|
||||||
const port = Number.parseInt(String(opts.port ?? "18789"), 10);
|
const port = Number.parseInt(String(opts.port ?? "18789"), 10);
|
||||||
if (Number.isNaN(port) || port <= 0) {
|
if (Number.isNaN(port) || port <= 0) {
|
||||||
defaultRuntime.error("Invalid port");
|
defaultRuntime.error("Invalid port");
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ import { setHeartbeatsEnabled } from "../web/auto-reply.js";
|
|||||||
import { sendMessageWhatsApp } from "../web/outbound.js";
|
import { sendMessageWhatsApp } from "../web/outbound.js";
|
||||||
import { requestReplyHeartbeatNow } from "../web/reply-heartbeat-wake.js";
|
import { requestReplyHeartbeatNow } from "../web/reply-heartbeat-wake.js";
|
||||||
import { buildMessageWithAttachments } from "./chat-attachments.js";
|
import { buildMessageWithAttachments } from "./chat-attachments.js";
|
||||||
|
import { getGatewayWsLogStyle } from "./ws-logging.js";
|
||||||
import {
|
import {
|
||||||
type ConnectParams,
|
type ConnectParams,
|
||||||
ErrorCodes,
|
ErrorCodes,
|
||||||
@@ -475,6 +476,11 @@ function logWs(
|
|||||||
meta?: Record<string, unknown>,
|
meta?: Record<string, unknown>,
|
||||||
) {
|
) {
|
||||||
if (!isVerbose()) return;
|
if (!isVerbose()) return;
|
||||||
|
if (getGatewayWsLogStyle() === "compact") {
|
||||||
|
logWsCompact(direction, kind, meta);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const connId = typeof meta?.connId === "string" ? meta.connId : undefined;
|
const connId = typeof meta?.connId === "string" ? meta.connId : undefined;
|
||||||
const id = typeof meta?.id === "string" ? meta.id : undefined;
|
const id = typeof meta?.id === "string" ? meta.id : undefined;
|
||||||
@@ -546,6 +552,100 @@ function logWs(
|
|||||||
console.log(tokens.join(" "));
|
console.log(tokens.join(" "));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WsInflightEntry = {
|
||||||
|
ts: number;
|
||||||
|
method?: string;
|
||||||
|
meta?: Record<string, unknown>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const wsInflightCompact = new Map<string, WsInflightEntry>();
|
||||||
|
let wsLastCompactConnId: string | undefined;
|
||||||
|
|
||||||
|
function logWsCompact(
|
||||||
|
direction: "in" | "out",
|
||||||
|
kind: string,
|
||||||
|
meta?: Record<string, unknown>,
|
||||||
|
) {
|
||||||
|
const now = Date.now();
|
||||||
|
const connId = typeof meta?.connId === "string" ? meta.connId : undefined;
|
||||||
|
const id = typeof meta?.id === "string" ? meta.id : undefined;
|
||||||
|
const method = typeof meta?.method === "string" ? meta.method : undefined;
|
||||||
|
const ok = typeof meta?.ok === "boolean" ? meta.ok : undefined;
|
||||||
|
const inflightKey = connId && id ? `${connId}:${id}` : undefined;
|
||||||
|
|
||||||
|
// Pair req/res into a single line (printed on response).
|
||||||
|
if (kind === "req" && direction === "in" && inflightKey) {
|
||||||
|
wsInflightCompact.set(inflightKey, { ts: now, method, meta });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const compactArrow = (() => {
|
||||||
|
if (kind === "req" || kind === "res") return "⇄";
|
||||||
|
return direction === "in" ? "←" : "→";
|
||||||
|
})();
|
||||||
|
const arrowColor =
|
||||||
|
kind === "req" || kind === "res"
|
||||||
|
? chalk.yellowBright
|
||||||
|
: direction === "in"
|
||||||
|
? chalk.greenBright
|
||||||
|
: chalk.cyanBright;
|
||||||
|
|
||||||
|
const prefix = `${chalk.gray("[gws]")} ${arrowColor(compactArrow)} ${chalk.bold(kind)}`;
|
||||||
|
|
||||||
|
const statusToken =
|
||||||
|
kind === "res" && ok !== undefined
|
||||||
|
? ok
|
||||||
|
? chalk.greenBright("✓")
|
||||||
|
: chalk.redBright("✗")
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const startedAt =
|
||||||
|
kind === "res" && direction === "out" && inflightKey
|
||||||
|
? wsInflightCompact.get(inflightKey)?.ts
|
||||||
|
: undefined;
|
||||||
|
if (kind === "res" && direction === "out" && inflightKey) {
|
||||||
|
wsInflightCompact.delete(inflightKey);
|
||||||
|
}
|
||||||
|
const durationToken =
|
||||||
|
typeof startedAt === "number" ? chalk.dim(`${now - startedAt}ms`) : undefined;
|
||||||
|
|
||||||
|
const headline =
|
||||||
|
(kind === "req" || kind === "res") && method
|
||||||
|
? chalk.bold(method)
|
||||||
|
: kind === "event" && typeof meta?.event === "string"
|
||||||
|
? chalk.bold(meta.event)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const restMeta: string[] = [];
|
||||||
|
if (meta) {
|
||||||
|
for (const [key, value] of Object.entries(meta)) {
|
||||||
|
if (value === undefined) continue;
|
||||||
|
if (key === "connId" || key === "id") continue;
|
||||||
|
if (key === "method" || key === "ok") continue;
|
||||||
|
if (key === "event") continue;
|
||||||
|
restMeta.push(`${chalk.dim(key)}=${formatForLog(value)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const trailing: string[] = [];
|
||||||
|
if (connId && connId !== wsLastCompactConnId) {
|
||||||
|
trailing.push(`${chalk.dim("conn")}=${chalk.gray(shortId(connId))}`);
|
||||||
|
wsLastCompactConnId = connId;
|
||||||
|
}
|
||||||
|
if (id) trailing.push(`${chalk.dim("id")}=${chalk.gray(shortId(id))}`);
|
||||||
|
|
||||||
|
const tokens = [
|
||||||
|
prefix,
|
||||||
|
statusToken,
|
||||||
|
headline,
|
||||||
|
durationToken,
|
||||||
|
...restMeta,
|
||||||
|
...trailing,
|
||||||
|
].filter((t): t is string => Boolean(t));
|
||||||
|
|
||||||
|
console.log(tokens.join(" "));
|
||||||
|
}
|
||||||
|
|
||||||
const UUID_RE =
|
const UUID_RE =
|
||||||
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||||
|
|
||||||
|
|||||||
11
src/gateway/ws-logging.ts
Normal file
11
src/gateway/ws-logging.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export type GatewayWsLogStyle = "full" | "compact";
|
||||||
|
|
||||||
|
let gatewayWsLogStyle: GatewayWsLogStyle = "full";
|
||||||
|
|
||||||
|
export function setGatewayWsLogStyle(style: GatewayWsLogStyle): void {
|
||||||
|
gatewayWsLogStyle = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getGatewayWsLogStyle(): GatewayWsLogStyle {
|
||||||
|
return gatewayWsLogStyle;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user