Files
speckle-server/packages/server/observability/index.ts
T
Iain Sproat 5e4a6c5635 fix(server/prometheus): ensure consistent Prometheus registry is used (#4106)
* fix(server/prometheus): ensure consistent Prometheus registry is used
- there was a conflicting dependency between Metrics initialization and Modules initialization; resolved by separating registry initialization from metrics initialization
- pass in the registry to prevent implicit dependency being broken
- when registering a metric, first attempt to remove any of existing metrics with same name to prevent errors
- to prevent sneaky uses of the implicit registry, replace default import with explicit import so it is clearer when prometheusClient.registry is used

* Add tests for registering metrics
2025-03-04 08:41:53 +00:00

77 lines
2.3 KiB
TypeScript

/* istanbul ignore file */
import prometheusClient, { Registry } from 'prom-client'
import promBundle from 'express-prom-bundle'
import { initKnexPrometheusMetrics } from '@/observability/components/knex/knexMonitoring'
import { initHighFrequencyMonitoring } from '@/observability/components/highFrequencyMetrics/highfrequencyMonitoring'
import { highFrequencyMetricsCollectionPeriodMs } from '@/modules/shared/helpers/envHelper'
import { startupLogger as logger } from '@/observability/logging'
import type express from 'express'
import { getAllRegisteredDbClients } from '@/modules/multiregion/utils/dbSelector'
let prometheusInitialized = false
let prometheusRegistryInitialized = false
/**
* This has to be called prior to using Prometheus
* @returns The registry of Prometheus metrics which will be served
*/
export function initPrometheusRegistry() {
if (!prometheusRegistryInitialized) {
prometheusRegistryInitialized = true
prometheusClient.register.clear()
prometheusClient.register.setDefaultLabels({
project: 'speckle-server',
app: 'server'
})
}
return prometheusClient.register
}
export default async function (params: { app: express.Express; registry: Registry }) {
const { app, registry } = params
if (!prometheusInitialized) {
prometheusInitialized = true
prometheusClient.collectDefaultMetrics({
register: registry
})
const highfrequencyMonitoring = initHighFrequencyMonitoring({
registers: [registry],
collectionPeriodMilliseconds: highFrequencyMetricsCollectionPeriodMs(),
config: {
getDbClients: getAllRegisteredDbClients
}
})
highfrequencyMonitoring.start()
await initKnexPrometheusMetrics({
registers: [registry],
getAllDbClients: getAllRegisteredDbClients,
logger
})
app.use(
promBundle({
includeMethod: true,
includePath: true,
httpDurationMetricName: 'speckle_server_request_duration',
metricType: 'summary',
autoregister: false,
promRegistry: registry
})
)
}
// Expose prometheus metrics
app.get('/metrics', async (req, res, next) => {
try {
res.set('Content-Type', registry.contentType)
res.end(await registry.metrics())
} catch (ex: unknown) {
res.status(500).end(ex instanceof Error ? ex.message : `${ex}`)
next(ex)
}
})
}