Skip to content

Commit 84bbaf8

Browse files
authored
Merge pull request #9 from theodo-group/refactor/unified-logger
Unify logging into single typed JSONL logger
2 parents a33e723 + d5af4c9 commit 84bbaf8

18 files changed

Lines changed: 549 additions & 504 deletions

File tree

src/cdp/client.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ProtocolMapping } from "devtools-protocol/types/protocol-mapping.js";
22
import { REQUEST_TIMEOUT_MS } from "../constants.ts";
3-
import type { CdpLogger } from "./logger.ts";
3+
import type { Logger } from "../logger/index.ts";
44
import type { CdpEvent, CdpRequest, CdpResponse } from "./types.ts";
55

66
type CdpCommand = keyof ProtocolMapping.Commands;
@@ -21,18 +21,20 @@ export class CdpClient {
2121
private pending = new Map<number, PendingRequest>();
2222
private listeners = new Map<string, Set<AnyHandler>>();
2323
private isConnected = false;
24-
private logger: CdpLogger | null;
24+
private logger: Logger<"cdp"> | null;
2525
/** Map request id → method name for response logging */
2626
private sentMethods = new Map<number, string>();
27+
/** Map request id → send timestamp for latency tracking */
28+
private sendTimestamps = new Map<number, number>();
2729

28-
private constructor(ws: WebSocket, logger?: CdpLogger) {
30+
private constructor(ws: WebSocket, logger?: Logger<"cdp">) {
2931
this.ws = ws;
3032
this.logger = logger ?? null;
3133
this.isConnected = true;
3234
this.setupHandlers();
3335
}
3436

35-
static async connect(wsUrl: string, logger?: CdpLogger): Promise<CdpClient> {
37+
static async connect(wsUrl: string, logger?: Logger<"cdp">): Promise<CdpClient> {
3638
return new Promise<CdpClient>((resolve, reject) => {
3739
const ws = new WebSocket(wsUrl);
3840

@@ -78,7 +80,8 @@ export class CdpClient {
7880
}
7981

8082
this.sentMethods.set(id, method);
81-
this.logger?.logSend(id, method, params as Record<string, unknown> | undefined);
83+
this.sendTimestamps.set(id, Date.now());
84+
this.logger?.trace("send", { id, method, params: params as Record<string, unknown> });
8285

8386
return new Promise<unknown>((resolve, reject) => {
8487
const timer = setTimeout(() => {
@@ -246,8 +249,17 @@ export class CdpClient {
246249
const response = parsed as CdpResponse;
247250
const method = this.sentMethods.get(response.id) ?? "unknown";
248251
this.sentMethods.delete(response.id);
249-
250-
this.logger?.logResponse(response.id, method, response.result, response.error);
252+
const sendTime = this.sendTimestamps.get(response.id);
253+
this.sendTimestamps.delete(response.id);
254+
const ms = sendTime != null ? Date.now() - sendTime : 0;
255+
256+
this.logger?.trace("recv", {
257+
id: response.id,
258+
method,
259+
ms,
260+
result: response.result,
261+
error: response.error,
262+
});
251263

252264
const pending = this.pending.get(response.id);
253265
if (!pending) {
@@ -263,7 +275,10 @@ export class CdpClient {
263275
}
264276
} else if ("method" in parsed) {
265277
const event = parsed as CdpEvent;
266-
this.logger?.logEvent(event.method, event.params);
278+
this.logger?.trace("event", {
279+
method: event.method,
280+
params: event.params as Record<string, unknown>,
281+
});
267282

268283
const handlers = this.listeners.get(event.method);
269284
if (handlers) {

src/cdp/logger.ts

Lines changed: 0 additions & 69 deletions
This file was deleted.

src/cdp/session.ts

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import type { Subprocess } from "bun";
22
import type Protocol from "devtools-protocol/types/protocol.js";
3-
import { DaemonLogger } from "../daemon/logger.ts";
4-
import { ensureSocketDir, getDaemonLogPath, getLogPath } from "../daemon/paths.ts";
3+
import { ensureSocketDir, getLogPath } from "../daemon/paths.ts";
54
import type { RemoteObject } from "../formatter/values.ts";
65
import { formatValue } from "../formatter/values.ts";
6+
import { createLogger, type Logger } from "../logger/index.ts";
77
import { BaseSession } from "../session/base-session.ts";
88
import type { BreakpointListItem, SessionCapabilities, SourceMapInfo } from "../session/session.ts";
99
import type {
@@ -21,7 +21,6 @@ import { SourceMapResolver } from "../sourcemap/resolver.ts";
2121
import { createAdapter } from "./adapters/index.ts";
2222
import { CdpClient } from "./client.ts";
2323
import type { CdpDialect } from "./dialect.ts";
24-
import { CdpLogger } from "./logger.ts";
2524
import {
2625
addBlackbox as addBlackboxImpl,
2726
listBlackbox as listBlackboxImpl,
@@ -99,8 +98,8 @@ export class CdpSession extends BaseSession {
9998
launchCommand: string[] | null = null;
10099
launchOptions: { brk?: boolean; port?: number } | null = null;
101100
adapter: CdpDialect;
102-
cdpLogger: CdpLogger;
103-
daemonLogger: DaemonLogger;
101+
private log: Logger<"session">;
102+
private cdpLog: Logger<"cdp">;
104103

105104
readonly capabilities: SessionCapabilities = {
106105
functionBreakpoints: false,
@@ -135,11 +134,12 @@ export class CdpSession extends BaseSession {
135134
this.sourceMapResolver.setDisabled(true);
136135
}
137136

138-
constructor(session: string, options?: { daemonLogger?: DaemonLogger }) {
137+
constructor(session: string, options?: { logger?: Logger<"daemon"> }) {
139138
super(session);
140139
ensureSocketDir();
141-
this.cdpLogger = new CdpLogger(getLogPath(session));
142-
this.daemonLogger = options?.daemonLogger ?? new DaemonLogger(getDaemonLogPath(session));
140+
const rootLogger = options?.logger ?? createLogger(getLogPath(session));
141+
this.log = rootLogger.child("session");
142+
this.cdpLog = rootLogger.child("cdp");
143143
// Default to NodeAdapter; overridden in launch() when command is known
144144
this.adapter = createAdapter(["node"]);
145145
}
@@ -186,10 +186,7 @@ export class CdpSession extends BaseSession {
186186
});
187187
this.childProcess = proc;
188188

189-
this.daemonLogger.info("child.spawn", `Spawned process pid=${proc.pid}`, {
190-
pid: proc.pid,
191-
command: spawnArgs,
192-
});
189+
this.log.info("child.spawn", { pid: proc.pid ?? 0, command: spawnArgs });
193190

194191
// Monitor child process exit in the background
195192
this.monitorProcessExit(proc);
@@ -198,9 +195,7 @@ export class CdpSession extends BaseSession {
198195
const wsUrl = await this.readInspectorUrl(proc.stderr);
199196
this.wsUrl = wsUrl;
200197

201-
this.daemonLogger.info("inspector.detected", `Inspector URL: ${wsUrl}`, {
202-
wsUrl,
203-
});
198+
this.log.info("cdp.connected", { url: wsUrl });
204199

205200
// Connect CDP
206201
await this.connectCdp(wsUrl);
@@ -776,15 +771,12 @@ export class CdpSession extends BaseSession {
776771

777772
this.refs.bind(entry.ref, r.breakpointId);
778773

779-
this.daemonLogger.info(
780-
"breakpoint.rebound",
781-
`${entry.ref} bound by scriptId at ${scriptUrl}:${compiledLine}`,
782-
);
774+
this.log.info("breakpoint.rebound", { file: scriptUrl, line: compiledLine });
783775
} catch (err) {
784-
this.daemonLogger.debug(
785-
"breakpoint.rebind.failed",
786-
`Failed to rebind ${entry.ref}: ${err instanceof Error ? err.message : String(err)}`,
787-
);
776+
this.log.debug("breakpoint.rebind.failed", {
777+
ref: entry.ref,
778+
error: err instanceof Error ? err.message : String(err),
779+
});
788780
}
789781
}
790782
}
@@ -796,10 +788,10 @@ export class CdpSession extends BaseSession {
796788
}
797789

798790
private async connectCdp(wsUrl: string): Promise<void> {
799-
this.daemonLogger.debug("cdp.connecting", `Connecting to ${wsUrl}`);
800-
const cdp = await CdpClient.connect(wsUrl, this.cdpLogger);
791+
this.log.debug("cdp.connecting", { url: wsUrl });
792+
const cdp = await CdpClient.connect(wsUrl, this.cdpLog);
801793
this.cdp = cdp;
802-
this.daemonLogger.info("cdp.connected", `CDP connected to ${wsUrl}`);
794+
this.log.info("cdp.connected", { url: wsUrl });
803795

804796
// Set up event handlers before enabling domains so we don't miss any events
805797
this.setupCdpEventHandlers(cdp);
@@ -873,10 +865,10 @@ export class CdpSession extends BaseSession {
873865
if (p.url) return this.rebindPendingBreakpoints(scriptId, p.url);
874866
})
875867
.catch((err) => {
876-
this.daemonLogger.debug(
877-
"sourcemap.load.failed",
878-
`Failed to load source map for ${info.url}: ${err instanceof Error ? err.message : String(err)}`,
879-
);
868+
this.log.debug("sourcemap.load.failed", {
869+
file: info.url,
870+
reason: err instanceof Error ? err.message : String(err),
871+
});
880872
});
881873
this._pendingRebinds.add(rebindPromise);
882874
rebindPromise.finally(() => this._pendingRebinds.delete(rebindPromise));
@@ -953,10 +945,7 @@ export class CdpSession extends BaseSession {
953945
private monitorProcessExit(proc: Subprocess<"ignore", "ignore", "pipe">): void {
954946
proc.exited
955947
.then((exitCode) => {
956-
this.daemonLogger.info("child.exit", `Process exited with code ${exitCode ?? "unknown"}`, {
957-
pid: proc.pid,
958-
exitCode: exitCode ?? null,
959-
});
948+
this.log.info("child.exit", { code: exitCode ?? null });
960949
// Child process has exited
961950
this.childProcess = null;
962951
if (this.cdp) {
@@ -970,9 +959,7 @@ export class CdpSession extends BaseSession {
970959
this.onProcessExit.clear();
971960
})
972961
.catch((err) => {
973-
this.daemonLogger.error("child.exit.error", `Error waiting for process exit: ${err}`, {
974-
pid: proc.pid,
975-
});
962+
this.log.error("child.exit.error", { error: String(err) });
976963
// Error waiting for exit, treat as exited
977964
this.childProcess = null;
978965
this.state = "idle";
@@ -1000,7 +987,7 @@ export class CdpSession extends BaseSession {
1000987
}
1001988
const chunk = decoder.decode(value, { stream: true });
1002989
accumulated += chunk;
1003-
this.daemonLogger.debug("child.stderr", chunk.trimEnd());
990+
this.log.debug("child.stderr", { text: chunk.trimEnd() });
1004991

1005992
const match = INSPECTOR_URL_REGEX.exec(accumulated);
1006993
if (match?.[1]) {
@@ -1016,7 +1003,7 @@ export class CdpSession extends BaseSession {
10161003
}
10171004

10181005
clearTimeout(timeout);
1019-
this.daemonLogger.error("inspector.failed", "Failed to detect inspector URL", {
1006+
this.log.error("inspector.failed", {
10201007
stderr: accumulated.slice(0, 2000),
10211008
timeoutMs: INSPECTOR_TIMEOUT_MS,
10221009
});

0 commit comments

Comments
 (0)