Skip to content

Commit ef890a6

Browse files
alban bertoliniclaude
andcommitted
feat(workflow-executor): add info logging around step execution
Add info level to Logger interface. Log step execution start (with context: runId, stepId, stepType, collection) and completion (with status) around doExecute in BaseStepExecutor. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent cf8e699 commit ef890a6

12 files changed

Lines changed: 52 additions & 23 deletions
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
import type { Logger } from '../ports/logger-port';
22

33
export default class ConsoleLogger implements Logger {
4+
info(message: string, context: Record<string, unknown>): void {
5+
// eslint-disable-next-line no-console
6+
console.log(
7+
JSON.stringify({ level: 'info', message, timestamp: new Date().toISOString(), ...context }),
8+
);
9+
}
10+
411
error(message: string, context: Record<string, unknown>): void {
5-
console.error(JSON.stringify({ message, timestamp: new Date().toISOString(), ...context }));
12+
console.error(
13+
JSON.stringify({ level: 'error', message, timestamp: new Date().toISOString(), ...context }),
14+
);
615
}
716
}

packages/workflow-executor/src/executors/base-step-executor.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,27 @@ export default abstract class BaseStepExecutor<TStep extends StepDefinition = St
3535
}
3636

3737
async execute(): Promise<StepExecutionResult> {
38+
const { runId, stepId, stepIndex, stepDefinition, baseRecordRef } = this.context;
39+
40+
this.context.logger.info('Step execution started', {
41+
runId,
42+
stepId,
43+
stepIndex,
44+
stepType: stepDefinition.type,
45+
collection: baseRecordRef.collectionName,
46+
});
47+
3848
try {
39-
return await this.doExecute();
49+
const result = await this.doExecute();
50+
51+
this.context.logger.info('Step execution completed', {
52+
runId,
53+
stepId,
54+
stepIndex,
55+
status: result.stepOutcome.status,
56+
});
57+
58+
return result;
4059
} catch (error) {
4160
if (error instanceof WorkflowExecutorError) {
4261
if (error.cause !== undefined) {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export interface Logger {
2+
info(message: string, context: Record<string, unknown>): void;
23
error(message: string, context: Record<string, unknown>): void;
34
}

packages/workflow-executor/test/executors/base-step-executor.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ function makeMockRunStore(stepExecutions: StepExecutionData[] = []): RunStore {
8787
}
8888

8989
function makeMockLogger(): Logger {
90-
return { error: jest.fn() };
90+
return { info: jest.fn(), error: jest.fn() };
9191
}
9292

9393
function makeContext(overrides: Partial<ExecutionContext> = {}): ExecutionContext {

packages/workflow-executor/test/executors/condition-step-executor.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ function makeContext(
6969
},
7070
schemaCache: new SchemaCache(),
7171
previousSteps: [],
72-
logger: { error: jest.fn() },
72+
logger: { info: jest.fn(), error: jest.fn() },
7373
...overrides,
7474
};
7575
}

packages/workflow-executor/test/executors/load-related-record-step-executor.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ function makeContext(
140140
},
141141
schemaCache: new SchemaCache(),
142142
previousSteps: [],
143-
logger: { error: jest.fn() },
143+
logger: { info: jest.fn(), error: jest.fn() },
144144
...overrides,
145145
};
146146
}
@@ -1295,7 +1295,7 @@ describe('LoadRelatedRecordStepExecutor', () => {
12951295
});
12961296

12971297
it('returns user message and logs cause when agentPort.getRelatedData throws an infra error', async () => {
1298-
const logger = { error: jest.fn() };
1298+
const logger = { info: jest.fn(), error: jest.fn() };
12991299
const agentPort = makeMockAgentPort();
13001300
(agentPort.getRelatedData as jest.Mock).mockRejectedValue(new Error('DB connection lost'));
13011301
const mockModel = makeMockModel({ relationName: 'Order', reasoning: 'test' });

packages/workflow-executor/test/executors/mcp-task-step-executor.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ function makeContext(
107107
},
108108
schemaCache: new SchemaCache(),
109109
previousSteps: [],
110-
logger: { error: jest.fn() },
110+
logger: { info: jest.fn(), error: jest.fn() },
111111
...overrides,
112112
};
113113
}
@@ -218,7 +218,7 @@ describe('McpTaskStepExecutor', () => {
218218
tool_calls: [{ name: 'send_notification', args: { message: 'Hi' }, id: 'call_1' }],
219219
})
220220
.mockResolvedValueOnce({ tool_calls: [] });
221-
const logger = { error: jest.fn() };
221+
const logger = { info: jest.fn(), error: jest.fn() };
222222
const runStore = makeMockRunStore();
223223
const context = makeContext({
224224
model,
@@ -299,7 +299,7 @@ describe('McpTaskStepExecutor', () => {
299299

300300
it('returns error when saveStepExecution fails (Branch C)', async () => {
301301
const { model } = makeMockModel('send_notification', { message: 'Hello' });
302-
const logger = { error: jest.fn() };
302+
const logger = { info: jest.fn(), error: jest.fn() };
303303
const runStore = makeMockRunStore({
304304
saveStepExecution: jest.fn().mockRejectedValue(new Error('DB unavailable')),
305305
});
@@ -491,7 +491,7 @@ describe('McpTaskStepExecutor', () => {
491491
invoke: invokeFn,
492492
});
493493
const { model } = makeMockModel('send_notification', { message: 'Hello' });
494-
const logger = { error: jest.fn() };
494+
const logger = { info: jest.fn(), error: jest.fn() };
495495
const runStore = makeMockRunStore({
496496
saveStepExecution: jest.fn().mockRejectedValue(new Error('Disk full')),
497497
});
@@ -529,7 +529,7 @@ describe('McpTaskStepExecutor', () => {
529529
userConfirmed: true,
530530
},
531531
};
532-
const logger = { error: jest.fn() };
532+
const logger = { info: jest.fn(), error: jest.fn() };
533533
const runStore = makeMockRunStore({
534534
getStepExecutions: jest.fn().mockResolvedValue([execution]),
535535
saveStepExecution: jest.fn().mockRejectedValue(new Error('Disk full')),
@@ -638,7 +638,7 @@ describe('McpTaskStepExecutor', () => {
638638
invoke: invokeFn,
639639
});
640640
const { model } = makeMockModel('send_notification', {});
641-
const logger = { error: jest.fn() };
641+
const logger = { info: jest.fn(), error: jest.fn() };
642642
const context = makeContext({
643643
model,
644644
stepDefinition: makeStep({ automaticExecution: true }),

packages/workflow-executor/test/executors/read-record-step-executor.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ function makeContext(
129129
},
130130
schemaCache: new SchemaCache(),
131131
previousSteps: [],
132-
logger: { error: jest.fn() },
132+
logger: { info: jest.fn(), error: jest.fn() },
133133
...overrides,
134134
};
135135
}
@@ -668,7 +668,7 @@ describe('ReadRecordStepExecutor', () => {
668668
});
669669

670670
it('returns user message and logs cause when agentPort.getRecord throws an infra error', async () => {
671-
const logger = { error: jest.fn() };
671+
const logger = { info: jest.fn(), error: jest.fn() };
672672
const agentPort = makeMockAgentPort();
673673
(agentPort.getRecord as jest.Mock).mockRejectedValue(new Error('DB connection lost'));
674674
const mockModel = makeMockModel({ fieldNames: ['email'] });

packages/workflow-executor/test/executors/trigger-record-action-step-executor.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ function makeContext(
128128
},
129129
schemaCache: new SchemaCache(),
130130
previousSteps: [],
131-
logger: { error: jest.fn() },
131+
logger: { info: jest.fn(), error: jest.fn() },
132132
...overrides,
133133
};
134134
}
@@ -518,7 +518,7 @@ describe('TriggerRecordActionStepExecutor', () => {
518518
});
519519

520520
it('returns user message and logs cause when agentPort.executeAction throws an infra error', async () => {
521-
const logger = { error: jest.fn() };
521+
const logger = { info: jest.fn(), error: jest.fn() };
522522
const agentPort = makeMockAgentPort();
523523
(agentPort.executeAction as jest.Mock).mockRejectedValue(new Error('DB connection lost'));
524524
const mockModel = makeMockModel({

packages/workflow-executor/test/executors/update-record-step-executor.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ function makeContext(
130130
},
131131
schemaCache: new SchemaCache(),
132132
previousSteps: [],
133-
logger: { error: jest.fn() },
133+
logger: { info: jest.fn(), error: jest.fn() },
134134
...overrides,
135135
};
136136
}
@@ -681,7 +681,7 @@ describe('UpdateRecordStepExecutor', () => {
681681
});
682682

683683
it('returns user message and logs cause when agentPort.updateRecord throws an infra error', async () => {
684-
const logger = { error: jest.fn() };
684+
const logger = { info: jest.fn(), error: jest.fn() };
685685
const agentPort = makeMockAgentPort();
686686
(agentPort.updateRecord as jest.Mock).mockRejectedValue(new Error('DB connection lost'));
687687
const mockModel = makeMockModel({

0 commit comments

Comments
 (0)