Prometheus Metrics
Exposes extension metrics in Prometheus text exposition format. No external dependencies. Zero overhead when not enabled - every metric call uses optional chaining on a nullish reference, so V8 short-circuits on a single pointer check.
Setup
// src/lib/server/metrics.js
import { createMetrics } from 'svelte-adapter-uws-extensions/prometheus';
export const metrics = createMetrics({
prefix: 'myapp_',
mapTopic: (topic) => topic.startsWith('room:') ? 'room:*' : topic
});
Pass the metrics object to any extension via its options:
import { metrics } from './metrics.js';
import { redis } from './redis.js';
import { createPresence } from 'svelte-adapter-uws-extensions/redis/presence';
import { createPubSubBus } from 'svelte-adapter-uws-extensions/redis/pubsub';
import { createReplay } from 'svelte-adapter-uws-extensions/redis/replay';
import { createRateLimit } from 'svelte-adapter-uws-extensions/redis/ratelimit';
import { createGroup } from 'svelte-adapter-uws-extensions/redis/groups';
import { createCursor } from 'svelte-adapter-uws-extensions/redis/cursor';
export const bus = createPubSubBus(redis, { metrics });
export const presence = createPresence(redis, { metrics, key: 'id' });
export const replay = createReplay(redis, { metrics });
export const limiter = createRateLimit(redis, { points: 10, interval: 1000, metrics });
export const lobby = createGroup(redis, 'lobby', { metrics });
export const cursors = createCursor(redis, { metrics });
Mounting the endpoint
With uWebSockets.js:
app.get('/metrics', metrics.handler);
Or use metrics.serialize() to get the raw text and serve it however you like.
Options
| Option | Default | Description |
|---|
prefix | '' | Prefix for all metric names |
mapTopic | identity | Map topic names to bounded label values for cardinality control |
defaultBuckets | [1, 5, 10, 25, 50, 100, 250, 500, 1000] | Default histogram buckets |
Metric names must match [a-zA-Z_:][a-zA-Z0-9_:]* and label names must match [a-zA-Z_][a-zA-Z0-9_]* (no __ prefix). Invalid names throw at registration time. HELP text containing backslashes or newlines is escaped automatically.
Cardinality control
If your topics are user-generated (e.g. room:abc123), per-topic labels will grow unbounded. Use mapTopic to collapse them:
const metrics = createMetrics({
mapTopic: (topic) => {
if (topic.startsWith('room:')) return 'room:*';
if (topic.startsWith('user:')) return 'user:*';
return topic;
}
});
Metrics reference
Pub/sub bus
| Metric | Type | Description |
|---|
pubsub_messages_relayed_total | counter | Messages relayed to Redis |
pubsub_messages_received_total | counter | Messages received from Redis |
pubsub_echo_suppressed_total | counter | Messages dropped by echo suppression |
pubsub_relay_batch_size | histogram | Relay batch size per flush |
Presence
| Metric | Type | Labels | Description |
|---|
presence_joins_total | counter | topic | Join events |
presence_leaves_total | counter | topic | Leave events |
presence_heartbeats_total | counter | | Heartbeat refresh cycles |
presence_stale_cleaned_total | counter | | Stale entries removed by cleanup |
Replay buffer (Redis and Postgres)
| Metric | Type | Labels | Description |
|---|
replay_publishes_total | counter | topic | Messages published |
replay_messages_replayed_total | counter | topic | Messages replayed to clients |
replay_truncations_total | counter | topic | Truncation events detected |
Rate limiting
| Metric | Type | Description |
|---|
ratelimit_allowed_total | counter | Requests allowed |
ratelimit_denied_total | counter | Requests denied |
ratelimit_bans_total | counter | Bans applied |
Broadcast groups
| Metric | Type | Labels | Description |
|---|
group_joins_total | counter | group | Join events |
group_joins_rejected_total | counter | group | Joins rejected (full) |
group_leaves_total | counter | group | Leave events |
group_publishes_total | counter | group | Publish events |
Cursor
| Metric | Type | Labels | Description |
|---|
cursor_updates_total | counter | topic | Cursor update calls |
cursor_broadcasts_total | counter | topic | Broadcasts actually sent |
cursor_throttled_total | counter | topic | Updates deferred by throttle |
LISTEN/NOTIFY bridge
| Metric | Type | Labels | Description |
|---|
notify_received_total | counter | channel | Notifications received |
notify_parse_errors_total | counter | channel | Parse failures |
notify_reconnects_total | counter | | Reconnect attempts |