@outputai/core 0.8.2-next.42a0ddf.0 → 0.8.2-next.57bc8d6.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 +11 -11
- package/src/bus.js +1 -1
- package/src/consts.js +5 -2
- package/src/helpers/component.js +12 -0
- package/src/helpers/component.spec.js +54 -0
- package/src/helpers/fetch.js +105 -0
- package/src/helpers/fetch.spec.js +203 -0
- package/src/helpers/function.js +15 -0
- package/src/helpers/function.spec.js +48 -0
- package/src/helpers/object.js +98 -0
- package/src/helpers/object.spec.js +377 -0
- package/src/helpers/promise.js +29 -0
- package/src/helpers/promise.spec.js +35 -0
- package/src/helpers/string.js +30 -0
- package/src/helpers/string.spec.js +64 -0
- package/src/hooks/index.d.ts +102 -30
- package/src/hooks/index.js +16 -1
- package/src/hooks/index.spec.js +55 -1
- package/src/index.d.ts +2 -2
- package/src/interface/evaluator.d.ts +2 -2
- package/src/interface/evaluator.js +14 -12
- package/src/interface/evaluator.spec.js +10 -6
- package/src/interface/index.d.ts +7 -6
- package/src/interface/index.js +2 -0
- package/src/interface/logger.d.ts +61 -0
- package/src/interface/logger.js +73 -0
- package/src/interface/logger.spec.js +172 -0
- package/src/interface/step.d.ts +1 -1
- package/src/interface/step.js +15 -12
- package/src/interface/step.spec.js +10 -6
- package/src/interface/webhook.d.ts +21 -2
- package/src/interface/workflow.d.ts +2 -2
- package/src/interface/workflow.js +85 -78
- package/src/interface/workflow.spec.js +11 -4
- package/src/internal_activities/index.js +38 -35
- package/src/internal_activities/index.spec.js +27 -4
- package/src/logger/development.js +2 -2
- package/src/logger/development.spec.js +19 -2
- package/src/logger/production.js +1 -1
- package/src/logger/production.spec.js +24 -5
- package/src/sdk/README.md +47 -0
- package/src/sdk/helpers/component_metadata.d.ts +17 -0
- package/src/sdk/helpers/component_metadata.js +6 -0
- package/src/sdk/helpers/component_metadata.spec.js +30 -0
- package/src/sdk/helpers/index.d.ts +12 -0
- package/src/sdk/helpers/index.js +3 -0
- package/src/sdk/helpers/objects.d.ts +51 -0
- package/src/sdk/helpers/objects.js +8 -0
- package/src/sdk/helpers/objects.spec.js +16 -0
- package/src/sdk/helpers/path.d.ts +11 -0
- package/src/sdk/helpers/path.js +32 -0
- package/src/{utils/resolve_invocation_dir.spec.js → sdk/helpers/path.spec.js} +9 -9
- package/src/sdk/runtime/context.d.ts +30 -0
- package/src/sdk/runtime/context.js +15 -0
- package/src/{activity_integration → sdk/runtime}/context.spec.js +5 -5
- package/src/sdk/runtime/events.d.ts +15 -0
- package/src/sdk/runtime/events.js +18 -0
- package/src/{activity_integration → sdk/runtime}/events.spec.js +8 -9
- package/src/sdk/runtime/index.d.ts +12 -0
- package/src/sdk/runtime/index.js +3 -0
- package/src/sdk/runtime/tracing.d.ts +46 -0
- package/src/sdk/runtime/tracing.js +11 -0
- package/src/tracing/processors/s3/redis_client.spec.js +0 -6
- package/src/tracing/processors/s3/s3_client.spec.js +0 -6
- package/src/tracing/trace_engine.js +1 -1
- package/src/worker/catalog_workflow/catalog_job.js +1 -1
- package/src/worker/catalog_workflow/index.spec.js +8 -11
- package/src/worker/configs.js +1 -1
- package/src/worker/connection_monitor.js +1 -1
- package/src/worker/global_functions.js +14 -0
- package/src/worker/global_functions.spec.js +55 -0
- package/src/worker/index.js +4 -1
- package/src/worker/index.spec.js +7 -0
- package/src/worker/interceptors/activity.js +8 -11
- package/src/worker/interceptors/activity.spec.js +25 -26
- package/src/worker/interceptors/workflow.js +3 -3
- package/src/worker/interceptors/workflow.spec.js +1 -1
- package/src/worker/loader/matchers.js +1 -1
- package/src/worker/log_hooks.js +14 -0
- package/src/worker/log_hooks.spec.js +83 -2
- package/src/worker/sinks.js +7 -1
- package/src/worker/sinks.spec.js +203 -0
- package/src/activity_integration/context.d.ts +0 -23
- package/src/activity_integration/context.js +0 -18
- package/src/activity_integration/event_id_integration.spec.js +0 -52
- package/src/activity_integration/events.d.ts +0 -10
- package/src/activity_integration/events.js +0 -15
- package/src/activity_integration/index.d.ts +0 -9
- package/src/activity_integration/index.js +0 -3
- package/src/activity_integration/tracing.d.ts +0 -40
- package/src/activity_integration/tracing.js +0 -48
- package/src/utils/index.d.ts +0 -180
- package/src/utils/index.js +0 -2
- package/src/utils/resolve_invocation_dir.js +0 -34
- package/src/utils/utils.js +0 -334
- package/src/utils/utils.spec.js +0 -723
- /package/src/{internal_utils → helpers}/aggregations.js +0 -0
- /package/src/{internal_utils → helpers}/aggregations.spec.js +0 -0
- /package/src/{internal_utils → helpers}/errors.js +0 -0
- /package/src/{internal_utils → helpers}/errors.spec.js +0 -0
- /package/src/{internal_utils → helpers}/temporal_context.js +0 -0
- /package/src/{internal_utils → helpers}/temporal_context.spec.ts +0 -0
- /package/src/{internal_utils → helpers}/trace_info.js +0 -0
- /package/src/{internal_utils → helpers}/trace_info.spec.js +0 -0
- /package/src/{internal_utils → helpers}/workflow_context.js +0 -0
- /package/src/{internal_utils → helpers}/workflow_context.spec.js +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* > [!WARNING]
|
|
3
|
+
* > **Internal use only.** Not part of the public API; may change without notice.
|
|
4
|
+
*
|
|
5
|
+
* These are helpers for other SDK modules integration.
|
|
6
|
+
* These need Temporal activity runtime to work, as they access state, emit events and use node:* modules.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
export * from './context.js';
|
|
11
|
+
export * from './events.js';
|
|
12
|
+
export * from './tracing.js';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Attribute } from '#trace_attribute';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tools to interact with Tracing
|
|
5
|
+
*/
|
|
6
|
+
export declare const Tracing: {
|
|
7
|
+
Attribute: typeof Attribute;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Creates a new event.
|
|
11
|
+
*
|
|
12
|
+
* @param args
|
|
13
|
+
* @param args.id - A unique id for the Event.
|
|
14
|
+
* @param args.kind - The kind of Event, like HTTP, DiskWrite, DBOp, etc.
|
|
15
|
+
* @param args.name - The human-friendly name of the Event: query, request, create.
|
|
16
|
+
* @param args.details - Arbitrary data to add to this event, it will be used as the "input" field.
|
|
17
|
+
*/
|
|
18
|
+
addEventStart( args: { id: string; kind: string; name: string; details: unknown } ): void;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Concludes an event.
|
|
22
|
+
*
|
|
23
|
+
* @param args
|
|
24
|
+
* @param args.id - The id of the event to conclude.
|
|
25
|
+
* @param args.details - Arbitrary data to add to this event, it will be used as the "output" field.
|
|
26
|
+
*/
|
|
27
|
+
addEventEnd( args: { id: string; details: unknown } ): void;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Concludes an event with an error.
|
|
31
|
+
*
|
|
32
|
+
* @param args
|
|
33
|
+
* @param args.id - The id of the event to conclude.
|
|
34
|
+
* @param args.details - Arbitrary data to add to this event, it will be used as the "error" field.
|
|
35
|
+
*/
|
|
36
|
+
addEventError( args: { id: string; details: unknown } ): void;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Adds an attribute to an event.
|
|
40
|
+
*
|
|
41
|
+
* @param args
|
|
42
|
+
* @param args.eventId - The id of the event to attach the attribute to.
|
|
43
|
+
* @param args.attribute - The attribute to attach to the event.
|
|
44
|
+
*/
|
|
45
|
+
addEventAttribute( args: { eventId: string; attribute: Attribute.Instance } ): void;
|
|
46
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { addEventActionWithContext as send, EventAction } from '#tracing';
|
|
2
|
+
|
|
3
|
+
import { Attribute } from '#trace_attribute';
|
|
4
|
+
|
|
5
|
+
export const Tracing = {
|
|
6
|
+
Attribute,
|
|
7
|
+
addEventStart: ( { id, kind, name, details } ) => send( EventAction.START, { kind, name, details, id } ),
|
|
8
|
+
addEventEnd: ( { id, details } ) => send( EventAction.END, { id, details } ),
|
|
9
|
+
addEventError: ( { id, details } ) => send( EventAction.ERROR, { id, details } ),
|
|
10
|
+
addEventAttribute: ( { eventId, attribute } ) => send( EventAction.ADD_ATTR, { id: eventId, details: attribute } )
|
|
11
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Storage } from '#async_storage';
|
|
2
2
|
import { EventEmitter } from 'node:events';
|
|
3
3
|
import { serializeError } from './tools/utils.js';
|
|
4
|
-
import { isStringboolTrue } from '#
|
|
4
|
+
import { isStringboolTrue } from '#helpers/string';
|
|
5
5
|
import * as localProcessor from './processors/local/index.js';
|
|
6
6
|
import * as s3Processor from './processors/s3/index.js';
|
|
7
7
|
import { ComponentType } from '#consts';
|
|
@@ -3,7 +3,7 @@ import { WorkflowExecutionAlreadyStartedError, WorkflowIdConflictPolicy } from '
|
|
|
3
3
|
import { WORKFLOW_CATALOG } from '#consts';
|
|
4
4
|
import { catalogId, taskQueue } from '../configs.js';
|
|
5
5
|
import { createChildLogger } from '#logger';
|
|
6
|
-
import { CancellablePromise } from '#
|
|
6
|
+
import { CancellablePromise } from '#helpers/promise';
|
|
7
7
|
|
|
8
8
|
const log = createChildLogger( 'Catalog' );
|
|
9
9
|
|
|
@@ -7,9 +7,6 @@ vi.mock( '#consts', () => ( {
|
|
|
7
7
|
METADATA_ACCESS_SYMBOL
|
|
8
8
|
} ) );
|
|
9
9
|
|
|
10
|
-
const setMetadata = ( target, values ) =>
|
|
11
|
-
Object.defineProperty( target, METADATA_ACCESS_SYMBOL, { value: values, writable: false, enumerable: false, configurable: false } );
|
|
12
|
-
|
|
13
10
|
describe( 'createCatalog', () => {
|
|
14
11
|
it( 'builds catalog with activities grouped by workflow path and returns Catalog with CatalogWorkflow entries', async () => {
|
|
15
12
|
const { createCatalog } = await import( './index.js' );
|
|
@@ -32,40 +29,40 @@ describe( 'createCatalog', () => {
|
|
|
32
29
|
];
|
|
33
30
|
|
|
34
31
|
const activity1 = () => {};
|
|
35
|
-
|
|
32
|
+
activity1[METADATA_ACCESS_SYMBOL] = {
|
|
36
33
|
name: 'A1',
|
|
37
34
|
path: '/flows/flow1#A1',
|
|
38
35
|
description: 'desc-a1',
|
|
39
36
|
inputSchema: z.object( { in: z.literal( 'a1' ) } ),
|
|
40
37
|
outputSchema: z.object( { out: z.literal( 'a1' ) } )
|
|
41
|
-
}
|
|
38
|
+
};
|
|
42
39
|
|
|
43
40
|
const activity2 = () => {};
|
|
44
|
-
|
|
41
|
+
activity2[METADATA_ACCESS_SYMBOL] = {
|
|
45
42
|
name: 'A2',
|
|
46
43
|
path: '/flows/flow1#A2',
|
|
47
44
|
description: 'desc-a2',
|
|
48
45
|
inputSchema: z.object( { in: z.literal( 'a2' ) } ),
|
|
49
46
|
outputSchema: z.object( { out: z.literal( 'a2' ) } )
|
|
50
|
-
}
|
|
47
|
+
};
|
|
51
48
|
|
|
52
49
|
const activity3 = () => {};
|
|
53
|
-
|
|
50
|
+
activity3[METADATA_ACCESS_SYMBOL] = {
|
|
54
51
|
name: 'B1',
|
|
55
52
|
path: '/flows/flow2#B1',
|
|
56
53
|
description: 'desc-b1',
|
|
57
54
|
inputSchema: z.object( { in: z.literal( 'b1' ) } ),
|
|
58
55
|
outputSchema: z.object( { out: z.literal( 'b1' ) } )
|
|
59
|
-
}
|
|
56
|
+
};
|
|
60
57
|
|
|
61
58
|
const activity4 = () => {};
|
|
62
|
-
|
|
59
|
+
activity4[METADATA_ACCESS_SYMBOL] = {
|
|
63
60
|
name: 'X',
|
|
64
61
|
path: '/other#X',
|
|
65
62
|
description: 'desc-x',
|
|
66
63
|
inputSchema: z.object( { in: z.literal( 'x' ) } ),
|
|
67
64
|
outputSchema: z.object( { out: z.literal( 'x' ) } )
|
|
68
|
-
}
|
|
65
|
+
};
|
|
69
66
|
|
|
70
67
|
const activities = {
|
|
71
68
|
'/flows/flow1#A1': activity1,
|
package/src/worker/configs.js
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { messageBus } from '#bus';
|
|
2
|
+
import { ACTIVITY_LOGGER_SYMBOL, BusEventType } from '#consts';
|
|
3
|
+
import { activityInfo as activityInfoFn } from '@temporalio/activity';
|
|
4
|
+
import { assignImmutableProperty } from '#helpers/object';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Sets global functions on globalThis
|
|
8
|
+
*/
|
|
9
|
+
export const bindGlobalFunctions = () => {
|
|
10
|
+
/** Defines the activity logger function, accessible in activity context via logger interface */
|
|
11
|
+
assignImmutableProperty( globalThis, ACTIVITY_LOGGER_SYMBOL, ( { level, message, metadata } ) =>
|
|
12
|
+
messageBus.emit( BusEventType.ACTIVITY_LOG, { level, message, metadata, activityInfo: activityInfoFn() } )
|
|
13
|
+
);
|
|
14
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
ACTIVITY_LOGGER_SYMBOL,
|
|
5
|
+
BusEventType
|
|
6
|
+
} = vi.hoisted( () => ( {
|
|
7
|
+
ACTIVITY_LOGGER_SYMBOL: Symbol( 'activity_logger' ),
|
|
8
|
+
BusEventType: {
|
|
9
|
+
ACTIVITY_LOG: 'activity:log'
|
|
10
|
+
}
|
|
11
|
+
} ) );
|
|
12
|
+
|
|
13
|
+
const messageBusMock = vi.hoisted( () => ( {
|
|
14
|
+
emit: vi.fn()
|
|
15
|
+
} ) );
|
|
16
|
+
const activityInfoMock = vi.hoisted( () => vi.fn( () => ( {
|
|
17
|
+
activityId: 'act-1',
|
|
18
|
+
activityType: 'myStep'
|
|
19
|
+
} ) ) );
|
|
20
|
+
|
|
21
|
+
vi.mock( '#consts', () => ( { ACTIVITY_LOGGER_SYMBOL, BusEventType } ) );
|
|
22
|
+
vi.mock( '#bus', () => ( { messageBus: messageBusMock } ) );
|
|
23
|
+
vi.mock( '@temporalio/activity', () => ( {
|
|
24
|
+
activityInfo: activityInfoMock
|
|
25
|
+
} ) );
|
|
26
|
+
|
|
27
|
+
describe( 'worker/global_functions', () => {
|
|
28
|
+
beforeEach( () => {
|
|
29
|
+
vi.clearAllMocks();
|
|
30
|
+
delete globalThis[ACTIVITY_LOGGER_SYMBOL];
|
|
31
|
+
} );
|
|
32
|
+
|
|
33
|
+
it( 'binds activity logger global function that emits activity log events with metadata', async () => {
|
|
34
|
+
const { bindGlobalFunctions } = await import( './global_functions.js' );
|
|
35
|
+
const metadata = { requestId: 'req-1' };
|
|
36
|
+
|
|
37
|
+
bindGlobalFunctions();
|
|
38
|
+
globalThis[ACTIVITY_LOGGER_SYMBOL]( {
|
|
39
|
+
level: 'debug',
|
|
40
|
+
message: 'activity detail',
|
|
41
|
+
metadata
|
|
42
|
+
} );
|
|
43
|
+
|
|
44
|
+
expect( activityInfoMock ).toHaveBeenCalledTimes( 1 );
|
|
45
|
+
expect( messageBusMock.emit ).toHaveBeenCalledWith( BusEventType.ACTIVITY_LOG, {
|
|
46
|
+
level: 'debug',
|
|
47
|
+
message: 'activity detail',
|
|
48
|
+
metadata,
|
|
49
|
+
activityInfo: {
|
|
50
|
+
activityId: 'act-1',
|
|
51
|
+
activityType: 'myStep'
|
|
52
|
+
}
|
|
53
|
+
} );
|
|
54
|
+
} );
|
|
55
|
+
} );
|
package/src/worker/index.js
CHANGED
|
@@ -17,7 +17,8 @@ import { messageBus } from '#bus';
|
|
|
17
17
|
import { BusEventType } from '#consts';
|
|
18
18
|
import { setupTelemetry } from './telemetry.js';
|
|
19
19
|
import { TemporalConnectionMonitor } from './connection_monitor.js';
|
|
20
|
-
import {
|
|
20
|
+
import { bindGlobalFunctions } from './global_functions.js';
|
|
21
|
+
import { runOnce } from '#helpers/function';
|
|
21
22
|
|
|
22
23
|
import './log_hooks.js';
|
|
23
24
|
|
|
@@ -76,6 +77,8 @@ const execute = async () => {
|
|
|
76
77
|
log.info( 'Using gRPC proxy', { targetHost: grpcProxy } );
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
bindGlobalFunctions();
|
|
81
|
+
|
|
79
82
|
state.connection = await NativeConnection.connect( { address, tls: Boolean( apiKey ), apiKey, proxy } );
|
|
80
83
|
|
|
81
84
|
log.info( 'Creating connection monitor...' );
|
package/src/worker/index.spec.js
CHANGED
|
@@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
4
|
catalogJobInstance,
|
|
5
|
+
bindGlobalFunctionsMock,
|
|
5
6
|
configValues,
|
|
6
7
|
connectionMonitorInstance,
|
|
7
8
|
createCatalogMock,
|
|
@@ -98,6 +99,7 @@ const {
|
|
|
98
99
|
|
|
99
100
|
return {
|
|
100
101
|
catalogJobInstance,
|
|
102
|
+
bindGlobalFunctionsMock: vi.fn(),
|
|
101
103
|
configValues,
|
|
102
104
|
connectionMonitorInstance,
|
|
103
105
|
createCatalogMock: vi.fn().mockReturnValue( { workflows: [], activities: {} } ),
|
|
@@ -145,6 +147,7 @@ vi.mock( './interceptors/index.js', () => ( { initInterceptors: initInterceptors
|
|
|
145
147
|
vi.mock( './proxy.js', () => ( { bootstrapFetchProxy: vi.fn() } ) );
|
|
146
148
|
vi.mock( './telemetry.js', () => ( { setupTelemetry: setupTelemetryMock } ) );
|
|
147
149
|
vi.mock( './interruption.js', () => ( { setupInterruptionHandler: setupInterruptionHandlerMock } ) );
|
|
150
|
+
vi.mock( './global_functions.js', () => ( { bindGlobalFunctions: bindGlobalFunctionsMock } ) );
|
|
148
151
|
vi.mock( './connection_monitor.js', () => ( {
|
|
149
152
|
TemporalConnectionMonitor: vi.fn( function () {
|
|
150
153
|
return connectionMonitorInstance;
|
|
@@ -222,6 +225,10 @@ describe( 'worker/index', () => {
|
|
|
222
225
|
apiKey: undefined,
|
|
223
226
|
proxy: undefined
|
|
224
227
|
} );
|
|
228
|
+
expect( bindGlobalFunctionsMock ).toHaveBeenCalledTimes( 1 );
|
|
229
|
+
expect( bindGlobalFunctionsMock.mock.invocationCallOrder[0] ).toBeLessThan(
|
|
230
|
+
NativeConnection.connect.mock.invocationCallOrder[0]
|
|
231
|
+
);
|
|
225
232
|
expect( TemporalConnectionMonitor ).toHaveBeenCalledWith( mockConnection );
|
|
226
233
|
expect( CatalogJob ).toHaveBeenCalledWith( {
|
|
227
234
|
connection: mockConnection,
|
|
@@ -5,8 +5,8 @@ import { headersToObject } from './headers.js';
|
|
|
5
5
|
import { ACTIVITY_WRAPPER_VERSION_FIELD, BusEventType, METADATA_ACCESS_SYMBOL } from '#consts';
|
|
6
6
|
import { activityHeartbeatEnabled, activityHeartbeatIntervalMs } from '../configs.js';
|
|
7
7
|
import { messageBus } from '#bus';
|
|
8
|
-
import { aggregateAttributes } from '#
|
|
9
|
-
import { buildApplicationFailureWithDetails } from '#
|
|
8
|
+
import { aggregateAttributes } from '#helpers/aggregations';
|
|
9
|
+
import { buildApplicationFailureWithDetails } from '#helpers/errors';
|
|
10
10
|
|
|
11
11
|
/*
|
|
12
12
|
This interceptor wraps every activity execution with cross-cutting concerns:
|
|
@@ -77,20 +77,17 @@ export class ActivityExecutionInterceptor {
|
|
|
77
77
|
|
|
78
78
|
const output = await Storage.runWithContext( async _ => next( input ), storageContext );
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
const aggregations = state.attributes.length > 0 ? aggregateAttributes( state.attributes ) : null;
|
|
81
|
+
|
|
82
|
+
messageBus.emit( BusEventType.ACTIVITY_END, { activityInfo, aggregations, workflowDetails, outputActivityKind } );
|
|
81
83
|
Tracing.addEventEnd( { id: activityId, details: output, traceInfo } );
|
|
82
84
|
|
|
83
|
-
return {
|
|
84
|
-
[ACTIVITY_WRAPPER_VERSION_FIELD]: 1,
|
|
85
|
-
output,
|
|
86
|
-
aggregations: state.attributes.length > 0 ? aggregateAttributes( state.attributes ) : null
|
|
87
|
-
};
|
|
85
|
+
return { [ACTIVITY_WRAPPER_VERSION_FIELD]: 1, output, aggregations };
|
|
88
86
|
|
|
89
87
|
} catch ( error ) {
|
|
90
|
-
messageBus.emit( BusEventType.ACTIVITY_ERROR, { activityInfo, workflowDetails, outputActivityKind, error } );
|
|
91
|
-
Tracing.addEventError( { id: activityId, details: error, traceInfo } );
|
|
92
|
-
|
|
93
88
|
const aggregations = state.attributes.length > 0 ? aggregateAttributes( state.attributes ) : null;
|
|
89
|
+
messageBus.emit( BusEventType.ACTIVITY_ERROR, { activityInfo, aggregations, workflowDetails, outputActivityKind, error } );
|
|
90
|
+
Tracing.addEventError( { id: activityId, details: error, traceInfo } );
|
|
94
91
|
|
|
95
92
|
throw aggregations ? buildApplicationFailureWithDetails( error, { aggregations } ) : error;
|
|
96
93
|
} finally {
|
|
@@ -100,6 +100,12 @@ const httpRequestAttribute = {
|
|
|
100
100
|
requestId: 'req-1'
|
|
101
101
|
};
|
|
102
102
|
|
|
103
|
+
const httpRequestAggregations = {
|
|
104
|
+
cost: { total: 0 },
|
|
105
|
+
tokens: { total: 0 },
|
|
106
|
+
httpRequests: { total: 1 }
|
|
107
|
+
};
|
|
108
|
+
|
|
103
109
|
describe( 'ActivityExecutionInterceptor', () => {
|
|
104
110
|
beforeEach( () => {
|
|
105
111
|
vi.clearAllMocks();
|
|
@@ -136,7 +142,7 @@ describe( 'ActivityExecutionInterceptor', () => {
|
|
|
136
142
|
);
|
|
137
143
|
expect( messageBusEmitMock ).toHaveBeenCalledWith(
|
|
138
144
|
BusEventType.ACTIVITY_END,
|
|
139
|
-
{ activityInfo: activityInfoMock, workflowDetails: workflowDetailsMock, outputActivityKind: 'step' }
|
|
145
|
+
{ activityInfo: activityInfoMock, aggregations: null, workflowDetails: workflowDetailsMock, outputActivityKind: 'step' }
|
|
140
146
|
);
|
|
141
147
|
expect( addEventStartMock ).toHaveBeenCalledWith( {
|
|
142
148
|
id: 'act-1',
|
|
@@ -189,14 +195,16 @@ describe( 'ActivityExecutionInterceptor', () => {
|
|
|
189
195
|
|
|
190
196
|
await expect( interceptor.execute( makeInput(), next ) ).resolves.toEqual( {
|
|
191
197
|
output: { result: 'ok' },
|
|
192
|
-
aggregations:
|
|
193
|
-
cost: { total: 0 },
|
|
194
|
-
tokens: { total: 0 },
|
|
195
|
-
httpRequests: { total: 1 }
|
|
196
|
-
},
|
|
198
|
+
aggregations: httpRequestAggregations,
|
|
197
199
|
[ACTIVITY_WRAPPER_VERSION_FIELD]: 1
|
|
198
200
|
} );
|
|
199
201
|
|
|
202
|
+
expect( messageBusEmitMock ).toHaveBeenCalledWith( BusEventType.ACTIVITY_END, {
|
|
203
|
+
activityInfo: activityInfoMock,
|
|
204
|
+
aggregations: httpRequestAggregations,
|
|
205
|
+
workflowDetails: workflowDetailsMock,
|
|
206
|
+
outputActivityKind: 'step'
|
|
207
|
+
} );
|
|
200
208
|
} );
|
|
201
209
|
|
|
202
210
|
it( 'stores collected aggregations in ApplicationFailure details after failed execution', async () => {
|
|
@@ -215,15 +223,17 @@ describe( 'ActivityExecutionInterceptor', () => {
|
|
|
215
223
|
message: 'step failed',
|
|
216
224
|
type: 'Error',
|
|
217
225
|
details: [ {
|
|
218
|
-
aggregations:
|
|
219
|
-
cost: { total: 0 },
|
|
220
|
-
tokens: { total: 0 },
|
|
221
|
-
httpRequests: { total: 1 }
|
|
222
|
-
}
|
|
226
|
+
aggregations: httpRequestAggregations
|
|
223
227
|
} ],
|
|
224
228
|
cause: error
|
|
225
229
|
} );
|
|
226
|
-
expect( messageBusEmitMock ).toHaveBeenCalledWith( BusEventType.ACTIVITY_ERROR,
|
|
230
|
+
expect( messageBusEmitMock ).toHaveBeenCalledWith( BusEventType.ACTIVITY_ERROR, {
|
|
231
|
+
activityInfo: activityInfoMock,
|
|
232
|
+
aggregations: httpRequestAggregations,
|
|
233
|
+
workflowDetails: workflowDetailsMock,
|
|
234
|
+
outputActivityKind: 'step',
|
|
235
|
+
error
|
|
236
|
+
} );
|
|
227
237
|
expect( addEventErrorMock ).toHaveBeenCalledOnce();
|
|
228
238
|
expect( addEventEndMock ).not.toHaveBeenCalled();
|
|
229
239
|
} );
|
|
@@ -243,13 +253,7 @@ describe( 'ActivityExecutionInterceptor', () => {
|
|
|
243
253
|
|
|
244
254
|
expect( thrown.details ).toEqual( [
|
|
245
255
|
{ domain: { reason: 'bad-input' } },
|
|
246
|
-
{
|
|
247
|
-
aggregations: {
|
|
248
|
-
cost: { total: 0 },
|
|
249
|
-
tokens: { total: 0 },
|
|
250
|
-
httpRequests: { total: 1 }
|
|
251
|
-
}
|
|
252
|
-
}
|
|
256
|
+
{ aggregations: httpRequestAggregations }
|
|
253
257
|
] );
|
|
254
258
|
} );
|
|
255
259
|
|
|
@@ -300,13 +304,7 @@ describe( 'ActivityExecutionInterceptor', () => {
|
|
|
300
304
|
nonRetryable: true,
|
|
301
305
|
details: [
|
|
302
306
|
{ domain: { reason: 'bad-input' } },
|
|
303
|
-
{
|
|
304
|
-
aggregations: {
|
|
305
|
-
cost: { total: 0 },
|
|
306
|
-
tokens: { total: 0 },
|
|
307
|
-
httpRequests: { total: 1 }
|
|
308
|
-
}
|
|
309
|
-
}
|
|
307
|
+
{ aggregations: httpRequestAggregations }
|
|
310
308
|
]
|
|
311
309
|
} );
|
|
312
310
|
} );
|
|
@@ -324,6 +322,7 @@ describe( 'ActivityExecutionInterceptor', () => {
|
|
|
324
322
|
expect( messageBusEmitMock ).toHaveBeenCalledWith( BusEventType.ACTIVITY_START, expect.any( Object ) );
|
|
325
323
|
expect( messageBusEmitMock ).toHaveBeenCalledWith( BusEventType.ACTIVITY_ERROR, {
|
|
326
324
|
activityInfo: activityInfoMock,
|
|
325
|
+
aggregations: null,
|
|
327
326
|
workflowDetails: workflowDetailsMock,
|
|
328
327
|
outputActivityKind: 'step',
|
|
329
328
|
error
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// THIS RUNS IN THE TEMPORAL'S SANDBOX ENVIRONMENT
|
|
2
2
|
import { workflowInfo, proxySinks, ContinueAsNew, isCancellation } from '@temporalio/workflow';
|
|
3
3
|
import { memoToHeaders } from './headers.js';
|
|
4
|
-
import { deepMerge } from '#
|
|
5
|
-
import { buildApplicationFailureWithDetails } from '#
|
|
4
|
+
import { deepMerge } from '#helpers/object';
|
|
5
|
+
import { buildApplicationFailureWithDetails } from '#helpers/errors';
|
|
6
6
|
import { METADATA_ACCESS_SYMBOL, WorkflowSpecialOutput } from '#consts';
|
|
7
|
-
import { createWorkflowDetails } from '#
|
|
7
|
+
import { createWorkflowDetails } from '#helpers/temporal_context';
|
|
8
8
|
|
|
9
9
|
// this is a dynamic generated file with activity configs overwrites
|
|
10
10
|
import stepOptions from '../temp/__activity_options.js';
|
|
@@ -54,7 +54,7 @@ const memoToHeadersMock = vi.fn( memo => ( memo ? { ...memo, __asHeaders: true }
|
|
|
54
54
|
vi.mock( './headers.js', () => ( { memoToHeaders: ( ...args ) => memoToHeadersMock( ...args ) } ) );
|
|
55
55
|
|
|
56
56
|
const deepMergeMock = vi.fn( ( a, b ) => ( { ...( a || {} ), ...( b || {} ) } ) );
|
|
57
|
-
vi.mock( '#
|
|
57
|
+
vi.mock( '#helpers/object', () => ( { deepMerge: ( ...args ) => deepMergeMock( ...args ) } ) );
|
|
58
58
|
|
|
59
59
|
const stepOptionsDefault = {};
|
|
60
60
|
vi.mock( '../temp/__activity_options.js', () => ( { default: stepOptionsDefault } ) );
|
package/src/worker/log_hooks.js
CHANGED
|
@@ -47,6 +47,13 @@ messageBus.on( BusEventType.ACTIVITY_ERROR, ( { activityInfo, outputActivityKind
|
|
|
47
47
|
} )
|
|
48
48
|
);
|
|
49
49
|
|
|
50
|
+
messageBus.on( BusEventType.ACTIVITY_LOG, ( { level, message, metadata, activityInfo } ) =>
|
|
51
|
+
activityLog[level]( message, {
|
|
52
|
+
...metadata ?? {},
|
|
53
|
+
...serializedActivityFields( activityInfo )
|
|
54
|
+
} )
|
|
55
|
+
);
|
|
56
|
+
|
|
50
57
|
/*
|
|
51
58
|
╔═════════════════╗
|
|
52
59
|
║ Workflow events ║
|
|
@@ -82,3 +89,10 @@ messageBus.on( BusEventType.WORKFLOW_ERROR, ( { workflowDetails, error } ) =>
|
|
|
82
89
|
error: error.message
|
|
83
90
|
} )
|
|
84
91
|
);
|
|
92
|
+
|
|
93
|
+
messageBus.on( BusEventType.WORKFLOW_LOG, ( { level, message, metadata, workflowDetails } ) =>
|
|
94
|
+
workflowLog[level]( message, {
|
|
95
|
+
...metadata ?? {},
|
|
96
|
+
...serializeWorkflowFields( workflowDetails )
|
|
97
|
+
} )
|
|
98
|
+
);
|
|
@@ -6,8 +6,17 @@ import {
|
|
|
6
6
|
WORKFLOW_CATALOG
|
|
7
7
|
} from '#consts';
|
|
8
8
|
|
|
9
|
-
const
|
|
10
|
-
|
|
9
|
+
const createLoggerMock = vi.hoisted( () => () => ( {
|
|
10
|
+
error: vi.fn(),
|
|
11
|
+
warn: vi.fn(),
|
|
12
|
+
info: vi.fn(),
|
|
13
|
+
http: vi.fn(),
|
|
14
|
+
verbose: vi.fn(),
|
|
15
|
+
debug: vi.fn(),
|
|
16
|
+
silly: vi.fn()
|
|
17
|
+
} ) );
|
|
18
|
+
const activityLogMock = vi.hoisted( () => createLoggerMock() );
|
|
19
|
+
const workflowLogMock = vi.hoisted( () => createLoggerMock() );
|
|
11
20
|
const createChildLoggerMock = vi.hoisted( () =>
|
|
12
21
|
vi.fn( name => ( name === 'Activity' ? activityLogMock : workflowLogMock ) )
|
|
13
22
|
);
|
|
@@ -125,6 +134,44 @@ describe( 'log_hooks', () => {
|
|
|
125
134
|
|
|
126
135
|
expect( activityLogMock.error ).not.toHaveBeenCalled();
|
|
127
136
|
} );
|
|
137
|
+
|
|
138
|
+
it( 'ACTIVITY_LOG logs dynamic levels with metadata and serialized activity fields', () => {
|
|
139
|
+
onHandlers[BusEventType.ACTIVITY_LOG]( {
|
|
140
|
+
activityInfo,
|
|
141
|
+
level: 'debug',
|
|
142
|
+
message: 'activity detail',
|
|
143
|
+
metadata: {
|
|
144
|
+
custom: 'value',
|
|
145
|
+
workflowId: 'metadata-workflow-id'
|
|
146
|
+
}
|
|
147
|
+
} );
|
|
148
|
+
|
|
149
|
+
expect( activityLogMock.debug ).toHaveBeenCalledTimes( 1 );
|
|
150
|
+
expect( activityLogMock.debug ).toHaveBeenCalledWith( 'activity detail', {
|
|
151
|
+
custom: 'value',
|
|
152
|
+
activityId: 'act-1',
|
|
153
|
+
activityType: 'myWorkflow#myStep',
|
|
154
|
+
workflowId: 'wf-1',
|
|
155
|
+
workflowType: 'myWorkflow',
|
|
156
|
+
runId: 'run-1'
|
|
157
|
+
} );
|
|
158
|
+
} );
|
|
159
|
+
|
|
160
|
+
it( 'ACTIVITY_LOG accepts omitted metadata', () => {
|
|
161
|
+
onHandlers[BusEventType.ACTIVITY_LOG]( {
|
|
162
|
+
activityInfo,
|
|
163
|
+
level: 'info',
|
|
164
|
+
message: 'activity detail'
|
|
165
|
+
} );
|
|
166
|
+
|
|
167
|
+
expect( activityLogMock.info ).toHaveBeenCalledWith( 'activity detail', {
|
|
168
|
+
activityId: 'act-1',
|
|
169
|
+
activityType: 'myWorkflow#myStep',
|
|
170
|
+
workflowId: 'wf-1',
|
|
171
|
+
workflowType: 'myWorkflow',
|
|
172
|
+
runId: 'run-1'
|
|
173
|
+
} );
|
|
174
|
+
} );
|
|
128
175
|
} );
|
|
129
176
|
|
|
130
177
|
describe( 'workflow events', () => {
|
|
@@ -210,5 +257,39 @@ describe( 'log_hooks', () => {
|
|
|
210
257
|
|
|
211
258
|
expect( workflowLogMock.error ).not.toHaveBeenCalled();
|
|
212
259
|
} );
|
|
260
|
+
|
|
261
|
+
it( 'WORKFLOW_LOG logs dynamic levels with metadata and serialized workflow fields', () => {
|
|
262
|
+
onHandlers[BusEventType.WORKFLOW_LOG]( {
|
|
263
|
+
workflowDetails,
|
|
264
|
+
level: 'warn',
|
|
265
|
+
message: 'workflow detail',
|
|
266
|
+
metadata: {
|
|
267
|
+
custom: 'value',
|
|
268
|
+
runId: 'metadata-run-id'
|
|
269
|
+
}
|
|
270
|
+
} );
|
|
271
|
+
|
|
272
|
+
expect( workflowLogMock.warn ).toHaveBeenCalledTimes( 1 );
|
|
273
|
+
expect( workflowLogMock.warn ).toHaveBeenCalledWith( 'workflow detail', {
|
|
274
|
+
custom: 'value',
|
|
275
|
+
workflowId: 'wf-1',
|
|
276
|
+
workflowType: 'myWorkflow',
|
|
277
|
+
runId: 'run-1'
|
|
278
|
+
} );
|
|
279
|
+
} );
|
|
280
|
+
|
|
281
|
+
it( 'WORKFLOW_LOG accepts omitted metadata', () => {
|
|
282
|
+
onHandlers[BusEventType.WORKFLOW_LOG]( {
|
|
283
|
+
workflowDetails,
|
|
284
|
+
level: 'info',
|
|
285
|
+
message: 'workflow detail'
|
|
286
|
+
} );
|
|
287
|
+
|
|
288
|
+
expect( workflowLogMock.info ).toHaveBeenCalledWith( 'workflow detail', {
|
|
289
|
+
workflowId: 'wf-1',
|
|
290
|
+
workflowType: 'myWorkflow',
|
|
291
|
+
runId: 'run-1'
|
|
292
|
+
} );
|
|
293
|
+
} );
|
|
213
294
|
} );
|
|
214
295
|
} );
|
package/src/worker/sinks.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BusEventType, ComponentType } from '#consts';
|
|
2
2
|
import * as Tracing from '#tracing';
|
|
3
3
|
import { messageBus } from '#bus';
|
|
4
|
-
import { createWorkflowDetails } from '#
|
|
4
|
+
import { createWorkflowDetails } from '#helpers/temporal_context';
|
|
5
5
|
|
|
6
6
|
// This sink allow for sandbox Temporal environment to send trace logs back to the main thread.
|
|
7
7
|
export const sinks = {
|
|
@@ -10,6 +10,12 @@ export const sinks = {
|
|
|
10
10
|
* Workflow lifecycle sinks
|
|
11
11
|
*/
|
|
12
12
|
workflow: {
|
|
13
|
+
log: {
|
|
14
|
+
fn: ( workflowInfo, { level, message, metadata } ) => {
|
|
15
|
+
messageBus.emit( BusEventType.WORKFLOW_LOG, { level, message, metadata, workflowDetails: createWorkflowDetails( workflowInfo ) } );
|
|
16
|
+
},
|
|
17
|
+
callDuringReplay: false
|
|
18
|
+
},
|
|
13
19
|
start: {
|
|
14
20
|
fn: ( workflowInfo, input ) => {
|
|
15
21
|
const { runId, workflowType, memo: { traceInfo }, parent } = workflowInfo;
|