Skip to content

Observability reference

Use this reference to select Chronicle client signals in your observability backend and control what they contain. The client follows the Cratis OpenTelemetry convention, with canonical built-in names after the announced minor-release overlap. TypeScript compatibility APIs remain available.

Both the tracer and meter use Cratis.Chronicle.Client, versioned with the installed @cratis/chronicle package version. The instrumentation scope already moved from @cratis/chronicle in 6.34 without an overlap. The naming cutoff does not change the scope again. The deprecated WellKnownTelemetryNames.legacyScope still identifies the historical scope but does not restore it. Update backend filters and SDK Views that still select @cratis/chronicle. ChronicleInstrumentationName, ChronicleMeterName, and WellKnownTelemetryNames.scope expose the current scope.

Instrumentation is always available. The library depends on @opentelemetry/api; it does not install an SDK, exporter, resource, context manager, or propagator. Without an application SDK, tracing and metrics are no-ops. Configure the Node.js SDK in your application’s bootstrap, preferably before importing the client, and use standard OTEL_* configuration for resources, sampling, and export. The host owns service.name and must opt into export; the client sends no telemetry to an endpoint itself.

Both ChronicleOptions.fromConnectionString(connectionString, options) and ChronicleOptions.development(options) accept an optional telemetry: ChronicleTelemetryOptions object. The settings apply only to that client, including its event log and dynamically created event sequences.

telemetry.eventSourceIdBehavior
AbsentOmit event source identifiers from span attributes.
{ mode: 'raw' }Record the unmodified identifier. Use only when your privacy policy permits it.
{ mode: 'hmac', key: Uint8Array }Record a lowercase hexadecimal HMAC-SHA256 using your deployment key.

Unknown modes and HMAC keys that are not non-empty Uint8Array values (including Node.js Buffer) are rejected when constructing ChronicleOptions.

Keep the HMAC key in your secret store, use a strong random key, and do not log it. Identical identifiers and keys produce identical values; rotating the key changes those values and breaks comparisons with earlier telemetry. Hashing does not make telemetry unrestricted data.

Event source identifiers remain off by default. Opt in explicitly if you need them. The policy affects telemetry only: identifiers sent to Chronicle are unchanged. Multi-source batches do not record identifier arrays, even with opt-in. Built-in metrics never include event source identifiers or correlation identifiers. Callers of the compatibility ChronicleMetrics API remain responsible for the attributes they supply.

Exceptions in spans and diagnostics contain error.type and exception.type, not exception messages or stacks. Reactor diagnostics exclude event payloads and partition identifiers. Error details required by Chronicle’s failure-reporting wire protocol are separate from telemetry and are unchanged.

cratis.event_sequence.number is emitted as an integer only when JavaScript can represent it exactly (Number.isSafeInteger); larger values are omitted from that attribute. The compatibility attribute chronicle.sequence_number remains an exact string for every value, including values above Number.MAX_SAFE_INTEGER and sentinels. Event sequence APIs continue to return bigint values without loss.

The client injects the active OpenTelemetry context into outgoing gRPC metadata using the host’s propagator. Configure W3C trace context and baggage in the host. Injection covers unary requests, compatibility checks, keep-alive calls, server and bidirectional streams, authentication retries, and clients rebuilt during reconnect.

Only cratis.correlation_id is allowed in baggage. The client replaces stale propagation headers, preserves authorization and unrelated metadata, and leaves the caller’s context and metadata unchanged. It does not install a global propagator or fall back to a private trace format.

An explicit append correlation override, or the resolved append correlation, is used consistently on the span and for outgoing baggage. Other operations use the current scoped business correlation when available. Business correlation and trace identifiers remain separate.

Client-owned background work—keep-alive, reactor/reducer observations, re-observation timers, and connection recovery—starts without the initiating caller’s trace or business correlation. Foreground calls retain their caller’s context. Host gRPC instrumentation can create independent spans for background RPCs.

Stream metadata describes the context when the stream opens, not individual delivered events. Persisting append trace context with events and linking later observer spans require separate kernel support; this client change does not provide those links.

Set the optional logger: IChronicleLogger option to route diagnostics to your application’s logging pipeline. Its synchronous log(entry: ChronicleLogEntry): void method receives one record with:

