@outputai/core 0.5.3-next.bdf47aa.0 → 0.6.1-dev.aab2335.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/activity_integration/context.d.ts +5 -9
- package/src/activity_integration/context.js +5 -4
- package/src/activity_integration/context.spec.js +10 -15
- package/src/activity_integration/events.d.ts +2 -4
- package/src/activity_integration/events.js +8 -3
- package/src/activity_integration/events.spec.js +58 -29
- package/src/bus.js +18 -9
- package/src/bus.spec.js +30 -0
- package/src/hooks/index.d.ts +112 -58
- package/src/hooks/index.js +15 -12
- package/src/hooks/index.spec.js +60 -32
- package/src/interface/workflow.js +19 -35
- package/src/interface/workflow.spec.js +104 -15
- package/src/internal_activities/index.js +3 -3
- package/src/internal_activities/index.spec.js +31 -1
- package/src/internal_utils/temporal_context.js +12 -0
- package/src/internal_utils/temporal_context.spec.ts +83 -0
- package/src/internal_utils/trace_info.js +21 -0
- package/src/internal_utils/trace_info.spec.js +47 -0
- package/src/internal_utils/workflow_context.js +29 -0
- package/src/internal_utils/workflow_context.spec.js +46 -0
- package/src/logger/development.js +61 -0
- package/src/logger/development.spec.js +70 -0
- package/src/logger/index.js +14 -0
- package/src/logger/index.spec.js +27 -0
- package/src/logger/production.js +15 -0
- package/src/logger/production.spec.js +52 -0
- package/src/tracing/internal_interface.js +4 -4
- package/src/tracing/processors/local/index.js +21 -26
- package/src/tracing/processors/local/index.spec.js +39 -45
- package/src/tracing/processors/s3/index.js +13 -23
- package/src/tracing/processors/s3/index.spec.js +33 -26
- package/src/tracing/trace_attribute.js +0 -1
- package/src/tracing/trace_engine.js +8 -12
- package/src/tracing/trace_engine.spec.js +31 -27
- package/src/worker/configs.js +2 -0
- package/src/worker/configs.spec.js +25 -0
- package/src/worker/index.js +4 -1
- package/src/worker/index.spec.js +4 -0
- package/src/worker/interceptors/activity.js +31 -29
- package/src/worker/interceptors/activity.spec.js +58 -26
- package/src/worker/interceptors/workflow.js +7 -2
- package/src/worker/interceptors/workflow.spec.js +42 -6
- package/src/worker/log_hooks.js +35 -46
- package/src/worker/log_hooks.spec.js +43 -46
- package/src/worker/setup_telemetry.js +19 -0
- package/src/worker/setup_telemetry.spec.js +80 -0
- package/src/worker/sinks.js +24 -24
- package/src/interface/workflow_context.js +0 -33
- package/src/logger.js +0 -73
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Context } from '@temporalio/activity';
|
|
1
|
+
import { Context, activityInfo as activityInfoFn } from '@temporalio/activity';
|
|
2
2
|
import { Storage } from '#async_storage';
|
|
3
3
|
import * as Tracing from '#tracing';
|
|
4
4
|
import { headersToObject } from '../sandboxed_utils.js';
|
|
@@ -25,7 +25,7 @@ const log = createChildLogger( 'ActivityInterceptor' );
|
|
|
25
25
|
|
|
26
26
|
Context information comes from two sources:
|
|
27
27
|
- Temporal's Activity Context (workflowId, activityId, activityType)
|
|
28
|
-
- Headers injected by the workflow interceptor
|
|
28
|
+
- Headers injected by the workflow interceptor
|
|
29
29
|
*/
|
|
30
30
|
export class ActivityExecutionInterceptor {
|
|
31
31
|
constructor( { activities, workflows, connection } ) {
|
|
@@ -42,25 +42,24 @@ export class ActivityExecutionInterceptor {
|
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Returns a workflow entry by its name or throws error
|
|
45
|
-
* @param {string}
|
|
45
|
+
* @param {string} workflowType
|
|
46
46
|
* @returns {object} Workflow entry
|
|
47
47
|
* @throws {Error}
|
|
48
48
|
*/
|
|
49
|
-
getWorkflowEntry(
|
|
50
|
-
const workflowEntry = this.workflowsMap.get(
|
|
49
|
+
getWorkflowEntry( workflowType ) {
|
|
50
|
+
const workflowEntry = this.workflowsMap.get( workflowType );
|
|
51
51
|
if ( !workflowEntry ) {
|
|
52
|
-
throw new Error( `Activity interceptor: workflow "${
|
|
52
|
+
throw new Error( `Activity interceptor: workflow "${workflowType}" not found in workflowsMap.` );
|
|
53
53
|
}
|
|
54
54
|
return workflowEntry;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
async execute( input, next ) {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
const {
|
|
61
|
-
const {
|
|
62
|
-
const {
|
|
63
|
-
const { path: workflowFilename } = this.getWorkflowEntry( workflowName );
|
|
58
|
+
const activityInfo = activityInfoFn();
|
|
59
|
+
const { workflowExecution: { workflowId, runId }, activityId, activityType, workflowType } = activityInfo;
|
|
60
|
+
const { traceInfo, workflowDetails } = headersToObject( input.headers );
|
|
61
|
+
const { type: outputActivityKind } = this.activities?.[activityType]?.[METADATA_ACCESS_SYMBOL];
|
|
62
|
+
const { path: workflowFilename } = this.getWorkflowEntry( workflowType );
|
|
64
63
|
|
|
65
64
|
const state = {
|
|
66
65
|
heartbeat: null,
|
|
@@ -76,32 +75,35 @@ export class ActivityExecutionInterceptor {
|
|
|
76
75
|
const workflowHandle = client.workflow.getHandle( workflowId );
|
|
77
76
|
await workflowHandle.signal( Signal.SEND_AGGREGATIONS, aggregateAttributes( state.attributes ) );
|
|
78
77
|
} catch ( error ) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
stack: error.stack,
|
|
82
|
-
activityId: id,
|
|
83
|
-
activityName: name,
|
|
84
|
-
workflowId,
|
|
85
|
-
workflowName
|
|
86
|
-
} );
|
|
78
|
+
const errorContext = { message: error.message, stack: error.stack, activityId, activityType, workflowId, workflowType, runId };
|
|
79
|
+
log.warn( `Signal "${Signal.SEND_AGGREGATIONS}" failed`, errorContext );
|
|
87
80
|
}
|
|
88
81
|
}
|
|
89
82
|
};
|
|
90
83
|
|
|
91
|
-
//
|
|
92
|
-
const
|
|
84
|
+
// Adds context accessible information
|
|
85
|
+
const storageContext = {
|
|
86
|
+
parentId: activityId,
|
|
87
|
+
outputActivityKind,
|
|
88
|
+
activityInfo,
|
|
89
|
+
workflowDetails,
|
|
90
|
+
workflowFilename,
|
|
91
|
+
traceInfo,
|
|
92
|
+
addAttribute
|
|
93
|
+
};
|
|
93
94
|
|
|
94
|
-
messageBus.emit( BusEventType.ACTIVITY_START, {
|
|
95
|
-
Tracing.addEventStart( { id, name, kind, parentId:
|
|
95
|
+
messageBus.emit( BusEventType.ACTIVITY_START, { activityInfo, workflowDetails, outputActivityKind } );
|
|
96
|
+
Tracing.addEventStart( { id: activityId, name: activityType, kind: outputActivityKind, parentId: runId, details: input.args[0], traceInfo } );
|
|
96
97
|
|
|
97
98
|
try {
|
|
98
99
|
// Sends heartbeat to communicate that activity is still alive
|
|
99
100
|
state.heartbeat = activityHeartbeatEnabled && setInterval( () => Context.current().heartbeat(), activityHeartbeatIntervalMs );
|
|
100
101
|
|
|
101
|
-
const output = await Storage.runWithContext( async _ => next( input ),
|
|
102
|
+
const output = await Storage.runWithContext( async _ => next( input ), storageContext );
|
|
103
|
+
|
|
104
|
+
messageBus.emit( BusEventType.ACTIVITY_END, { activityInfo, workflowDetails, outputActivityKind } );
|
|
105
|
+
Tracing.addEventEnd( { id: activityId, details: output, traceInfo } );
|
|
102
106
|
|
|
103
|
-
messageBus.emit( BusEventType.ACTIVITY_END, { id, name, kind, workflowId, workflowName, duration: Date.now() - startDate } );
|
|
104
|
-
Tracing.addEventEnd( { id, details: output, executionContext } );
|
|
105
107
|
return {
|
|
106
108
|
[ACTIVITY_WRAPPER_VERSION_FIELD]: 1,
|
|
107
109
|
output,
|
|
@@ -109,8 +111,8 @@ export class ActivityExecutionInterceptor {
|
|
|
109
111
|
};
|
|
110
112
|
|
|
111
113
|
} catch ( error ) {
|
|
112
|
-
messageBus.emit( BusEventType.ACTIVITY_ERROR, {
|
|
113
|
-
Tracing.addEventError( { id, details: error,
|
|
114
|
+
messageBus.emit( BusEventType.ACTIVITY_ERROR, { activityInfo, workflowDetails, outputActivityKind, error } );
|
|
115
|
+
Tracing.addEventError( { id: activityId, details: error, traceInfo } );
|
|
114
116
|
|
|
115
117
|
await sendAggregationsViaSignal();
|
|
116
118
|
|
|
@@ -7,20 +7,36 @@ const workflowHandleMock = vi.hoisted( () => ( { signal: vi.fn() } ) );
|
|
|
7
7
|
const getHandleMock = vi.hoisted( () => vi.fn( () => workflowHandleMock ) );
|
|
8
8
|
const clientConstructorMock = vi.hoisted( () => vi.fn() );
|
|
9
9
|
const logWarnMock = vi.hoisted( () => vi.fn() );
|
|
10
|
-
|
|
11
|
-
const heartbeatMock = vi.fn();
|
|
10
|
+
const heartbeatMock = vi.hoisted( () => vi.fn() );
|
|
12
11
|
const runWithContextMock = vi.hoisted( () => vi.fn().mockImplementation( async fn => fn() ) );
|
|
13
|
-
const
|
|
14
|
-
workflowExecution: { workflowId: 'wf-1' },
|
|
12
|
+
const activityInfoMock = vi.hoisted( () => ( {
|
|
13
|
+
workflowExecution: { workflowId: 'wf-1', runId: 'run-1' },
|
|
15
14
|
activityId: 'act-1',
|
|
16
15
|
activityType: 'myWorkflow#myStep',
|
|
17
16
|
workflowType: 'myWorkflow'
|
|
18
|
-
};
|
|
17
|
+
} ) );
|
|
18
|
+
const traceInfoMock = vi.hoisted( () => ( {
|
|
19
|
+
workflowId: 'wf-1',
|
|
20
|
+
runId: 'run-1',
|
|
21
|
+
workflowType: 'myWorkflow',
|
|
22
|
+
startTime: 1710000000000,
|
|
23
|
+
disableTrace: false
|
|
24
|
+
} ) );
|
|
25
|
+
const workflowDetailsMock = vi.hoisted( () => ( {
|
|
26
|
+
workflowId: 'wf-1',
|
|
27
|
+
runId: 'run-1',
|
|
28
|
+
workflowType: 'myWorkflow',
|
|
29
|
+
firstExecutionRunId: 'run-1',
|
|
30
|
+
startTime: 1710000000000,
|
|
31
|
+
runStartTime: 1710000000000,
|
|
32
|
+
attempt: 1
|
|
33
|
+
} ) );
|
|
19
34
|
|
|
20
35
|
vi.mock( '@temporalio/activity', () => ( {
|
|
36
|
+
activityInfo: () => activityInfoMock,
|
|
21
37
|
Context: {
|
|
22
38
|
current: () => ( {
|
|
23
|
-
info:
|
|
39
|
+
info: activityInfoMock,
|
|
24
40
|
heartbeat: heartbeatMock
|
|
25
41
|
} )
|
|
26
42
|
}
|
|
@@ -58,7 +74,7 @@ vi.mock( '#tracing', () => ( {
|
|
|
58
74
|
} ) );
|
|
59
75
|
|
|
60
76
|
vi.mock( '../sandboxed_utils.js', () => ( {
|
|
61
|
-
headersToObject: () => ( {
|
|
77
|
+
headersToObject: () => ( { traceInfo: traceInfoMock, workflowDetails: workflowDetailsMock } )
|
|
62
78
|
} ) );
|
|
63
79
|
|
|
64
80
|
const messageBusEmitMock = vi.fn();
|
|
@@ -105,6 +121,7 @@ const httpRequestAttribute = {
|
|
|
105
121
|
describe( 'ActivityExecutionInterceptor', () => {
|
|
106
122
|
beforeEach( () => {
|
|
107
123
|
vi.clearAllMocks();
|
|
124
|
+
activityInfoMock.workflowType = 'myWorkflow';
|
|
108
125
|
workflowHandleMock.signal.mockResolvedValue( undefined );
|
|
109
126
|
vi.useFakeTimers();
|
|
110
127
|
vi.resetModules();
|
|
@@ -132,22 +149,34 @@ describe( 'ActivityExecutionInterceptor', () => {
|
|
|
132
149
|
aggregations: null,
|
|
133
150
|
[ACTIVITY_WRAPPER_VERSION_FIELD]: 1
|
|
134
151
|
} );
|
|
135
|
-
expect( messageBusEmitMock ).toHaveBeenCalledWith(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
152
|
+
expect( messageBusEmitMock ).toHaveBeenCalledWith(
|
|
153
|
+
BusEventType.ACTIVITY_START,
|
|
154
|
+
{ activityInfo: activityInfoMock, workflowDetails: workflowDetailsMock, outputActivityKind: 'step' }
|
|
155
|
+
);
|
|
156
|
+
expect( messageBusEmitMock ).toHaveBeenCalledWith(
|
|
157
|
+
BusEventType.ACTIVITY_END,
|
|
158
|
+
{ activityInfo: activityInfoMock, workflowDetails: workflowDetailsMock, outputActivityKind: 'step' }
|
|
159
|
+
);
|
|
160
|
+
expect( addEventStartMock ).toHaveBeenCalledWith( {
|
|
161
|
+
id: 'act-1',
|
|
162
|
+
name: 'myWorkflow#myStep',
|
|
163
|
+
kind: 'step',
|
|
164
|
+
parentId: 'run-1',
|
|
165
|
+
details: { someInput: 'data' },
|
|
166
|
+
traceInfo: traceInfoMock
|
|
167
|
+
} );
|
|
168
|
+
expect( addEventEndMock ).toHaveBeenCalledWith( { id: 'act-1', details: { result: 'ok' }, traceInfo: traceInfoMock } );
|
|
143
169
|
expect( addEventErrorMock ).not.toHaveBeenCalled();
|
|
144
170
|
expect( clientConstructorMock ).not.toHaveBeenCalled();
|
|
145
171
|
expect( runWithContextMock ).toHaveBeenCalledWith(
|
|
146
172
|
expect.any( Function ),
|
|
147
173
|
expect.objectContaining( {
|
|
148
174
|
parentId: 'act-1',
|
|
149
|
-
|
|
175
|
+
activityInfo: activityInfoMock,
|
|
176
|
+
workflowDetails: workflowDetailsMock,
|
|
177
|
+
outputActivityKind: 'step',
|
|
150
178
|
workflowFilename: '/workflows/myWorkflow.js',
|
|
179
|
+
traceInfo: traceInfoMock,
|
|
151
180
|
addAttribute: expect.any( Function )
|
|
152
181
|
} )
|
|
153
182
|
);
|
|
@@ -166,7 +195,7 @@ describe( 'ActivityExecutionInterceptor', () => {
|
|
|
166
195
|
} );
|
|
167
196
|
|
|
168
197
|
expect( messageBusEmitMock ).toHaveBeenCalledWith( BusEventType.ACTIVITY_END, expect.any( Object ) );
|
|
169
|
-
expect( addEventEndMock ).toHaveBeenCalledWith( { id: 'act-1', details: { result: 'sync' },
|
|
198
|
+
expect( addEventEndMock ).toHaveBeenCalledWith( { id: 'act-1', details: { result: 'sync' }, traceInfo: traceInfoMock } );
|
|
170
199
|
expect( addEventErrorMock ).not.toHaveBeenCalled();
|
|
171
200
|
} );
|
|
172
201
|
|
|
@@ -244,9 +273,10 @@ describe( 'ActivityExecutionInterceptor', () => {
|
|
|
244
273
|
expect( logWarnMock ).toHaveBeenCalledWith( `Signal "${Signal.SEND_AGGREGATIONS}" failed`, expect.objectContaining( {
|
|
245
274
|
message: 'signal failed',
|
|
246
275
|
activityId: 'act-1',
|
|
247
|
-
|
|
276
|
+
activityType: 'myWorkflow#myStep',
|
|
248
277
|
workflowId: 'wf-1',
|
|
249
|
-
|
|
278
|
+
workflowType: 'myWorkflow',
|
|
279
|
+
runId: 'run-1'
|
|
250
280
|
} ) );
|
|
251
281
|
} );
|
|
252
282
|
|
|
@@ -261,12 +291,14 @@ describe( 'ActivityExecutionInterceptor', () => {
|
|
|
261
291
|
|
|
262
292
|
await expect( promise ).rejects.toThrow( 'step failed' );
|
|
263
293
|
expect( messageBusEmitMock ).toHaveBeenCalledWith( BusEventType.ACTIVITY_START, expect.any( Object ) );
|
|
264
|
-
expect( messageBusEmitMock ).toHaveBeenCalledWith( BusEventType.ACTIVITY_ERROR,
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
294
|
+
expect( messageBusEmitMock ).toHaveBeenCalledWith( BusEventType.ACTIVITY_ERROR, {
|
|
295
|
+
activityInfo: activityInfoMock,
|
|
296
|
+
workflowDetails: workflowDetailsMock,
|
|
297
|
+
outputActivityKind: 'step',
|
|
298
|
+
error
|
|
299
|
+
} );
|
|
268
300
|
expect( addEventStartMock ).toHaveBeenCalledOnce();
|
|
269
|
-
expect( addEventErrorMock ).
|
|
301
|
+
expect( addEventErrorMock ).toHaveBeenCalledWith( { id: 'act-1', details: error, traceInfo: traceInfoMock } );
|
|
270
302
|
expect( addEventEndMock ).not.toHaveBeenCalled();
|
|
271
303
|
} );
|
|
272
304
|
|
|
@@ -331,7 +363,7 @@ describe( 'ActivityExecutionInterceptor', () => {
|
|
|
331
363
|
const interceptor = new ActivityExecutionInterceptor( { activities: makeActivities(), workflows } );
|
|
332
364
|
|
|
333
365
|
// Override context to use alias as workflowType
|
|
334
|
-
|
|
366
|
+
activityInfoMock.workflowType = 'myWorkflowOld';
|
|
335
367
|
const next = vi.fn().mockResolvedValue( { result: 'ok' } );
|
|
336
368
|
|
|
337
369
|
const promise = interceptor.execute( makeInput(), next );
|
|
@@ -345,7 +377,7 @@ describe( 'ActivityExecutionInterceptor', () => {
|
|
|
345
377
|
);
|
|
346
378
|
|
|
347
379
|
// Restore for other tests
|
|
348
|
-
|
|
380
|
+
activityInfoMock.workflowType = 'myWorkflow';
|
|
349
381
|
} );
|
|
350
382
|
|
|
351
383
|
it( 'does not heartbeat when OUTPUT_ACTIVITY_HEARTBEAT_ENABLED is false', async () => {
|
|
@@ -5,6 +5,7 @@ import { deepMerge } from '#utils';
|
|
|
5
5
|
import { METADATA_ACCESS_SYMBOL, WorkflowSpecialOutput } from '#consts';
|
|
6
6
|
// this is a dynamic generated file with activity configs overwrites
|
|
7
7
|
import stepOptions from '../temp/__activity_options.js';
|
|
8
|
+
import { createWorkflowDetails } from '#internal_utils/temporal_context';
|
|
8
9
|
|
|
9
10
|
/*
|
|
10
11
|
This is not an AI comment!
|
|
@@ -17,8 +18,12 @@ import stepOptions from '../temp/__activity_options.js';
|
|
|
17
18
|
*/
|
|
18
19
|
class HeadersInjectionInterceptor {
|
|
19
20
|
async scheduleActivity( input, next ) {
|
|
20
|
-
const
|
|
21
|
-
|
|
21
|
+
const info = workflowInfo();
|
|
22
|
+
const memo = info.memo ?? {};
|
|
23
|
+
Object.assign( input.headers, memoToHeaders( {
|
|
24
|
+
...memo,
|
|
25
|
+
workflowDetails: createWorkflowDetails( info )
|
|
26
|
+
} ) );
|
|
22
27
|
// apply per-invocation options passed as second argument by rewritten calls
|
|
23
28
|
const options = stepOptions[input.activityType];
|
|
24
29
|
if ( options ) {
|
|
@@ -7,6 +7,35 @@ const workflowStartMock = vi.fn();
|
|
|
7
7
|
const workflowEndMock = vi.fn();
|
|
8
8
|
const workflowErrorMock = vi.fn();
|
|
9
9
|
const isCancellationMock = vi.fn();
|
|
10
|
+
const startTime = new Date( '2026-06-02T09:00:00.000Z' );
|
|
11
|
+
const runStartTime = new Date( '2026-06-02T09:05:00.000Z' );
|
|
12
|
+
const workflowDetails = {
|
|
13
|
+
attempt: 1,
|
|
14
|
+
continuedFromExecutionRunId: undefined,
|
|
15
|
+
firstExecutionRunId: 'first-run',
|
|
16
|
+
parent: undefined,
|
|
17
|
+
root: undefined,
|
|
18
|
+
runId: 'run-1',
|
|
19
|
+
runStartTime: runStartTime.getTime(),
|
|
20
|
+
startTime: startTime.getTime(),
|
|
21
|
+
workflowId: 'workflow-1',
|
|
22
|
+
workflowType: 'MyWorkflow'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const workflowInfo = {
|
|
26
|
+
attempt: 1,
|
|
27
|
+
continuedFromExecutionRunId: undefined,
|
|
28
|
+
firstExecutionRunId: 'first-run',
|
|
29
|
+
parent: undefined,
|
|
30
|
+
root: undefined,
|
|
31
|
+
runId: 'run-1',
|
|
32
|
+
runStartTime,
|
|
33
|
+
startTime,
|
|
34
|
+
workflowId: 'workflow-1',
|
|
35
|
+
workflowType: 'MyWorkflow',
|
|
36
|
+
memo: { traceInfo: { runId: 'root-run' } }
|
|
37
|
+
};
|
|
38
|
+
|
|
10
39
|
vi.mock( '@temporalio/workflow', () => ( {
|
|
11
40
|
workflowInfo: ( ...args ) => workflowInfoMock( ...args ),
|
|
12
41
|
proxySinks: () => ( {
|
|
@@ -53,7 +82,7 @@ describe( 'workflow interceptors', () => {
|
|
|
53
82
|
beforeEach( () => {
|
|
54
83
|
vi.clearAllMocks();
|
|
55
84
|
isCancellationMock.mockReturnValue( false );
|
|
56
|
-
workflowInfoMock.mockReturnValue(
|
|
85
|
+
workflowInfoMock.mockReturnValue( workflowInfo );
|
|
57
86
|
} );
|
|
58
87
|
|
|
59
88
|
describe( 'HeadersInjectionInterceptor', () => {
|
|
@@ -64,12 +93,19 @@ describe( 'workflow interceptors', () => {
|
|
|
64
93
|
const input = { headers: { existing: 'header' }, activityType: 'MyWorkflow#step1' };
|
|
65
94
|
const next = vi.fn().mockResolvedValue( 'result' );
|
|
66
95
|
|
|
67
|
-
memoToHeadersMock.mockReturnValue( {
|
|
96
|
+
memoToHeadersMock.mockReturnValue( { traceInfo: workflowInfo.memo.traceInfo, workflowDetails } );
|
|
68
97
|
|
|
69
98
|
const out = await interceptor.scheduleActivity( input, next );
|
|
70
99
|
|
|
71
|
-
expect( memoToHeadersMock ).toHaveBeenCalledWith( {
|
|
72
|
-
|
|
100
|
+
expect( memoToHeadersMock ).toHaveBeenCalledWith( {
|
|
101
|
+
traceInfo: workflowInfo.memo.traceInfo,
|
|
102
|
+
workflowDetails
|
|
103
|
+
} );
|
|
104
|
+
expect( input.headers ).toEqual( {
|
|
105
|
+
existing: 'header',
|
|
106
|
+
traceInfo: workflowInfo.memo.traceInfo,
|
|
107
|
+
workflowDetails
|
|
108
|
+
} );
|
|
73
109
|
expect( next ).toHaveBeenCalledWith( input );
|
|
74
110
|
expect( out ).toBe( 'result' );
|
|
75
111
|
} );
|
|
@@ -77,8 +113,8 @@ describe( 'workflow interceptors', () => {
|
|
|
77
113
|
it( 'merges stepOptions with memo.activityOptions when stepOptions exist for activityType', async () => {
|
|
78
114
|
stepOptionsDefault['MyWorkflow#step1'] = { scheduleToCloseTimeout: 60 };
|
|
79
115
|
workflowInfoMock.mockReturnValue( {
|
|
80
|
-
|
|
81
|
-
memo: {
|
|
116
|
+
...workflowInfo,
|
|
117
|
+
memo: { traceInfo: workflowInfo.memo.traceInfo, activityOptions: { heartbeatTimeout: 10 } }
|
|
82
118
|
} );
|
|
83
119
|
memoToHeadersMock.mockReturnValue( {} );
|
|
84
120
|
deepMergeMock.mockReturnValue( { heartbeatTimeout: 10, scheduleToCloseTimeout: 60 } );
|
package/src/worker/log_hooks.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { messageBus } from '#bus';
|
|
2
2
|
import { createChildLogger } from '#logger';
|
|
3
|
-
import {
|
|
3
|
+
import { ACTIVITY_GET_TRACE_DESTINATIONS, BusEventType, LifecycleEvent, WORKFLOW_CATALOG } from '#consts';
|
|
4
4
|
|
|
5
5
|
const activityLog = createChildLogger( 'Activity' );
|
|
6
6
|
const workflowLog = createChildLogger( 'Workflow' );
|
|
@@ -15,43 +15,34 @@ const workflowLog = createChildLogger( 'Workflow' );
|
|
|
15
15
|
╚═════════════════╝
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
const serializedActivityFields = activityInfo => ( {
|
|
19
|
+
activityId: activityInfo.activityId,
|
|
20
|
+
activityType: activityInfo.activityType,
|
|
21
|
+
workflowId: activityInfo.workflowExecution.workflowId,
|
|
22
|
+
workflowType: activityInfo.workflowType,
|
|
23
|
+
runId: activityInfo.workflowExecution.runId
|
|
24
|
+
} );
|
|
25
|
+
|
|
26
|
+
const shouldLogActivity = activityInfo => activityInfo.activityType !== ACTIVITY_GET_TRACE_DESTINATIONS;
|
|
22
27
|
|
|
23
|
-
messageBus.on( BusEventType.ACTIVITY_START, ( {
|
|
24
|
-
|
|
28
|
+
messageBus.on( BusEventType.ACTIVITY_START, ( { activityInfo, outputActivityKind } ) =>
|
|
29
|
+
shouldLogActivity( activityInfo ) && activityLog.info( `Started ${activityInfo.activityType} ${outputActivityKind}`, {
|
|
25
30
|
event: LifecycleEvent.START,
|
|
26
|
-
|
|
27
|
-
activityName: name,
|
|
28
|
-
activityKind: kind,
|
|
29
|
-
workflowId,
|
|
30
|
-
workflowName
|
|
31
|
+
...serializedActivityFields( activityInfo )
|
|
31
32
|
} )
|
|
32
33
|
);
|
|
33
34
|
|
|
34
|
-
messageBus.on( BusEventType.ACTIVITY_END, ( {
|
|
35
|
-
|
|
35
|
+
messageBus.on( BusEventType.ACTIVITY_END, ( { activityInfo, outputActivityKind } ) =>
|
|
36
|
+
shouldLogActivity( activityInfo ) && activityLog.info( `Ended ${activityInfo.activityType} ${outputActivityKind}`, {
|
|
36
37
|
event: LifecycleEvent.END,
|
|
37
|
-
|
|
38
|
-
activityName: name,
|
|
39
|
-
activityKind: kind,
|
|
40
|
-
workflowId,
|
|
41
|
-
workflowName,
|
|
42
|
-
durationMs: duration
|
|
38
|
+
...serializedActivityFields( activityInfo )
|
|
43
39
|
} )
|
|
44
40
|
);
|
|
45
41
|
|
|
46
|
-
messageBus.on( BusEventType.ACTIVITY_ERROR, ( {
|
|
47
|
-
|
|
42
|
+
messageBus.on( BusEventType.ACTIVITY_ERROR, ( { activityInfo, outputActivityKind, error } ) =>
|
|
43
|
+
shouldLogActivity( activityInfo ) && activityLog.error( `Error ${activityInfo.activityType} ${outputActivityKind}: ${error.constructor.name}`, {
|
|
48
44
|
event: LifecycleEvent.ERROR,
|
|
49
|
-
|
|
50
|
-
activityName: name,
|
|
51
|
-
activityKind: kind,
|
|
52
|
-
workflowId,
|
|
53
|
-
workflowName,
|
|
54
|
-
durationMs: duration,
|
|
45
|
+
...serializedActivityFields( activityInfo ),
|
|
55
46
|
error: error.message
|
|
56
47
|
} )
|
|
57
48
|
);
|
|
@@ -62,34 +53,32 @@ messageBus.on( BusEventType.ACTIVITY_ERROR, ( { id, name, kind, workflowId, work
|
|
|
62
53
|
╚═════════════════╝
|
|
63
54
|
*/
|
|
64
55
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
56
|
+
const serializeWorkflowFields = workflowDetails => ( {
|
|
57
|
+
workflowId: workflowDetails.workflowId,
|
|
58
|
+
workflowType: workflowDetails.workflowType,
|
|
59
|
+
runId: workflowDetails.runId
|
|
60
|
+
} );
|
|
61
|
+
|
|
62
|
+
const shouldLogWorkflow = workflowDetails => workflowDetails.workflowType !== WORKFLOW_CATALOG;
|
|
69
63
|
|
|
70
|
-
messageBus.on( BusEventType.WORKFLOW_START, ( {
|
|
71
|
-
|
|
64
|
+
messageBus.on( BusEventType.WORKFLOW_START, ( { workflowDetails } ) =>
|
|
65
|
+
shouldLogWorkflow( workflowDetails ) && workflowLog.info( `Started ${workflowDetails.workflowType} workflow`, {
|
|
72
66
|
event: LifecycleEvent.START,
|
|
73
|
-
|
|
74
|
-
workflowName: name
|
|
67
|
+
...serializeWorkflowFields( workflowDetails )
|
|
75
68
|
} )
|
|
76
69
|
);
|
|
77
70
|
|
|
78
|
-
messageBus.on( BusEventType.WORKFLOW_END, ( {
|
|
79
|
-
|
|
71
|
+
messageBus.on( BusEventType.WORKFLOW_END, ( { workflowDetails } ) =>
|
|
72
|
+
shouldLogWorkflow( workflowDetails ) && workflowLog.info( `Ended ${workflowDetails.workflowType} workflow`, {
|
|
80
73
|
event: LifecycleEvent.END,
|
|
81
|
-
|
|
82
|
-
workflowName: name,
|
|
83
|
-
durationMs: duration
|
|
74
|
+
...serializeWorkflowFields( workflowDetails )
|
|
84
75
|
} )
|
|
85
76
|
);
|
|
86
77
|
|
|
87
|
-
messageBus.on( BusEventType.WORKFLOW_ERROR, ( {
|
|
88
|
-
|
|
78
|
+
messageBus.on( BusEventType.WORKFLOW_ERROR, ( { workflowDetails, error } ) =>
|
|
79
|
+
shouldLogWorkflow( workflowDetails ) && workflowLog.error( `Error ${workflowDetails.workflowType} workflow: ${error.constructor.name}`, {
|
|
89
80
|
event: LifecycleEvent.ERROR,
|
|
90
|
-
|
|
91
|
-
workflowName: name,
|
|
92
|
-
durationMs: duration,
|
|
81
|
+
...serializeWorkflowFields( workflowDetails ),
|
|
93
82
|
error: error.message
|
|
94
83
|
} )
|
|
95
84
|
);
|