openvibers/openrestream Commits

Loopback /metrics (Track O): openvibe-shared/metrics on openre-api (request rates and latencies, process metrics, release_info) plus openre_sessions{state} for starting and live ingest sessions

OP OpenVibers committed 24 Sept 2026 · f03a2c0a9a · 2 files, +10 −2


Changes

ddiff --git a/server/app.js b/server/app.js
iindex eacdd52..5d73c8f 100644
---- a/server/app.js
++++ b/server/app.js
@@@ -16,6 +16,11 @@ function createApp({ rt, auth, keys, log = console, fetchImpl }) {
const app = express();
app.disable('x-powered-by');
app.set('trust proxy', config.trustProxy);
++ // GET /metrics (loopback only, Track O): request rates and latencies, process metrics and live
++ // sessions by state, from openvibe-shared/metrics (mounted before every route).
++ const metrics = require('openvibe-shared/metrics').instrument(app, { service: 'openre' });
++ metrics.registry.gauge({ name: 'openre_sessions', help: 'Ingest sessions by state', labelNames: ['state'],
++ collect: () => db.prepare("SELECT state, count(*) AS n FROM ingest_sessions WHERE state IN ('starting','live') GROUP BY state").all().map((r) => ({ labels: { state: r.state }, value: r.n })) });
app.use(contracts.http.middleware());
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
@@@ -41,10 +46,11 @@ function createApp({ rt, auth, keys, log = console, fetchImpl }) {
// A release directory (/opt/openre.stream/releases/<sha>) is named by its commit and has no .git of its own.
const releaseRoot = require('path').join(__dirname, '..');
const commit = [process.env.RELEASE_COMMIT, config.release, require('path').basename(releaseRoot)].find((v) => /^[0-9a-f]{7,40}$/.test(String(v || '')));
-- require('openvibe-shared/release').createRelease({
++ const release = require('openvibe-shared/release').createRelease({
service: 'openre', root: releaseRoot, packages: ['openvibe-shared', 'openvibe-sdk', 'openvibe-contracts'],
env: commit ? { ...process.env, RELEASE_COMMIT: commit } : process.env,
-- }).mount(app);
++ });
++ release.mount(app);
// Ready = the database answers and the Network key is loaded. Worker and coordinator state is
// reported, not required: the API serves reads while a worker generation rolls over.
ddiff --git a/test/drill-mode.test.js b/test/drill-mode.test.js
iindex 3ee3bbd..817386d 100644
---- a/test/drill-mode.test.js
++++ b/test/drill-mode.test.js
@@@ -69,6 +69,8 @@ t('openre-api in a drill: reads work and match a normal instance; writes, /play/
const rel = await request(normal.base, 'GET', '/release.json');
assert.strictEqual(rel.status, 200, 'the release manifest (D43) is served');
assert.strictEqual((rel.json || JSON.parse(rel.text || rel.body || '{}')).service, 'openre');
++ const met = await request(normal.base, 'GET', '/metrics');
++ assert.strictEqual(met.status, 200, 'loopback /metrics (Track O)');
const health = await request(normal.base, 'GET', '/api/health');
const list = await request(normal.base, 'GET', '/api/v1/streams', { token: reader });
const outboxBefore = normal.rt.db.prepare('SELECT count(*) AS n FROM event_outbox').get().n;