Skip to content

Commit

Permalink
feat: metrics endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
NGPixel committed Nov 11, 2023
1 parent 4f4b05e commit dec9272
Show file tree
Hide file tree
Showing 15 changed files with 356 additions and 0 deletions.
2 changes: 2 additions & 0 deletions server/app/data.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ defaults:
host: ''
secure: true
verifySSL: true
metrics:
isEnabled: false
auth:
autoLogin: false
enforce2FA: false
Expand Down
15 changes: 15 additions & 0 deletions server/controllers/common.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,21 @@ export default function () {
return res.sendStatus(404)
})

/**
* Metrics (Prometheus)
*/
router.get('/metrics', async (req, res, next) => {
if (!WIKI.auth.checkAccess(req.user, ['read:metrics'])) {
return res.sendStatus(403)
}

if (WIKI.config.metrics.isEnabled) {
WIKI.metrics.render(res)
} else {
next()
}
})

// /**
// * View document / asset
// */
Expand Down
2 changes: 2 additions & 0 deletions server/core/kernel.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import db from './db.mjs'
import extensions from './extensions.mjs'
import scheduler from './scheduler.mjs'
import servers from './servers.mjs'
import metrics from './metrics.mjs'

let isShuttingDown = false

Expand Down Expand Up @@ -47,6 +48,7 @@ export default {
}
WIKI.extensions = extensions
WIKI.asar = asar
WIKI.metrics = await metrics.init()
} catch (err) {
WIKI.logger.error(err)
process.exit(1)
Expand Down
66 changes: 66 additions & 0 deletions server/core/metrics.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { collectDefaultMetrics, register, Gauge } from 'prom-client'
import { toSafeInteger } from 'lodash-es'

export default {
customMetrics: {},
async init () {
if (WIKI.config.metrics.isEnabled) {
WIKI.logger.info('Initializing metrics...')

register.setDefaultLabels({
WIKI_INSTANCE: WIKI.INSTANCE_ID
})

collectDefaultMetrics()

this.customMetrics.groupsTotal = new Gauge({
name: 'wiki_groups_total',
help: 'Total number of groups',
async collect() {
const total = await WIKI.db.groups.query().count('* as total').first()
this.set(toSafeInteger(total.total))
}
})

this.customMetrics.pagesTotal = new Gauge({
name: 'wiki_pages_total',
help: 'Total number of pages',
async collect() {
const total = await WIKI.db.pages.query().count('* as total').first()
this.set(toSafeInteger(total.total))
}
})

this.customMetrics.tagsTotal = new Gauge({
name: 'wiki_tags_total',
help: 'Total number of tags',
async collect() {
const total = await WIKI.db.tags.query().count('* as total').first()
this.set(toSafeInteger(total.total))
}
})

this.customMetrics.usersTotal = new Gauge({
name: 'wiki_users_total',
help: 'Total number of users',
async collect() {
const total = await WIKI.db.users.query().count('* as total').first()
this.set(toSafeInteger(total.total))
}
})
WIKI.logger.info('Metrics ready [ OK ]')
} else {
this.customMetrics = {}
register.clear()
}
return this
},
async render (res) {
try {
res.contentType(register.contentType)
res.send(await register.metrics())
} catch (err) {
res.status(500).end(err.message)
}
}
}
12 changes: 12 additions & 0 deletions server/db/migrations/3.0.0.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,12 @@ export async function up (knex) {
})

await knex('settings').insert([
{
key: 'api',
value: {
isEnabled: false
}
},
{
key: 'auth',
value: {
Expand Down Expand Up @@ -516,6 +522,12 @@ export async function up (knex) {
dkimPrivateKey: ''
}
},
{
key: 'metrics',
value: {
isEnabled: false
}
},
{
key: 'search',
value: {
Expand Down
24 changes: 24 additions & 0 deletions server/graph/resolvers/system.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ const getos = util.promisify(getosSync)

export default {
Query: {
/**
* Metrics Endpoint State
*/
metricsState () {
return WIKI.config.metrics?.isEnabled ?? false
},
/**
* System Flags
*/
Expand Down Expand Up @@ -281,6 +287,24 @@ export default {
return generateError(err)
}
},
/**
* Set Metrics endpoint state
*/
async setMetricsState (obj, args, context) {
try {
if (!WIKI.auth.checkAccess(context.req.user, ['manage:system'])) {
throw new Error('ERR_FORBIDDEN')
}

WIKI.config.metrics.isEnabled = args.enabled
await WIKI.configSvc.saveToDb(['metrics'])
return {
operation: generateSuccess('Metrics endpoint state changed successfully')
}
} catch (err) {
return generateError(err)
}
},
async updateSystemFlags (obj, args, context) {
try {
if (!WIKI.auth.checkAccess(context.req.user, ['manage:system'])) {
Expand Down
5 changes: 5 additions & 0 deletions server/graph/schemas/system.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# ===============================================

extend type Query {
metricsState: Boolean
systemExtensions: [SystemExtension]
systemFlags: JSON
systemInfo: SystemInfo
Expand Down Expand Up @@ -35,6 +36,10 @@ extend type Mutation {
id: UUID!
): DefaultResponse

setMetricsState(
enabled: Boolean!
): DefaultResponse

updateSystemSearch(
termHighlighting: Boolean
dictOverrides: String
Expand Down
10 changes: 10 additions & 0 deletions server/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,16 @@
"admin.mail.testRecipientHint": "Email address that should receive the test email.",
"admin.mail.testSend": "Send Email",
"admin.mail.title": "Mail",
"admin.metrics.auth": "You must provide the {headerName} header with a {tokenType} token. Generate an API key with the {permission} permission and use it as the token.",
"admin.metrics.disabled": "Endpoint Disabled",
"admin.metrics.enabled": "Endpoint Enabled",
"admin.metrics.endpoint": "The metrics endpoint can be scraped at {endpoint}",
"admin.metrics.endpointWarning": "Note that this override any page at this path.",
"admin.metrics.refreshSuccess": "Metrics endpoint state has been refreshed.",
"admin.metrics.subtitle": "Manage the Prometheus metrics endpoint",
"admin.metrics.title": "Metrics",
"admin.metrics.toggleStateDisabledSuccess": "Metrics endpoint disabled successfully.",
"admin.metrics.toggleStateEnabledSuccess": "Metrics endpoint enabled successfully.",
"admin.nav.modules": "Modules",
"admin.nav.site": "Site",
"admin.nav.system": "System",
Expand Down
1 change: 1 addition & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@
"pg-query-stream": "4.5.3",
"pg-tsquery": "8.4.1",
"poolifier": "2.7.5",
"prom-client": "15.0.0",
"punycode": "2.3.0",
"puppeteer-core": "21.4.0",
"qr-image": "3.2.0",
Expand Down
21 changes: 21 additions & 0 deletions server/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ux/public/_assets/icons/fluent-graph.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions ux/src/layouts/AdminLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ q-layout.admin(view='hHh Lpr lff')
q-item-section {{ t('admin.mail.title') }}
q-item-section(side)
status-light(:color='adminStore.info.isMailConfigured ? `positive` : `warning`', :pulse='!adminStore.info.isMailConfigured')
q-item(to='/_admin/metrics', v-ripple, active-class='bg-primary text-white')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-graph.svg')
q-item-section {{ t('admin.metrics.title') }}
q-item-section(side)
status-light(:color='adminStore.info.isMetricsEnabled ? `positive` : `negative`')
q-item(to='/_admin/rendering', v-ripple, active-class='bg-primary text-white')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-rich-text-converter.svg')
Expand Down
Loading

0 comments on commit dec9272

Please sign in to comment.