@cratis/chronicle 6.32.1 → 6.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ChronicleClient.ts +35 -33
- package/ChronicleOptions.ts +27 -1
- package/EventStore.ts +14 -12
- package/Metrics.ts +70 -96
- package/Tracing.ts +4 -2
- package/WellKnownTelemetryNames.ts +67 -0
- package/artifacts/validateArtifactSchemas.spec.ts +1 -1
- package/artifacts/withActivatedArtifact.ts +7 -5
- package/connection/ChronicleConnection.ts +18 -6
- package/connection/CompatibilityPreflight.ts +2 -1
- package/connection/KernelKeepAlive.ts +13 -7
- package/connection/TokenProvider.ts +3 -1
- package/connection/TraceContextMiddleware.ts +30 -0
- package/connection/for_ChronicleConnection/when_creating_clients/with_correlated_multi_host_callers.spec.ts +73 -0
- package/connection/for_OAuthTokenProvider/when_acquiring_tokens/with_http_instrumentation.spec.ts +102 -0
- package/connection/for_TraceContextMiddleware/when_calling/with_active_context.spec.ts +235 -0
- package/connection/for_TraceContextMiddleware/when_calling/with_grpc_instrumentation.spec.ts +102 -0
- package/correlation/CorrelationIdManager.ts +10 -0
- package/dist/ChronicleClient.d.ts.map +1 -1
- package/dist/ChronicleClient.js +35 -33
- package/dist/ChronicleClient.js.map +1 -1
- package/dist/ChronicleOptions.d.ts +8 -0
- package/dist/ChronicleOptions.d.ts.map +1 -1
- package/dist/ChronicleOptions.js +19 -1
- package/dist/ChronicleOptions.js.map +1 -1
- package/dist/EventStore.d.ts +3 -1
- package/dist/EventStore.d.ts.map +1 -1
- package/dist/EventStore.js +14 -12
- package/dist/EventStore.js.map +1 -1
- package/dist/Metrics.d.ts +23 -64
- package/dist/Metrics.d.ts.map +1 -1
- package/dist/Metrics.js +62 -92
- package/dist/Metrics.js.map +1 -1
- package/dist/Tracing.d.ts +1 -1
- package/dist/Tracing.d.ts.map +1 -1
- package/dist/Tracing.js +4 -2
- package/dist/Tracing.js.map +1 -1
- package/dist/WellKnownTelemetryNames.d.ts +65 -0
- package/dist/WellKnownTelemetryNames.d.ts.map +1 -0
- package/dist/WellKnownTelemetryNames.js +67 -0
- package/dist/WellKnownTelemetryNames.js.map +1 -0
- package/dist/artifacts/withActivatedArtifact.d.ts +2 -1
- package/dist/artifacts/withActivatedArtifact.d.ts.map +1 -1
- package/dist/artifacts/withActivatedArtifact.js +5 -4
- package/dist/artifacts/withActivatedArtifact.js.map +1 -1
- package/dist/connection/ChronicleConnection.d.ts +5 -0
- package/dist/connection/ChronicleConnection.d.ts.map +1 -1
- package/dist/connection/ChronicleConnection.js +13 -6
- package/dist/connection/ChronicleConnection.js.map +1 -1
- package/dist/connection/CompatibilityPreflight.d.ts.map +1 -1
- package/dist/connection/CompatibilityPreflight.js +2 -1
- package/dist/connection/CompatibilityPreflight.js.map +1 -1
- package/dist/connection/KernelKeepAlive.d.ts +3 -1
- package/dist/connection/KernelKeepAlive.d.ts.map +1 -1
- package/dist/connection/KernelKeepAlive.js +10 -7
- package/dist/connection/KernelKeepAlive.js.map +1 -1
- package/dist/connection/TokenProvider.d.ts.map +1 -1
- package/dist/connection/TokenProvider.js +3 -1
- package/dist/connection/TokenProvider.js.map +1 -1
- package/dist/connection/TraceContextMiddleware.d.ts +4 -0
- package/dist/connection/TraceContextMiddleware.d.ts.map +1 -0
- package/dist/connection/TraceContextMiddleware.js +30 -0
- package/dist/connection/TraceContextMiddleware.js.map +1 -0
- package/dist/correlation/CorrelationIdManager.d.ts +4 -0
- package/dist/correlation/CorrelationIdManager.d.ts.map +1 -1
- package/dist/correlation/CorrelationIdManager.js +8 -0
- package/dist/correlation/CorrelationIdManager.js.map +1 -1
- package/dist/eventSequences/EventLog.d.ts +2 -1
- package/dist/eventSequences/EventLog.d.ts.map +1 -1
- package/dist/eventSequences/EventLog.js +2 -2
- package/dist/eventSequences/EventLog.js.map +1 -1
- package/dist/eventSequences/EventSequence.d.ts +4 -1
- package/dist/eventSequences/EventSequence.d.ts.map +1 -1
- package/dist/eventSequences/EventSequence.js +63 -80
- package/dist/eventSequences/EventSequence.js.map +1 -1
- package/dist/eventStoreSubscriptions/EventStoreSubscriptions.d.ts.map +1 -1
- package/dist/eventStoreSubscriptions/EventStoreSubscriptions.js +3 -4
- package/dist/eventStoreSubscriptions/EventStoreSubscriptions.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/logging/ChronicleLogEntry.d.ts +11 -0
- package/dist/logging/ChronicleLogEntry.d.ts.map +1 -0
- package/dist/logging/ChronicleLogEntry.js +4 -0
- package/dist/logging/ChronicleLogEntry.js.map +1 -0
- package/dist/logging/ChronicleLogLevel.d.ts +9 -0
- package/dist/logging/ChronicleLogLevel.d.ts.map +1 -0
- package/dist/logging/ChronicleLogLevel.js +12 -0
- package/dist/logging/ChronicleLogLevel.js.map +1 -0
- package/dist/logging/DiagChronicleLogger.d.ts +7 -0
- package/dist/logging/DiagChronicleLogger.d.ts.map +1 -0
- package/dist/logging/DiagChronicleLogger.js +10 -0
- package/dist/logging/DiagChronicleLogger.js.map +1 -0
- package/dist/logging/IChronicleLogger.d.ts +7 -0
- package/dist/logging/IChronicleLogger.d.ts.map +1 -0
- package/dist/logging/IChronicleLogger.js +4 -0
- package/dist/logging/IChronicleLogger.js.map +1 -0
- package/dist/logging/createLogger.d.ts +10 -0
- package/dist/logging/createLogger.d.ts.map +1 -0
- package/dist/logging/createLogger.js +55 -0
- package/dist/logging/createLogger.js.map +1 -0
- package/dist/projections/Projections.d.ts.map +1 -1
- package/dist/projections/Projections.js +4 -3
- package/dist/projections/Projections.js.map +1 -1
- package/dist/reactors/Reactors.d.ts.map +1 -1
- package/dist/reactors/Reactors.js +26 -24
- package/dist/reactors/Reactors.js.map +1 -1
- package/dist/readModels/IReadModels.d.ts +14 -0
- package/dist/readModels/IReadModels.d.ts.map +1 -1
- package/dist/readModels/ReadModels.d.ts +6 -0
- package/dist/readModels/ReadModels.d.ts.map +1 -1
- package/dist/readModels/ReadModels.js +110 -0
- package/dist/readModels/ReadModels.js.map +1 -1
- package/dist/readModels/for_ReadModels/given/a_stored_read_model.d.ts +20 -0
- package/dist/readModels/for_ReadModels/given/a_stored_read_model.d.ts.map +1 -0
- package/dist/readModels/for_ReadModels/given/a_stored_read_model.js +79 -0
- package/dist/readModels/for_ReadModels/given/a_stored_read_model.js.map +1 -0
- package/dist/reducers/Reducers.d.ts.map +1 -1
- package/dist/reducers/Reducers.js +28 -25
- package/dist/reducers/Reducers.js.map +1 -1
- package/dist/seeding/EventSeeding.d.ts.map +1 -1
- package/dist/seeding/EventSeeding.js +4 -3
- package/dist/seeding/EventSeeding.js.map +1 -1
- package/dist/telemetry/ChronicleTelemetryOptions.d.ts +11 -0
- package/dist/telemetry/ChronicleTelemetryOptions.d.ts.map +1 -0
- package/dist/telemetry/ChronicleTelemetryOptions.js +4 -0
- package/dist/telemetry/ChronicleTelemetryOptions.js.map +1 -0
- package/dist/telemetry/observeOperation.d.ts +6 -0
- package/dist/telemetry/observeOperation.d.ts.map +1 -0
- package/dist/telemetry/observeOperation.js +23 -0
- package/dist/telemetry/observeOperation.js.map +1 -0
- package/dist/telemetry/runInBackgroundContext.d.ts +3 -0
- package/dist/telemetry/runInBackgroundContext.d.ts.map +1 -0
- package/dist/telemetry/runInBackgroundContext.js +9 -0
- package/dist/telemetry/runInBackgroundContext.js.map +1 -0
- package/dist/telemetry/spanAttributes.d.ts +14 -0
- package/dist/telemetry/spanAttributes.d.ts.map +1 -0
- package/dist/telemetry/spanAttributes.js +47 -0
- package/dist/telemetry/spanAttributes.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/eventSequences/EventLog.ts +4 -2
- package/eventSequences/EventSequence.ts +64 -81
- package/eventStoreSubscriptions/EventStoreSubscriptions.ts +5 -5
- package/for_ChronicleClient/when_starting_background_work/with_a_correlated_caller.spec.ts +150 -0
- package/for_ChronicleOptions/when_configuring_telemetry/with_event_source_identifiers.spec.ts +44 -0
- package/index.ts +7 -1
- package/logging/ChronicleLogEntry.ts +14 -0
- package/logging/ChronicleLogLevel.ts +11 -0
- package/logging/DiagChronicleLogger.ts +13 -0
- package/logging/IChronicleLogger.ts +10 -0
- package/logging/createLogger.ts +50 -0
- package/logging/for_ChronicleLogger/when_logging/with_client_owned_sinks.spec.ts +96 -0
- package/package.json +7 -1
- package/projections/Projections.ts +4 -3
- package/reactors/Reactors.ts +27 -25
- package/reactors/for_Reactors/when_delivering/with_activation.spec.ts +14 -14
- package/readModels/IReadModels.ts +15 -0
- package/readModels/ReadModels.ts +118 -1
- package/readModels/for_ReadModels/given/a_stored_read_model.ts +62 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_a_default_subject.spec.ts +39 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_a_frozen_document.spec.ts +28 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_a_malformed_response.spec.ts +24 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_a_reported_failure.spec.ts +38 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_an_empty_released_value.spec.ts +19 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_an_unknown_subject_property.spec.ts +42 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_bookkeeping_fields.spec.ts +27 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_conflicting_or_missing_properties.spec.ts +32 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_empty_subject_metadata.spec.ts +56 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_invalid_subject_metadata.spec.ts +27 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_mixed_subjects.spec.ts +38 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_property_subjects.spec.ts +52 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_whitespace_subjects.spec.ts +24 -0
- package/readModels/for_ReadModels/when_releasing_a_document/without_subjects.spec.ts +24 -0
- package/reducers/Reducers.ts +29 -26
- package/seeding/EventSeeding.ts +6 -4
- package/telemetry/ChronicleTelemetryOptions.ts +8 -0
- package/telemetry/for_ChronicleTelemetry/given/a_telemetry_session.fixture.ts +36 -0
- package/telemetry/for_ChronicleTelemetry/given/an_event_sequence.fixture.ts +29 -0
- package/telemetry/for_ChronicleTelemetry/when_appending/with_shared_and_legacy_attributes.spec.ts +58 -0
- package/telemetry/for_ChronicleTelemetry/when_appending/without_an_sdk.spec.ts +15 -0
- package/telemetry/for_ChronicleTelemetry/when_documenting/with_public_names.spec.ts +15 -0
- package/telemetry/for_ChronicleTelemetry/when_recording_failures/with_sensitive_exception_details.spec.ts +34 -0
- package/telemetry/for_ChronicleTelemetry/when_recording_identifiers/with_per_client_privacy.spec.ts +92 -0
- package/telemetry/for_ChronicleTelemetry/when_recording_metrics/with_legacy_overlap.spec.ts +40 -0
- package/telemetry/for_ChronicleTelemetry/when_recording_sequence_numbers/with_integer_precision.spec.ts +23 -0
- package/telemetry/observeOperation.ts +25 -0
- package/telemetry/runInBackgroundContext.ts +10 -0
- package/telemetry/spanAttributes.ts +49 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
4
|
import { ChronicleConnection } from '../connection/index.js';
|
|
5
|
-
import { SpanStatusCode } from '@opentelemetry/api';
|
|
5
|
+
import { SpanStatusCode, type Span } from '@opentelemetry/api';
|
|
6
6
|
import type {
|
|
7
7
|
AppendedEventResponse as ContractsAppendedEvent,
|
|
8
8
|
AppendManyResponse as ContractsAppendManyResponse,
|
|
@@ -37,7 +37,10 @@ import type { WaitForCompletionOptions } from './WaitForCompletionOptions.js';
|
|
|
37
37
|
|
|
38
38
|
/** Default timeout for {@link AppendResult.waitForCompletion}, matching the C# client's default. */
|
|
39
39
|
const DEFAULT_WAIT_FOR_COMPLETION_TIMEOUT_MS = 5000;
|
|
40
|
-
import {
|
|
40
|
+
import { observeOperation } from '../telemetry/observeOperation.js';
|
|
41
|
+
import { setTelemetryAttribute, setEventSourceId, setSequenceNumber, recordSafeException } from '../telemetry/spanAttributes.js';
|
|
42
|
+
import type { ChronicleTelemetryOptions } from '../telemetry/ChronicleTelemetryOptions.js';
|
|
43
|
+
import { WellKnownTelemetryNames as names } from '../WellKnownTelemetryNames.js';
|
|
41
44
|
import { ChronicleMetrics } from '../Metrics.js';
|
|
42
45
|
import { identityProvider, Identity } from '../identity/index.js';
|
|
43
46
|
import { causationManager, CausationType } from '../auditing/index.js';
|
|
@@ -60,7 +63,8 @@ export class EventSequence implements IEventSequence {
|
|
|
60
63
|
private readonly _namespace: string,
|
|
61
64
|
private readonly _connection: ChronicleConnection,
|
|
62
65
|
private readonly _unitOfWorkManager: IUnitOfWorkManager,
|
|
63
|
-
private readonly _resolveConstraintMessage?: (violation: ConstraintViolation) => ConstraintViolation
|
|
66
|
+
private readonly _resolveConstraintMessage?: (violation: ConstraintViolation) => ConstraintViolation,
|
|
67
|
+
private readonly _telemetry?: ChronicleTelemetryOptions
|
|
64
68
|
) {
|
|
65
69
|
this.transactional = new TransactionalEventSequence(this, this._unitOfWorkManager);
|
|
66
70
|
}
|
|
@@ -76,14 +80,13 @@ export class EventSequence implements IEventSequence {
|
|
|
76
80
|
'chronicle.event_type_id': eventType.id.value
|
|
77
81
|
};
|
|
78
82
|
|
|
79
|
-
return
|
|
80
|
-
|
|
81
|
-
span
|
|
82
|
-
span
|
|
83
|
-
span
|
|
84
|
-
span.setAttribute(
|
|
85
|
-
|
|
86
|
-
const startTime = Date.now();
|
|
83
|
+
return observeOperation(names.spans.append, async span => {
|
|
84
|
+
this.setSequenceAttributes(span);
|
|
85
|
+
setEventSourceId(span, eventSourceId, this._telemetry);
|
|
86
|
+
setTelemetryAttribute(span, 'eventTypeId', eventType.id.value);
|
|
87
|
+
setTelemetryAttribute(span, 'eventTypeGeneration', eventType.generation.value);
|
|
88
|
+
if (options?.sourceType) span.setAttribute(names.attributes.eventSourceType, options.sourceType);
|
|
89
|
+
const startTime = performance.now();
|
|
87
90
|
try {
|
|
88
91
|
const response = await this._connection.eventSequences.append({
|
|
89
92
|
EventStore: this._eventStoreName,
|
|
@@ -113,14 +116,14 @@ export class EventSequence implements IEventSequence {
|
|
|
113
116
|
});
|
|
114
117
|
|
|
115
118
|
const appendResponse = ensureCommandResponse('append event', response);
|
|
116
|
-
const duration =
|
|
119
|
+
const duration = performance.now() - startTime;
|
|
117
120
|
const result = this.mapAppendResponse(
|
|
118
121
|
appendResponse.SequenceNumber,
|
|
119
122
|
appendResponse.ConstraintViolations ?? [],
|
|
120
123
|
appendResponse.Errors ?? [],
|
|
121
124
|
appendResponse.ConcurrencyViolation
|
|
122
125
|
);
|
|
123
|
-
span
|
|
126
|
+
setSequenceNumber(span, result.sequenceNumber.value);
|
|
124
127
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
125
128
|
|
|
126
129
|
ChronicleMetrics.eventsAppended.add(1, metricAttributes);
|
|
@@ -147,8 +150,7 @@ export class EventSequence implements IEventSequence {
|
|
|
147
150
|
|
|
148
151
|
return result;
|
|
149
152
|
} catch (error) {
|
|
150
|
-
span
|
|
151
|
-
span.recordException(error as Error);
|
|
153
|
+
recordSafeException(span, error);
|
|
152
154
|
ChronicleMetrics.appendErrors.add(1, {
|
|
153
155
|
'chronicle.event_store': this._eventStoreName,
|
|
154
156
|
'chronicle.namespace': this._namespace,
|
|
@@ -158,7 +160,7 @@ export class EventSequence implements IEventSequence {
|
|
|
158
160
|
} finally {
|
|
159
161
|
span.end();
|
|
160
162
|
}
|
|
161
|
-
});
|
|
163
|
+
}, correlationId);
|
|
162
164
|
}
|
|
163
165
|
|
|
164
166
|
/** @inheritdoc */
|
|
@@ -181,15 +183,13 @@ export class EventSequence implements IEventSequence {
|
|
|
181
183
|
'chronicle.events_count': eventsForEventSourceIds.length
|
|
182
184
|
};
|
|
183
185
|
|
|
184
|
-
return
|
|
185
|
-
|
|
186
|
-
span.setAttribute('chronicle.namespace', this._namespace);
|
|
187
|
-
span.setAttribute('chronicle.event_sequence_id', this.id.value);
|
|
186
|
+
return observeOperation(names.spans.appendMany, async span => {
|
|
187
|
+
this.setSequenceAttributes(span);
|
|
188
188
|
if (distinctEventSourceIds.length === 1) {
|
|
189
|
-
span
|
|
189
|
+
setEventSourceId(span, distinctEventSourceIds[0], this._telemetry);
|
|
190
190
|
}
|
|
191
|
-
span
|
|
192
|
-
const startTime =
|
|
191
|
+
setTelemetryAttribute(span, 'eventCount', eventsForEventSourceIds.length);
|
|
192
|
+
const startTime = performance.now();
|
|
193
193
|
try {
|
|
194
194
|
const response = await this._connection.eventSequences.appendManyForEventSources({
|
|
195
195
|
EventStore: this._eventStoreName,
|
|
@@ -210,7 +210,7 @@ export class EventSequence implements IEventSequence {
|
|
|
210
210
|
});
|
|
211
211
|
|
|
212
212
|
const appendManyResponse = ensureCommandResponse('append many events', response);
|
|
213
|
-
const duration =
|
|
213
|
+
const duration = performance.now() - startTime;
|
|
214
214
|
// Mirrors the C# client: every per-event AppendResult in a batch carries all
|
|
215
215
|
// constraint violations and the first concurrency violation of the whole batch —
|
|
216
216
|
// the wire response doesn't correlate either back to a specific event index.
|
|
@@ -269,8 +269,7 @@ export class EventSequence implements IEventSequence {
|
|
|
269
269
|
|
|
270
270
|
return result;
|
|
271
271
|
} catch (error) {
|
|
272
|
-
span
|
|
273
|
-
span.recordException(error as Error);
|
|
272
|
+
recordSafeException(span, error);
|
|
274
273
|
ChronicleMetrics.appendErrors.add(1, {
|
|
275
274
|
'chronicle.event_store': this._eventStoreName,
|
|
276
275
|
'chronicle.namespace': this._namespace,
|
|
@@ -280,7 +279,7 @@ export class EventSequence implements IEventSequence {
|
|
|
280
279
|
} finally {
|
|
281
280
|
span.end();
|
|
282
281
|
}
|
|
283
|
-
});
|
|
282
|
+
}, correlationId);
|
|
284
283
|
}
|
|
285
284
|
|
|
286
285
|
/** @inheritdoc */
|
|
@@ -300,13 +299,9 @@ export class EventSequence implements IEventSequence {
|
|
|
300
299
|
eventStreamId?: string,
|
|
301
300
|
filterEventTypes?: Constructor[]
|
|
302
301
|
): Promise<EventSequenceNumber> {
|
|
303
|
-
return
|
|
304
|
-
|
|
305
|
-
span
|
|
306
|
-
span.setAttribute('chronicle.event_sequence_id', this.id.value);
|
|
307
|
-
if (eventSourceId !== undefined) {
|
|
308
|
-
span.setAttribute('chronicle.event_source_id', eventSourceId);
|
|
309
|
-
}
|
|
302
|
+
return observeOperation(names.spans.getTailSequenceNumber, async span => {
|
|
303
|
+
this.setSequenceAttributes(span);
|
|
304
|
+
setEventSourceId(span, eventSourceId, this._telemetry);
|
|
310
305
|
try {
|
|
311
306
|
const response = await this._connection.eventSequences.tailSequenceNumber({
|
|
312
307
|
EventStore: this._eventStoreName,
|
|
@@ -324,12 +319,11 @@ export class EventSequence implements IEventSequence {
|
|
|
324
319
|
|
|
325
320
|
const data = ensureQuerySuccess('get tail sequence number', response);
|
|
326
321
|
const result = new EventSequenceNumber(data?.SequenceNumber ?? 0n);
|
|
327
|
-
span
|
|
322
|
+
setSequenceNumber(span, result.value);
|
|
328
323
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
329
324
|
return result;
|
|
330
325
|
} catch (error) {
|
|
331
|
-
span
|
|
332
|
-
span.recordException(error as Error);
|
|
326
|
+
recordSafeException(span, error);
|
|
333
327
|
throw error;
|
|
334
328
|
} finally {
|
|
335
329
|
span.end();
|
|
@@ -345,11 +339,9 @@ export class EventSequence implements IEventSequence {
|
|
|
345
339
|
|
|
346
340
|
/** @inheritdoc */
|
|
347
341
|
async hasEventsFor(eventSourceId: string): Promise<boolean> {
|
|
348
|
-
return
|
|
349
|
-
|
|
350
|
-
span
|
|
351
|
-
span.setAttribute('chronicle.event_sequence_id', this.id.value);
|
|
352
|
-
span.setAttribute('chronicle.event_source_id', eventSourceId);
|
|
342
|
+
return observeOperation(names.spans.hasEventsFor, async span => {
|
|
343
|
+
this.setSequenceAttributes(span);
|
|
344
|
+
setEventSourceId(span, eventSourceId, this._telemetry);
|
|
353
345
|
try {
|
|
354
346
|
const response = await this._connection.eventSequences.hasEventsForEventSourceId({
|
|
355
347
|
EventStore: this._eventStoreName,
|
|
@@ -359,12 +351,11 @@ export class EventSequence implements IEventSequence {
|
|
|
359
351
|
});
|
|
360
352
|
|
|
361
353
|
const result = ensureQuerySuccess('has events for event source', response)?.HasEvents ?? false;
|
|
362
|
-
span
|
|
354
|
+
setTelemetryAttribute(span, 'hasEvents', result);
|
|
363
355
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
364
356
|
return result;
|
|
365
357
|
} catch (error) {
|
|
366
|
-
span
|
|
367
|
-
span.recordException(error as Error);
|
|
358
|
+
recordSafeException(span, error);
|
|
368
359
|
throw error;
|
|
369
360
|
} finally {
|
|
370
361
|
span.end();
|
|
@@ -380,11 +371,9 @@ export class EventSequence implements IEventSequence {
|
|
|
380
371
|
eventStreamId?: string,
|
|
381
372
|
eventSourceType?: string
|
|
382
373
|
): Promise<AppendedEvent[]> {
|
|
383
|
-
return
|
|
384
|
-
|
|
385
|
-
span
|
|
386
|
-
span.setAttribute('chronicle.event_sequence_id', this.id.value);
|
|
387
|
-
span.setAttribute('chronicle.event_source_id', eventSourceId);
|
|
374
|
+
return observeOperation(names.spans.getForEventSourceIdAndEventTypes, async span => {
|
|
375
|
+
this.setSequenceAttributes(span);
|
|
376
|
+
setEventSourceId(span, eventSourceId, this._telemetry);
|
|
388
377
|
try {
|
|
389
378
|
const response = await this._connection.eventSequences.forEventSourceIdAndEventTypes({
|
|
390
379
|
EventStore: this._eventStoreName,
|
|
@@ -403,8 +392,7 @@ export class EventSequence implements IEventSequence {
|
|
|
403
392
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
404
393
|
return result;
|
|
405
394
|
} catch (error) {
|
|
406
|
-
span
|
|
407
|
-
span.recordException(error as Error);
|
|
395
|
+
recordSafeException(span, error);
|
|
408
396
|
throw error;
|
|
409
397
|
} finally {
|
|
410
398
|
span.end();
|
|
@@ -418,11 +406,10 @@ export class EventSequence implements IEventSequence {
|
|
|
418
406
|
eventSourceId?: string,
|
|
419
407
|
filterEventTypes?: Constructor[]
|
|
420
408
|
): Promise<AppendedEvent[]> {
|
|
421
|
-
return
|
|
422
|
-
|
|
423
|
-
span
|
|
424
|
-
span
|
|
425
|
-
span.setAttribute('chronicle.sequence_number', sequenceNumber.value.toString());
|
|
409
|
+
return observeOperation(names.spans.getFromSequenceNumber, async span => {
|
|
410
|
+
this.setSequenceAttributes(span);
|
|
411
|
+
setSequenceNumber(span, sequenceNumber.value);
|
|
412
|
+
setEventSourceId(span, eventSourceId, this._telemetry);
|
|
426
413
|
try {
|
|
427
414
|
const response = await this._connection.eventSequences.fromSequenceNumber({
|
|
428
415
|
EventStore: this._eventStoreName,
|
|
@@ -437,8 +424,7 @@ export class EventSequence implements IEventSequence {
|
|
|
437
424
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
438
425
|
return result;
|
|
439
426
|
} catch (error) {
|
|
440
|
-
span
|
|
441
|
-
span.recordException(error as Error);
|
|
427
|
+
recordSafeException(span, error);
|
|
442
428
|
throw error;
|
|
443
429
|
} finally {
|
|
444
430
|
span.end();
|
|
@@ -452,11 +438,9 @@ export class EventSequence implements IEventSequence {
|
|
|
452
438
|
const causationChain = causationManager.getCurrentChain();
|
|
453
439
|
const identity = identityProvider.getCurrent();
|
|
454
440
|
|
|
455
|
-
return
|
|
456
|
-
|
|
457
|
-
span
|
|
458
|
-
span.setAttribute('chronicle.event_sequence_id', this.id.value);
|
|
459
|
-
span.setAttribute('chronicle.sequence_number', sequenceNumber.value.toString());
|
|
441
|
+
return observeOperation(names.spans.redact, async span => {
|
|
442
|
+
this.setSequenceAttributes(span);
|
|
443
|
+
setSequenceNumber(span, sequenceNumber.value);
|
|
460
444
|
try {
|
|
461
445
|
ensureCommandSuccess('redact event', await this._connection.eventSequences.redact({
|
|
462
446
|
EventStore: this._eventStoreName,
|
|
@@ -473,8 +457,7 @@ export class EventSequence implements IEventSequence {
|
|
|
473
457
|
}));
|
|
474
458
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
475
459
|
} catch (error) {
|
|
476
|
-
span
|
|
477
|
-
span.recordException(error as Error);
|
|
460
|
+
recordSafeException(span, error);
|
|
478
461
|
throw error;
|
|
479
462
|
} finally {
|
|
480
463
|
span.end();
|
|
@@ -489,11 +472,9 @@ export class EventSequence implements IEventSequence {
|
|
|
489
472
|
const identity = identityProvider.getCurrent();
|
|
490
473
|
const wireEventTypeIds = (eventTypes ?? []).map(constructor => getEventTypeFor(constructor as unknown as Function).id.value);
|
|
491
474
|
|
|
492
|
-
return
|
|
493
|
-
|
|
494
|
-
span
|
|
495
|
-
span.setAttribute('chronicle.event_sequence_id', this.id.value);
|
|
496
|
-
span.setAttribute('chronicle.event_source_id', eventSourceId);
|
|
475
|
+
return observeOperation(names.spans.redactForEventSource, async span => {
|
|
476
|
+
this.setSequenceAttributes(span);
|
|
477
|
+
setEventSourceId(span, eventSourceId, this._telemetry);
|
|
497
478
|
try {
|
|
498
479
|
ensureCommandSuccess('redact event source', await this._connection.eventSequences.redactForEventSource({
|
|
499
480
|
EventStore: this._eventStoreName,
|
|
@@ -511,8 +492,7 @@ export class EventSequence implements IEventSequence {
|
|
|
511
492
|
}));
|
|
512
493
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
513
494
|
} catch (error) {
|
|
514
|
-
span
|
|
515
|
-
span.recordException(error as Error);
|
|
495
|
+
recordSafeException(span, error);
|
|
516
496
|
throw error;
|
|
517
497
|
} finally {
|
|
518
498
|
span.end();
|
|
@@ -538,12 +518,10 @@ export class EventSequence implements IEventSequence {
|
|
|
538
518
|
|
|
539
519
|
/** @inheritdoc */
|
|
540
520
|
async completeStream(eventStreamType: string, eventStreamId: string): Promise<CompleteStreamResult> {
|
|
541
|
-
return
|
|
542
|
-
|
|
543
|
-
span
|
|
544
|
-
span
|
|
545
|
-
span.setAttribute('chronicle.event_stream_type', eventStreamType);
|
|
546
|
-
span.setAttribute('chronicle.event_stream_id', eventStreamId);
|
|
521
|
+
return observeOperation(names.spans.completeStream, async span => {
|
|
522
|
+
this.setSequenceAttributes(span);
|
|
523
|
+
setTelemetryAttribute(span, 'eventStreamType', eventStreamType);
|
|
524
|
+
setTelemetryAttribute(span, 'eventStreamId', eventStreamId);
|
|
547
525
|
try {
|
|
548
526
|
const response = await this._connection.eventSequences.completeStream({
|
|
549
527
|
EventStore: this._eventStoreName,
|
|
@@ -561,8 +539,7 @@ export class EventSequence implements IEventSequence {
|
|
|
561
539
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
562
540
|
return result;
|
|
563
541
|
} catch (error) {
|
|
564
|
-
span
|
|
565
|
-
span.recordException(error as Error);
|
|
542
|
+
recordSafeException(span, error);
|
|
566
543
|
throw error;
|
|
567
544
|
} finally {
|
|
568
545
|
span.end();
|
|
@@ -570,6 +547,12 @@ export class EventSequence implements IEventSequence {
|
|
|
570
547
|
});
|
|
571
548
|
}
|
|
572
549
|
|
|
550
|
+
private setSequenceAttributes(span: Span): void {
|
|
551
|
+
setTelemetryAttribute(span, 'eventStore', this._eventStoreName);
|
|
552
|
+
setTelemetryAttribute(span, 'namespace', this._namespace);
|
|
553
|
+
setTelemetryAttribute(span, 'eventSequenceId', this.id.value);
|
|
554
|
+
}
|
|
555
|
+
|
|
573
556
|
private toClientCompleteStreamError(error: number): CompleteStreamError {
|
|
574
557
|
// Mirrors the C# client's switch: any wire value other than DefaultStreamCannotBeCompleted
|
|
575
558
|
// (including UNRECOGNIZED) is treated as AlreadyCompleted.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Copyright (c) Cratis. All rights reserved.
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { createLogger } from '../logging/createLogger.js';
|
|
5
5
|
import { ChronicleConnection } from '../connection/index.js';
|
|
6
6
|
import { EventStoreName } from '../EventStoreName.js';
|
|
7
7
|
import { EventType, EventTypeGeneration, EventTypeId, IEventTypes } from '../events/index.js';
|
|
@@ -15,15 +15,15 @@ import { IEventStoreSubscriptions } from './IEventStoreSubscriptions.js';
|
|
|
15
15
|
* Represents an implementation of {@link IEventStoreSubscriptions}.
|
|
16
16
|
*/
|
|
17
17
|
export class EventStoreSubscriptions implements IEventStoreSubscriptions {
|
|
18
|
-
private readonly _logger
|
|
19
|
-
namespace: '@cratis/chronicle/EventStoreSubscriptions'
|
|
20
|
-
});
|
|
18
|
+
private readonly _logger: ReturnType<typeof createLogger>;
|
|
21
19
|
|
|
22
20
|
constructor(
|
|
23
21
|
private readonly _eventTypes: IEventTypes,
|
|
24
22
|
private readonly _targetEventStore: string,
|
|
25
23
|
private readonly _connection: ChronicleConnection
|
|
26
|
-
) {
|
|
24
|
+
) {
|
|
25
|
+
this._logger = createLogger('@cratis/chronicle/EventStoreSubscriptions', _connection.logger);
|
|
26
|
+
}
|
|
27
27
|
|
|
28
28
|
/** @inheritdoc */
|
|
29
29
|
async subscribe(
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { afterEach, beforeEach, chai, describe, it } from 'vitest';
|
|
5
|
+
import { context, propagation, trace } from '@opentelemetry/api';
|
|
6
|
+
import { createServer, ServerError, Status, type ServiceImplementation } from 'nice-grpc';
|
|
7
|
+
import { Metadata } from 'nice-grpc-common';
|
|
8
|
+
import { ConnectionServiceDefinition, EventStoresDefinition, ServerDefinition, ReactorsDefinition, ReducersDefinition, ReadModelsDefinition } from '@cratis/chronicle.contracts';
|
|
9
|
+
import { ChronicleClient } from '../../ChronicleClient.js';
|
|
10
|
+
import { ChronicleOptions } from '../../ChronicleOptions.js';
|
|
11
|
+
import type { IClientArtifactsProvider } from '../../artifacts/IClientArtifactsProvider.js';
|
|
12
|
+
import { CorrelationId, correlationIdManager } from '../../correlation/index.js';
|
|
13
|
+
import type { ChronicleLogEntry } from '../../logging/ChronicleLogEntry.js';
|
|
14
|
+
import { reactor } from '../../reactors/reactor.js';
|
|
15
|
+
import { reducer } from '../../reducers/reducer.js';
|
|
16
|
+
import { telemetrySession } from '../../telemetry/for_ChronicleTelemetry/given/a_telemetry_session.fixture.js';
|
|
17
|
+
|
|
18
|
+
const should = chai.should();
|
|
19
|
+
const telemetry = telemetrySession();
|
|
20
|
+
class BackgroundReactor {}
|
|
21
|
+
reactor('background-reactor')(BackgroundReactor);
|
|
22
|
+
class BackgroundReducer {}
|
|
23
|
+
reducer('background-reducer')(BackgroundReducer);
|
|
24
|
+
const artifacts: IClientArtifactsProvider = {
|
|
25
|
+
eventTypes: [], readModels: [], reactors: [BackgroundReactor], reducers: [BackgroundReducer],
|
|
26
|
+
seeders: [], constraints: [], projections: [], webhooks: [], eventTypeMigrations: [], globalForHandlers: []
|
|
27
|
+
};
|
|
28
|
+
let server: ReturnType<typeof createServer>;
|
|
29
|
+
let client: ChronicleClient;
|
|
30
|
+
let received: { method: string; metadata: Metadata }[];
|
|
31
|
+
let logs: ChronicleLogEntry[];
|
|
32
|
+
let hostTraceId: string;
|
|
33
|
+
const correlation = new CorrelationId('a4450131-7cd4-40ba-9438-69c680efc1f3');
|
|
34
|
+
|
|
35
|
+
function deferred() {
|
|
36
|
+
let resolve!: () => void;
|
|
37
|
+
const promise = new Promise<void>(complete => { resolve = complete; });
|
|
38
|
+
return { promise, resolve };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
beforeEach(async () => {
|
|
42
|
+
received = [];
|
|
43
|
+
logs = [];
|
|
44
|
+
const reobserved = deferred();
|
|
45
|
+
const reconnected = deferred();
|
|
46
|
+
const endKeepAlive = deferred();
|
|
47
|
+
const counts = new Map<string, number>();
|
|
48
|
+
server = createServer().use(async function* (call, callContext) {
|
|
49
|
+
const method = call.method.path;
|
|
50
|
+
received.push({ method, metadata: Metadata(callContext.metadata) });
|
|
51
|
+
const count = (counts.get(method) ?? 0) + 1;
|
|
52
|
+
counts.set(method, count);
|
|
53
|
+
const observations = [...counts].filter(([path]) => path.endsWith('/Observe'));
|
|
54
|
+
if (observations.length === 2 && observations.every(([, total]) => total >= 2)) reobserved.resolve();
|
|
55
|
+
if (observations.length === 2 && observations.every(([, total]) => total >= 3)) reconnected.resolve();
|
|
56
|
+
return yield* call.next(call.request, callContext);
|
|
57
|
+
});
|
|
58
|
+
const unused = async () => { throw new Error('Unexpected RPC'); };
|
|
59
|
+
const success = async () => ({ IsAuthorized: true, ValidationResults: [], ExceptionMessages: [] });
|
|
60
|
+
let connections = 0;
|
|
61
|
+
server.add(ConnectionServiceDefinition, {
|
|
62
|
+
...Object.fromEntries(Object.keys(ConnectionServiceDefinition.methods).map(name => [name, unused])),
|
|
63
|
+
checkCompatibility: async () => ({ IsCompatible: true, Incompatibilities: [], ServerVersion: 'test' }),
|
|
64
|
+
connectionKeepAlive: async () => ({}),
|
|
65
|
+
connect: async function* (request, callContext) {
|
|
66
|
+
const first = ++connections === 1;
|
|
67
|
+
yield { ConnectionId: request.ConnectionId };
|
|
68
|
+
if (first) await endKeepAlive.promise;
|
|
69
|
+
else await new Promise<void>(resolve => callContext.signal.addEventListener('abort', () => resolve(), { once: true }));
|
|
70
|
+
}
|
|
71
|
+
} as ServiceImplementation<typeof ConnectionServiceDefinition>);
|
|
72
|
+
server.add(ServerDefinition, {
|
|
73
|
+
...Object.fromEntries(Object.keys(ServerDefinition.methods).map(name => [name, unused])),
|
|
74
|
+
getVersionInfo: async () => ({})
|
|
75
|
+
} as ServiceImplementation<typeof ServerDefinition>);
|
|
76
|
+
server.add(EventStoresDefinition, {
|
|
77
|
+
allEventStores: unused, ensureEventStore: success, observeEventStores: unused
|
|
78
|
+
} as ServiceImplementation<typeof EventStoresDefinition>);
|
|
79
|
+
server.add(ReadModelsDefinition, {
|
|
80
|
+
...Object.fromEntries(Object.keys(ReadModelsDefinition.methods).map(name => [name, unused])),
|
|
81
|
+
registerMany: success
|
|
82
|
+
} as ServiceImplementation<typeof ReadModelsDefinition>);
|
|
83
|
+
server.add(ReactorsDefinition, {
|
|
84
|
+
...Object.fromEntries(Object.keys(ReactorsDefinition.methods).map(name => [name, unused])),
|
|
85
|
+
observe: async function* (requests) {
|
|
86
|
+
for await (const _request of requests) {
|
|
87
|
+
yield { Events: [], Partition: '', ReplayState: 0 };
|
|
88
|
+
throw new ServerError(Status.UNAVAILABLE, 'End stream to exercise re-observation');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} as ServiceImplementation<typeof ReactorsDefinition>);
|
|
92
|
+
server.add(ReducersDefinition, {
|
|
93
|
+
...Object.fromEntries(Object.keys(ReducersDefinition.methods).map(name => [name, unused])),
|
|
94
|
+
observe: async function* (requests) {
|
|
95
|
+
for await (const _request of requests) {
|
|
96
|
+
yield { Events: [], Partition: '', ReplayState: 0 };
|
|
97
|
+
throw new ServerError(Status.UNAVAILABLE, 'End stream to exercise re-observation');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} as ServiceImplementation<typeof ReducersDefinition>);
|
|
101
|
+
const port = await server.listen('127.0.0.1:0');
|
|
102
|
+
const span = telemetry.traces.getTracer('host').startSpan('host');
|
|
103
|
+
hostTraceId = span.spanContext().traceId;
|
|
104
|
+
const active = propagation.setBaggage(trace.setSpan(context.active(), span), propagation.createBaggage({
|
|
105
|
+
'cratis.correlation_id': { value: correlation.toString() }, email: { value: 'private@example.com' }
|
|
106
|
+
}));
|
|
107
|
+
try {
|
|
108
|
+
await context.with(active, () => correlationIdManager.run(correlation, async () => {
|
|
109
|
+
client = new ChronicleClient(ChronicleOptions.fromConnectionString(`chronicle://127.0.0.1:${port}?disableTls=true&apiKey=test`, {
|
|
110
|
+
discoveryPatterns: [], clientArtifactsProvider: artifacts, logger: { log: entry => logs.push(entry) }
|
|
111
|
+
}));
|
|
112
|
+
await client.getEventStore('store');
|
|
113
|
+
context.active().should.equal(active);
|
|
114
|
+
correlationIdManager.scoped!.should.equal(correlation);
|
|
115
|
+
}));
|
|
116
|
+
await reobserved.promise;
|
|
117
|
+
endKeepAlive.resolve();
|
|
118
|
+
await reconnected.promise;
|
|
119
|
+
} finally { span.end(); endKeepAlive.resolve(); }
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
afterEach(async () => {
|
|
123
|
+
client?.dispose();
|
|
124
|
+
await server?.shutdown();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('when a correlated getEventStore starts background streams and their recovery loops', () => {
|
|
128
|
+
it('should not propagate the caller trace or baggage on observations or keep-alive RPCs', () => {
|
|
129
|
+
const background = received.filter(({ method }) => /\/(Observe|Connect|ConnectionKeepAlive)$/.test(method));
|
|
130
|
+
background.length.should.be.greaterThanOrEqual(10);
|
|
131
|
+
for (const { metadata } of background) {
|
|
132
|
+
should.equal(metadata.get('traceparent'), undefined);
|
|
133
|
+
should.equal(metadata.get('baggage'), undefined);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
it('should retain the caller context on the foreground operation', () => {
|
|
137
|
+
const foreground = received.find(({ method }) => method.endsWith('/EnsureEventStore'))!;
|
|
138
|
+
foreground.metadata.get('traceparent')!.should.contain(hostTraceId);
|
|
139
|
+
should.equal(foreground.metadata.get('baggage'), `cratis.correlation_id=${correlation}`);
|
|
140
|
+
});
|
|
141
|
+
it('should not label background-loop diagnostics with the initiating business correlation or trace', () => {
|
|
142
|
+
const background = logs.filter(entry => /observation|keep-alive|Reconnect/.test(entry.message));
|
|
143
|
+
background.length.should.be.greaterThan(0);
|
|
144
|
+
for (const entry of background) {
|
|
145
|
+
entry.attributes.should.not.have.property('cratis.correlation_id');
|
|
146
|
+
entry.attributes.should.not.have.property('trace_id');
|
|
147
|
+
entry.attributes.should.not.have.property('span_id');
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { chai, describe, it } from 'vitest';
|
|
5
|
+
import { runInNewContext } from 'node:vm';
|
|
6
|
+
import { ChronicleOptions } from '../../ChronicleOptions.js';
|
|
7
|
+
import type { ChronicleTelemetryOptions } from '../../telemetry/ChronicleTelemetryOptions.js';
|
|
8
|
+
|
|
9
|
+
chai.should();
|
|
10
|
+
|
|
11
|
+
for (const policy of [
|
|
12
|
+
{ mode: 'unknown' }, {}, null,
|
|
13
|
+
{ mode: 'hmac' }, { mode: 'hmac', key: '' }, { mode: 'hmac', key: 'secret' },
|
|
14
|
+
{ mode: 'hmac', key: [] }, { mode: 'hmac', key: new Uint8Array() },
|
|
15
|
+
{ mode: 'hmac', key: Buffer.alloc(0) }, { mode: 'hmac', key: new Uint16Array([1]) },
|
|
16
|
+
{ mode: 'hmac', key: new DataView(new ArrayBuffer(1)) },
|
|
17
|
+
{ mode: 'hmac', key: runInNewContext('new Uint8Array()') },
|
|
18
|
+
{ mode: 'hmac', key: runInNewContext('new Uint16Array([1])') }
|
|
19
|
+
]) {
|
|
20
|
+
describe(`when configuring event source privacy with ${JSON.stringify(policy)}`, () => {
|
|
21
|
+
it('should reject invalid telemetry configuration before a client can make business calls', () => {
|
|
22
|
+
const telemetry = { eventSourceId: policy } as unknown as ChronicleTelemetryOptions;
|
|
23
|
+
(() => ChronicleOptions.development({ telemetry })).should.throw(TypeError);
|
|
24
|
+
(() => ChronicleOptions.fromConnectionString('chronicle://localhost:35000', { telemetry })).should.throw(TypeError);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('when configuring an HMAC key created in another realm', () => {
|
|
30
|
+
it('should accept a non-empty byte array without relying on its realm constructor', () => {
|
|
31
|
+
const key = runInNewContext('new Uint8Array([1, 2, 3])') as Uint8Array;
|
|
32
|
+
(key instanceof Uint8Array).should.be.false;
|
|
33
|
+
(() => ChronicleOptions.development({ telemetry: { eventSourceId: { mode: 'hmac', key } } })).should.not.throw();
|
|
34
|
+
(() => ChronicleOptions.fromConnectionString('chronicle://localhost:35000', { telemetry: { eventSourceId: { mode: 'hmac', key } } })).should.not.throw();
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
for (const eventSourceId of [undefined, { mode: 'raw' }, { mode: 'hmac', key: new Uint8Array([1]) }, { mode: 'hmac', key: Buffer.from('key') }] as const) {
|
|
39
|
+
describe('when configuring a supported event source privacy policy', () => {
|
|
40
|
+
it('should accept omitted identifiers, raw identifiers and non-empty byte keys', () => {
|
|
41
|
+
(() => ChronicleOptions.development({ telemetry: { eventSourceId } })).should.not.throw();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
package/index.ts
CHANGED
|
@@ -37,7 +37,13 @@ export { EventStoreNamespaceName } from './EventStoreNamespaceName.js';
|
|
|
37
37
|
export { Guid } from '@cratis/fundamentals';
|
|
38
38
|
export { ReplayState } from '@cratis/chronicle.contracts';
|
|
39
39
|
export { ChronicleInstrumentationName, ChronicleTracer } from './Tracing.js';
|
|
40
|
-
export { ChronicleMeter, ChronicleMeterName, ChronicleMetrics } from './Metrics.js';
|
|
40
|
+
export { ChronicleMeter, ChronicleMeterName, ChronicleMetrics, ChronicleConventionMetrics } from './Metrics.js';
|
|
41
|
+
export { WellKnownTelemetryNames } from './WellKnownTelemetryNames.js';
|
|
42
|
+
export type { ChronicleTelemetryOptions } from './telemetry/ChronicleTelemetryOptions.js';
|
|
43
|
+
export type { IChronicleLogger } from './logging/IChronicleLogger.js';
|
|
44
|
+
export type { ChronicleLogEntry } from './logging/ChronicleLogEntry.js';
|
|
45
|
+
export { ChronicleLogLevel } from './logging/ChronicleLogLevel.js';
|
|
46
|
+
export { DiagChronicleLogger } from './logging/DiagChronicleLogger.js';
|
|
41
47
|
|
|
42
48
|
export * from './events/index.js';
|
|
43
49
|
export * from './eventSequences/index.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import type { Attributes } from '@opentelemetry/api';
|
|
5
|
+
import type { ChronicleLogLevel } from './ChronicleLogLevel.js';
|
|
6
|
+
|
|
7
|
+
/** Structured diagnostic without event payloads, error messages or error stacks. */
|
|
8
|
+
export interface ChronicleLogEntry {
|
|
9
|
+
readonly category: string;
|
|
10
|
+
readonly level: ChronicleLogLevel;
|
|
11
|
+
readonly message: string;
|
|
12
|
+
/** Safe diagnostic fields, including correlation and valid trace/span identifiers when available. */
|
|
13
|
+
readonly attributes: Readonly<Attributes>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
/** Severity of a Chronicle diagnostic. */
|
|
5
|
+
export enum ChronicleLogLevel {
|
|
6
|
+
Verbose = 'verbose',
|
|
7
|
+
Debug = 'debug',
|
|
8
|
+
Info = 'info',
|
|
9
|
+
Warn = 'warn',
|
|
10
|
+
Error = 'error'
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { diag } from '@opentelemetry/api';
|
|
5
|
+
import type { IChronicleLogger } from './IChronicleLogger.js';
|
|
6
|
+
import type { ChronicleLogEntry } from './ChronicleLogEntry.js';
|
|
7
|
+
|
|
8
|
+
/** Compatibility adapter used when no application logger is supplied during the minor-release overlap. */
|
|
9
|
+
export class DiagChronicleLogger implements IChronicleLogger {
|
|
10
|
+
log(entry: ChronicleLogEntry): void {
|
|
11
|
+
diag.createComponentLogger({ namespace: entry.category })[entry.level](entry.message, entry.attributes);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import type { ChronicleLogEntry } from './ChronicleLogEntry.js';
|
|
5
|
+
|
|
6
|
+
/** Application-owned diagnostic sink, configured separately for each Chronicle client. */
|
|
7
|
+
export interface IChronicleLogger {
|
|
8
|
+
/** Receives one sanitized diagnostic. Implementations should not throw. */
|
|
9
|
+
log(entry: ChronicleLogEntry): void;
|
|
10
|
+
}
|