FieldTypeMeaning
categorystringComponent category, retaining the @cratis/chronicle/ prefix.
levelChronicleLogLevelVerbose, Debug, Info, Warn, or Error.
messagestringDiagnostic description, without serialized exceptions or event payloads.
attributesRead-only OpenTelemetry attributesSafe diagnostic fields; cratis.correlation_id when scoped, valid trace_id/span_id when a trace is active, and numeric rpc.grpc.status_code for gRPC failures. Exception messages and stacks are excluded.

Each client has its own sink. Sink failures do not change RPC results or observation acknowledgements. The host decides how to ingest these records; do not send them through a second logging path too.

When logger is absent, DiagChronicleLogger forwards sanitized records to OpenTelemetry diag for compatibility. Existing diag.setLogger(...) configuration still works. The diag default is unchanged by the naming cutoff. See connection diagnostics.

This minor completes the separately announced revised timetable in the telemetry migration issue, replacing v6.40’s “removed in the next major” plan. It follows ADR 0001’s one-minor overlap. The notice precedes the cutoff while legacy emission still works; allow that overlap before adopting this release.

Built-in operations now produce one CLIENT span with a cratis.chronicle.client.* name. They stop emitting legacy span names, superseded chronicle.* attributes, and legacy metric instruments, including millisecond duration histograms. Update dashboards, alerts, and SDK Views before upgrading using the tables below. This is source-compatible, but consumers that still select retired signals must migrate. chronicle.sequence_number is the sole built-in legacy attribute exception and remains an exact string.

The deprecated ChronicleTelemetryOptions.spanNames?: 'legacy' | 'convention' remains accepted without a removal deadline. Both values are documented no-ops: built-in names are canonical whether the option is absent, 'legacy', or 'convention'. Invalid values still throw a TypeError when constructing options. Removing the option is optional, not required to start the client.

Public constants retain their original values and literal types:

WellKnownTelemetryNames memberStatus and preferred API
spansDeprecated; retains original legacy names. Use conventionSpans for built-in span-name queries.
conventionSpansPreferred canonical map; not deprecated.
legacyScopeDeprecated historical constant; use scope (Cratis.Chronicle.Client, unchanged).
legacyAttributesDeprecated constants; use attributes, except the retained exact-string sequence number.
legacyMetricsDeprecated constants; use metrics and migrate instrument selectors and duration thresholds.

These compatibility constants have no scheduled removal. The public ChronicleMetrics bridge also remains available with its existing behavior: millisecond inputs, historical keys, caller attributes on legacy measurements, and dual recording. Its canonical measurements still translate and restrict dimensions to event store, namespace, event sequence, and event type. Built-in instrumentation bypasses the bridge and records ChronicleConventionMetrics directly.

The scope, diag fallback, privacy options, and application/RPC behavior are unchanged. Both canonical names and retained compatibility mappings below are generated from WellKnownTelemetryNames.

OperationCompatibility constant (not emitted)Built-in name
appendchronicle.event_sequences.appendcratis.chronicle.client.event_sequence.append
appendManychronicle.event_sequences.append_manycratis.chronicle.client.event_sequence.append_many
getTailSequenceNumberchronicle.event_sequences.get_tail_sequence_numbercratis.chronicle.client.event_sequence.get_tail_sequence_number
hasEventsForchronicle.event_sequences.has_events_forcratis.chronicle.client.event_sequence.has_events_for
getForEventSourceIdAndEventTypeschronicle.event_sequences.get_for_event_source_id_and_event_typescratis.chronicle.client.event_sequence.get_for_event_source_id_and_event_types
getFromSequenceNumberchronicle.event_sequences.get_from_sequence_numbercratis.chronicle.client.event_sequence.get_from_sequence_number
redactchronicle.event_sequences.redactcratis.chronicle.client.event_sequence.redact
redactForEventSourcechronicle.event_sequences.redact_for_event_sourcecratis.chronicle.client.event_sequence.redact_for_event_source
completeStreamchronicle.event_sequences.complete_streamcratis.chronicle.client.event_sequence.complete_stream
getEventStorechronicle.client.get_event_storecratis.chronicle.client.event_store.get
getEventStoreschronicle.client.get_event_storescratis.chronicle.client.event_store.list
getNamespaceschronicle.event_store.get_namespacescratis.chronicle.client.event_store.get_namespaces
ConceptCompatibility nameCanonical name
correlationId—cratis.correlation_id
eventStorechronicle.event_storecratis.event_store.name
namespacechronicle.namespacecratis.event_store.namespace
eventSequenceIdchronicle.event_sequence_idcratis.event_sequence.id
sequenceNumberchronicle.sequence_numbercratis.event_sequence.number
eventTypeIdchronicle.event_type_idcratis.event_type.id
eventTypeGenerationchronicle.event_type_generationcratis.event_type.generation
eventSourceType—cratis.event_source.type
eventSourceIdchronicle.event_source_idcratis.event_source.id
eventCountchronicle.events_countcratis.event.count
hasEventschronicle.has_eventscratis.chronicle.event_sequence.has_events
eventStreamTypechronicle.event_stream_typecratis.chronicle.event_stream.type
eventStreamIdchronicle.event_stream_idcratis.chronicle.event_stream.id
errorType—error.type
exceptionType—exception.type

