Skip to content

Commit 34ad849

Browse files
committed
improve flow logging timestamp prefixes
1 parent cf9d8d0 commit 34ad849

File tree

2 files changed

+95
-19
lines changed

2 files changed

+95
-19
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { Stagehand } from "../lib/v3";
2+
3+
async function run(): Promise<void> {
4+
const apiKey = process.env.OPENAI_API_KEY;
5+
if (!apiKey) {
6+
throw new Error(
7+
"Set OPENAI_API_KEY to a valid OpenAI key before running this demo.",
8+
);
9+
}
10+
11+
const stagehand = new Stagehand({
12+
env: "LOCAL",
13+
verbose: 2,
14+
model: { modelName: "openai/gpt-4.1-mini", apiKey },
15+
localBrowserLaunchOptions: {
16+
headless: true,
17+
args: ["--window-size=1280,720"],
18+
},
19+
disablePino: true,
20+
});
21+
22+
try {
23+
await stagehand.init();
24+
25+
const [page] = stagehand.context.pages();
26+
await page.goto("https://example.com/", { waitUntil: "load" });
27+
28+
const agent = stagehand.agent({
29+
systemPrompt:
30+
"You are a QA assistant. Keep answers short and deterministic. Finish quickly.",
31+
});
32+
const agentResult = await agent.execute(
33+
"Glance at the Example Domain page and confirm that you see the hero text.",
34+
);
35+
console.log("Agent result:", agentResult);
36+
37+
const observations = await stagehand.observe(
38+
"Find any links on the page",
39+
);
40+
console.log("Observe result:", observations);
41+
42+
if (observations.length > 0) {
43+
await stagehand.act(observations[0]);
44+
} else {
45+
await stagehand.act("click the link on the page");
46+
}
47+
48+
const extraction = await stagehand.extract(
49+
"Summarize the current page title and URL.",
50+
);
51+
console.log("Extraction result:", extraction);
52+
} finally {
53+
await stagehand.close({ force: true }).catch(() => {});
54+
}
55+
}
56+
57+
run().catch((error) => {
58+
console.error(error);
59+
process.exitCode = 1;
60+
});

packages/core/lib/v3/flowLogger.ts

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ function formatTag(label: string, id: string | null): string {
100100
}
101101

102102
function formatCdpTag(sessionId?: string | null): string {
103-
if (!sessionId) return "[CDP]";
103+
if (!sessionId) return "[CDP #????]";
104104
return `[CDP #${shortId(sessionId).toUpperCase()}]`;
105105
}
106106

@@ -109,6 +109,17 @@ function shortId(id: string | null): string {
109109
return id.slice(-4);
110110
}
111111

