mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-02 16:15:39 +00:00
- `/api/metrics` in Prometheus text format, off until `METRICS_TOKEN` or `METRICS_TOKEN_FILE` is set (404 before, 401 without the bearer token): requests per route group with status class and latency, sign-in outcomes, calls to Stalwart, WOPI, translation, the plugin proxy and OAuth, browser errors, errors today, process memory, event-loop lag, uptime and build info. Labels come from fixed lists. Each replica serves its own. - `/api/health/live` answers while the process runs; `/api/health/ready` checks the config, the shared store, writable data dirs and Stalwart's session resource. `/api/health` and `/api/ready` are unchanged. - the Docker image gets a HEALTHCHECK on the live endpoint - adds prom-client, and the OpenTelemetry packages the tracing step loads on demand
40 lines
1.5 KiB
TypeScript
40 lines
1.5 KiB
TypeScript
import diagnostics from 'node:diagnostics_channel';
|
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
import { metricsEnabled, observeRequest } from './index';
|
|
|
|
/**
|
|
* Counts every request this process serves, through Node's own HTTP
|
|
* diagnostics channels: nothing is wrapped or patched, and the proxy's
|
|
* matcher doesn't matter. Installed once at startup when the metrics
|
|
* endpoint is on (instrumentation.node.ts).
|
|
*/
|
|
|
|
interface HttpState {
|
|
installed: boolean;
|
|
started: WeakMap<IncomingMessage, number>;
|
|
}
|
|
|
|
const KEY = Symbol.for('bulwark.metrics.http');
|
|
function state(): HttpState {
|
|
const g = globalThis as typeof globalThis & { [KEY]?: HttpState };
|
|
return (g[KEY] ??= { installed: false, started: new WeakMap() });
|
|
}
|
|
|
|
export function installHttpMetrics(basePath = process.env.NEXT_PUBLIC_BASE_PATH || ''): boolean {
|
|
const s = state();
|
|
if (s.installed || !metricsEnabled()) return false;
|
|
s.installed = true;
|
|
diagnostics.subscribe('http.server.request.start', (message) => {
|
|
const { request } = message as { request: IncomingMessage };
|
|
s.started.set(request, performance.now());
|
|
});
|
|
diagnostics.subscribe('http.server.response.finish', (message) => {
|
|
const { request, response } = message as { request: IncomingMessage; response: ServerResponse };
|
|
const start = s.started.get(request);
|
|
if (start === undefined) return;
|
|
s.started.delete(request);
|
|
observeRequest(request.url ?? '/', request.method, response.statusCode, (performance.now() - start) / 1000, basePath);
|
|
});
|
|
return true;
|
|
}
|