@event-driven-io/emmett 0.43.0-beta.23 → 0.43.0-beta.24

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 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) => JSON.stringify(object, serializerOptions ? JSONReplacer(serializerOptions) : defaultReplacer),
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,15 @@ const getInMemoryDatabase = () => {
978
995
  } };
979
996
  };
980
997
 
998
+ //#endregion
999
+ //#region src/processors/checkpoints.ts
1000
+ const ProcessorCheckpoint = (checkpoint) => checkpoint;
1001
+ const bigIntProcessorCheckpoint = (value) => bigInt.toNormalizedString(value);
1002
+ const parseBigIntProcessorCheckpoint = (value) => BigInt(value);
1003
+ const getCheckpoint = (message) => {
1004
+ return message.metadata.checkpoint;
1005
+ };
1006
+
981
1007
  //#endregion
982
1008
  //#region src/observability/attributes.ts
983
1009
  const EmmettAttributes = {
@@ -1065,281 +1091,21 @@ const MessagingSystemName = "emmett";
1065
1091
 
1066
1092
  //#endregion
1067
1093
  //#region src/observability/options.ts
1068
- const resolveConsumerObservability = (options, parent) => ({
1069
- tracer: options?.observability?.tracer ?? parent?.observability?.tracer ?? (0, _event_driven_io_almanac.noopTracer)(),
1070
- meter: options?.observability?.meter ?? parent?.observability?.meter ?? (0, _event_driven_io_almanac.noopMeter)(),
1071
- pollTracing: options?.observability?.pollTracing ?? parent?.observability?.pollTracing ?? "off",
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
1094
+ const mergeObservabilityOptions = (options, defaults) => {
1095
+ const observability = defaults === void 0 ? options.observability : options.observability === void 0 ? defaults : {
1096
+ ...defaults,
1097
+ ...options.observability
1145
1098
  };
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);
1099
+ if (observability === options.observability) return options;
1169
1100
  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
- }
1278
- };
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);
1291
- return {
1292
- startScope: (context, fn) => {
1293
- const start = Date.now();
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
- }
1101
+ ...options,
1102
+ observability
1328
1103
  };
1329
1104
  };
1330
1105
 
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
1106
  //#endregion
1341
1107
  //#region src/processors/observability/processorCollector.ts
