@hatchet-dev/typescript-sdk 1.17.1 → 1.18.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/clients/event/event-client.d.ts +8 -0
- package/clients/event/event-client.js +8 -0
- package/clients/hatchet-client/client-config.d.ts +45 -0
- package/clients/hatchet-client/client-config.js +14 -1
- package/opentelemetry/hatchet-exporter.d.ts +24 -0
- package/opentelemetry/hatchet-exporter.js +123 -0
- package/opentelemetry/hatchet-span-context.d.ts +22 -0
- package/opentelemetry/hatchet-span-context.js +27 -0
- package/opentelemetry/index.d.ts +3 -0
- package/opentelemetry/index.js +7 -0
- package/opentelemetry/instrumentor.d.ts +78 -0
- package/opentelemetry/instrumentor.js +595 -0
- package/opentelemetry/types.d.ts +6 -0
- package/opentelemetry/types.js +8 -0
- package/package.json +6 -1
- package/util/config-loader/config-loader.d.ts +1 -0
- package/util/config-loader/config-loader.js +18 -4
- package/util/opentelemetry.d.ts +34 -0
- package/util/opentelemetry.js +38 -0
- package/v1/client/admin.d.ts +6 -0
- package/v1/client/admin.js +6 -0
- package/v1/client/features/metrics.d.ts +11 -0
- package/v1/client/features/metrics.js +20 -0
- package/v1/client/features/schedules.d.ts +3 -0
- package/v1/client/features/schedules.js +3 -0
- package/v1/client/worker/worker-internal.d.ts +10 -2
- package/v1/client/worker/worker-internal.js +13 -2
- package/v1/examples/opentelemetry_instrumentation/run.d.ts +1 -0
- package/v1/examples/opentelemetry_instrumentation/run.js +57 -0
- package/v1/examples/opentelemetry_instrumentation/setup.d.ts +9 -0
- package/v1/examples/opentelemetry_instrumentation/setup.js +26 -0
- package/v1/examples/opentelemetry_instrumentation/worker.d.ts +7 -0
- package/v1/examples/opentelemetry_instrumentation/worker.js +123 -0
- package/version.d.ts +1 -1
- package/version.js +1 -1
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Hatchet OpenTelemetry Instrumentor
|
|
4
|
+
*
|
|
5
|
+
* This module provides automatic instrumentation for Hatchet SDK operations
|
|
6
|
+
* including workflow runs, event pushes, and step executions.
|
|
7
|
+
*
|
|
8
|
+
* The instrumentor follows the OpenTelemetry instrumentation pattern,
|
|
9
|
+
* patching module prototypes to automatically instrument all instances.
|
|
10
|
+
*/
|
|
11
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
12
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
13
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
14
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
15
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
16
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
17
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.HatchetInstrumentor = void 0;
|
|
22
|
+
const version_1 = require("../version");
|
|
23
|
+
const opentelemetry_1 = require("../util/opentelemetry");
|
|
24
|
+
const parse_1 = require("../util/parse");
|
|
25
|
+
const types_1 = require("./types");
|
|
26
|
+
const hatchet_span_context_1 = require("./hatchet-span-context");
|
|
27
|
+
try {
|
|
28
|
+
require.resolve('@opentelemetry/api');
|
|
29
|
+
require.resolve('@opentelemetry/instrumentation');
|
|
30
|
+
}
|
|
31
|
+
catch (_a) {
|
|
32
|
+
throw new Error('To use HatchetInstrumentor, you must install OpenTelemetry packages: npm install @opentelemetry/api @opentelemetry/instrumentation');
|
|
33
|
+
}
|
|
34
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
35
|
+
const otelApi = require('@opentelemetry/api');
|
|
36
|
+
const otelInstrumentation = require('@opentelemetry/instrumentation');
|
|
37
|
+
/* eslint-enable @typescript-eslint/no-require-imports */
|
|
38
|
+
const { context, propagation, SpanKind, SpanStatusCode, diag } = otelApi;
|
|
39
|
+
const { InstrumentationBase, InstrumentationNodeModuleDefinition, InstrumentationNodeModuleFile, isWrapped, } = otelInstrumentation;
|
|
40
|
+
const INSTRUMENTOR_NAME = '@hatchet-dev/typescript-sdk';
|
|
41
|
+
// FIXME: refactor version check to use the new pattern introduced in #2954
|
|
42
|
+
const SUPPORTED_VERSIONS = ['>=1.16.0'];
|
|
43
|
+
function extractContext(carrier) {
|
|
44
|
+
return propagation.extract(context.active(), carrier !== null && carrier !== void 0 ? carrier : {});
|
|
45
|
+
}
|
|
46
|
+
function injectContext(carrier) {
|
|
47
|
+
propagation.inject(context.active(), carrier);
|
|
48
|
+
}
|
|
49
|
+
function getActionOtelAttributes(action, excludedAttributes = [], workerId) {
|
|
50
|
+
const attributes = {
|
|
51
|
+
[opentelemetry_1.OTelAttribute.TENANT_ID]: action.tenantId,
|
|
52
|
+
[opentelemetry_1.OTelAttribute.WORKER_ID]: workerId,
|
|
53
|
+
[opentelemetry_1.OTelAttribute.WORKFLOW_RUN_ID]: action.workflowRunId,
|
|
54
|
+
[opentelemetry_1.OTelAttribute.STEP_ID]: action.taskId,
|
|
55
|
+
[opentelemetry_1.OTelAttribute.STEP_RUN_ID]: action.taskRunExternalId,
|
|
56
|
+
[opentelemetry_1.OTelAttribute.RETRY_COUNT]: action.retryCount,
|
|
57
|
+
[opentelemetry_1.OTelAttribute.PARENT_WORKFLOW_RUN_ID]: action.parentWorkflowRunId,
|
|
58
|
+
[opentelemetry_1.OTelAttribute.CHILD_WORKFLOW_INDEX]: action.childWorkflowIndex,
|
|
59
|
+
[opentelemetry_1.OTelAttribute.CHILD_WORKFLOW_KEY]: action.childWorkflowKey,
|
|
60
|
+
[opentelemetry_1.OTelAttribute.ACTION_PAYLOAD]: action.actionPayload,
|
|
61
|
+
[opentelemetry_1.OTelAttribute.WORKFLOW_NAME]: action.jobName,
|
|
62
|
+
[opentelemetry_1.OTelAttribute.ACTION_NAME]: action.actionId,
|
|
63
|
+
[opentelemetry_1.OTelAttribute.STEP_NAME]: action.taskName,
|
|
64
|
+
[opentelemetry_1.OTelAttribute.WORKFLOW_ID]: action.workflowId,
|
|
65
|
+
[opentelemetry_1.OTelAttribute.WORKFLOW_VERSION_ID]: action.workflowVersionId,
|
|
66
|
+
};
|
|
67
|
+
const filtered = { instrumentor: 'hatchet' };
|
|
68
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
69
|
+
if (!excludedAttributes.includes(key) && value !== undefined && value !== '') {
|
|
70
|
+
filtered[key] = value;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return filtered;
|
|
74
|
+
}
|
|
75
|
+
function filterAttributes(attributes, excludedAttributes = []) {
|
|
76
|
+
const filtered = { instrumentor: 'hatchet' };
|
|
77
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
78
|
+
if (!excludedAttributes.includes(key) &&
|
|
79
|
+
value !== undefined &&
|
|
80
|
+
value !== null &&
|
|
81
|
+
value !== '' &&
|
|
82
|
+
value !== '{}' &&
|
|
83
|
+
value !== '[]') {
|
|
84
|
+
filtered[`hatchet.${key}`] =
|
|
85
|
+
typeof value === 'object' ? JSON.stringify(value) : value;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return filtered;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* HatchetInstrumentor provides OpenTelemetry instrumentation for Hatchet SDK v1.
|
|
92
|
+
*
|
|
93
|
+
* It automatically instruments:
|
|
94
|
+
* - Workflow runs (runWorkflow, runWorkflows)
|
|
95
|
+
* - Scheduled workflow runs (schedules.create)
|
|
96
|
+
* - Event pushes (push, bulkPush)
|
|
97
|
+
* - Step executions (handleStartStepRun, handleCancelStepRun)
|
|
98
|
+
*
|
|
99
|
+
* Traceparent context is automatically propagated through metadata.
|
|
100
|
+
*
|
|
101
|
+
* The instrumentor uses the global tracer/meter providers by default.
|
|
102
|
+
* Use `setTracerProvider()` and `setMeterProvider()` to configure custom providers.
|
|
103
|
+
*/
|
|
104
|
+
class HatchetInstrumentor extends InstrumentationBase {
|
|
105
|
+
constructor(config = {}) {
|
|
106
|
+
const mergedConfig = Object.assign(Object.assign({}, types_1.DEFAULT_CONFIG), config);
|
|
107
|
+
super(INSTRUMENTOR_NAME, version_1.HATCHET_VERSION, mergedConfig);
|
|
108
|
+
this._getConfig = () => this.getConfig();
|
|
109
|
+
if (mergedConfig.enableHatchetCollector) {
|
|
110
|
+
this._setupHatchetCollector(config.clientConfig, config.bspConfig);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Sets up the Hatchet OTLP exporter on the current TracerProvider.
|
|
115
|
+
* Loads client config from environment if not provided.
|
|
116
|
+
*/
|
|
117
|
+
_setupHatchetCollector(clientConfig, bspConfig) {
|
|
118
|
+
try {
|
|
119
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
120
|
+
const { addHatchetExporter } = require('./hatchet-exporter.js');
|
|
121
|
+
let config = clientConfig;
|
|
122
|
+
if (!config) {
|
|
123
|
+
// Load config from environment (same as HatchetClient would)
|
|
124
|
+
const { ConfigLoader } = require('../util/config-loader/config-loader');
|
|
125
|
+
config = ConfigLoader.loadClientConfig();
|
|
126
|
+
}
|
|
127
|
+
// Get the SDK TracerProvider - either from the global provider or create one
|
|
128
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
129
|
+
let sdkTracerProvider;
|
|
130
|
+
try {
|
|
131
|
+
const sdkTrace = require('@opentelemetry/sdk-trace-base');
|
|
132
|
+
/* eslint-enable @typescript-eslint/no-require-imports */
|
|
133
|
+
// Check if the global tracer provider is an SDK TracerProvider
|
|
134
|
+
const globalProvider = otelApi.trace.getTracerProvider();
|
|
135
|
+
if (globalProvider instanceof sdkTrace.BasicTracerProvider) {
|
|
136
|
+
sdkTracerProvider = globalProvider;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
// Create a new SDK TracerProvider and set it as global
|
|
140
|
+
sdkTracerProvider = new sdkTrace.BasicTracerProvider();
|
|
141
|
+
sdkTracerProvider.register();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (_a) {
|
|
145
|
+
diag.warn('hatchet instrumentation: @opentelemetry/sdk-trace-base is required for enableHatchetCollector');
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
addHatchetExporter(sdkTracerProvider, config, bspConfig);
|
|
149
|
+
diag.info('hatchet instrumentation: Hatchet OTLP collector enabled');
|
|
150
|
+
}
|
|
151
|
+
catch (e) {
|
|
152
|
+
diag.warn(`hatchet instrumentation: Failed to set up Hatchet collector: ${e}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
setConfig(config = {}) {
|
|
156
|
+
super.setConfig(Object.assign(Object.assign({}, types_1.DEFAULT_CONFIG), config));
|
|
157
|
+
}
|
|
158
|
+
init() {
|
|
159
|
+
const eventClientModuleFile = new InstrumentationNodeModuleFile('@hatchet-dev/typescript-sdk/clients/event/event-client.js', SUPPORTED_VERSIONS, this.patchEventClient.bind(this), this.unpatchEventClient.bind(this));
|
|
160
|
+
const adminClientModuleFile = new InstrumentationNodeModuleFile('@hatchet-dev/typescript-sdk/v1/client/admin.js', SUPPORTED_VERSIONS, this.patchAdminClient.bind(this), this.unpatchAdminClient.bind(this));
|
|
161
|
+
const scheduleClientModuleFile = new InstrumentationNodeModuleFile('@hatchet-dev/typescript-sdk/v1/client/features/schedules.js', SUPPORTED_VERSIONS, this.patchScheduleClient.bind(this), this.unpatchScheduleClient.bind(this));
|
|
162
|
+
const workerModuleFile = new InstrumentationNodeModuleFile('@hatchet-dev/typescript-sdk/v1/client/worker/worker-internal.js', SUPPORTED_VERSIONS, this.patchWorker.bind(this), this.unpatchWorker.bind(this));
|
|
163
|
+
const durableContextModuleFile = new InstrumentationNodeModuleFile('@hatchet-dev/typescript-sdk/v1/client/worker/context.js', SUPPORTED_VERSIONS, this.patchDurableContext.bind(this), this.unpatchDurableContext.bind(this));
|
|
164
|
+
const moduleDefinition = new InstrumentationNodeModuleDefinition(INSTRUMENTOR_NAME, SUPPORTED_VERSIONS, undefined, undefined, [
|
|
165
|
+
eventClientModuleFile,
|
|
166
|
+
adminClientModuleFile,
|
|
167
|
+
workerModuleFile,
|
|
168
|
+
scheduleClientModuleFile,
|
|
169
|
+
durableContextModuleFile,
|
|
170
|
+
]);
|
|
171
|
+
return [moduleDefinition];
|
|
172
|
+
}
|
|
173
|
+
patchEventClient(moduleExports, _moduleVersion) {
|
|
174
|
+
var _a;
|
|
175
|
+
const exports = moduleExports;
|
|
176
|
+
if (!((_a = exports === null || exports === void 0 ? void 0 : exports.EventClient) === null || _a === void 0 ? void 0 : _a.prototype)) {
|
|
177
|
+
diag.debug('hatchet instrumentation: EventClient not found in module exports');
|
|
178
|
+
return moduleExports;
|
|
179
|
+
}
|
|
180
|
+
this._patchPushEvent(exports.EventClient.prototype);
|
|
181
|
+
this._patchBulkPushEvent(exports.EventClient.prototype);
|
|
182
|
+
return moduleExports;
|
|
183
|
+
}
|
|
184
|
+
unpatchEventClient(moduleExports, _moduleVersion) {
|
|
185
|
+
var _a;
|
|
186
|
+
const exports = moduleExports;
|
|
187
|
+
if (!((_a = exports === null || exports === void 0 ? void 0 : exports.EventClient) === null || _a === void 0 ? void 0 : _a.prototype)) {
|
|
188
|
+
return moduleExports;
|
|
189
|
+
}
|
|
190
|
+
if (isWrapped(exports.EventClient.prototype.push)) {
|
|
191
|
+
this._unwrap(exports.EventClient.prototype, 'push');
|
|
192
|
+
}
|
|
193
|
+
if (isWrapped(exports.EventClient.prototype.bulkPush)) {
|
|
194
|
+
this._unwrap(exports.EventClient.prototype, 'bulkPush');
|
|
195
|
+
}
|
|
196
|
+
return moduleExports;
|
|
197
|
+
}
|
|
198
|
+
_patchPushEvent(prototype) {
|
|
199
|
+
if (isWrapped(prototype.push)) {
|
|
200
|
+
this._unwrap(prototype, 'push');
|
|
201
|
+
}
|
|
202
|
+
const { tracer, _getConfig: getConfig } = this;
|
|
203
|
+
this._wrap(prototype, 'push', (original) => {
|
|
204
|
+
return function wrappedPush(type, input, options = {}) {
|
|
205
|
+
const attributes = filterAttributes({
|
|
206
|
+
[opentelemetry_1.OTelAttribute.EVENT_KEY]: type,
|
|
207
|
+
[opentelemetry_1.OTelAttribute.ACTION_PAYLOAD]: JSON.stringify(input),
|
|
208
|
+
[opentelemetry_1.OTelAttribute.ADDITIONAL_METADATA]: options.additionalMetadata
|
|
209
|
+
? JSON.stringify(options.additionalMetadata)
|
|
210
|
+
: undefined,
|
|
211
|
+
[opentelemetry_1.OTelAttribute.PRIORITY]: options.priority,
|
|
212
|
+
[opentelemetry_1.OTelAttribute.FILTER_SCOPE]: options.scope,
|
|
213
|
+
}, getConfig().excludedAttributes);
|
|
214
|
+
return tracer.startActiveSpan('hatchet.push_event', {
|
|
215
|
+
kind: SpanKind.PRODUCER,
|
|
216
|
+
attributes,
|
|
217
|
+
}, (span) => {
|
|
218
|
+
var _a;
|
|
219
|
+
const enhancedMetadata = Object.assign({}, ((_a = options.additionalMetadata) !== null && _a !== void 0 ? _a : {}));
|
|
220
|
+
injectContext(enhancedMetadata);
|
|
221
|
+
const enhancedOptions = Object.assign(Object.assign({}, options), { additionalMetadata: enhancedMetadata });
|
|
222
|
+
const result = original.call(this, type, input, enhancedOptions);
|
|
223
|
+
return result.finally(() => {
|
|
224
|
+
span.end();
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
};
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
_patchBulkPushEvent(prototype) {
|
|
231
|
+
if (isWrapped(prototype.bulkPush)) {
|
|
232
|
+
this._unwrap(prototype, 'bulkPush');
|
|
233
|
+
}
|
|
234
|
+
const { tracer, _getConfig: getConfig } = this;
|
|
235
|
+
this._wrap(prototype, 'bulkPush', (original) => {
|
|
236
|
+
return function wrappedBulkPush(type, inputs, options = {}) {
|
|
237
|
+
const attributes = filterAttributes({
|
|
238
|
+
[opentelemetry_1.OTelAttribute.EVENT_KEY]: type,
|
|
239
|
+
[opentelemetry_1.OTelAttribute.ACTION_PAYLOAD]: JSON.stringify(inputs),
|
|
240
|
+
[opentelemetry_1.OTelAttribute.ADDITIONAL_METADATA]: options.additionalMetadata
|
|
241
|
+
? JSON.stringify(options.additionalMetadata)
|
|
242
|
+
: undefined,
|
|
243
|
+
[opentelemetry_1.OTelAttribute.PRIORITY]: options.priority,
|
|
244
|
+
}, getConfig().excludedAttributes);
|
|
245
|
+
return tracer.startActiveSpan('hatchet.bulk_push_event', {
|
|
246
|
+
kind: SpanKind.PRODUCER,
|
|
247
|
+
attributes,
|
|
248
|
+
}, (span) => {
|
|
249
|
+
const enhancedInputs = inputs.map((input) => {
|
|
250
|
+
var _a;
|
|
251
|
+
const enhancedMetadata = Object.assign({}, ((_a = input.additionalMetadata) !== null && _a !== void 0 ? _a : {}));
|
|
252
|
+
injectContext(enhancedMetadata);
|
|
253
|
+
return Object.assign(Object.assign({}, input), { additionalMetadata: enhancedMetadata });
|
|
254
|
+
});
|
|
255
|
+
const result = original.call(this, type, enhancedInputs, options);
|
|
256
|
+
return result.finally(() => {
|
|
257
|
+
span.end();
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
};
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
patchAdminClient(moduleExports, _moduleVersion) {
|
|
264
|
+
var _a;
|
|
265
|
+
const exports = moduleExports;
|
|
266
|
+
if (!((_a = exports === null || exports === void 0 ? void 0 : exports.AdminClient) === null || _a === void 0 ? void 0 : _a.prototype)) {
|
|
267
|
+
diag.debug('hatchet instrumentation: AdminClient not found in module exports');
|
|
268
|
+
return moduleExports;
|
|
269
|
+
}
|
|
270
|
+
this._patchRunWorkflow(exports.AdminClient.prototype);
|
|
271
|
+
this._patchRunWorkflows(exports.AdminClient.prototype);
|
|
272
|
+
return moduleExports;
|
|
273
|
+
}
|
|
274
|
+
unpatchAdminClient(moduleExports, _moduleVersion) {
|
|
275
|
+
var _a;
|
|
276
|
+
const exports = moduleExports;
|
|
277
|
+
if (!((_a = exports === null || exports === void 0 ? void 0 : exports.AdminClient) === null || _a === void 0 ? void 0 : _a.prototype)) {
|
|
278
|
+
return moduleExports;
|
|
279
|
+
}
|
|
280
|
+
if (isWrapped(exports.AdminClient.prototype.runWorkflow)) {
|
|
281
|
+
this._unwrap(exports.AdminClient.prototype, 'runWorkflow');
|
|
282
|
+
}
|
|
283
|
+
if (isWrapped(exports.AdminClient.prototype.runWorkflows)) {
|
|
284
|
+
this._unwrap(exports.AdminClient.prototype, 'runWorkflows');
|
|
285
|
+
}
|
|
286
|
+
return moduleExports;
|
|
287
|
+
}
|
|
288
|
+
_patchRunWorkflow(prototype) {
|
|
289
|
+
if (isWrapped(prototype.runWorkflow)) {
|
|
290
|
+
this._unwrap(prototype, 'runWorkflow');
|
|
291
|
+
}
|
|
292
|
+
const { tracer, _getConfig: getConfig } = this;
|
|
293
|
+
this._wrap(prototype, 'runWorkflow', (original) => {
|
|
294
|
+
return function wrappedRunWorkflow(workflowName, input, options) {
|
|
295
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
296
|
+
const attributes = filterAttributes({
|
|
297
|
+
[opentelemetry_1.OTelAttribute.WORKFLOW_NAME]: workflowName,
|
|
298
|
+
[opentelemetry_1.OTelAttribute.ACTION_PAYLOAD]: JSON.stringify(input),
|
|
299
|
+
[opentelemetry_1.OTelAttribute.PARENT_ID]: options === null || options === void 0 ? void 0 : options.parentId,
|
|
300
|
+
[opentelemetry_1.OTelAttribute.PARENT_STEP_RUN_ID]: options === null || options === void 0 ? void 0 : options.parentStepRunId,
|
|
301
|
+
[opentelemetry_1.OTelAttribute.CHILD_INDEX]: options === null || options === void 0 ? void 0 : options.childIndex,
|
|
302
|
+
[opentelemetry_1.OTelAttribute.CHILD_KEY]: options === null || options === void 0 ? void 0 : options.childKey,
|
|
303
|
+
[opentelemetry_1.OTelAttribute.ADDITIONAL_METADATA]: (options === null || options === void 0 ? void 0 : options.additionalMetadata)
|
|
304
|
+
? JSON.stringify(options.additionalMetadata)
|
|
305
|
+
: undefined,
|
|
306
|
+
[opentelemetry_1.OTelAttribute.PRIORITY]: options === null || options === void 0 ? void 0 : options.priority,
|
|
307
|
+
[opentelemetry_1.OTelAttribute.DESIRED_WORKER_ID]: options === null || options === void 0 ? void 0 : options.desiredWorkerId,
|
|
308
|
+
}, getConfig().excludedAttributes);
|
|
309
|
+
return tracer.startActiveSpan('hatchet.run_workflow', {
|
|
310
|
+
kind: SpanKind.PRODUCER,
|
|
311
|
+
attributes,
|
|
312
|
+
}, (span) => {
|
|
313
|
+
var _a;
|
|
314
|
+
const enhancedMetadata = Object.assign({}, ((_a = options === null || options === void 0 ? void 0 : options.additionalMetadata) !== null && _a !== void 0 ? _a : {}));
|
|
315
|
+
injectContext(enhancedMetadata);
|
|
316
|
+
const enhancedOptions = Object.assign(Object.assign({}, options), { additionalMetadata: enhancedMetadata });
|
|
317
|
+
return original
|
|
318
|
+
.call(this, workflowName, input, enhancedOptions)
|
|
319
|
+
.catch((error) => {
|
|
320
|
+
span.recordException(error);
|
|
321
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: error === null || error === void 0 ? void 0 : error.message });
|
|
322
|
+
throw error;
|
|
323
|
+
})
|
|
324
|
+
.finally(() => {
|
|
325
|
+
span.end();
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
};
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
_patchRunWorkflows(prototype) {
|
|
333
|
+
if (isWrapped(prototype.runWorkflows)) {
|
|
334
|
+
this._unwrap(prototype, 'runWorkflows');
|
|
335
|
+
}
|
|
336
|
+
const { tracer, _getConfig: getConfig } = this;
|
|
337
|
+
this._wrap(prototype, 'runWorkflows', (original) => {
|
|
338
|
+
return function wrappedRunWorkflows(workflowRuns, batchSize) {
|
|
339
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
340
|
+
const attributes = filterAttributes({
|
|
341
|
+
[opentelemetry_1.OTelAttribute.WORKFLOW_NAME]: JSON.stringify(workflowRuns.map((r) => r.workflowName)),
|
|
342
|
+
[opentelemetry_1.OTelAttribute.ACTION_PAYLOAD]: JSON.stringify(workflowRuns),
|
|
343
|
+
}, getConfig().excludedAttributes);
|
|
344
|
+
return tracer.startActiveSpan('hatchet.run_workflows', {
|
|
345
|
+
kind: SpanKind.PRODUCER,
|
|
346
|
+
attributes,
|
|
347
|
+
}, (span) => {
|
|
348
|
+
const enhancedWorkflowRuns = workflowRuns.map((run) => {
|
|
349
|
+
var _a, _b;
|
|
350
|
+
const enhancedMetadata = Object.assign({}, ((_b = (_a = run.options) === null || _a === void 0 ? void 0 : _a.additionalMetadata) !== null && _b !== void 0 ? _b : {}));
|
|
351
|
+
injectContext(enhancedMetadata);
|
|
352
|
+
return Object.assign(Object.assign({}, run), { options: Object.assign(Object.assign({}, run.options), { additionalMetadata: enhancedMetadata }) });
|
|
353
|
+
});
|
|
354
|
+
return original
|
|
355
|
+
.call(this, enhancedWorkflowRuns, batchSize)
|
|
356
|
+
.catch((error) => {
|
|
357
|
+
span.recordException(error);
|
|
358
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: error === null || error === void 0 ? void 0 : error.message });
|
|
359
|
+
throw error;
|
|
360
|
+
})
|
|
361
|
+
.finally(() => {
|
|
362
|
+
span.end();
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
};
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
patchWorker(moduleExports, _moduleVersion) {
|
|
370
|
+
var _a;
|
|
371
|
+
const exports = moduleExports;
|
|
372
|
+
if (!((_a = exports === null || exports === void 0 ? void 0 : exports.InternalWorker) === null || _a === void 0 ? void 0 : _a.prototype)) {
|
|
373
|
+
diag.debug('hatchet instrumentation: InternalWorker not found in module exports');
|
|
374
|
+
return moduleExports;
|
|
375
|
+
}
|
|
376
|
+
this._patchHandleStartStepRun(exports.InternalWorker.prototype);
|
|
377
|
+
this._patchHandleCancelStepRun(exports.InternalWorker.prototype);
|
|
378
|
+
return moduleExports;
|
|
379
|
+
}
|
|
380
|
+
unpatchWorker(moduleExports, _moduleVersion) {
|
|
381
|
+
var _a;
|
|
382
|
+
const exports = moduleExports;
|
|
383
|
+
if (!((_a = exports === null || exports === void 0 ? void 0 : exports.InternalWorker) === null || _a === void 0 ? void 0 : _a.prototype)) {
|
|
384
|
+
return moduleExports;
|
|
385
|
+
}
|
|
386
|
+
if (isWrapped(exports.InternalWorker.prototype.handleStartStepRun)) {
|
|
387
|
+
this._unwrap(exports.InternalWorker.prototype, 'handleStartStepRun');
|
|
388
|
+
}
|
|
389
|
+
if (isWrapped(exports.InternalWorker.prototype.handleCancelStepRun)) {
|
|
390
|
+
this._unwrap(exports.InternalWorker.prototype, 'handleCancelStepRun');
|
|
391
|
+
}
|
|
392
|
+
return moduleExports;
|
|
393
|
+
}
|
|
394
|
+
// IMPORTANT: Keep this wrapper's signature in sync with InternalWorker.handleStartStepRun
|
|
395
|
+
_patchHandleStartStepRun(prototype) {
|
|
396
|
+
if (isWrapped(prototype.handleStartStepRun)) {
|
|
397
|
+
this._unwrap(prototype, 'handleStartStepRun');
|
|
398
|
+
}
|
|
399
|
+
const { tracer, _getConfig: getConfig } = this;
|
|
400
|
+
this._wrap(prototype, 'handleStartStepRun', (original) => {
|
|
401
|
+
return function wrappedHandleStartStepRun(action) {
|
|
402
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
403
|
+
const additionalMetadata = action.additionalMetadata
|
|
404
|
+
? (0, parse_1.parseJSON)(action.additionalMetadata)
|
|
405
|
+
: undefined;
|
|
406
|
+
const parentContext = extractContext(additionalMetadata);
|
|
407
|
+
const attributes = getActionOtelAttributes(action, getConfig().excludedAttributes, this.workerId);
|
|
408
|
+
let spanName = 'hatchet.start_step_run';
|
|
409
|
+
if (getConfig().includeTaskNameInSpanName) {
|
|
410
|
+
spanName += `.${action.actionId}`;
|
|
411
|
+
}
|
|
412
|
+
// Store hatchet.* attributes in async context so the SpanProcessor
|
|
413
|
+
// can inject them into child spans (mirrors Go/Python attribute propagation).
|
|
414
|
+
const hatchetAttrs = {};
|
|
415
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
416
|
+
if (value !== undefined) {
|
|
417
|
+
hatchetAttrs[`hatchet.${key}`] = value;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
(0, hatchet_span_context_1.setHatchetSpanAttributes)(hatchetAttrs);
|
|
421
|
+
return tracer.startActiveSpan(spanName, {
|
|
422
|
+
kind: SpanKind.CONSUMER,
|
|
423
|
+
attributes,
|
|
424
|
+
}, parentContext, (span) => {
|
|
425
|
+
return original
|
|
426
|
+
.call(this, action)
|
|
427
|
+
.then((taskError) => {
|
|
428
|
+
if (taskError instanceof Error) {
|
|
429
|
+
span.recordException(taskError);
|
|
430
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: taskError.message });
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
434
|
+
}
|
|
435
|
+
return taskError;
|
|
436
|
+
})
|
|
437
|
+
.finally(() => {
|
|
438
|
+
span.end();
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
};
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
_patchHandleCancelStepRun(prototype) {
|
|
446
|
+
if (isWrapped(prototype.handleCancelStepRun)) {
|
|
447
|
+
this._unwrap(prototype, 'handleCancelStepRun');
|
|
448
|
+
}
|
|
449
|
+
const { tracer } = this;
|
|
450
|
+
this._wrap(prototype, 'handleCancelStepRun', (original) => {
|
|
451
|
+
return function wrappedHandleCancelStepRun(action) {
|
|
452
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
453
|
+
const attributes = {
|
|
454
|
+
instrumentor: 'hatchet',
|
|
455
|
+
[`hatchet.${opentelemetry_1.OTelAttribute.STEP_RUN_ID}`]: action.taskRunExternalId,
|
|
456
|
+
};
|
|
457
|
+
return tracer.startActiveSpan('hatchet.cancel_step_run', {
|
|
458
|
+
kind: SpanKind.CONSUMER,
|
|
459
|
+
attributes,
|
|
460
|
+
}, (span) => {
|
|
461
|
+
const result = original.call(this, action);
|
|
462
|
+
return result.finally(() => {
|
|
463
|
+
span.end();
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
};
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
patchScheduleClient(moduleExports, _moduleVersion) {
|
|
471
|
+
var _a;
|
|
472
|
+
const exports = moduleExports;
|
|
473
|
+
if (!((_a = exports === null || exports === void 0 ? void 0 : exports.ScheduleClient) === null || _a === void 0 ? void 0 : _a.prototype)) {
|
|
474
|
+
diag.debug('hatchet instrumentation: ScheduleClient not found in module exports');
|
|
475
|
+
return moduleExports;
|
|
476
|
+
}
|
|
477
|
+
this._patchScheduleCreate(exports.ScheduleClient.prototype);
|
|
478
|
+
return moduleExports;
|
|
479
|
+
}
|
|
480
|
+
unpatchScheduleClient(moduleExports, _moduleVersion) {
|
|
481
|
+
var _a;
|
|
482
|
+
const exports = moduleExports;
|
|
483
|
+
if (!((_a = exports === null || exports === void 0 ? void 0 : exports.ScheduleClient) === null || _a === void 0 ? void 0 : _a.prototype)) {
|
|
484
|
+
return moduleExports;
|
|
485
|
+
}
|
|
486
|
+
if (isWrapped(exports.ScheduleClient.prototype.create)) {
|
|
487
|
+
this._unwrap(exports.ScheduleClient.prototype, 'create');
|
|
488
|
+
}
|
|
489
|
+
return moduleExports;
|
|
490
|
+
}
|
|
491
|
+
// IMPORTANT: Keep this wrapper's signature in sync with ScheduleClient.create
|
|
492
|
+
_patchScheduleCreate(prototype) {
|
|
493
|
+
if (isWrapped(prototype.create)) {
|
|
494
|
+
this._unwrap(prototype, 'create');
|
|
495
|
+
}
|
|
496
|
+
const { tracer, _getConfig: getConfig } = this;
|
|
497
|
+
this._wrap(prototype, 'create', (original) => {
|
|
498
|
+
return function wrappedCreate(workflow, input) {
|
|
499
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
500
|
+
const triggerAtIso = input.triggerAt instanceof Date
|
|
501
|
+
? input.triggerAt.toISOString()
|
|
502
|
+
: new Date(input.triggerAt).toISOString();
|
|
503
|
+
const attributes = filterAttributes({
|
|
504
|
+
[opentelemetry_1.OTelAttribute.WORKFLOW_NAME]: workflow,
|
|
505
|
+
[opentelemetry_1.OTelAttribute.RUN_AT_TIMESTAMPS]: JSON.stringify([triggerAtIso]),
|
|
506
|
+
[opentelemetry_1.OTelAttribute.ACTION_PAYLOAD]: JSON.stringify(input.input),
|
|
507
|
+
[opentelemetry_1.OTelAttribute.ADDITIONAL_METADATA]: input.additionalMetadata
|
|
508
|
+
? JSON.stringify(input.additionalMetadata)
|
|
509
|
+
: undefined,
|
|
510
|
+
[opentelemetry_1.OTelAttribute.PRIORITY]: input.priority,
|
|
511
|
+
}, getConfig().excludedAttributes);
|
|
512
|
+
return tracer.startActiveSpan('hatchet.schedule_workflow', {
|
|
513
|
+
kind: SpanKind.PRODUCER,
|
|
514
|
+
attributes,
|
|
515
|
+
}, (span) => {
|
|
516
|
+
var _a;
|
|
517
|
+
// Inject traceparent into additionalMetadata for context propagation
|
|
518
|
+
const enhancedMetadata = Object.assign({}, ((_a = input.additionalMetadata) !== null && _a !== void 0 ? _a : {}));
|
|
519
|
+
injectContext(enhancedMetadata);
|
|
520
|
+
const enhancedInput = Object.assign(Object.assign({}, input), { additionalMetadata: enhancedMetadata });
|
|
521
|
+
return original
|
|
522
|
+
.call(this, workflow, enhancedInput)
|
|
523
|
+
.catch((error) => {
|
|
524
|
+
span.recordException(error);
|
|
525
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: error === null || error === void 0 ? void 0 : error.message });
|
|
526
|
+
throw error;
|
|
527
|
+
})
|
|
528
|
+
.finally(() => {
|
|
529
|
+
span.end();
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
});
|
|
533
|
+
};
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
// --- DurableContext patching ---
|
|
537
|
+
patchDurableContext(moduleExports) {
|
|
538
|
+
var _a;
|
|
539
|
+
const exports = moduleExports;
|
|
540
|
+
if (!((_a = exports === null || exports === void 0 ? void 0 : exports.DurableContext) === null || _a === void 0 ? void 0 : _a.prototype)) {
|
|
541
|
+
return moduleExports;
|
|
542
|
+
}
|
|
543
|
+
this._patchWaitFor(exports.DurableContext.prototype);
|
|
544
|
+
return moduleExports;
|
|
545
|
+
}
|
|
546
|
+
unpatchDurableContext(moduleExports) {
|
|
547
|
+
var _a;
|
|
548
|
+
const exports = moduleExports;
|
|
549
|
+
if (!((_a = exports === null || exports === void 0 ? void 0 : exports.DurableContext) === null || _a === void 0 ? void 0 : _a.prototype)) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
if (isWrapped(exports.DurableContext.prototype.waitFor)) {
|
|
553
|
+
this._unwrap(exports.DurableContext.prototype, 'waitFor');
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
// IMPORTANT: Keep this wrapper's signature in sync with DurableContext.waitFor
|
|
557
|
+
_patchWaitFor(prototype) {
|
|
558
|
+
if (isWrapped(prototype.waitFor)) {
|
|
559
|
+
this._unwrap(prototype, 'waitFor');
|
|
560
|
+
}
|
|
561
|
+
const { tracer } = this;
|
|
562
|
+
this._wrap(prototype, 'waitFor', (original) => {
|
|
563
|
+
return function wrappedWaitFor(...args) {
|
|
564
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
565
|
+
var _a, _b;
|
|
566
|
+
return tracer.startActiveSpan('hatchet.durable.wait_for', {
|
|
567
|
+
kind: SpanKind.INTERNAL,
|
|
568
|
+
attributes: {
|
|
569
|
+
instrumentor: 'hatchet',
|
|
570
|
+
'hatchet.step_run_id': (_b = (_a = this.action) === null || _a === void 0 ? void 0 : _a.taskRunExternalId) !== null && _b !== void 0 ? _b : '',
|
|
571
|
+
},
|
|
572
|
+
}, (span) => __awaiter(this, void 0, void 0, function* () {
|
|
573
|
+
try {
|
|
574
|
+
const result = yield original.apply(this, args);
|
|
575
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
576
|
+
return result;
|
|
577
|
+
}
|
|
578
|
+
catch (error) {
|
|
579
|
+
if (error instanceof Error) {
|
|
580
|
+
span.recordException(error);
|
|
581
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
|
|
582
|
+
}
|
|
583
|
+
throw error;
|
|
584
|
+
}
|
|
585
|
+
finally {
|
|
586
|
+
span.end();
|
|
587
|
+
}
|
|
588
|
+
}));
|
|
589
|
+
});
|
|
590
|
+
};
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
exports.HatchetInstrumentor = HatchetInstrumentor;
|
|
595
|
+
exports.default = HatchetInstrumentor;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hatchet-dev/typescript-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"description": "Background task orchestration & visibility for developers",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -61,6 +61,11 @@
|
|
|
61
61
|
"zod-to-json-schema": "^3.24.1"
|
|
62
62
|
},
|
|
63
63
|
"optionalDependencies": {
|
|
64
|
+
"@opentelemetry/api": "^1.9.0",
|
|
65
|
+
"@opentelemetry/core": "^1.30.1",
|
|
66
|
+
"@opentelemetry/exporter-trace-otlp-grpc": "^0.208.0",
|
|
67
|
+
"@opentelemetry/instrumentation": "^0.208.0",
|
|
68
|
+
"@opentelemetry/sdk-trace-base": "^1.30.1",
|
|
64
69
|
"prom-client": "^15.1.3"
|
|
65
70
|
},
|
|
66
71
|
"scripts": {
|
|
@@ -5,6 +5,7 @@ interface LoadClientConfigOptions {
|
|
|
5
5
|
}
|
|
6
6
|
export declare class ConfigLoader {
|
|
7
7
|
static loadClientConfig(override?: Partial<ClientConfig>, config?: LoadClientConfigOptions): Partial<ClientConfig>;
|
|
8
|
+
private static parseJsonArray;
|
|
8
9
|
static get default_yaml_config_path(): string;
|
|
9
10
|
static createCredentials(config: ClientConfig['tls_config']): ChannelCredentials;
|
|
10
11
|
static loadYamlConfig(path?: string): ClientConfig | undefined;
|