Skip to content

Commit 96c6af0

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 cf7d069 commit 96c6af0

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
@@ -37,8 +37,27 @@ export default abstract class BaseStepExecutor<TStep extends StepDefinition = St
3737
}
3838

3939
async execute(): Promise<StepExecutionResult> {
40+
const { runId, stepId, stepIndex, stepDefinition, baseRecordRef } = this.context;
41+
42+
this.context.logger.info('Step execution started', {
43+
runId,
44+
stepId,
45+
stepIndex,
46+
stepType: stepDefinition.type,
47+
collection: baseRecordRef.collectionName,
48+
});
49+
4050
try {
41-
return await this.doExecute();
51+
const result = await this.doExecute();
52+
53+
this.context.logger.info('Step execution completed', {
54+
runId,
55+
stepId,
56+
stepIndex,
57+
status: result.stepOutcome.status,
58+
});
59+
60+
return result;
4261
} catch (error) {
4362
if (error instanceof WorkflowExecutorError) {
4463
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
@@ -86,7 +86,7 @@ function makeMockRunStore(stepExecutions: StepExecutionData[] = []): RunStore {
8686
}
8787

8888
function makeMockLogger(): Logger {
89-
return { error: jest.fn() };
89+
return { info: jest.fn(), error: jest.fn() };
9090
}
9191

9292
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
@@ -56,7 +56,7 @@ function makeContext(
5656
workflowPort: {} as ExecutionContext['workflowPort'],
5757
runStore: makeMockRunStore(),
5858
previousSteps: [],
59-
logger: { error: jest.fn() },
59+
logger: { info: jest.fn(), error: jest.fn() },
6060
...overrides,
6161
};
6262
}

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
@@ -127,7 +127,7 @@ function makeContext(
127127
workflowPort: makeMockWorkflowPort(),
128128
runStore: makeMockRunStore(),
129129
previousSteps: [],
130-
logger: { error: jest.fn() },
130+
logger: { info: jest.fn(), error: jest.fn() },
131131
...overrides,
132132
};
133133
}
@@ -1290,7 +1290,7 @@ describe('LoadRelatedRecordStepExecutor', () => {
12901290
});
12911291

12921292
it('returns user message and logs cause when agentPort.getRelatedData throws an infra error', async () => {
1293-
const logger = { error: jest.fn() };
1293+
const logger = { info: jest.fn(), error: jest.fn() };
12941294
const agentPort = makeMockAgentPort();
12951295
(agentPort.getRelatedData as jest.Mock).mockRejectedValue(new Error('DB connection lost'));
12961296
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
@@ -94,7 +94,7 @@ function makeContext(
9494
workflowPort: makeMockWorkflowPort(),
9595
runStore: makeMockRunStore(),
9696
previousSteps: [],
97-
logger: { error: jest.fn() },
97+
logger: { info: jest.fn(), error: jest.fn() },
9898
...overrides,
9999
};
100100
}
@@ -205,7 +205,7 @@ describe('McpTaskStepExecutor', () => {
205205
tool_calls: [{ name: 'send_notification', args: { message: 'Hi' }, id: 'call_1' }],
206206
})
207207
.mockResolvedValueOnce({ tool_calls: [] });
208-
const logger = { error: jest.fn() };
208+
const logger = { info: jest.fn(), error: jest.fn() };
209209
const runStore = makeMockRunStore();
210210
const context = makeContext({
211211
model,
@@ -286,7 +286,7 @@ describe('McpTaskStepExecutor', () => {
286286

287287
it('returns error when saveStepExecution fails (Branch C)', async () => {
288288
const { model } = makeMockModel('send_notification', { message: 'Hello' });
289-
const logger = { error: jest.fn() };
289+
const logger = { info: jest.fn(), error: jest.fn() };
290290
const runStore = makeMockRunStore({
291291
saveStepExecution: jest.fn().mockRejectedValue(new Error('DB unavailable')),
292292
});
@@ -478,7 +478,7 @@ describe('McpTaskStepExecutor', () => {
478478
invoke: invokeFn,
479479
});
480480
const { model } = makeMockModel('send_notification', { message: 'Hello' });
481-
const logger = { error: jest.fn() };
481+
const logger = { info: jest.fn(), error: jest.fn() };
482482
const runStore = makeMockRunStore({
483483
saveStepExecution: jest.fn().mockRejectedValue(new Error('Disk full')),
484484
});
@@ -516,7 +516,7 @@ describe('McpTaskStepExecutor', () => {
516516
userConfirmed: true,
517517
},
518518
};
519-
const logger = { error: jest.fn() };
519+
const logger = { info: jest.fn(), error: jest.fn() };
520520
const runStore = makeMockRunStore({
521521
getStepExecutions: jest.fn().mockResolvedValue([execution]),
522522
saveStepExecution: jest.fn().mockRejectedValue(new Error('Disk full')),
@@ -625,7 +625,7 @@ describe('McpTaskStepExecutor', () => {
625625
invoke: invokeFn,
626626
});
627627
const { model } = makeMockModel('send_notification', {});
628-
const logger = { error: jest.fn() };
628+
const logger = { info: jest.fn(), error: jest.fn() };
629629
const context = makeContext({
630630
model,
631631
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
@@ -116,7 +116,7 @@ function makeContext(
116116
workflowPort: makeMockWorkflowPort(),
117117
runStore: makeMockRunStore(),
118118
previousSteps: [],
119-
logger: { error: jest.fn() },
119+
logger: { info: jest.fn(), error: jest.fn() },
120120
...overrides,
121121
};
122122
}
@@ -657,7 +657,7 @@ describe('ReadRecordStepExecutor', () => {
657657
});
658658

659659
it('returns user message and logs cause when agentPort.getRecord throws an infra error', async () => {
660-
const logger = { error: jest.fn() };
660+
const logger = { info: jest.fn(), error: jest.fn() };
661661
const agentPort = makeMockAgentPort();
662662
(agentPort.getRecord as jest.Mock).mockRejectedValue(new Error('DB connection lost'));
663663
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
@@ -111,7 +111,7 @@ function makeContext(
111111
workflowPort: makeMockWorkflowPort(),
112112
runStore: makeMockRunStore(),
113113
previousSteps: [],
114-
logger: { error: jest.fn() },
114+
logger: { info: jest.fn(), error: jest.fn() },
115115
...overrides,
116116
};
117117
}
@@ -501,7 +501,7 @@ describe('TriggerRecordActionStepExecutor', () => {
501501
});
502502

503503
it('returns user message and logs cause when agentPort.executeAction throws an infra error', async () => {
504-
const logger = { error: jest.fn() };
504+
const logger = { info: jest.fn(), error: jest.fn() };
505505
const agentPort = makeMockAgentPort();
506506
(agentPort.executeAction as jest.Mock).mockRejectedValue(new Error('DB connection lost'));
507507
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
@@ -117,7 +117,7 @@ function makeContext(
117117
workflowPort: makeMockWorkflowPort(),
118118
runStore: makeMockRunStore(),
119119
previousSteps: [],
120-
logger: { error: jest.fn() },
120+
logger: { info: jest.fn(), error: jest.fn() },
121121
...overrides,
122122
};
123123
}
@@ -670,7 +670,7 @@ describe('UpdateRecordStepExecutor', () => {
670670
});
671671

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

0 commit comments

Comments
 (0)