* docs: refresh docs for 3.2.0 — correct stale content, document recent features The hand-maintained VitePress docs under doc/ had drifted behind a lot of recent work. They are authored prose (not generated from the OpenAPI spec), so they need manual upkeep. This pass corrects content that was actively wrong and documents features shipped since they were last touched. Corrections (was wrong / misleading): - cli.md: every command used `node bin/foo.js`, but the scripts are TypeScript run via pnpm — copy-paste failed. Rewrote to `pnpm run --filter bin <script>`, documented ~13 previously-undocumented operator tools, and split running-vs-stopped requirements. Registered the missing `compactStalePads` script in bin/package.json so the documented invocation actually works. - stats.md: described a pre-Prometheus world. Rewrote for the gated `/stats` (JSON) and `/stats/prometheus` endpoints, the live metric set, the opt-in `scalingDiveMetrics` instruments (#7756), and `measured-core`. - admin/updates.md: removed three false "SMTP not yet wired" claims (it is, via nodemailer + the `mail.*` block), documented the `node-engine-mismatch` preflight check and the rollback/preflight failure emails, and stripped obsolete "PR 1 / PR 2" staging language now that all tiers ship. - api/http_api.md: added the undocumented `anonymizeAuthor` (GDPR Art. 17) call, fixed copyPad/movePad version annotations (1.2.8 → 1.2.9), corrected getPadID's param name (readOnlyID → roID), and dropped a reference to a non-existent `getEtherpad` API call. - skins.md: colibris is the current default, not an "experimental" skin for a future 2.0. - localization.md: bare `window._('key')` is unbound and returns undefined; recommend `window.html10n.get(...)` / data-l10n-id instead. - README.md: bumped the v2.2.5 upgrade example to v3.2.0; fixed a docker.adoc link to docker.md. - docker.md: added MAIL_*, ENABLE_METRICS, GDPR_AUTHOR_ERASURE_ENABLED, PRIVACY_BANNER_*, PUBLIC_URL, AUTHENTICATION_METHOD, ENABLE_DARK_MODE, ENABLE_PAD_WIDE_SETTINGS; fixed the SOCKETIO_MAX_HTTP_BUFFER_SIZE default (50000 → 1000000). New documentation: - configuration.md (new): how settings + `${VAR:default}` substitution work, trustProxy, and — the previously-undocumented feature — running under a subpath/ingress via x-proxy-path / X-Forwarded-Prefix / X-Ingress-Path, with the sanitizer rules and Traefik/NGINX examples. Wired into the VitePress sidebar and the index hero. - hooks_server-side.md: ccRegisterBlockElements (the server-side companion plugin authors miss), exportConvert, exportHTMLSend, createServer, restartServer, and clientReady (marked deprecated). - hooks_client-side.md: aceDrop, acePaste, handleClientTimesliderMessage_<name>. VitePress build passes. The legacy .adoc set was intentionally left in place — it still feeds the per-version doc archives published to ether.github.com at release time (bin/release.ts), so it is not dead and is out of scope here. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: address Qodo review — drop .js invocations from configuration.md and CLI help - configuration.md: the settings-override example referenced a nonexistent `node src/node/server.js`. Use the supported launcher instead (`bin/run.sh -s <file>`), and note the runtime is server.ts via tsx. - compactStalePads.ts / compactPad.ts / compactAllPads.ts: their header comments and runtime usage output still printed `node bin/*.js`, which points at files that don't exist. Switched to the documented `pnpm run --filter bin <script>` form so the --help text matches the docs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.3 KiB
Statistics and metrics
Etherpad tracks runtime statistics about the edit machinery, the database layer, and the Node.js process, and can expose them over HTTP for monitoring.
There are two endpoints:
GET /stats— a JSON dump of the internalmeasured-corecollection.GET /stats/prometheus— the same kind of data (plus process/runtime metrics) in the Prometheus text exposition format.
Enabling the endpoints
Both endpoints are gated behind the enableMetrics setting, which defaults to
true:
{
"enableMetrics": true
}
When enableMetrics is false the routes are not registered at all — a
request to /stats or /stats/prometheus returns the normal 404 handling, not
an empty response. The admin-panel statistics view is unaffected by this
setting.
GET /stats (JSON)
Returns the current snapshot of the measured-core collection as JSON. The
following metrics are collected:
| Metric | Type | Meaning |
|---|---|---|
totalUsers |
gauge | Number of users currently connected across all pads. |
activePads |
gauge | Number of pads with at least one connected user. |
connects |
meter | Rate of new client connections. |
disconnects |
meter | Rate of client disconnections. |
rateLimited |
meter | Rate of messages dropped by the per-connection rate limiter. |
pendingEdits |
counter | Edits received but not yet fully processed. |
edits |
timer | Time taken to process an incoming USER_CHANGES edit (full handler span). |
failedChangesets |
meter | Rate of changesets that failed to apply. |
httpRequests |
timer | Duration of HTTP requests served by Express. |
http500 |
meter | Rate of HTTP 500 responses. |
memoryUsage |
gauge | Process resident set size (process.memoryUsage().rss). |
memoryUsageHeap |
gauge | Process heap usage (process.memoryUsage().heapUsed). |
lastDisconnect |
gauge | Timestamp (ms) of the most recent socket disconnect. |
ueberdb_* |
gauge | One gauge per ueberDB database statistic, e.g. read/write counts and timings (ueberdb_reads, ueberdb_writes, …). The exact set depends on the configured database driver. |
Under the hood these are provided by
measured-core.
To read or extend them from a plugin, require the shared collection:
const stats = require('ep_etherpad-lite/node/stats');
// stats is a measured-core Collection
stats.counter('my_plugin_events').inc();
console.log(stats.toJSON());
GET /stats/prometheus (Prometheus exposition format)
Served from a dedicated prom-client
registry. This is the endpoint you point a Prometheus scraper at.
Metrics exposed by default
| Metric | Type | Labels | Meaning |
|---|---|---|---|
etherpad_total_users |
gauge | — | Total number of connected users. |
etherpad_active_pads |
gauge | — | Total number of active pads. |
ueberdb_stats |
gauge | type |
ueberDB statistics, one series per numeric ueberDB metric (the metric name is carried in the type label). |
In addition, the registry calls prom-client's collectDefaultMetrics(), so
the standard Node.js / process metrics are also exposed, including (names as
emitted by prom-client):
process_cpu_user_seconds_total,process_cpu_system_seconds_total,process_cpu_seconds_totalprocess_resident_memory_bytes,process_heap_bytes,process_virtual_memory_bytesprocess_open_fds,process_max_fdsprocess_start_time_secondsnodejs_eventloop_lag_secondsand thenodejs_eventloop_lag_*familynodejs_active_handles,nodejs_active_requests,nodejs_active_resources(and their_totalvariants)nodejs_heap_size_total_bytes,nodejs_heap_size_used_bytes,nodejs_external_memory_bytes,nodejs_heap_space_size_*_bytesnodejs_gc_duration_secondsnodejs_version_info
The exact default-metric set is determined by prom-client and the Node.js
version, not by Etherpad.
Opt-in scaling-dive metrics (scalingDiveMetrics)
A second, more detailed instrument set was added for the scaling investigation
(PR #7756). It is gated behind the scalingDiveMetrics setting, which defaults
to false:
{
"scalingDiveMetrics": false
}
When the flag is off, these metrics are never registered and their recording
helpers short-circuit to no-ops, so production deployments pay nothing for the
instrumentation. When enabled, the following are added to the
/stats/prometheus output:
| Metric | Type | Labels | Meaning |
|---|---|---|---|
etherpad_changeset_apply_duration_seconds |
histogram | — | Time spent applying an incoming USER_CHANGES message on the server (apply path only; excludes fan-out to other clients). Buckets: 0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 5 seconds. |
etherpad_socket_emits_total |
counter | type |
Number of socket.io broadcast emits, bucketed by message type. |
etherpad_pad_users |
gauge | padId |
Active users connected to each pad, keyed by pad id. |
Cardinality caution. The scaling-dive metrics carry high-cardinality labels:
etherpad_pad_usersadds one time series per active pad (padIdlabel). On instances with many pads this can produce a large number of series; stale labels are reset on each scrape so drained pads drop out.etherpad_socket_emits_totaluses thetypelabel. To keep cardinality bounded, only a fixed allowlist of known message types is reported; any other (or missing) type is rolled into a singleotherbucket, so a misbehaving plugin or API caller cannot explode the label space.
Enable scalingDiveMetrics for targeted load-testing or capacity
investigations, not as a permanent production default.
Scraping with Prometheus
Add a scrape job pointing at the /stats/prometheus endpoint, for example:
scrape_configs:
- job_name: etherpad
metrics_path: /stats/prometheus
static_configs:
- targets: ['localhost:9001']
Make sure enableMetrics is true (the default) so the endpoint exists. If
your instance is reachable from untrusted networks, restrict access to
/stats and /stats/prometheus at your reverse proxy, since they expose
operational details about the deployment.