@event-driven-io/emmett 0.43.0-beta.23 → 0.43.0-beta.26
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/dist/index.cjs +347 -312
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +145 -117
- package/dist/index.d.ts +145 -117
- package/dist/index.js +340 -307
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -619,6 +619,11 @@ const bigIntReplacer = (_key, value) => {
|
|
|
619
619
|
const dateReplacer = (_key, value) => {
|
|
620
620
|
return value instanceof Date ? value.toISOString() : value;
|
|
621
621
|
};
|
|
622
|
+
const errorReplacer = (_key, value) => value instanceof Error ? {
|
|
623
|
+
type: value.constructor.name || "error",
|
|
624
|
+
message: value.message,
|
|
625
|
+
stack: value.stack
|
|
626
|
+
} : value;
|
|
622
627
|
const isFirstLetterNumeric = (str) => {
|
|
623
628
|
const c = str.charCodeAt(0);
|
|
624
629
|
return c >= 48 && c <= 57;
|
|
@@ -660,11 +665,12 @@ const composeJSONRevivers = (...revivers) => {
|
|
|
660
665
|
if (filteredRevivers.length === 0) return void 0;
|
|
661
666
|
return (key, value, context) => filteredRevivers.reduce((accValue, reviver) => reviver(key, accValue, context), value);
|
|
662
667
|
};
|
|
663
|
-
const JSONReplacer = (opts) => composeJSONReplacers(opts?.replacer, opts?.failOnBigIntSerialization !== true ? JSONReplacers.bigInt : void 0, opts?.useDefaultDateSerialization !== true ? JSONReplacers.date : void 0);
|
|
668
|
+
const JSONReplacer = (opts) => composeJSONReplacers(opts?.replacer, opts?.failOnBigIntSerialization !== true ? JSONReplacers.bigInt : void 0, opts?.useDefaultDateSerialization !== true ? JSONReplacers.date : void 0, opts?.skipErrorSerialization !== true ? JSONReplacers.error : void 0);
|
|
664
669
|
const JSONReviver = (opts) => composeJSONRevivers(opts?.reviver, opts?.parseBigInts === true ? JSONRevivers.bigInt : void 0, opts?.parseDates === true ? JSONRevivers.date : void 0);
|
|
665
670
|
const JSONReplacers = {
|
|
666
671
|
bigInt: bigIntReplacer,
|
|
667
|
-
date: dateReplacer
|
|
672
|
+
date: dateReplacer,
|
|
673
|
+
error: errorReplacer
|
|
668
674
|
};
|
|
669
675
|
const JSONRevivers = {
|
|
670
676
|
bigInt: bigIntReviver,
|
|
@@ -673,8 +679,19 @@ const JSONRevivers = {
|
|
|
673
679
|
const jsonSerializer = (options) => {
|
|
674
680
|
const defaultReplacer = JSONReplacer(options);
|
|
675
681
|
const defaultReviver = JSONReviver(options);
|
|
682
|
+
const defaultFormat = options?.format ?? "compact";
|
|
683
|
+
const defaultSafe = options?.safe ?? false;
|
|
676
684
|
return {
|
|
677
|
-
serialize: (object, serializerOptions) =>
|
|
685
|
+
serialize: (object, serializerOptions) => {
|
|
686
|
+
const replacer = serializerOptions ? JSONReplacer(serializerOptions) : defaultReplacer;
|
|
687
|
+
const indent = (serializerOptions?.format ?? defaultFormat) === "pretty" ? 2 : void 0;
|
|
688
|
+
if (serializerOptions?.safe ?? defaultSafe) try {
|
|
689
|
+
return JSON.stringify(object, replacer, indent);
|
|
690
|
+
} catch (e) {
|
|
691
|
+
return `{"__serializationError":${JSON.stringify(String(e))}}`;
|
|
692
|
+
}
|
|
693
|
+
return JSON.stringify(object, replacer, indent);
|
|
694
|
+
},
|
|
678
695
|
deserialize: (payload, deserializerOptions) => JSON.parse(payload, deserializerOptions ? JSONReviver(deserializerOptions) : defaultReviver)
|
|
679
696
|
};
|
|
680
697
|
};
|
|
@@ -978,6 +995,21 @@ const getInMemoryDatabase = () => {
|
|
|
978
995
|
} };
|
|
979
996
|
};
|
|
980
997
|
|
|
998
|
+
//#endregion
|
|
999
|
+
//#region src/processors/checkpoints.ts
|
|
1000
|
+
const ProcessorCheckpoint = Object.assign((checkpoint) => checkpoint, {
|
|
1001
|
+
END: "END",
|
|
1002
|
+
BEGINNING: "BEGINNING",
|
|
1003
|
+
isBeginning: (checkpoint) => checkpoint === void 0 || checkpoint === "BEGINNING",
|
|
1004
|
+
isEnd: (checkpoint) => checkpoint === "END",
|
|
1005
|
+
compare: (a, b) => a > b ? 1 : -1
|
|
1006
|
+
});
|
|
1007
|
+
const bigIntProcessorCheckpoint = (value) => bigInt.toNormalizedString(value);
|
|
1008
|
+
const parseBigIntProcessorCheckpoint = (value) => BigInt(value);
|
|
1009
|
+
const getCheckpoint = (message) => {
|
|
1010
|
+
return message.metadata.checkpoint;
|
|
1011
|
+
};
|
|
1012
|
+
|
|
981
1013
|
//#endregion
|
|
982
1014
|
//#region src/observability/attributes.ts
|
|
983
1015
|
const EmmettAttributes = {
|
|
@@ -1065,281 +1097,21 @@ const MessagingSystemName = "emmett";
|
|
|
1065
1097
|
|
|
1066
1098
|
//#endregion
|
|
1067
1099
|
//#region src/observability/options.ts
|
|
1068
|
-
const
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
attributeTarget: options?.observability?.attributeTarget ?? parent?.observability?.attributeTarget ?? "both"
|
|
1073
|
-
});
|
|
1074
|
-
const resolveWorkflowObservability = (options, parent) => ({
|
|
1075
|
-
tracer: options?.observability?.tracer ?? parent?.observability?.tracer ?? (0, _event_driven_io_almanac.noopTracer)(),
|
|
1076
|
-
meter: options?.observability?.meter ?? parent?.observability?.meter ?? (0, _event_driven_io_almanac.noopMeter)(),
|
|
1077
|
-
propagation: options?.observability?.propagation ?? parent?.observability?.propagation ?? "links",
|
|
1078
|
-
attributeTarget: options?.observability?.attributeTarget ?? parent?.observability?.attributeTarget ?? "both",
|
|
1079
|
-
includeMessagePayloads: options?.observability?.includeMessagePayloads ?? parent?.observability?.includeMessagePayloads ?? false
|
|
1080
|
-
});
|
|
1081
|
-
|
|
1082
|
-
//#endregion
|
|
1083
|
-
//#region src/observability/tracer.ts
|
|
1084
|
-
const tracer = () => {};
|
|
1085
|
-
const LogLevel = {
|
|
1086
|
-
DISABLED: "DISABLED",
|
|
1087
|
-
INFO: "INFO",
|
|
1088
|
-
LOG: "LOG",
|
|
1089
|
-
WARN: "WARN",
|
|
1090
|
-
ERROR: "ERROR"
|
|
1091
|
-
};
|
|
1092
|
-
const LogStyle = {
|
|
1093
|
-
RAW: "RAW",
|
|
1094
|
-
PRETTY: "PRETTY"
|
|
1095
|
-
};
|
|
1096
|
-
const getEnvVariable = (name) => {
|
|
1097
|
-
try {
|
|
1098
|
-
if (typeof process !== "undefined" && process.env) return process.env[name];
|
|
1099
|
-
return;
|
|
1100
|
-
} catch {
|
|
1101
|
-
return;
|
|
1102
|
-
}
|
|
1103
|
-
};
|
|
1104
|
-
const shouldLog = (logLevel) => {
|
|
1105
|
-
const definedLogLevel = getEnvVariable("DUMBO_LOG_LEVEL") ?? LogLevel.ERROR;
|
|
1106
|
-
if (definedLogLevel === LogLevel.ERROR && logLevel === LogLevel.ERROR) return true;
|
|
1107
|
-
if (definedLogLevel === LogLevel.WARN && [LogLevel.ERROR, LogLevel.WARN].includes(logLevel)) return true;
|
|
1108
|
-
if (definedLogLevel === LogLevel.LOG && [
|
|
1109
|
-
LogLevel.ERROR,
|
|
1110
|
-
LogLevel.WARN,
|
|
1111
|
-
LogLevel.LOG
|
|
1112
|
-
].includes(logLevel)) return true;
|
|
1113
|
-
if (definedLogLevel === LogLevel.INFO && [
|
|
1114
|
-
LogLevel.ERROR,
|
|
1115
|
-
LogLevel.WARN,
|
|
1116
|
-
LogLevel.LOG,
|
|
1117
|
-
LogLevel.INFO
|
|
1118
|
-
].includes(logLevel)) return true;
|
|
1119
|
-
return false;
|
|
1120
|
-
};
|
|
1121
|
-
const nulloTraceEventRecorder = () => {};
|
|
1122
|
-
const getTraceEventFormatter = (logStyle, serializer) => (event) => {
|
|
1123
|
-
serializer = serializer ?? JSONSerializer.from();
|
|
1124
|
-
switch (logStyle) {
|
|
1125
|
-
case "RAW": return serializer.serialize(event);
|
|
1126
|
-
case "PRETTY": return serializer.serialize(event);
|
|
1127
|
-
}
|
|
1128
|
-
};
|
|
1129
|
-
const getTraceEventRecorder = (logLevel, logStyle) => {
|
|
1130
|
-
const format = getTraceEventFormatter(logStyle);
|
|
1131
|
-
switch (logLevel) {
|
|
1132
|
-
case "DISABLED": return nulloTraceEventRecorder;
|
|
1133
|
-
case "INFO": return (event) => console.info(format(event));
|
|
1134
|
-
case "LOG": return (event) => console.log(format(event));
|
|
1135
|
-
case "WARN": return (event) => console.warn(format(event));
|
|
1136
|
-
case "ERROR": return (event) => console.error(format(event));
|
|
1137
|
-
}
|
|
1138
|
-
};
|
|
1139
|
-
const recordTraceEvent = (logLevel, eventName, attributes) => {
|
|
1140
|
-
if (!shouldLog(LogLevel.LOG)) return;
|
|
1141
|
-
const event = {
|
|
1142
|
-
name: eventName,
|
|
1143
|
-
timestamp: (/* @__PURE__ */ new Date()).getTime(),
|
|
1144
|
-
...attributes
|
|
1145
|
-
};
|
|
1146
|
-
getTraceEventRecorder(logLevel, getEnvVariable("DUMBO_LOG_STYLE") ?? "RAW")(event);
|
|
1147
|
-
};
|
|
1148
|
-
tracer.info = (eventName, attributes) => recordTraceEvent(LogLevel.INFO, eventName, attributes);
|
|
1149
|
-
tracer.warn = (eventName, attributes) => recordTraceEvent(LogLevel.WARN, eventName, attributes);
|
|
1150
|
-
tracer.log = (eventName, attributes) => recordTraceEvent(LogLevel.LOG, eventName, attributes);
|
|
1151
|
-
tracer.error = (eventName, attributes) => recordTraceEvent(LogLevel.ERROR, eventName, attributes);
|
|
1152
|
-
|
|
1153
|
-
//#endregion
|
|
1154
|
-
//#region src/eventStore/observability/eventStoreCollector.ts
|
|
1155
|
-
const resolveEventStoreObservability = (options, parent) => ({
|
|
1156
|
-
tracer: options?.observability?.tracer ?? parent?.observability?.tracer ?? (0, _event_driven_io_almanac.noopTracer)(),
|
|
1157
|
-
meter: options?.observability?.meter ?? parent?.observability?.meter ?? (0, _event_driven_io_almanac.noopMeter)(),
|
|
1158
|
-
attributeTarget: options?.observability?.attributeTarget ?? parent?.observability?.attributeTarget ?? "both"
|
|
1159
|
-
});
|
|
1160
|
-
const eventStoreCollector = (observability) => {
|
|
1161
|
-
const A = EmmettAttributes;
|
|
1162
|
-
const M = _event_driven_io_almanac.MessagingAttributes;
|
|
1163
|
-
const streamReadingDuration = observability.meter.histogram(EmmettMetrics.stream.readingDuration);
|
|
1164
|
-
const streamReadingSize = observability.meter.histogram(EmmettMetrics.stream.readingSize);
|
|
1165
|
-
const eventReadingCount = observability.meter.counter(EmmettMetrics.event.readingCount);
|
|
1166
|
-
const streamAppendingDuration = observability.meter.histogram(EmmettMetrics.stream.appendingDuration);
|
|
1167
|
-
const streamAppendingSize = observability.meter.histogram(EmmettMetrics.stream.appendingSize);
|
|
1168
|
-
const eventAppendingCount = observability.meter.counter(EmmettMetrics.event.appendingCount);
|
|
1169
|
-
return {
|
|
1170
|
-
instrumentRead: (streamName, fn) => {
|
|
1171
|
-
const start = Date.now();
|
|
1172
|
-
return observability.tracer.startSpan("eventStore.readStream", async (span) => {
|
|
1173
|
-
span.setAttributes({
|
|
1174
|
-
[A.eventStore.operation]: "readStream",
|
|
1175
|
-
[A.stream.name]: streamName,
|
|
1176
|
-
[M.operation.type]: "receive",
|
|
1177
|
-
[M.destination.name]: streamName,
|
|
1178
|
-
[M.system]: MessagingSystemName
|
|
1179
|
-
});
|
|
1180
|
-
let status = "success";
|
|
1181
|
-
try {
|
|
1182
|
-
const result = await fn();
|
|
1183
|
-
const events = result.events;
|
|
1184
|
-
span.setAttributes({
|
|
1185
|
-
[A.eventStore.read.status]: status,
|
|
1186
|
-
[A.eventStore.read.eventCount]: events.length,
|
|
1187
|
-
[A.eventStore.read.eventTypes]: [...new Set(events.map((e) => e.type))]
|
|
1188
|
-
});
|
|
1189
|
-
streamReadingSize.record(events.length, { [A.eventStore.read.status]: status });
|
|
1190
|
-
for (const event of events) eventReadingCount.add(1, { [A.event.type]: event.type });
|
|
1191
|
-
return result;
|
|
1192
|
-
} catch (err) {
|
|
1193
|
-
status = "failure";
|
|
1194
|
-
span.setAttributes({ [A.eventStore.read.status]: status });
|
|
1195
|
-
throw err;
|
|
1196
|
-
} finally {
|
|
1197
|
-
streamReadingDuration.record(Date.now() - start, { [A.eventStore.read.status]: status });
|
|
1198
|
-
}
|
|
1199
|
-
});
|
|
1200
|
-
},
|
|
1201
|
-
instrumentAppend: (streamName, events, fn) => {
|
|
1202
|
-
const start = Date.now();
|
|
1203
|
-
return observability.tracer.startSpan("eventStore.appendToStream", async (span) => {
|
|
1204
|
-
span.setAttributes({
|
|
1205
|
-
[A.eventStore.operation]: "appendToStream",
|
|
1206
|
-
[A.stream.name]: streamName,
|
|
1207
|
-
[A.eventStore.append.batchSize]: events.length,
|
|
1208
|
-
[M.operation.type]: "send",
|
|
1209
|
-
[M.batch.messageCount]: events.length,
|
|
1210
|
-
[M.destination.name]: streamName,
|
|
1211
|
-
[M.system]: MessagingSystemName
|
|
1212
|
-
});
|
|
1213
|
-
let status = "success";
|
|
1214
|
-
try {
|
|
1215
|
-
const result = await fn();
|
|
1216
|
-
span.setAttributes({
|
|
1217
|
-
[A.eventStore.append.status]: status,
|
|
1218
|
-
[A.stream.versionAfter]: Number(result.nextExpectedStreamVersion)
|
|
1219
|
-
});
|
|
1220
|
-
streamAppendingSize.record(events.length, { [A.eventStore.append.status]: status });
|
|
1221
|
-
for (const event of events) eventAppendingCount.add(1, { [A.event.type]: event.type });
|
|
1222
|
-
return result;
|
|
1223
|
-
} catch (err) {
|
|
1224
|
-
status = "failure";
|
|
1225
|
-
span.setAttributes({ [A.eventStore.append.status]: status });
|
|
1226
|
-
throw err;
|
|
1227
|
-
} finally {
|
|
1228
|
-
streamAppendingDuration.record(Date.now() - start, { [A.eventStore.append.status]: status });
|
|
1229
|
-
}
|
|
1230
|
-
});
|
|
1231
|
-
}
|
|
1232
|
-
};
|
|
1233
|
-
};
|
|
1234
|
-
|
|
1235
|
-
//#endregion
|
|
1236
|
-
//#region src/observability/collectors/consumerCollector.ts
|
|
1237
|
-
const consumerCollector = (observability) => {
|
|
1238
|
-
const { startScope } = (0, _event_driven_io_almanac.ObservabilityScope)({
|
|
1239
|
-
...observability,
|
|
1240
|
-
attributePrefix: "emmett"
|
|
1241
|
-
});
|
|
1242
|
-
const A = EmmettAttributes;
|
|
1243
|
-
const M = _event_driven_io_almanac.MessagingAttributes;
|
|
1244
|
-
const pollDuration = observability.meter.histogram(EmmettMetrics.consumer.pollDuration);
|
|
1245
|
-
const deliveryDuration = observability.meter.histogram(EmmettMetrics.consumer.deliveryDuration);
|
|
1246
|
-
return {
|
|
1247
|
-
tracePoll: (context, fn) => {
|
|
1248
|
-
if (observability.pollTracing === "off" || observability.pollTracing === "active" && context.batchSize === 0) return fn(_event_driven_io_almanac.noopScope);
|
|
1249
|
-
return startScope("consumer.poll", async (scope) => {
|
|
1250
|
-
scope.setAttributes({
|
|
1251
|
-
[A.scope.type]: ScopeTypes.consumer,
|
|
1252
|
-
[A.consumer.batchSize]: context.batchSize,
|
|
1253
|
-
[A.consumer.processorCount]: context.processorCount,
|
|
1254
|
-
[M.system]: MessagingSystemName,
|
|
1255
|
-
[M.operation.type]: "receive",
|
|
1256
|
-
...context.empty ? { "emmett.consumer.poll.empty": true } : {},
|
|
1257
|
-
...context.waitMs != null ? { "emmett.consumer.poll.wait_ms": context.waitMs } : {}
|
|
1258
|
-
});
|
|
1259
|
-
return fn(scope);
|
|
1260
|
-
});
|
|
1261
|
-
},
|
|
1262
|
-
recordPollMetrics: (durationMs, attrs) => {
|
|
1263
|
-
pollDuration.record(durationMs, attrs);
|
|
1264
|
-
},
|
|
1265
|
-
traceDelivery: (scope, processorId, fn) => {
|
|
1266
|
-
const start = Date.now();
|
|
1267
|
-
return scope.scope(`consumer.deliver.${processorId}`, async (child) => {
|
|
1268
|
-
try {
|
|
1269
|
-
return await fn();
|
|
1270
|
-
} catch (error) {
|
|
1271
|
-
if (error instanceof Error) child.recordException(error);
|
|
1272
|
-
throw error;
|
|
1273
|
-
} finally {
|
|
1274
|
-
deliveryDuration.record(Date.now() - start, { [A.consumer.delivery.processorId]: processorId });
|
|
1275
|
-
}
|
|
1276
|
-
}, { attributes: { [A.consumer.delivery.processorId]: processorId } });
|
|
1277
|
-
}
|
|
1100
|
+
const mergeObservabilityOptions = (options, defaults) => {
|
|
1101
|
+
const observability = defaults === void 0 ? options.observability : options.observability === void 0 ? defaults : {
|
|
1102
|
+
...defaults,
|
|
1103
|
+
...options.observability
|
|
1278
1104
|
};
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
//#endregion
|
|
1282
|
-
//#region src/observability/collectors/workflowCollector.ts
|
|
1283
|
-
const workflowCollector = (observability) => {
|
|
1284
|
-
const { startScope } = (0, _event_driven_io_almanac.ObservabilityScope)({
|
|
1285
|
-
...observability,
|
|
1286
|
-
attributePrefix: "emmett"
|
|
1287
|
-
});
|
|
1288
|
-
const A = EmmettAttributes;
|
|
1289
|
-
const M = _event_driven_io_almanac.MessagingAttributes;
|
|
1290
|
-
const processingDuration = observability.meter.histogram(EmmettMetrics.workflow.processingDuration);
|
|
1105
|
+
if (observability === options.observability) return options;
|
|
1291
1106
|
return {
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
return startScope("workflow.handle", async (scope) => {
|
|
1295
|
-
scope.setAttributes({
|
|
1296
|
-
[A.scope.type]: ScopeTypes.workflow,
|
|
1297
|
-
[A.workflow.id]: context.workflowId,
|
|
1298
|
-
[A.workflow.type]: context.workflowType,
|
|
1299
|
-
[A.workflow.inputType]: context.inputType,
|
|
1300
|
-
[M.system]: MessagingSystemName
|
|
1301
|
-
});
|
|
1302
|
-
let status = "success";
|
|
1303
|
-
try {
|
|
1304
|
-
return await fn(scope);
|
|
1305
|
-
} catch (err) {
|
|
1306
|
-
status = "failure";
|
|
1307
|
-
throw err;
|
|
1308
|
-
} finally {
|
|
1309
|
-
processingDuration.record(Date.now() - start, {
|
|
1310
|
-
[A.workflow.type]: context.workflowType,
|
|
1311
|
-
status
|
|
1312
|
-
});
|
|
1313
|
-
}
|
|
1314
|
-
}, { attributes: {
|
|
1315
|
-
[A.scope.type]: ScopeTypes.workflow,
|
|
1316
|
-
[A.workflow.type]: context.workflowType
|
|
1317
|
-
} });
|
|
1318
|
-
},
|
|
1319
|
-
recordOutputs: (scope, outputs) => {
|
|
1320
|
-
scope.setAttributes({
|
|
1321
|
-
[A.workflow.outputs]: outputs.map((o) => o.type),
|
|
1322
|
-
[A.workflow.outputsCount]: outputs.length
|
|
1323
|
-
});
|
|
1324
|
-
},
|
|
1325
|
-
recordStateRebuild: (scope, eventCount) => {
|
|
1326
|
-
scope.setAttributes({ [A.workflow.stateRebuildEventCount]: eventCount });
|
|
1327
|
-
}
|
|
1107
|
+
...options,
|
|
1108
|
+
observability
|
|
1328
1109
|
};
|
|
1329
1110
|
};
|
|
1330
1111
|
|
|
1331
|
-
//#endregion
|
|
1332
|
-
//#region src/processors/checkpoints.ts
|
|
1333
|
-
const ProcessorCheckpoint = (checkpoint) => checkpoint;
|
|
1334
|
-
const bigIntProcessorCheckpoint = (value) => bigInt.toNormalizedString(value);
|
|
1335
|
-
const parseBigIntProcessorCheckpoint = (value) => BigInt(value);
|
|
1336
|
-
const getCheckpoint = (message) => {
|
|
1337
|
-
return message.metadata.checkpoint;
|
|
1338
|
-
};
|
|
1339
|
-
|
|
1340
1112
|
//#endregion
|
|
1341
1113
|
//#region src/processors/observability/processorCollector.ts
|
|
1342
|
-
const
|
|
1114
|
+
const processorObservability = (options, parent) => ({
|
|
1343
1115
|
tracer: options?.observability?.tracer ?? parent?.observability?.tracer ?? (0, _event_driven_io_almanac.noopTracer)(),
|
|
1344
1116
|
meter: options?.observability?.meter ?? parent?.observability?.meter ?? (0, _event_driven_io_almanac.noopMeter)(),
|
|
1345
1117
|
propagation: options?.observability?.propagation ?? parent?.observability?.propagation ?? "links",
|
|
@@ -1421,6 +1193,20 @@ const processorCollector = (observability) => {
|
|
|
1421
1193
|
|
|
1422
1194
|
//#endregion
|
|
1423
1195
|
//#region src/processors/processors.ts
|
|
1196
|
+
const CurrentMessageProcessorPosition = {
|
|
1197
|
+
compare: (a, b, compareCheckpoints = (chkA, chkB) => chkA > chkB ? 1 : chkA < chkB ? -1 : 0) => {
|
|
1198
|
+
if (a === b) return 0;
|
|
1199
|
+
if (a === "BEGINNING") return -1;
|
|
1200
|
+
if (b === "BEGINNING") return 1;
|
|
1201
|
+
if (a === "END") return 1;
|
|
1202
|
+
if (b === "END") return -1;
|
|
1203
|
+
return compareCheckpoints(a.lastCheckpoint, b.lastCheckpoint);
|
|
1204
|
+
},
|
|
1205
|
+
zip: (checkpoints, compareCheckpoints) => {
|
|
1206
|
+
if (checkpoints.length === 0) return "BEGINNING";
|
|
1207
|
+
return checkpoints.map((pos) => pos === void 0 ? "BEGINNING" : pos).sort((a, b) => CurrentMessageProcessorPosition.compare(a, b, compareCheckpoints))[0] ?? "BEGINNING";
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1424
1210
|
const wasMessageHandled = (message, checkpoint) => {
|
|
1425
1211
|
const messageCheckpoint = getCheckpoint(message);
|
|
1426
1212
|
return messageCheckpoint !== null && messageCheckpoint !== void 0 && checkpoint !== null && checkpoint !== void 0 && messageCheckpoint <= checkpoint;
|
|
@@ -1447,9 +1233,11 @@ const defaultProcessorVersion = 1;
|
|
|
1447
1233
|
const defaultProcessorPartition = defaultTag;
|
|
1448
1234
|
const getProcessorInstanceId = (processorId) => `${processorId}:${(0, uuid.v7)()}`;
|
|
1449
1235
|
const getProjectorId = (options) => `emt:processor:projector:${options.projectionName}`;
|
|
1236
|
+
const { info, error } = _event_driven_io_almanac.LogEvent;
|
|
1450
1237
|
const reactor = (options) => {
|
|
1451
1238
|
const { checkpoints, processorId, processorInstanceId: instanceId = getProcessorInstanceId(processorId), type = MessageProcessorType.REACTOR, version = 1, partition = defaultProcessorPartition, hooks = {}, processingScope = defaultProcessingMessageProcessingScope, startFrom, canHandle, stopAfter } = options;
|
|
1452
|
-
const
|
|
1239
|
+
const checkpointer = checkpoints === "DISABLED" ? inMemoryCheckpointer() : checkpoints;
|
|
1240
|
+
const collector = processorCollector(processorObservability(options));
|
|
1453
1241
|
const isCustomBatch = "eachBatch" in options && !!options.eachBatch;
|
|
1454
1242
|
const eachBatch = isCustomBatch ? options.eachBatch : async (messages, context) => {
|
|
1455
1243
|
const batchCtx = context.observabilityScope.spanContext();
|
|
@@ -1531,28 +1319,31 @@ const reactor = (options) => {
|
|
|
1531
1319
|
...partialOptions,
|
|
1532
1320
|
observabilityScope: ("observabilityScope" in partialOptions ? partialOptions.observabilityScope ?? _event_driven_io_almanac.noopScope : _event_driven_io_almanac.noopScope) ?? _event_driven_io_almanac.noopScope
|
|
1533
1321
|
};
|
|
1322
|
+
const log = startOptions.observabilityScope.log;
|
|
1534
1323
|
if (isActive) {
|
|
1535
|
-
|
|
1324
|
+
log(info(`Processor ${processorId} with instance id ${instanceId} is already active. Start request ignored.`));
|
|
1536
1325
|
return;
|
|
1537
1326
|
}
|
|
1538
|
-
|
|
1327
|
+
log(info(`Starting processor ${processorId} with instance id ${instanceId}`));
|
|
1539
1328
|
await init(startOptions);
|
|
1540
1329
|
isActive = true;
|
|
1541
1330
|
closeSignal = onShutdown(() => close(startOptions));
|
|
1542
1331
|
if (lastCheckpoint !== null) {
|
|
1543
|
-
|
|
1332
|
+
log(info(`Processor ${processorId} started with instance id ${instanceId}, checkpoint: ${JSONSerializer.serialize(lastCheckpoint)}`));
|
|
1544
1333
|
return { lastCheckpoint };
|
|
1545
1334
|
}
|
|
1546
1335
|
return await processingScope(async (context) => {
|
|
1336
|
+
const log = context.observabilityScope.log;
|
|
1547
1337
|
if (hooks.onStart) {
|
|
1548
|
-
|
|
1338
|
+
log(info(`Executing onStart hook for processor ${processorId} with instance id ${instanceId}`));
|
|
1549
1339
|
await hooks.onStart(context);
|
|
1550
1340
|
}
|
|
1551
|
-
if (startFrom && startFrom !== "CURRENT") {
|
|
1552
|
-
|
|
1341
|
+
if (startFrom !== void 0 && startFrom !== "CURRENT") {
|
|
1342
|
+
if (typeof startFrom !== "string") lastCheckpoint = startFrom.lastCheckpoint;
|
|
1343
|
+
log(info(`Processor ${processorId} with instance id ${instanceId} starting from: ${JSONSerializer.serialize(startFrom)}`));
|
|
1553
1344
|
return startFrom;
|
|
1554
1345
|
}
|
|
1555
|
-
if (
|
|
1346
|
+
if (checkpointer) lastCheckpoint = (await checkpointer.read({
|
|
1556
1347
|
processorId,
|
|
1557
1348
|
partition
|
|
1558
1349
|
}, {
|
|
@@ -1560,10 +1351,10 @@ const reactor = (options) => {
|
|
|
1560
1351
|
...context
|
|
1561
1352
|
})).lastCheckpoint;
|
|
1562
1353
|
if (lastCheckpoint === null) {
|
|
1563
|
-
|
|
1354
|
+
log(info(`Processor ${processorId} with instance id ${instanceId} starting from: BEGINNING`));
|
|
1564
1355
|
return "BEGINNING";
|
|
1565
1356
|
}
|
|
1566
|
-
|
|
1357
|
+
log(info(`Checkpoint read for processor ${processorId} with instance id ${instanceId}: ${JSONSerializer.serialize(lastCheckpoint)}`));
|
|
1567
1358
|
return { lastCheckpoint };
|
|
1568
1359
|
}, startOptions);
|
|
1569
1360
|
},
|
|
@@ -1601,8 +1392,8 @@ const reactor = (options) => {
|
|
|
1601
1392
|
} : batchResult;
|
|
1602
1393
|
const isStop = messageProcessingResult && messageProcessingResult.type === "STOP";
|
|
1603
1394
|
const checkpointMessage = messageProcessingResult?.type === "STOP" ? messageProcessingResult.lastSuccessfulMessage : messagesAboveCheckpoint[messagesAboveCheckpoint.length - 1];
|
|
1604
|
-
if (checkpointMessage &&
|
|
1605
|
-
const storeCheckpointResult = await
|
|
1395
|
+
if (checkpointMessage && checkpointer) {
|
|
1396
|
+
const storeCheckpointResult = await checkpointer.store({
|
|
1606
1397
|
processorId,
|
|
1607
1398
|
version,
|
|
1608
1399
|
message: checkpointMessage,
|
|
@@ -1620,12 +1411,12 @@ const reactor = (options) => {
|
|
|
1620
1411
|
...partialContext,
|
|
1621
1412
|
observabilityScope: scope
|
|
1622
1413
|
});
|
|
1623
|
-
} catch (
|
|
1624
|
-
|
|
1414
|
+
} catch (err) {
|
|
1415
|
+
scope.log(error({ err }, `Error during message processing for processor ${processorId} with instance id ${instanceId}. Stopping the processor.`));
|
|
1625
1416
|
isActive = false;
|
|
1626
1417
|
return {
|
|
1627
1418
|
type: "STOP",
|
|
1628
|
-
error,
|
|
1419
|
+
error: err,
|
|
1629
1420
|
reason: "Error during message processing"
|
|
1630
1421
|
};
|
|
1631
1422
|
}
|
|
@@ -1656,14 +1447,14 @@ const projector = (options) => {
|
|
|
1656
1447
|
//#endregion
|
|
1657
1448
|
//#region src/processors/inMemoryProcessors.ts
|
|
1658
1449
|
const inMemoryCheckpointer = () => {
|
|
1450
|
+
let fallbackDatabase;
|
|
1451
|
+
const resolveDatabase = (context) => context?.database ?? (fallbackDatabase ??= getInMemoryDatabase());
|
|
1659
1452
|
return {
|
|
1660
|
-
read: async ({ processorId },
|
|
1661
|
-
|
|
1662
|
-
return Promise.resolve({ lastCheckpoint: checkpoint?.lastCheckpoint ?? null });
|
|
1453
|
+
read: async ({ processorId }, context) => {
|
|
1454
|
+
return { lastCheckpoint: (await resolveDatabase(context).collection("emt_processor_checkpoints").findOne((d) => d._id === processorId))?.lastCheckpoint ?? null };
|
|
1663
1455
|
},
|
|
1664
|
-
store: async (
|
|
1665
|
-
const
|
|
1666
|
-
const checkpoints = database.collection("emt_processor_checkpoints");
|
|
1456
|
+
store: async ({ message, processorId, lastCheckpoint }, context) => {
|
|
1457
|
+
const checkpoints = resolveDatabase(context).collection("emt_processor_checkpoints");
|
|
1667
1458
|
const currentPosition = (await checkpoints.findOne((d) => d._id === processorId))?.lastCheckpoint ?? null;
|
|
1668
1459
|
const newCheckpoint = getCheckpoint(message);
|
|
1669
1460
|
if (currentPosition && (currentPosition === newCheckpoint || currentPosition !== lastCheckpoint)) return {
|
|
@@ -1733,6 +1524,49 @@ const inMemoryReactor = (options) => {
|
|
|
1733
1524
|
return Object.assign(processor, { database });
|
|
1734
1525
|
};
|
|
1735
1526
|
|
|
1527
|
+
//#endregion
|
|
1528
|
+
//#region src/processors/processorStartPositions.ts
|
|
1529
|
+
const ProcessorStartPositions = (options) => {
|
|
1530
|
+
const positions = /* @__PURE__ */ new Map();
|
|
1531
|
+
return {
|
|
1532
|
+
set: (processorId, position) => {
|
|
1533
|
+
positions.set(processorId, position);
|
|
1534
|
+
},
|
|
1535
|
+
with: (expected) => [...positions.entries()].filter(([, position]) => position === expected).map((p) => p[0]),
|
|
1536
|
+
zip: () => CurrentMessageProcessorPosition.zip([...positions.values()], options?.compareCheckpoints),
|
|
1537
|
+
afterStartPosition: (processorId, messages) => {
|
|
1538
|
+
const position = positions.get(processorId);
|
|
1539
|
+
if (position === void 0 || position === "BEGINNING" || position === "END") return messages;
|
|
1540
|
+
const { lastCheckpoint } = position;
|
|
1541
|
+
return messages.filter((message) => {
|
|
1542
|
+
const checkpoint = getCheckpoint(message);
|
|
1543
|
+
return checkpoint !== null && checkpoint > lastCheckpoint;
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
};
|
|
1547
|
+
};
|
|
1548
|
+
const ConsumerStartPositions = { resolve: async ({ processors, handlerContext: handlerOptions, readLastMessageCheckpoint, compareCheckpoints }) => {
|
|
1549
|
+
const positions = ProcessorStartPositions({ compareCheckpoints });
|
|
1550
|
+
await Promise.all(processors.map(async (o) => {
|
|
1551
|
+
try {
|
|
1552
|
+
const position = await o.start(handlerOptions);
|
|
1553
|
+
positions.set(o.id, position);
|
|
1554
|
+
} catch (error) {
|
|
1555
|
+
console.log(`Error during processor start position retrieval for processor: ${o.id}. Stopping it.`, error);
|
|
1556
|
+
throw error;
|
|
1557
|
+
}
|
|
1558
|
+
}));
|
|
1559
|
+
const endProcessorIds = positions.with("END");
|
|
1560
|
+
if (endProcessorIds.length > 0) {
|
|
1561
|
+
const lastCheckpoint = await readLastMessageCheckpoint(handlerOptions);
|
|
1562
|
+
for (const processorId of endProcessorIds) positions.set(processorId, lastCheckpoint ? { lastCheckpoint } : "BEGINNING");
|
|
1563
|
+
}
|
|
1564
|
+
return {
|
|
1565
|
+
earliestPosition: positions.zip(),
|
|
1566
|
+
afterStartPosition: (processorId, messages) => positions.afterStartPosition(processorId, messages)
|
|
1567
|
+
};
|
|
1568
|
+
} };
|
|
1569
|
+
|
|
1736
1570
|
//#endregion
|
|
1737
1571
|
//#region src/eventStore/projections/inMemory/inMemoryProjection.ts
|
|
1738
1572
|
const DATABASE_REQUIRED_ERROR_MESSAGE = "Database is required in context for InMemory projections";
|
|
@@ -2198,7 +2032,7 @@ const InMemoryProjectionSpec = { for: (options) => {
|
|
|
2198
2032
|
return Promise.resolve(false);
|
|
2199
2033
|
}
|
|
2200
2034
|
},
|
|
2201
|
-
observability:
|
|
2035
|
+
observability: eventStoreObservability(void 0)
|
|
2202
2036
|
});
|
|
2203
2037
|
};
|
|
2204
2038
|
return {
|
|
@@ -2303,6 +2137,88 @@ const upcastRecordedMessages = (recordedMessages, options) => {
|
|
|
2303
2137
|
return recordedMessages.map((recordedMessage) => upcastRecordedMessage(recordedMessage, options));
|
|
2304
2138
|
};
|
|
2305
2139
|
|
|
2140
|
+
//#endregion
|
|
2141
|
+
//#region src/eventStore/observability/eventStoreCollector.ts
|
|
2142
|
+
const eventStoreObservability = (options, parent) => ({
|
|
2143
|
+
tracer: options?.observability?.tracer ?? parent?.observability?.tracer ?? (0, _event_driven_io_almanac.noopTracer)(),
|
|
2144
|
+
meter: options?.observability?.meter ?? parent?.observability?.meter ?? (0, _event_driven_io_almanac.noopMeter)(),
|
|
2145
|
+
attributeTarget: options?.observability?.attributeTarget ?? parent?.observability?.attributeTarget ?? "both"
|
|
2146
|
+
});
|
|
2147
|
+
const eventStoreCollector = (observability) => {
|
|
2148
|
+
const A = EmmettAttributes;
|
|
2149
|
+
const M = _event_driven_io_almanac.MessagingAttributes;
|
|
2150
|
+
const streamReadingDuration = observability.meter.histogram(EmmettMetrics.stream.readingDuration);
|
|
2151
|
+
const streamReadingSize = observability.meter.histogram(EmmettMetrics.stream.readingSize);
|
|
2152
|
+
const eventReadingCount = observability.meter.counter(EmmettMetrics.event.readingCount);
|
|
2153
|
+
const streamAppendingDuration = observability.meter.histogram(EmmettMetrics.stream.appendingDuration);
|
|
2154
|
+
const streamAppendingSize = observability.meter.histogram(EmmettMetrics.stream.appendingSize);
|
|
2155
|
+
const eventAppendingCount = observability.meter.counter(EmmettMetrics.event.appendingCount);
|
|
2156
|
+
return {
|
|
2157
|
+
instrumentRead: (streamName, fn) => {
|
|
2158
|
+
const start = Date.now();
|
|
2159
|
+
return observability.tracer.startSpan("eventStore.readStream", async (span) => {
|
|
2160
|
+
span.setAttributes({
|
|
2161
|
+
[A.eventStore.operation]: "readStream",
|
|
2162
|
+
[A.stream.name]: streamName,
|
|
2163
|
+
[M.operation.type]: "receive",
|
|
2164
|
+
[M.destination.name]: streamName,
|
|
2165
|
+
[M.system]: MessagingSystemName
|
|
2166
|
+
});
|
|
2167
|
+
let status = "success";
|
|
2168
|
+
try {
|
|
2169
|
+
const result = await fn();
|
|
2170
|
+
const events = result.events;
|
|
2171
|
+
span.setAttributes({
|
|
2172
|
+
[A.eventStore.read.status]: status,
|
|
2173
|
+
[A.eventStore.read.eventCount]: events.length,
|
|
2174
|
+
[A.eventStore.read.eventTypes]: [...new Set(events.map((e) => e.type))]
|
|
2175
|
+
});
|
|
2176
|
+
streamReadingSize.record(events.length, { [A.eventStore.read.status]: status });
|
|
2177
|
+
for (const event of events) eventReadingCount.add(1, { [A.event.type]: event.type });
|
|
2178
|
+
return result;
|
|
2179
|
+
} catch (err) {
|
|
2180
|
+
status = "failure";
|
|
2181
|
+
span.setAttributes({ [A.eventStore.read.status]: status });
|
|
2182
|
+
throw err;
|
|
2183
|
+
} finally {
|
|
2184
|
+
streamReadingDuration.record(Date.now() - start, { [A.eventStore.read.status]: status });
|
|
2185
|
+
}
|
|
2186
|
+
});
|
|
2187
|
+
},
|
|
2188
|
+
instrumentAppend: (streamName, events, fn) => {
|
|
2189
|
+
const start = Date.now();
|
|
2190
|
+
return observability.tracer.startSpan("eventStore.appendToStream", async (span) => {
|
|
2191
|
+
span.setAttributes({
|
|
2192
|
+
[A.eventStore.operation]: "appendToStream",
|
|
2193
|
+
[A.stream.name]: streamName,
|
|
2194
|
+
[A.eventStore.append.batchSize]: events.length,
|
|
2195
|
+
[M.operation.type]: "send",
|
|
2196
|
+
[M.batch.messageCount]: events.length,
|
|
2197
|
+
[M.destination.name]: streamName,
|
|
2198
|
+
[M.system]: MessagingSystemName
|
|
2199
|
+
});
|
|
2200
|
+
let status = "success";
|
|
2201
|
+
try {
|
|
2202
|
+
const result = await fn();
|
|
2203
|
+
span.setAttributes({
|
|
2204
|
+
[A.eventStore.append.status]: status,
|
|
2205
|
+
[A.stream.versionAfter]: Number(result.nextExpectedStreamVersion)
|
|
2206
|
+
});
|
|
2207
|
+
streamAppendingSize.record(events.length, { [A.eventStore.append.status]: status });
|
|
2208
|
+
for (const event of events) eventAppendingCount.add(1, { [A.event.type]: event.type });
|
|
2209
|
+
return result;
|
|
2210
|
+
} catch (err) {
|
|
2211
|
+
status = "failure";
|
|
2212
|
+
span.setAttributes({ [A.eventStore.append.status]: status });
|
|
2213
|
+
throw err;
|
|
2214
|
+
} finally {
|
|
2215
|
+
streamAppendingDuration.record(Date.now() - start, { [A.eventStore.append.status]: status });
|
|
2216
|
+
}
|
|
2217
|
+
});
|
|
2218
|
+
}
|
|
2219
|
+
};
|
|
2220
|
+
};
|
|
2221
|
+
|
|
2306
2222
|
//#endregion
|
|
2307
2223
|
//#region src/eventStore/inMemoryEventStore.ts
|
|
2308
2224
|
const InMemoryEventStoreDefaultStreamVersion = 0n;
|
|
@@ -2313,7 +2229,7 @@ const getInMemoryEventStore = (eventStoreOptions) => {
|
|
|
2313
2229
|
};
|
|
2314
2230
|
const database = eventStoreOptions?.database || getInMemoryDatabase();
|
|
2315
2231
|
const inlineProjections = (eventStoreOptions?.projections ?? []).filter(({ type }) => type === "inline").map(({ projection }) => projection);
|
|
2316
|
-
const observability =
|
|
2232
|
+
const observability = eventStoreObservability(eventStoreOptions);
|
|
2317
2233
|
const collector = eventStoreCollector(observability);
|
|
2318
2234
|
const eventStore = {
|
|
2319
2235
|
database,
|
|
@@ -2395,7 +2311,7 @@ const getInMemoryEventStore = (eventStoreOptions) => {
|
|
|
2395
2311
|
|
|
2396
2312
|
//#endregion
|
|
2397
2313
|
//#region src/commandHandling/observability/commandHandlerCollector.ts
|
|
2398
|
-
const
|
|
2314
|
+
const commandObservability = (options, parent) => ({
|
|
2399
2315
|
tracer: options?.observability?.tracer ?? parent?.observability?.tracer ?? (0, _event_driven_io_almanac.noopTracer)(),
|
|
2400
2316
|
meter: options?.observability?.meter ?? parent?.observability?.meter ?? (0, _event_driven_io_almanac.noopMeter)(),
|
|
2401
2317
|
attributeTarget: options?.observability?.attributeTarget ?? parent?.observability?.attributeTarget ?? "both",
|
|
@@ -2439,7 +2355,7 @@ const commandHandlerCollector = (observability) => {
|
|
|
2439
2355
|
"exception.message": err instanceof Error ? err.message : String(err),
|
|
2440
2356
|
"exception.type": err instanceof Error ? err.constructor.name : "unknown"
|
|
2441
2357
|
});
|
|
2442
|
-
scope.
|
|
2358
|
+
scope.log(_event_driven_io_almanac.LogEvent.error(err instanceof Error ? err : new Error(String(err))));
|
|
2443
2359
|
throw err;
|
|
2444
2360
|
} finally {
|
|
2445
2361
|
commandHandlingDuration.record(Date.now() - start, {
|
|
@@ -2488,7 +2404,7 @@ const CommandHandlerStreamVersionConflictRetryOptions = {
|
|
|
2488
2404
|
shouldRetryError: isExpectedVersionConflictError
|
|
2489
2405
|
};
|
|
2490
2406
|
const CommandHandler = (options) => async (store, id, handle, handleOptions) => {
|
|
2491
|
-
const collector = commandHandlerCollector(
|
|
2407
|
+
const collector = commandHandlerCollector(commandObservability(options));
|
|
2492
2408
|
const streamName = (options.mapToStreamId ?? ((id) => id))(id);
|
|
2493
2409
|
const commandType = handleOptions?.commandType ?? options.commandType ?? options.name ?? handlerNames(handle);
|
|
2494
2410
|
const correlationId = handleOptions?.observability?.correlationId ?? (0, uuid.v7)();
|
|
@@ -2587,6 +2503,61 @@ const DeciderCommandHandler = (options) => async (eventStore, id, commands, hand
|
|
|
2587
2503
|
});
|
|
2588
2504
|
};
|
|
2589
2505
|
|
|
2506
|
+
//#endregion
|
|
2507
|
+
//#region src/consumers/observability/consumerCollector.ts
|
|
2508
|
+
const consumerObservability = (options, parent) => {
|
|
2509
|
+
const observability = mergeObservabilityOptions({ observability: options?.observability }, parent?.observability).observability;
|
|
2510
|
+
return {
|
|
2511
|
+
tracer: observability?.tracer ?? (0, _event_driven_io_almanac.noopTracer)(),
|
|
2512
|
+
meter: observability?.meter ?? (0, _event_driven_io_almanac.noopMeter)(),
|
|
2513
|
+
pollTracing: observability?.pollTracing ?? "off",
|
|
2514
|
+
attributeTarget: observability?.attributeTarget ?? "both"
|
|
2515
|
+
};
|
|
2516
|
+
};
|
|
2517
|
+
const consumerCollector = (observability) => {
|
|
2518
|
+
const { startScope } = (0, _event_driven_io_almanac.ObservabilityScope)({
|
|
2519
|
+
...observability,
|
|
2520
|
+
attributePrefix: "emmett"
|
|
2521
|
+
});
|
|
2522
|
+
const A = EmmettAttributes;
|
|
2523
|
+
const M = _event_driven_io_almanac.MessagingAttributes;
|
|
2524
|
+
const pollDuration = observability.meter.histogram(EmmettMetrics.consumer.pollDuration);
|
|
2525
|
+
const deliveryDuration = observability.meter.histogram(EmmettMetrics.consumer.deliveryDuration);
|
|
2526
|
+
return {
|
|
2527
|
+
tracePoll: (context, fn) => {
|
|
2528
|
+
if (observability.pollTracing === "off" || observability.pollTracing === "active" && context.batchSize === 0) return fn(_event_driven_io_almanac.noopScope);
|
|
2529
|
+
return startScope("consumer.poll", async (scope) => {
|
|
2530
|
+
scope.setAttributes({
|
|
2531
|
+
[A.scope.type]: ScopeTypes.consumer,
|
|
2532
|
+
[A.consumer.batchSize]: context.batchSize,
|
|
2533
|
+
[A.consumer.processorCount]: context.processorCount,
|
|
2534
|
+
[M.system]: MessagingSystemName,
|
|
2535
|
+
[M.operation.type]: "receive",
|
|
2536
|
+
...context.empty ? { "emmett.consumer.poll.empty": true } : {},
|
|
2537
|
+
...context.waitMs != null ? { "emmett.consumer.poll.wait_ms": context.waitMs } : {}
|
|
2538
|
+
});
|
|
2539
|
+
return fn(scope);
|
|
2540
|
+
});
|
|
2541
|
+
},
|
|
2542
|
+
recordPollMetrics: (durationMs, attrs) => {
|
|
2543
|
+
pollDuration.record(durationMs, attrs);
|
|
2544
|
+
},
|
|
2545
|
+
traceDelivery: (scope, processorId, fn) => {
|
|
2546
|
+
const start = Date.now();
|
|
2547
|
+
return scope.scope(`consumer.deliver.${processorId}`, async (child) => {
|
|
2548
|
+
try {
|
|
2549
|
+
return await fn();
|
|
2550
|
+
} catch (error) {
|
|
2551
|
+
if (error instanceof Error) child.log(_event_driven_io_almanac.LogEvent.error(error));
|
|
2552
|
+
throw error;
|
|
2553
|
+
} finally {
|
|
2554
|
+
deliveryDuration.record(Date.now() - start, { [A.consumer.delivery.processorId]: processorId });
|
|
2555
|
+
}
|
|
2556
|
+
}, { attributes: { [A.consumer.delivery.processorId]: processorId } });
|
|
2557
|
+
}
|
|
2558
|
+
};
|
|
2559
|
+
};
|
|
2560
|
+
|
|
2590
2561
|
//#endregion
|
|
2591
2562
|
//#region src/messageBus/index.ts
|
|
2592
2563
|
const getInMemoryMessageBus = () => {
|
|
@@ -2653,6 +2624,66 @@ const projections = {
|
|
|
2653
2624
|
async: asyncProjections
|
|
2654
2625
|
};
|
|
2655
2626
|
|
|
2627
|
+
//#endregion
|
|
2628
|
+
//#region src/workflows/observability/workflowCollector.ts
|
|
2629
|
+
const workflowObservability = (options, parent) => {
|
|
2630
|
+
const observability = mergeObservabilityOptions({ observability: options?.observability }, parent?.observability).observability;
|
|
2631
|
+
return {
|
|
2632
|
+
tracer: observability?.tracer ?? (0, _event_driven_io_almanac.noopTracer)(),
|
|
2633
|
+
meter: observability?.meter ?? (0, _event_driven_io_almanac.noopMeter)(),
|
|
2634
|
+
propagation: observability?.propagation ?? "links",
|
|
2635
|
+
attributeTarget: observability?.attributeTarget ?? "both",
|
|
2636
|
+
includeMessagePayloads: observability?.includeMessagePayloads ?? false
|
|
2637
|
+
};
|
|
2638
|
+
};
|
|
2639
|
+
const workflowCollector = (observability) => {
|
|
2640
|
+
const { startScope } = (0, _event_driven_io_almanac.ObservabilityScope)({
|
|
2641
|
+
...observability,
|
|
2642
|
+
attributePrefix: "emmett"
|
|
2643
|
+
});
|
|
2644
|
+
const A = EmmettAttributes;
|
|
2645
|
+
const M = _event_driven_io_almanac.MessagingAttributes;
|
|
2646
|
+
const processingDuration = observability.meter.histogram(EmmettMetrics.workflow.processingDuration);
|
|
2647
|
+
return {
|
|
2648
|
+
startScope: (context, fn) => {
|
|
2649
|
+
const start = Date.now();
|
|
2650
|
+
return startScope("workflow.handle", async (scope) => {
|
|
2651
|
+
scope.setAttributes({
|
|
2652
|
+
[A.scope.type]: ScopeTypes.workflow,
|
|
2653
|
+
[A.workflow.id]: context.workflowId,
|
|
2654
|
+
[A.workflow.type]: context.workflowType,
|
|
2655
|
+
[A.workflow.inputType]: context.inputType,
|
|
2656
|
+
[M.system]: MessagingSystemName
|
|
2657
|
+
});
|
|
2658
|
+
let status = "success";
|
|
2659
|
+
try {
|
|
2660
|
+
return await fn(scope);
|
|
2661
|
+
} catch (err) {
|
|
2662
|
+
status = "failure";
|
|
2663
|
+
throw err;
|
|
2664
|
+
} finally {
|
|
2665
|
+
processingDuration.record(Date.now() - start, {
|
|
2666
|
+
[A.workflow.type]: context.workflowType,
|
|
2667
|
+
status
|
|
2668
|
+
});
|
|
2669
|
+
}
|
|
2670
|
+
}, { attributes: {
|
|
2671
|
+
[A.scope.type]: ScopeTypes.workflow,
|
|
2672
|
+
[A.workflow.type]: context.workflowType
|
|
2673
|
+
} });
|
|
2674
|
+
},
|
|
2675
|
+
recordOutputs: (scope, outputs) => {
|
|
2676
|
+
scope.setAttributes({
|
|
2677
|
+
[A.workflow.outputs]: outputs.map((o) => o.type),
|
|
2678
|
+
[A.workflow.outputsCount]: outputs.length
|
|
2679
|
+
});
|
|
2680
|
+
},
|
|
2681
|
+
recordStateRebuild: (scope, eventCount) => {
|
|
2682
|
+
scope.setAttributes({ [A.workflow.stateRebuildEventCount]: eventCount });
|
|
2683
|
+
}
|
|
2684
|
+
};
|
|
2685
|
+
};
|
|
2686
|
+
|
|
2656
2687
|
//#endregion
|
|
2657
2688
|
//#region src/workflows/handleWorkflow.ts
|
|
2658
2689
|
const WorkflowHandlerStreamVersionConflictRetryOptions = {
|
|
@@ -2722,7 +2753,7 @@ const createWrappedEvolve = (evolve, workflowName, separateInputInboxFromProcess
|
|
|
2722
2753
|
};
|
|
2723
2754
|
const workflowStreamName = ({ workflowName, workflowId }) => `emt:workflow:${workflowName}:${workflowId}`;
|
|
2724
2755
|
const WorkflowHandler = (options) => async (store, message, handleOptions) => {
|
|
2725
|
-
const collector = workflowCollector(
|
|
2756
|
+
const collector = workflowCollector(workflowObservability(options));
|
|
2726
2757
|
const workflowType = options.workflow.name;
|
|
2727
2758
|
const inputType = message.type;
|
|
2728
2759
|
const workflowId = options.getWorkflowId(message) ?? "";
|
|
@@ -2890,6 +2921,8 @@ var src_exports = /* @__PURE__ */ require_plugins.__exportAll({
|
|
|
2890
2921
|
CommandHandlerStreamVersionConflictRetryOptions: () => CommandHandlerStreamVersionConflictRetryOptions,
|
|
2891
2922
|
ConcurrencyError: () => require_plugins.ConcurrencyError,
|
|
2892
2923
|
ConcurrencyInMemoryDatabaseError: () => require_plugins.ConcurrencyInMemoryDatabaseError,
|
|
2924
|
+
ConsumerStartPositions: () => ConsumerStartPositions,
|
|
2925
|
+
CurrentMessageProcessorPosition: () => CurrentMessageProcessorPosition,
|
|
2893
2926
|
DATABASE_REQUIRED_ERROR_MESSAGE: () => DATABASE_REQUIRED_ERROR_MESSAGE,
|
|
2894
2927
|
DeciderCommandHandler: () => DeciderCommandHandler,
|
|
2895
2928
|
DeciderSpecification: () => DeciderSpecification,
|
|
@@ -2908,8 +2941,6 @@ var src_exports = /* @__PURE__ */ require_plugins.__exportAll({
|
|
|
2908
2941
|
JSONReviver: () => JSONReviver,
|
|
2909
2942
|
JSONRevivers: () => JSONRevivers,
|
|
2910
2943
|
JSONSerializer: () => JSONSerializer,
|
|
2911
|
-
LogLevel: () => LogLevel,
|
|
2912
|
-
LogStyle: () => LogStyle,
|
|
2913
2944
|
MessageProcessor: () => MessageProcessor,
|
|
2914
2945
|
MessageProcessorType: () => MessageProcessorType,
|
|
2915
2946
|
MessagingSystemName: () => MessagingSystemName,
|
|
@@ -2917,6 +2948,7 @@ var src_exports = /* @__PURE__ */ require_plugins.__exportAll({
|
|
|
2917
2948
|
NoRetries: () => NoRetries,
|
|
2918
2949
|
NotFoundError: () => require_plugins.NotFoundError,
|
|
2919
2950
|
ProcessorCheckpoint: () => ProcessorCheckpoint,
|
|
2951
|
+
ProcessorStartPositions: () => ProcessorStartPositions,
|
|
2920
2952
|
STREAM_DOES_NOT_EXIST: () => STREAM_DOES_NOT_EXIST,
|
|
2921
2953
|
STREAM_EXISTS: () => STREAM_EXISTS,
|
|
2922
2954
|
ScopeTypes: () => ScopeTypes,
|
|
@@ -2965,6 +2997,7 @@ var src_exports = /* @__PURE__ */ require_plugins.__exportAll({
|
|
|
2965
2997
|
composeJSONReplacers: () => composeJSONReplacers,
|
|
2966
2998
|
composeJSONRevivers: () => composeJSONRevivers,
|
|
2967
2999
|
consumerCollector: () => consumerCollector,
|
|
3000
|
+
consumerObservability: () => consumerObservability,
|
|
2968
3001
|
deepEquals: () => deepEquals,
|
|
2969
3002
|
defaultProcessingMessageProcessingScope: () => defaultProcessingMessageProcessingScope,
|
|
2970
3003
|
defaultProcessorPartition: () => defaultProcessorPartition,
|
|
@@ -2975,9 +3008,11 @@ var src_exports = /* @__PURE__ */ require_plugins.__exportAll({
|
|
|
2975
3008
|
downcastRecordedMessage: () => downcastRecordedMessage,
|
|
2976
3009
|
downcastRecordedMessages: () => downcastRecordedMessages,
|
|
2977
3010
|
emmettPrefix: () => "emt",
|
|
3011
|
+
errorReplacer: () => errorReplacer,
|
|
2978
3012
|
event: () => event,
|
|
2979
3013
|
eventInStream: () => eventInStream,
|
|
2980
3014
|
eventStoreCollector: () => eventStoreCollector,
|
|
3015
|
+
eventStoreObservability: () => eventStoreObservability,
|
|
2981
3016
|
eventsInStream: () => eventsInStream,
|
|
2982
3017
|
expectInMemoryDocuments: () => expectInMemoryDocuments,
|
|
2983
3018
|
filterProjections: () => filterProjections,
|
|
@@ -3019,6 +3054,7 @@ var src_exports = /* @__PURE__ */ require_plugins.__exportAll({
|
|
|
3019
3054
|
jsonSerializer: () => jsonSerializer,
|
|
3020
3055
|
matchesExpectedVersion: () => matchesExpectedVersion,
|
|
3021
3056
|
merge: () => merge,
|
|
3057
|
+
mergeObservabilityOptions: () => mergeObservabilityOptions,
|
|
3022
3058
|
message: () => message,
|
|
3023
3059
|
newEventsInStream: () => newEventsInStream,
|
|
3024
3060
|
nulloSessionFactory: () => nulloSessionFactory,
|
|
@@ -3026,18 +3062,14 @@ var src_exports = /* @__PURE__ */ require_plugins.__exportAll({
|
|
|
3026
3062
|
parseBigIntProcessorCheckpoint: () => parseBigIntProcessorCheckpoint,
|
|
3027
3063
|
parseDateFromUtcYYYYMMDD: () => require_plugins.parseDateFromUtcYYYYMMDD,
|
|
3028
3064
|
processorCollector: () => processorCollector,
|
|
3065
|
+
processorObservability: () => processorObservability,
|
|
3029
3066
|
projection: () => projection,
|
|
3030
3067
|
projections: () => projections,
|
|
3031
3068
|
projector: () => projector,
|
|
3032
3069
|
reactor: () => reactor,
|
|
3033
3070
|
reduceAsync: () => reduceAsync,
|
|
3034
|
-
resolveConsumerObservability: () => resolveConsumerObservability,
|
|
3035
|
-
resolveEventStoreObservability: () => resolveEventStoreObservability,
|
|
3036
|
-
resolveProcessorObservability: () => resolveProcessorObservability,
|
|
3037
|
-
resolveWorkflowObservability: () => resolveWorkflowObservability,
|
|
3038
3071
|
sum: () => sum,
|
|
3039
3072
|
toNormalizedString: () => toNormalizedString,
|
|
3040
|
-
tracer: () => tracer,
|
|
3041
3073
|
tryPublishMessagesAfterCommit: () => tryPublishMessagesAfterCommit,
|
|
3042
3074
|
unknownTag: () => unknownTag,
|
|
3043
3075
|
upcastRecordedMessage: () => upcastRecordedMessage,
|
|
@@ -3045,6 +3077,7 @@ var src_exports = /* @__PURE__ */ require_plugins.__exportAll({
|
|
|
3045
3077
|
verifyThat: () => verifyThat,
|
|
3046
3078
|
wasMessageHandled: () => wasMessageHandled,
|
|
3047
3079
|
workflowCollector: () => workflowCollector,
|
|
3080
|
+
workflowObservability: () => workflowObservability,
|
|
3048
3081
|
workflowOutputHandler: () => workflowOutputHandler,
|
|
3049
3082
|
workflowProcessor: () => workflowProcessor,
|
|
3050
3083
|
workflowStreamName: () => workflowStreamName
|
|
@@ -3056,6 +3089,8 @@ exports.CommandHandler = CommandHandler;
|
|
|
3056
3089
|
exports.CommandHandlerStreamVersionConflictRetryOptions = CommandHandlerStreamVersionConflictRetryOptions;
|
|
3057
3090
|
exports.ConcurrencyError = require_plugins.ConcurrencyError;
|
|
3058
3091
|
exports.ConcurrencyInMemoryDatabaseError = require_plugins.ConcurrencyInMemoryDatabaseError;
|
|
3092
|
+
exports.ConsumerStartPositions = ConsumerStartPositions;
|
|
3093
|
+
exports.CurrentMessageProcessorPosition = CurrentMessageProcessorPosition;
|
|
3059
3094
|
exports.DATABASE_REQUIRED_ERROR_MESSAGE = DATABASE_REQUIRED_ERROR_MESSAGE;
|
|
3060
3095
|
exports.DeciderCommandHandler = DeciderCommandHandler;
|
|
3061
3096
|
exports.DeciderSpecification = DeciderSpecification;
|
|
@@ -3074,8 +3109,6 @@ exports.JSONReplacers = JSONReplacers;
|
|
|
3074
3109
|
exports.JSONReviver = JSONReviver;
|
|
3075
3110
|
exports.JSONRevivers = JSONRevivers;
|
|
3076
3111
|
exports.JSONSerializer = JSONSerializer;
|
|
3077
|
-
exports.LogLevel = LogLevel;
|
|
3078
|
-
exports.LogStyle = LogStyle;
|
|
3079
3112
|
exports.MessageProcessor = MessageProcessor;
|
|
3080
3113
|
exports.MessageProcessorType = MessageProcessorType;
|
|
3081
3114
|
exports.MessagingSystemName = MessagingSystemName;
|
|
@@ -3083,6 +3116,7 @@ exports.NO_CONCURRENCY_CHECK = NO_CONCURRENCY_CHECK;
|
|
|
3083
3116
|
exports.NoRetries = NoRetries;
|
|
3084
3117
|
exports.NotFoundError = require_plugins.NotFoundError;
|
|
3085
3118
|
exports.ProcessorCheckpoint = ProcessorCheckpoint;
|
|
3119
|
+
exports.ProcessorStartPositions = ProcessorStartPositions;
|
|
3086
3120
|
exports.STREAM_DOES_NOT_EXIST = STREAM_DOES_NOT_EXIST;
|
|
3087
3121
|
exports.STREAM_EXISTS = STREAM_EXISTS;
|
|
3088
3122
|
exports.ScopeTypes = ScopeTypes;
|
|
@@ -3131,6 +3165,7 @@ exports.command = command;
|
|
|
3131
3165
|
exports.composeJSONReplacers = composeJSONReplacers;
|
|
3132
3166
|
exports.composeJSONRevivers = composeJSONRevivers;
|
|
3133
3167
|
exports.consumerCollector = consumerCollector;
|
|
3168
|
+
exports.consumerObservability = consumerObservability;
|
|
3134
3169
|
exports.deepEquals = deepEquals;
|
|
3135
3170
|
exports.defaultProcessingMessageProcessingScope = defaultProcessingMessageProcessingScope;
|
|
3136
3171
|
exports.defaultProcessorPartition = defaultProcessorPartition;
|
|
@@ -3141,9 +3176,11 @@ exports.documentExists = documentExists;
|
|
|
3141
3176
|
exports.downcastRecordedMessage = downcastRecordedMessage;
|
|
3142
3177
|
exports.downcastRecordedMessages = downcastRecordedMessages;
|
|
3143
3178
|
exports.emmettPrefix = emmettPrefix;
|
|
3179
|
+
exports.errorReplacer = errorReplacer;
|
|
3144
3180
|
exports.event = event;
|
|
3145
3181
|
exports.eventInStream = eventInStream;
|
|
3146
3182
|
exports.eventStoreCollector = eventStoreCollector;
|
|
3183
|
+
exports.eventStoreObservability = eventStoreObservability;
|
|
3147
3184
|
exports.eventsInStream = eventsInStream;
|
|
3148
3185
|
exports.expectInMemoryDocuments = expectInMemoryDocuments;
|
|
3149
3186
|
exports.filterProjections = filterProjections;
|
|
@@ -3185,6 +3222,7 @@ exports.isValidYYYYMMDD = require_plugins.isValidYYYYMMDD;
|
|
|
3185
3222
|
exports.jsonSerializer = jsonSerializer;
|
|
3186
3223
|
exports.matchesExpectedVersion = matchesExpectedVersion;
|
|
3187
3224
|
exports.merge = merge;
|
|
3225
|
+
exports.mergeObservabilityOptions = mergeObservabilityOptions;
|
|
3188
3226
|
exports.message = message;
|
|
3189
3227
|
exports.newEventsInStream = newEventsInStream;
|
|
3190
3228
|
exports.nulloSessionFactory = nulloSessionFactory;
|
|
@@ -3192,18 +3230,14 @@ exports.onShutdown = onShutdown;
|
|
|
3192
3230
|
exports.parseBigIntProcessorCheckpoint = parseBigIntProcessorCheckpoint;
|
|
3193
3231
|
exports.parseDateFromUtcYYYYMMDD = require_plugins.parseDateFromUtcYYYYMMDD;
|
|
3194
3232
|
exports.processorCollector = processorCollector;
|
|
3233
|
+
exports.processorObservability = processorObservability;
|
|
3195
3234
|
exports.projection = projection;
|
|
3196
3235
|
exports.projections = projections;
|
|
3197
3236
|
exports.projector = projector;
|
|
3198
3237
|
exports.reactor = reactor;
|
|
3199
3238
|
exports.reduceAsync = reduceAsync;
|
|
3200
|
-
exports.resolveConsumerObservability = resolveConsumerObservability;
|
|
3201
|
-
exports.resolveEventStoreObservability = resolveEventStoreObservability;
|
|
3202
|
-
exports.resolveProcessorObservability = resolveProcessorObservability;
|
|
3203
|
-
exports.resolveWorkflowObservability = resolveWorkflowObservability;
|
|
3204
3239
|
exports.sum = sum;
|
|
3205
3240
|
exports.toNormalizedString = toNormalizedString;
|
|
3206
|
-
exports.tracer = tracer;
|
|
3207
3241
|
exports.tryPublishMessagesAfterCommit = tryPublishMessagesAfterCommit;
|
|
3208
3242
|
exports.unknownTag = unknownTag;
|
|
3209
3243
|
exports.upcastRecordedMessage = upcastRecordedMessage;
|
|
@@ -3211,6 +3245,7 @@ exports.upcastRecordedMessages = upcastRecordedMessages;
|
|
|
3211
3245
|
exports.verifyThat = verifyThat;
|
|
3212
3246
|
exports.wasMessageHandled = wasMessageHandled;
|
|
3213
3247
|
exports.workflowCollector = workflowCollector;
|
|
3248
|
+
exports.workflowObservability = workflowObservability;
|
|
3214
3249
|
exports.workflowOutputHandler = workflowOutputHandler;
|
|
3215
3250
|
exports.workflowProcessor = workflowProcessor;
|
|
3216
3251
|
exports.workflowStreamName = workflowStreamName;
|