@docx-editor.dev/telemetry/server

Room telemetry for any Yjs collaboration server: rooms, connections, the people in each room, the memory each room holds, and why connections and messages were refused.

No metric carries a room name. Per-room detail is in [RoomTelemetry.list](RoomTelemetry.list), for an admin endpoint you protect.

Functions (2)

hmacRoomLabelfunctionSource ↗

A stable room label that does not reveal the name: an HMAC of the name with your secret. The same name gives the same label across processes and restarts.

declare function hmacRoomLabel(secret: string | undefined): (name: string) => string;
```ts
const rooms = roomTelemetry({
  exporters: [metrics],
  roomLabel: hmacRoomLabel(process.env.ROOM_LABEL_SECRET),
});
```

roomTelemetryfunctionSource ↗

Track rooms on a Yjs server.

declare function roomTelemetry(options: RoomTelemetryOptions): RoomTelemetry;
```ts
import { roomTelemetry } from '@docx-editor.dev/telemetry/server';
import { prometheusRegistry, metricsHandler } from '@docx-editor.dev/telemetry/prometheus';

const metrics = prometheusRegistry();
const rooms = roomTelemetry({ exporters: [metrics] });

const room = rooms.open(ydoc, { name, awareness });
await room.time('load', () => loadRoom(ydoc)); // the loaded state is not counted as edits
room.connected();
```

Interfaces (7)

AwarenessLikeinterfaceSource ↗

The part of a y-protocols Awareness the telemetry reads.

interface AwarenessLike
MemberTypeSummary
getStates
off
on

RoomHandleinterfaceSource ↗

One open room. Returned by [RoomTelemetry.open](RoomTelemetry.open).

interface RoomHandle
MemberTypeSummary
beginStart timing `operation` where `time` cannot wrap it; call the result once it settles.
closeStop tracking the room, for example when the server unloads it.
connected
disconnected
labelstringThe room's label, as [RoomTelemetryOptions.roomLabel](RoomTelemetryOptions.roomLabel) made it.
timeRun `operation` and record its duration and outcome. Rethrows its error.

RoomStatsinterfaceSource ↗

One room in [RoomTelemetry.list](RoomTelemetry.list).

interface RoomStats
MemberTypeSummary
ageSecondsnumber
blobBytesnumber | null
connectionsnumber
idleSecondsnumber
limitRationumber | nullThe largest share of any room limit in use, from 0 to 1.
nodesnumber | null
participantsnumberPeople present, from awareness states. A person with two tabs counts twice.
pendingBytesnumberBytes of updates applied since the room was last stored.
roomstring
stateBytesnumber | nullEncoded Yjs state at the last sample, or `null` before the first one.

RoomTelemetryinterfaceSource ↗

Room telemetry for one server process.

interface RoomTelemetry
MemberTypeSummary
beginStart timing an operation that has no room yet; call the result once it settles.
flush
listEvery open room, for an admin endpoint you protect.
migratedA room migration finished.
openStart tracking a loaded room. Pass the room's awareness to count people; without it the number of connections stands in.
refusedA message refused before it applied, such as `room-generation-changed`.
rejectedA connection refused at admission. `reason` can be a string or an error with `reason`.
sampleSample people, memory, and size now.
shutdownStop the timer, close every room, and shut the exporters down.
timeTime an operation that happens before the room is open, such as a load that fails.

RoomTelemetryOptionsinterfaceSource ↗

Options for [roomTelemetry](roomTelemetry).

interface RoomTelemetryOptions extends TelemetryPipelineOptions
MemberTypeSummary
measureState?booleanMeasure each room's encoded Yjs state. This encodes the room once per sample, and only when it changed since the last one. Defaults to `true`.
perRoomSeries?booleanAlso record each room's people and memory as gauges labeled `room`. Off by default: a label per room makes one series per room, and a large server has many rooms. A closed room's series is dropped. Only the default label or `hmacRoomLabel` is accepted, so no room name can become a label; any other `roomLabel` throws.
reasonOf?(error: unknown) => string | undefinedYour own reading of a refusal, for errors whose `reason` or `code` is free text. Return a common reason such as `unauthorized`, a collaboration failure code, or one of `reasons`; anything else is counted as `other`. Return undefined to use the default reading.
reasons?readonly string[]Refusal reasons to keep as metric labels. Compared without case and recorded in lowercase. Collaboration failure codes are always kept. Any other reason is counted as `other`, so a free-text error never becomes a time series.
roomLabel?(name: string) => stringHow a room appears in [RoomTelemetry.list](RoomTelemetry.list). Defaults to an index that is valid only inside this process, such as `room-3`. Use `(name) => name` to see real names, or [hmacRoomLabel](hmacRoomLabel) for a stable label that does not reveal the name.
sampleInterval?number | falseHow often to sample people, memory, and room size, in milliseconds. Defaults to 60 000. `false` turns the timer off; call `sample()` yourself, for example before each scrape.

TelemetryExporterinterfaceSource ↗

Where telemetry goes. Implement event, metric, or both.

An exporter never throws into the editor or the server: a failure is caught and reported once through onExporterError.

interface TelemetryExporter
```ts
const toMyLogger: TelemetryExporter = {
  name: 'my-logger',
  event: (event) => myLogger.warn(event.kind, { code: 'code' in event ? event.code : undefined }),
};
```
MemberTypeSummary
attachCalled once when a telemetry client or room server takes the exporter. An exporter that holds events, such as a batching one, reads `consented()` before it sends, so events queued before consent was withdrawn are discarded, not sent.
declareStart a series at zero before its first measurement. A scraper that sees a counter first at 1 cannot count that first increase, so the room server declares its known series when it starts. Optional: an exporter without it creates each series at its first measurement.
event
flushSend anything buffered. Called by `flush()` and when a page is hidden.
forgetDrop a series that no longer exists, such as a closed room's per-room gauge. Optional: an exporter without it keeps the series until it expires on its own.
metric
namestringShown in `onExporterError` reports.
shutdownFlush and release resources. Called by `shutdown()`.

TelemetryPipelineOptionsinterfaceSource ↗

Options shared by the editor client and the room server.

interface TelemetryPipelineOptions
MemberTypeSummary
exportersreadonly TelemetryExporter[]Where telemetry goes. Nothing is recorded or sent without at least one exporter.
metricLabels?readonly string[]Keys of your own attributes that may also label metrics, such as `tenant.tier` or `app.region`. Only for values with few distinct values: each value makes a new series. After [MAX_METRIC_LABEL_VALUES](MAX_METRIC_LABEL_VALUES) distinct values of one key, later values become `other`. Never list a user, document, or room id here; put those on events instead.
onExporterError?(error: unknown, exporter: string) => voidTold once per exporter when it throws. Defaults to one `console.warn`.
resource?AttributesValues added to every event, such as `{ 'service.name': 'contracts-app' }`. Metrics do not carry them: set resource attributes on your OpenTelemetry SDK or Prometheus target instead. You own these values: do not put a user, a document, or a room name here.

Type aliases (2)

RoomOperationtypeSource ↗

What a timed room operation is.

type RoomOperation = 'load' | 'store';

SettleOperationtypeSource ↗

Records a started operation's duration with its outcome. Later calls do nothing.

type SettleOperation = (outcome: 'success' | 'failure') => void;