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