Only chronicle.sequence_number remains emitted by built-in spans, as an exact string. Other compatibility attribute constants remain available but are not emitted.

InstrumentCompatibility instrument (ChronicleMetrics only)Built-in instrumentCompatibility / canonical unit
eventsAppendedchronicle.events.appendedcratis.chronicle.event_sequence.appended{event}
batchAppendsPerformedchronicle.events.batch_appendscratis.chronicle.event_sequence.batch_appends{operation}
eventStoreRetrievalschronicle.client.event_store_retrievalscratis.chronicle.event_store.retrievals{operation}
appendDurationchronicle.events.append_durationcratis.chronicle.event_sequence.append_durationms / s
appendManyDurationchronicle.events.append_many_durationcratis.chronicle.event_sequence.append_many_durationms / s
constraintViolationschronicle.events.constraint_violationscratis.chronicle.event_sequence.constraint_violations{violation}
appendErrorschronicle.events.append_errorscratis.chronicle.event_sequence.append_errors{error}

ChronicleMetrics is deprecated but preserves its existing method signatures and millisecond duration inputs. It records the legacy instrument with every caller attribute intact and also records the canonical instrument with translated, bounded dimensions, converting milliseconds to seconds exactly once. Do not divide durations before passing them to this bridge. To migrate to ChronicleConventionMetrics, pass canonical attribute keys and divide millisecond durations by 1,000 yourself; its duration histograms take seconds directly. Do not record both APIs for the same measurement.

Counters retain their existing counting semantics. Duration histograms measure completed append RPCs, including returned rejection results, but not thrown failures. Elapsed time is measured monotonically. Built-in metric dimensions are event store, namespace, event sequence, and event type when known; batch size is not a built-in metric dimension. The compatibility bridge still preserves caller-supplied batch size on legacy measurements. General cardinality overflow handling is deferred.

Divide duration thresholds only by 1,000 when switching from the legacy millisecond histograms to the convention second histograms. For example, an append latency alert at 250 ms becomes 0.25 s. Counter values and count/rate alert thresholds do not change scale.

Built-in append duration histograms advise the seconds boundaries below. The compatibility bridge retains millisecond buckets on its legacy histograms. An SDK View can override them:

Compatibility boundaries (ms)Canonical boundaries (s)
1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 50000.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5

Update histogram queries and any explicit View boundaries as well as alert thresholds; changing only the instrument name leaves millisecond buckets applied to second-valued measurements.

The convention instruments use these renamed dimensions where available:

Legacy dimensionConvention dimension
chronicle.event_storecratis.event_store.name
chronicle.namespacecratis.event_store.namespace
chronicle.event_sequence_idcratis.event_sequence.id
chronicle.event_type_idcratis.event_type.id
chronicle.events_countRemoved from convention metrics; batch size is not a dimension.

Update groupings, label filters, and View attribute allow-lists. The event count still appears on batch spans as cratis.event.count; it is not a replacement metric dimension. Event source and correlation identifiers remain excluded from built-in and canonical bridge measurements; the legacy bridge preserves caller-supplied attributes.

SDK Views select instruments by scope and instrument name. Use scope Cratis.Chronicle.Client and change each legacy instrument-name selector to its convention name, with seconds-based duration boundaries. Remove Views that target legacy built-in emission unless your application still records through ChronicleMetrics. Do not sum the bridge’s legacy and canonical measurements together. A View selecting the old @cratis/chronicle scope matches no client instruments.