112+
function formatTimestamp(): string {
113+
const now = new Date();
114+
const year = now.getFullYear();
115+
const month = String(now.getMonth() + 1).padStart(2, "0");
116+
const day = String(now.getDate()).padStart(2, "0");
117+
const hours = String(now.getHours()).padStart(2, "0");
118+
const minutes = String(now.getMinutes()).padStart(2, "0");
119+
const seconds = String(now.getSeconds()).padStart(2, "0");
120+
return `[${year}-${month}-${day} ${hours}:${minutes}:${seconds}]`;
121+
}
122+
112123
function sanitizeOptions(options: V3Options): Record<string, unknown> {
113124
const sensitiveKeys = [
114125
"apiKey",
@@ -309,7 +320,7 @@ export class SessionFileLogger {
309320

310321
private static ensureTaskContext(ctx: FlowLoggerContext): void {
311322
if (!ctx.taskId) {
312-
ctx.taskId = generateId("task");
323+
ctx.taskId = generateId("sesh");
313324
}
314325
}
315326

@@ -355,7 +366,7 @@ export class SessionFileLogger {
355366
const parts: string[] = [];
356367
if (includeTask) {
357368
SessionFileLogger.ensureTaskContext(ctx);
358-
parts.push(formatTag("TASK", ctx.taskId));
369+
parts.push(formatTag("SESH", ctx.taskId));
359370
}
360371
if (includeStep) {
361372
SessionFileLogger.ensureStepContext(ctx);
@@ -365,6 +376,7 @@ export class SessionFileLogger {
365376
SessionFileLogger.ensureActionContext(ctx);
366377
parts.push(formatTag(ctx.actionLabel ?? "ACTION", ctx.actionId));
367378
}
379+
parts[parts.length - 1] = parts[parts.length - 1].replace("[", "<").replace("]", ">");
368380
return parts.join(" ");
369381
}
370382

@@ -381,35 +393,36 @@ export class SessionFileLogger {
381393

382394
// --- Logging methods ---
383395

384-
static logTaskProgress({
396+
static logTaskProgress({ // log agent/session-level events like: Start, End, Execute
385397
invocation,
386398
args,
387399
}: {
388400
invocation: string;
389401
args?: unknown | unknown[];
390402
}): string {
391403
const ctx = loggerContext.getStore();
392-
if (!ctx) return generateId("task");
404+
if (!ctx) return generateId("sesh");
393405

394-
ctx.taskId = generateId("task");
406+
ctx.taskId = generateId("sesh");
395407
ctx.stepId = null;
396408
ctx.actionId = null;
397409
ctx.stepLabel = null;
398410
ctx.actionLabel = null;
399411

400412
const call = `${invocation}(${formatArgs(args)})`;
401-
const message = `${SessionFileLogger.buildPrefix(ctx, {
413+
const prefix = SessionFileLogger.buildPrefix(ctx, {
402414
includeTask: true,
403415
includeStep: false,
404416
includeAction: false,
405-
})} ${call}`;
417+
});
418+
const message = `${formatTimestamp()} ${prefix} ${call}`;
406419

407420
SessionFileLogger.writeToFile(ctx.logFiles.agent, message).then();
408421

409422
return ctx.taskId;
410423
}
411424

412-
static logStepProgress({
425+
static logStepProgress({ // log stagehand-level high-level API calls like: Act, Observe, Extract, Navigate
413426
invocation,
414427
args,
415428
label,
@@ -428,18 +441,19 @@ export class SessionFileLogger {
428441
ctx.actionLabel = null;
429442

430443
const call = `${invocation}(${formatArgs(args)})`;
431-
const message = `${SessionFileLogger.buildPrefix(ctx, {
444+
const prefix = SessionFileLogger.buildPrefix(ctx, {
432445
includeTask: true,
433446
includeStep: true,
434447
includeAction: false,
435-
})} ${call}`;
448+
});
449+
const message = `${formatTimestamp()} ${prefix} ${call}`;
436450

437451
SessionFileLogger.writeToFile(ctx.logFiles.stagehand, message).then();
438452

439453
return ctx.stepId;
440454
}
441455

442-
static logActionProgress({
456+
static logActionProgress({ // log understudy-level browser action calls like: Click, Type, Scroll
443457
actionType,
444458
target,
445459
args,
@@ -465,18 +479,19 @@ export class SessionFileLogger {
465479
details.push(`args=[${argString}]`);
466480
}
467481

468-
const message = `${SessionFileLogger.buildPrefix(ctx, {
482+
const prefix = SessionFileLogger.buildPrefix(ctx, {
469483
includeTask: true,
470484
includeStep: true,
471485
includeAction: true,
472-
})} ${details.join(" ")}`;
486+
});
487+
const message = `${formatTimestamp()} ${prefix} ${details.join(" ")}`;
473488

474489
SessionFileLogger.writeToFile(ctx.logFiles.understudy, message).then();
475490

476491
return ctx.actionId;
477492
}
478493

479-
static logCdpMessage({
494+
static logCdpMessage({ // log low-level CDP browser calls and events like: Page.getDocument, Runtime.evaluate, etc.
480495
method,
481496
params,
482497
sessionId,
@@ -488,16 +503,17 @@ export class SessionFileLogger {
488503
const ctx = loggerContext.getStore();
489504
if (!ctx) return;
490505

491-
const args = params ? formatArgs(params) : "";
492-
const call = args ? `${method}(${args})` : `${method}()`;
506+
const argsStr = params ? formatArgs(params) : "";
507+
const call = argsStr ? `${method}(${argsStr})` : `${method}()`;
493508
const prefix = SessionFileLogger.buildPrefix(ctx, {
494509
includeTask: true,
495510
includeStep: true,
496511
includeAction: true,
497512
});
498-
const rawMessage = `${prefix} ${formatCdpTag(sessionId)} ${call}`;
513+
const timestamp = formatTimestamp();
514+
const rawMessage = `${timestamp} ${prefix} ${formatCdpTag(sessionId)} ${call}`;
499515
const message =
500-
rawMessage.length > 120 ? `${rawMessage.slice(0, 117)}...` : rawMessage;
516+
rawMessage.length > 140 ? `${rawMessage.slice(0, 137)}...` : rawMessage;
501517

502518
SessionFileLogger.writeToFile(ctx.logFiles.cdp, message).then();
503519
}

0 commit comments

Comments
 (0)