1342
- const resolveProcessorObservability = (options, parent) => ({
1108
+ const processorObservability = (options, parent) => ({
1343
1109
  tracer: options?.observability?.tracer ?? parent?.observability?.tracer ?? (0, _event_driven_io_almanac.noopTracer)(),
1344
1110
  meter: options?.observability?.meter ?? parent?.observability?.meter ?? (0, _event_driven_io_almanac.noopMeter)(),
1345
1111
  propagation: options?.observability?.propagation ?? parent?.observability?.propagation ?? "links",
@@ -1447,9 +1213,10 @@ const defaultProcessorVersion = 1;
1447
1213
  const defaultProcessorPartition = defaultTag;
1448
1214
  const getProcessorInstanceId = (processorId) => `${processorId}:${(0, uuid.v7)()}`;
1449
1215
  const getProjectorId = (options) => `emt:processor:projector:${options.projectionName}`;
1216
+ const { info, error } = _event_driven_io_almanac.LogEvent;
1450
1217
  const reactor = (options) => {
1451
1218
  const { checkpoints, processorId, processorInstanceId: instanceId = getProcessorInstanceId(processorId), type = MessageProcessorType.REACTOR, version = 1, partition = defaultProcessorPartition, hooks = {}, processingScope = defaultProcessingMessageProcessingScope, startFrom, canHandle, stopAfter } = options;
1452
- const collector = processorCollector(resolveProcessorObservability(options));
1219
+ const collector = processorCollector(processorObservability(options));
1453
1220
  const isCustomBatch = "eachBatch" in options && !!options.eachBatch;
1454
1221
  const eachBatch = isCustomBatch ? options.eachBatch : async (messages, context) => {
1455
1222
  const batchCtx = context.observabilityScope.spanContext();
@@ -1531,25 +1298,27 @@ const reactor = (options) => {
1531
1298
  ...partialOptions,
1532
1299
  observabilityScope: ("observabilityScope" in partialOptions ? partialOptions.observabilityScope ?? _event_driven_io_almanac.noopScope : _event_driven_io_almanac.noopScope) ?? _event_driven_io_almanac.noopScope
1533
1300
  };
1301
+ const log = startOptions.observabilityScope.log;
1534
1302
  if (isActive) {
1535
- console.log(`Processor ${processorId} with instance id ${instanceId} is already active. Start request ignored.`);
1303
+ log(info(`Processor ${processorId} with instance id ${instanceId} is already active. Start request ignored.`));
1536
1304
  return;
1537
1305
  }
1538
- console.log(`Starting processor ${processorId} with instance id ${instanceId}`);
1306
+ log(info(`Starting processor ${processorId} with instance id ${instanceId}`));
1539
1307
  await init(startOptions);
1540
1308
  isActive = true;
1541
1309
  closeSignal = onShutdown(() => close(startOptions));
1542
1310
  if (lastCheckpoint !== null) {
1543
- console.log(`Processor ${processorId} started with instance id ${instanceId}, checkpoint: ${JSONSerializer.serialize(lastCheckpoint)}`);
1311
+ log(info(`Processor ${processorId} started with instance id ${instanceId}, checkpoint: ${JSONSerializer.serialize(lastCheckpoint)}`));
1544
1312
  return { lastCheckpoint };
1545
1313
  }
1546
1314
  return await processingScope(async (context) => {
1315
+ const log = context.observabilityScope.log;
1547
1316
  if (hooks.onStart) {
1548
- console.log(`Executing onStart hook for processor ${processorId} with instance id ${instanceId}`);
1317
+ log(info(`Executing onStart hook for processor ${processorId} with instance id ${instanceId}`));
1549
1318
  await hooks.onStart(context);
1550
1319
  }
1551
1320
  if (startFrom && startFrom !== "CURRENT") {
1552
- console.log(`Processor ${processorId} with instance id ${instanceId} starting from: ${JSONSerializer.serialize(startFrom)}`);
1321
+ log(info(`Processor ${processorId} with instance id ${instanceId} starting from: ${JSONSerializer.serialize(startFrom)}`));
1553
1322
  return startFrom;
1554
1323
  }
1555
1324
  if (checkpoints) lastCheckpoint = (await checkpoints?.read({
@@ -1560,10 +1329,10 @@ const reactor = (options) => {
1560
1329
  ...context
1561
1330
  })).lastCheckpoint;
1562
1331
  if (lastCheckpoint === null) {
1563
- console.log(`Processor ${processorId} with instance id ${instanceId} starting from: BEGINNING`);
1332
+ log(info(`Processor ${processorId} with instance id ${instanceId} starting from: BEGINNING`));
1564
1333
  return "BEGINNING";
1565
1334
  }
1566
- console.log(`Checkpoint read for processor ${processorId} with instance id ${instanceId}: ${JSONSerializer.serialize(lastCheckpoint)}`);
1335
+ log(info(`Checkpoint read for processor ${processorId} with instance id ${instanceId}: ${JSONSerializer.serialize(lastCheckpoint)}`));
1567
1336
  return { lastCheckpoint };
1568
1337
  }, startOptions);
1569
1338
  },
@@ -1620,12 +1389,12 @@ const reactor = (options) => {
1620
1389
  ...partialContext,
1621
1390
  observabilityScope: scope
1622
1391
  });
1623
- } catch (error) {
1624
- console.log(`Error during message processing for processor ${processorId} with instance id ${instanceId}. Stopping the processor.`, error);
1392
+ } catch (err) {
1393
+ scope.log(error({ err }, `Error during message processing for processor ${processorId} with instance id ${instanceId}. Stopping the processor.`));
1625
1394
  isActive = false;
1626
1395
  return {
1627
1396
  type: "STOP",
1628
- error,
1397
+ error: err,
1629
1398
  reason: "Error during message processing"
1630
1399
  };
1631
1400
  }
@@ -2198,7 +1967,7 @@ const InMemoryProjectionSpec = { for: (options) => {
2198
1967
  return Promise.resolve(false);
2199
1968
  }
2200
1969
  },
2201
- observability: resolveEventStoreObservability(void 0)
1970
+ observability: eventStoreObservability(void 0)
2202
1971
  });
2203
1972
  };
2204
1973
  return {
@@ -2303,6 +2072,88 @@ const upcastRecordedMessages = (recordedMessages, options) => {
2303
2072
  return recordedMessages.map((recordedMessage) => upcastRecordedMessage(recordedMessage, options));
2304
2073
  };
2305
2074
 
2075
+ //#endregion
2076
+ //#region src/eventStore/observability/eventStoreCollector.ts
2077
+ const eventStoreObservability = (options, parent) => ({
2078
+ tracer: options?.observability?.tracer ?? parent?.observability?.tracer ?? (0, _event_driven_io_almanac.noopTracer)(),
2079
+ meter: options?.observability?.meter ?? parent?.observability?.meter ?? (0, _event_driven_io_almanac.noopMeter)(),
2080
+ attributeTarget: options?.observability?.attributeTarget ?? parent?.observability?.attributeTarget ?? "both"
2081
+ });
2082
+ const eventStoreCollector = (observability) => {
2083
+ const A = EmmettAttributes;
2084
+ const M = _event_driven_io_almanac.MessagingAttributes;
2085
+ const streamReadingDuration = observability.meter.histogram(EmmettMetrics.stream.readingDuration);
2086
+ const streamReadingSize = observability.meter.histogram(EmmettMetrics.stream.readingSize);
2087
+ const eventReadingCount = observability.meter.counter(EmmettMetrics.event.readingCount);
2088
+ const streamAppendingDuration = observability.meter.histogram(EmmettMetrics.stream.appendingDuration);
2089
+ const streamAppendingSize = observability.meter.histogram(EmmettMetrics.stream.appendingSize);
2090
+ const eventAppendingCount = observability.meter.counter(EmmettMetrics.event.appendingCount);
2091
+ return {
2092
+ instrumentRead: (streamName, fn) => {
2093
+ const start = Date.now();
2094
+ return observability.tracer.startSpan("eventStore.readStream", async (span) => {
2095
+ span.setAttributes({
2096
+ [A.eventStore.operation]: "readStream",
2097
+ [A.stream.name]: streamName,
2098
+ [M.operation.type]: "receive",
2099
+ [M.destination.name]: streamName,
2100
+ [M.system]: MessagingSystemName
2101
+ });
2102
+ let status = "success";
2103
+ try {
2104
+ const result = await fn();
2105
+ const events = result.events;
2106
+ span.setAttributes({
2107
+ [A.eventStore.read.status]: status,
2108
+ [A.eventStore.read.eventCount]: events.length,
2109
+ [A.eventStore.read.eventTypes]: [...new Set(events.map((e) => e.type))]
2110
+ });
2111
+ streamReadingSize.record(events.length, { [A.eventStore.read.status]: status });
2112
+ for (const event of events) eventReadingCount.add(1, { [A.event.type]: event.type });
2113
+ return result;
2114
+ } catch (err) {
2115
+ status = "failure";
2116
+ span.setAttributes({ [A.eventStore.read.status]: status });
2117
+ throw err;
2118
+ } finally {
2119
+ streamReadingDuration.record(Date.now() - start, { [A.eventStore.read.status]: status });
2120
+ }
2121
+ });
2122
+ },
2123
+ instrumentAppend: (streamName, events, fn) => {
2124
+ const start = Date.now();
2125
+ return observability.tracer.startSpan("eventStore.appendToStream", async (span) => {
2126
+ span.setAttributes({
2127
+ [A.eventStore.operation]: "appendToStream",
2128
+ [A.stream.name]: streamName,
2129
+ [A.eventStore.append.batchSize]: events.length,
2130
+ [M.operation.type]: "send",
2131
+ [M.batch.messageCount]: events.length,
2132
+ [M.destination.name]: streamName,
2133
+ [M.system]: MessagingSystemName
2134
+ });
2135
+ let status = "success";
2136
+ try {
2137
+ const result = await fn();
2138
+ span.setAttributes({
2139
+ [A.eventStore.append.status]: status,
2140
+ [A.stream.versionAfter]: Number(result.nextExpectedStreamVersion)
2141
+ });
2142
+ streamAppendingSize.record(events.length, { [A.eventStore.append.status]: status });
2143
+ for (const event of events) eventAppendingCount.add(1, { [A.event.type]: event.type });
2144
+ return result;
2145
+ } catch (err) {
2146
+ status = "failure";
2147
+ span.setAttributes({ [A.eventStore.append.status]: status });
2148
+ throw err;
2149
+ } finally {
2150
+ streamAppendingDuration.record(Date.now() - start, { [A.eventStore.append.status]: status });
2151
+ }
2152
+ });
2153
+ }
2154
+ };
2155
+ };
2156
+
2306
2157
  //#endregion
2307
2158
  //#region src/eventStore/inMemoryEventStore.ts
2308
2159
  const InMemoryEventStoreDefaultStreamVersion = 0n;
@@ -2313,7 +2164,7 @@ const getInMemoryEventStore = (eventStoreOptions) => {
2313
2164
  };
2314
2165
  const database = eventStoreOptions?.database || getInMemoryDatabase();
2315
2166
  const inlineProjections = (eventStoreOptions?.projections ?? []).filter(({ type }) => type === "inline").map(({ projection }) => projection);
2316
- const observability = resolveEventStoreObservability(eventStoreOptions);
2167
+ const observability = eventStoreObservability(eventStoreOptions);
2317
2168
  const collector = eventStoreCollector(observability);
2318
2169
  const eventStore = {
2319
2170
  database,
@@ -2395,7 +2246,7 @@ const getInMemoryEventStore = (eventStoreOptions) => {
2395
2246
 
2396
2247
  //#endregion
2397
2248
  //#region src/commandHandling/observability/commandHandlerCollector.ts
2398
- const resolveCommandObservability = (options, parent) => ({
2249
+ const commandObservability = (options, parent) => ({
2399
2250
  tracer: options?.observability?.tracer ?? parent?.observability?.tracer ?? (0, _event_driven_io_almanac.noopTracer)(),
2400
2251
  meter: options?.observability?.meter ?? parent?.observability?.meter ?? (0, _event_driven_io_almanac.noopMeter)(),
2401
2252
  attributeTarget: options?.observability?.attributeTarget ?? parent?.observability?.attributeTarget ?? "both",
@@ -2439,7 +2290,7 @@ const commandHandlerCollector = (observability) => {
2439
2290
  "exception.message": err instanceof Error ? err.message : String(err),
2440
2291
  "exception.type": err instanceof Error ? err.constructor.name : "unknown"
2441
2292
  });
2442
- scope.recordException(err instanceof Error ? err : new Error(String(err)));
2293
+ scope.log(_event_driven_io_almanac.LogEvent.error(err instanceof Error ? err : new Error(String(err))));
2443
2294
  throw err;
2444
2295
  } finally {
2445
2296
  commandHandlingDuration.record(Date.now() - start, {
@@ -2488,7 +2339,7 @@ const CommandHandlerStreamVersionConflictRetryOptions = {
2488
2339
  shouldRetryError: isExpectedVersionConflictError
2489
2340
  };
2490
2341
  const CommandHandler = (options) => async (store, id, handle, handleOptions) => {
2491
- const collector = commandHandlerCollector(resolveCommandObservability(options));
2342
+ const collector = commandHandlerCollector(commandObservability(options));
2492
2343
  const streamName = (options.mapToStreamId ?? ((id) => id))(id);
2493
2344
  const commandType = handleOptions?.commandType ?? options.commandType ?? options.name ?? handlerNames(handle);
2494
2345
  const correlationId = handleOptions?.observability?.correlationId ?? (0, uuid.v7)();
@@ -2587,6 +2438,61 @@ const DeciderCommandHandler = (options) => async (eventStore, id, commands, hand
2587
2438
  });
2588
2439
  };
2589
2440
 
2441
+ //#endregion
2442
+ //#region src/consumers/observability/consumerCollector.ts
2443
+ const consumerObservability = (options, parent) => {
2444
+ const observability = mergeObservabilityOptions({ observability: options?.observability }, parent?.observability).observability;
2445
+ return {
2446
+ tracer: observability?.tracer ?? (0, _event_driven_io_almanac.noopTracer)(),
2447
+ meter: observability?.meter ?? (0, _event_driven_io_almanac.noopMeter)(),
2448
+ pollTracing: observability?.pollTracing ?? "off",
2449
+ attributeTarget: observability?.attributeTarget ?? "both"
2450
+ };
2451
+ };
2452
+ const consumerCollector = (observability) => {
2453
+ const { startScope } = (0, _event_driven_io_almanac.ObservabilityScope)({
2454
+ ...observability,
2455
+ attributePrefix: "emmett"
2456
+ });
2457
+ const A = EmmettAttributes;
2458
+ const M = _event_driven_io_almanac.MessagingAttributes;
2459
+ const pollDuration = observability.meter.histogram(EmmettMetrics.consumer.pollDuration);
2460
+ const deliveryDuration = observability.meter.histogram(EmmettMetrics.consumer.deliveryDuration);
2461
+ return {
2462
+ tracePoll: (context, fn) => {
2463
+ if (observability.pollTracing === "off" || observability.pollTracing === "active" && context.batchSize === 0) return fn(_event_driven_io_almanac.noopScope);
2464
+ return startScope("consumer.poll", async (scope) => {
2465
+ scope.setAttributes({
2466
+ [A.scope.type]: ScopeTypes.consumer,
2467
+ [A.consumer.batchSize]: context.batchSize,
2468
+ [A.consumer.processorCount]: context.processorCount,
2469
+ [M.system]: MessagingSystemName,
2470
+ [M.operation.type]: "receive",
2471
+ ...context.empty ? { "emmett.consumer.poll.empty": true } : {},
2472
+ ...context.waitMs != null ? { "emmett.consumer.poll.wait_ms": context.waitMs } : {}
2473
+ });
2474
+ return fn(scope);
2475
+ });
2476
+ },
2477
+ recordPollMetrics: (durationMs, attrs) => {
2478
+ pollDuration.record(durationMs, attrs);
2479
+ },
2480
+ traceDelivery: (scope, processorId, fn) => {
2481
+ const start = Date.now();
2482
+ return scope.scope(`consumer.deliver.${processorId}`, async (child) => {
2483
+ try {
2484
+ return await fn();
2485
+ } catch (error) {
2486
+ if (error instanceof Error) child.log(_event_driven_io_almanac.LogEvent.error(error));
2487
+ throw error;
2488
+ } finally {
2489
+ deliveryDuration.record(Date.now() - start, { [A.consumer.delivery.processorId]: processorId });
2490
+ }
2491
+ }, { attributes: { [A.consumer.delivery.processorId]: processorId } });
2492
+ }
2493
+ };
2494
+ };
2495
+
2590
2496
  //#endregion
2591
2497
  //#region src/messageBus/index.ts
2592
2498
  const getInMemoryMessageBus = () => {
@@ -2653,6 +2559,66 @@ const projections = {
2653
2559
  async: asyncProjections
2654
2560
  };
2655
2561
 
2562
+ //#endregion
2563
+ //#region src/workflows/observability/workflowCollector.ts
2564
+ const workflowObservability = (options, parent) => {
2565
+ const observability = mergeObservabilityOptions({ observability: options?.observability }, parent?.observability).observability;
2566
+ return {
2567
+ tracer: observability?.tracer ?? (0, _event_driven_io_almanac.noopTracer)(),
2568
+ meter: observability?.meter ?? (0, _event_driven_io_almanac.noopMeter)(),
2569
+ propagation: observability?.propagation ?? "links",
2570
+ attributeTarget: observability?.attributeTarget ?? "both",
2571
+ includeMessagePayloads: observability?.includeMessagePayloads ?? false
2572
+ };
2573
+ };
2574
+ const workflowCollector = (observability) => {
2575
+ const { startScope } = (0, _event_driven_io_almanac.ObservabilityScope)({
2576
+ ...observability,
2577
+ attributePrefix: "emmett"
2578
+ });
2579
+ const A = EmmettAttributes;
2580
+ const M = _event_driven_io_almanac.MessagingAttributes;
2581
+ const processingDuration = observability.meter.histogram(EmmettMetrics.workflow.processingDuration);
2582
+ return {
2583
+ startScope: (context, fn) => {
2584
+ const start = Date.now();
2585
+ return startScope("workflow.handle", async (scope) => {
2586
+ scope.setAttributes({
2587
+ [A.scope.type]: ScopeTypes.workflow,
2588
+ [A.workflow.id]: context.workflowId,
2589
+ [A.workflow.type]: context.workflowType,
2590
+ [A.workflow.inputType]: context.inputType,
2591
+ [M.system]: MessagingSystemName
2592
+ });
2593
+ let status = "success";
2594
+ try {
2595
+ return await fn(scope);
2596
+ } catch (err) {
2597
+ status = "failure";
2598
+ throw err;
2599
+ } finally {
2600
+ processingDuration.record(Date.now() - start, {
2601
+ [A.workflow.type]: context.workflowType,
2602
+ status
2603
+ });
2604
+ }
2605
+ }, { attributes: {
2606
+ [A.scope.type]: ScopeTypes.workflow,
2607
+ [A.workflow.type]: context.workflowType
2608
+ } });
2609
+ },
2610
+ recordOutputs: (scope, outputs) => {
2611
+ scope.setAttributes({
2612
+ [A.workflow.outputs]: outputs.map((o) => o.type),
2613
+ [A.workflow.outputsCount]: outputs.length
2614
+ });
2615
+ },
2616
+ recordStateRebuild: (scope, eventCount) => {
2617
+ scope.setAttributes({ [A.workflow.stateRebuildEventCount]: eventCount });
2618
+ }
2619
+ };
2620
+ };
2621
+
2656
2622
  //#endregion
2657
2623
  //#region src/workflows/handleWorkflow.ts
2658
2624
  const WorkflowHandlerStreamVersionConflictRetryOptions = {
@@ -2722,7 +2688,7 @@ const createWrappedEvolve = (evolve, workflowName, separateInputInboxFromProcess
2722
2688
  };
2723
2689
  const workflowStreamName = ({ workflowName, workflowId }) => `emt:workflow:${workflowName}:${workflowId}`;
2724
2690
  const WorkflowHandler = (options) => async (store, message, handleOptions) => {
2725
- const collector = workflowCollector(resolveWorkflowObservability(options));
2691
+ const collector = workflowCollector(workflowObservability(options));
2726
2692
  const workflowType = options.workflow.name;
2727
2693
  const inputType = message.type;
2728
2694
  const workflowId = options.getWorkflowId(message) ?? "";
@@ -2908,8 +2874,6 @@ var src_exports = /* @__PURE__ */ require_plugins.__exportAll({
2908
2874
  JSONReviver: () => JSONReviver,
2909
2875
  JSONRevivers: () => JSONRevivers,
2910
2876
  JSONSerializer: () => JSONSerializer,
2911
- LogLevel: () => LogLevel,
2912
- LogStyle: () => LogStyle,
2913
2877
  MessageProcessor: () => MessageProcessor,
2914
2878
  MessageProcessorType: () => MessageProcessorType,
2915
2879
  MessagingSystemName: () => MessagingSystemName,
@@ -2965,6 +2929,7 @@ var src_exports = /* @__PURE__ */ require_plugins.__exportAll({
2965
2929
  composeJSONReplacers: () => composeJSONReplacers,
2966
2930
  composeJSONRevivers: () => composeJSONRevivers,
2967
2931
  consumerCollector: () => consumerCollector,
2932
+ consumerObservability: () => consumerObservability,
2968
2933
  deepEquals: () => deepEquals,
2969
2934
  defaultProcessingMessageProcessingScope: () => defaultProcessingMessageProcessingScope,
2970
2935
  defaultProcessorPartition: () => defaultProcessorPartition,
@@ -2975,9 +2940,11 @@ var src_exports = /* @__PURE__ */ require_plugins.__exportAll({
2975
2940
  downcastRecordedMessage: () => downcastRecordedMessage,
2976
2941
  downcastRecordedMessages: () => downcastRecordedMessages,
2977
2942
  emmettPrefix: () => "emt",
2943
+ errorReplacer: () => errorReplacer,
2978
2944
  event: () => event,
2979
2945
  eventInStream: () => eventInStream,
2980
2946
  eventStoreCollector: () => eventStoreCollector,
2947
+ eventStoreObservability: () => eventStoreObservability,
2981
2948
  eventsInStream: () => eventsInStream,
2982
2949
  expectInMemoryDocuments: () => expectInMemoryDocuments,
2983
2950
  filterProjections: () => filterProjections,
@@ -3019,6 +2986,7 @@ var src_exports = /* @__PURE__ */ require_plugins.__exportAll({
3019
2986
  jsonSerializer: () => jsonSerializer,
3020
2987
  matchesExpectedVersion: () => matchesExpectedVersion,
3021
2988
  merge: () => merge,
2989
+ mergeObservabilityOptions: () => mergeObservabilityOptions,
3022
2990
  message: () => message,
3023
2991
  newEventsInStream: () => newEventsInStream,
3024
2992
  nulloSessionFactory: () => nulloSessionFactory,
@@ -3026,18 +2994,14 @@ var src_exports = /* @__PURE__ */ require_plugins.__exportAll({
3026
2994
  parseBigIntProcessorCheckpoint: () => parseBigIntProcessorCheckpoint,
3027
2995
  parseDateFromUtcYYYYMMDD: () => require_plugins.parseDateFromUtcYYYYMMDD,
3028
2996
  processorCollector: () => processorCollector,
2997
+ processorObservability: () => processorObservability,
3029
2998
  projection: () => projection,
3030
2999
  projections: () => projections,
3031
3000
  projector: () => projector,
3032
3001
  reactor: () => reactor,
3033
3002
  reduceAsync: () => reduceAsync,
3034
- resolveConsumerObservability: () => resolveConsumerObservability,
3035
- resolveEventStoreObservability: () => resolveEventStoreObservability,
3036
- resolveProcessorObservability: () => resolveProcessorObservability,
3037
- resolveWorkflowObservability: () => resolveWorkflowObservability,
3038
3003
  sum: () => sum,
3039
3004
  toNormalizedString: () => toNormalizedString,
3040
- tracer: () => tracer,
3041
3005
  tryPublishMessagesAfterCommit: () => tryPublishMessagesAfterCommit,
3042
3006
  unknownTag: () => unknownTag,
3043
3007
  upcastRecordedMessage: () => upcastRecordedMessage,
@@ -3045,6 +3009,7 @@ var src_exports = /* @__PURE__ */ require_plugins.__exportAll({
3045
3009
  verifyThat: () => verifyThat,
3046
3010
  wasMessageHandled: () => wasMessageHandled,
3047
3011
  workflowCollector: () => workflowCollector,
3012
+ workflowObservability: () => workflowObservability,
3048
3013
  workflowOutputHandler: () => workflowOutputHandler,
3049
3014
  workflowProcessor: () => workflowProcessor,
3050
3015
  workflowStreamName: () => workflowStreamName
@@ -3074,8 +3039,6 @@ exports.JSONReplacers = JSONReplacers;
3074
3039
  exports.JSONReviver = JSONReviver;
3075
3040
  exports.JSONRevivers = JSONRevivers;
3076
3041
  exports.JSONSerializer = JSONSerializer;
3077
- exports.LogLevel = LogLevel;
3078
- exports.LogStyle = LogStyle;
3079
3042
  exports.MessageProcessor = MessageProcessor;
3080
3043
  exports.MessageProcessorType = MessageProcessorType;
3081
3044
  exports.MessagingSystemName = MessagingSystemName;
@@ -3131,6 +3094,7 @@ exports.command = command;
3131
3094
  exports.composeJSONReplacers = composeJSONReplacers;
3132
3095
  exports.composeJSONRevivers = composeJSONRevivers;
3133
3096
  exports.consumerCollector = consumerCollector;
3097
+ exports.consumerObservability = consumerObservability;
3134
3098
  exports.deepEquals = deepEquals;
3135
3099
  exports.defaultProcessingMessageProcessingScope = defaultProcessingMessageProcessingScope;
3136
3100
  exports.defaultProcessorPartition = defaultProcessorPartition;
@@ -3141,9 +3105,11 @@ exports.documentExists = documentExists;
3141
3105
  exports.downcastRecordedMessage = downcastRecordedMessage;
3142
3106
  exports.downcastRecordedMessages = downcastRecordedMessages;
3143
3107
  exports.emmettPrefix = emmettPrefix;
3108
+ exports.errorReplacer = errorReplacer;
3144
3109
  exports.event = event;
3145
3110
  exports.eventInStream = eventInStream;
3146
3111
  exports.eventStoreCollector = eventStoreCollector;
3112
+ exports.eventStoreObservability = eventStoreObservability;
3147
3113
  exports.eventsInStream = eventsInStream;
3148
3114
  exports.expectInMemoryDocuments = expectInMemoryDocuments;
3149
3115
  exports.filterProjections = filterProjections;
@@ -3185,6 +3151,7 @@ exports.isValidYYYYMMDD = require_plugins.isValidYYYYMMDD;
3185
3151
  exports.jsonSerializer = jsonSerializer;
3186
3152
  exports.matchesExpectedVersion = matchesExpectedVersion;
3187
3153
  exports.merge = merge;
3154
+ exports.mergeObservabilityOptions = mergeObservabilityOptions;
3188
3155
  exports.message = message;
3189
3156
  exports.newEventsInStream = newEventsInStream;
3190
3157
  exports.nulloSessionFactory = nulloSessionFactory;
@@ -3192,18 +3159,14 @@ exports.onShutdown = onShutdown;
3192
3159
  exports.parseBigIntProcessorCheckpoint = parseBigIntProcessorCheckpoint;
3193
3160
  exports.parseDateFromUtcYYYYMMDD = require_plugins.parseDateFromUtcYYYYMMDD;
3194
3161
  exports.processorCollector = processorCollector;
3162
+ exports.processorObservability = processorObservability;
3195
3163
  exports.projection = projection;
3196
3164
  exports.projections = projections;
3197
3165
  exports.projector = projector;
3198
3166
  exports.reactor = reactor;
3199
3167
  exports.reduceAsync = reduceAsync;
3200
- exports.resolveConsumerObservability = resolveConsumerObservability;
3201
- exports.resolveEventStoreObservability = resolveEventStoreObservability;
3202
- exports.resolveProcessorObservability = resolveProcessorObservability;
3203
- exports.resolveWorkflowObservability = resolveWorkflowObservability;
3204
3168
  exports.sum = sum;
3205
3169
  exports.toNormalizedString = toNormalizedString;
3206
- exports.tracer = tracer;
3207
3170
  exports.tryPublishMessagesAfterCommit = tryPublishMessagesAfterCommit;
3208
3171
  exports.unknownTag = unknownTag;
3209
3172
  exports.upcastRecordedMessage = upcastRecordedMessage;
@@ -3211,6 +3174,7 @@ exports.upcastRecordedMessages = upcastRecordedMessages;
3211
3174
  exports.verifyThat = verifyThat;
3212
3175
  exports.wasMessageHandled = wasMessageHandled;
3213
3176
  exports.workflowCollector = workflowCollector;
3177
+ exports.workflowObservability = workflowObservability;
3214
3178
  exports.workflowOutputHandler = workflowOutputHandler;
3215
3179
  exports.workflowProcessor = workflowProcessor;
3216
3180
  exports.workflowStreamName = workflowStreamName;